diff --git a/Documentation/ABI/stable/sysfs-driver-mlxreg-io b/Documentation/ABI/stable/sysfs-driver-mlxreg-io index b312242d4f40..af0cbf143c48 100644 --- a/Documentation/ABI/stable/sysfs-driver-mlxreg-io +++ b/Documentation/ABI/stable/sysfs-driver-mlxreg-io @@ -1,7 +1,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic_health Date: June 2018 KernelVersion: 4.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file shows ASIC health status. The possible values are: 0 - health failed, 2 - health OK, 3 - ASIC in booting state. @@ -11,7 +11,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld1_version What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld2_version Date: June 2018 KernelVersion: 4.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show with which CPLD versions have been burned on carrier and switch boards. @@ -20,7 +20,7 @@ Description: These files show with which CPLD versions have been burned What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/fan_dir Date: December 2018 KernelVersion: 5.0 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file shows the system fans direction: forward direction - relevant bit is set 0; reversed direction - relevant bit is set 1. @@ -30,7 +30,7 @@ Description: This file shows the system fans direction: What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld3_version Date: November 2018 KernelVersion: 5.0 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show with which CPLD versions have been burned on LED or Gearbox board. @@ -39,7 +39,7 @@ Description: These files show with which CPLD versions have been burned What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/jtag_enable Date: November 2018 KernelVersion: 5.0 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files enable and disable the access to the JTAG domain. By default access to the JTAG domain is disabled. @@ -48,7 +48,7 @@ Description: These files enable and disable the access to the JTAG domain. What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/select_iio Date: June 2018 KernelVersion: 4.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file allows iio devices selection. Attribute select_iio can be written with 0 or with 1. It @@ -62,7 +62,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/psu1_on /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pwr_down Date: June 2018 KernelVersion: 4.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files allow asserting system power cycling, switching power supply units on and off and system's main power domain shutdown. @@ -89,7 +89,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_short_pb What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sw_reset Date: June 2018 KernelVersion: 4.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show the system reset cause, as following: power auxiliary outage or power refresh, ASIC thermal shutdown, halt, hotswap, watchdog, firmware reset, long press power button, @@ -106,7 +106,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_system What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_voltmon_upgrade_fail Date: November 2018 KernelVersion: 5.0 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show the system reset cause, as following: ComEx power fail, reset from ComEx, system platform reset, reset due to voltage monitor devices upgrade failure, @@ -119,7 +119,7 @@ Description: These files show the system reset cause, as following: ComEx What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld4_version Date: November 2018 KernelVersion: 5.0 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show with which CPLD versions have been burned on LED board. @@ -133,7 +133,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sff_wd What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_swb_wd Date: June 2019 KernelVersion: 5.3 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show the system reset cause, as following: COMEX thermal shutdown; wathchdog power off or reset was derived by one of the next components: COMEX, switch board or by Small Form @@ -148,7 +148,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/config1 What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/config2 Date: January 2020 KernelVersion: 5.6 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show system static topology identification like system's static I2C topology, number and type of FPGA devices within the system and so on. @@ -161,7 +161,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_soc What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/reset_sw_pwr_off Date: January 2020 KernelVersion: 5.6 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show the system reset causes, as following: reset due to AC power failure, reset invoked from software by assertion reset signal through CPLD. reset caused by signal @@ -173,7 +173,7 @@ Description: These files show the system reset causes, as following: reset What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/pcie_asic_reset_dis Date: January 2020 KernelVersion: 5.6 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file allows to retain ASIC up during PCIe root complex reset, when attribute is set 1. @@ -182,7 +182,7 @@ Description: This file allows to retain ASIC up during PCIe root complex What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/vpd_wp Date: January 2020 KernelVersion: 5.6 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file allows to overwrite system VPD hardware write protection when attribute is set 1. @@ -191,7 +191,7 @@ Description: This file allows to overwrite system VPD hardware write What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/voltreg_update_status Date: January 2020 KernelVersion: 5.6 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file exposes the configuration update status of burnable voltage regulator devices. The status values are as following: 0 - OK; 1 - CRC failure; 2 = I2C failure; 3 - in progress. @@ -201,7 +201,7 @@ Description: This file exposes the configuration update status of burnable What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/ufm_version Date: January 2020 KernelVersion: 5.6 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file exposes the firmware version of burnable voltage regulator devices. @@ -217,7 +217,7 @@ What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld3_version_min What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/cpld4_version_min Date: July 2020 KernelVersion: 5.9 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: These files show with which CPLD part numbers and minor versions have been burned CPLD devices equipped on a system. @@ -471,7 +471,7 @@ Description: These files provide the maximum powered required for line card What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/phy_reset Date: May 2022 KernelVersion: 5.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file allows to reset PHY 88E1548 when attribute is set 0 due to some abnormal PHY behavior. Expected behavior: @@ -483,7 +483,7 @@ Description: This file allows to reset PHY 88E1548 when attribute is set 0 What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/mac_reset Date: May 2022 KernelVersion: 5.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file allows to reset ASIC MT52132 when attribute is set 0 due to some abnormal ASIC behavior. Expected behavior: @@ -495,7 +495,7 @@ Description: This file allows to reset ASIC MT52132 when attribute is set 0 What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/qsfp_pwr_good Date: May 2022 KernelVersion: 5.19 -Contact: Vadim Pasternak +Contact: Vadim Pasternak Description: This file shows QSFP ports power status. The value is set to 0 when one of any QSFP ports is plugged. The value is set to 1 when there are no any QSFP ports are plugged. @@ -503,3 +503,42 @@ Description: This file shows QSFP ports power status. The value is set to 0 0 - Power good, 1 - Not power good. The files are read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic2_health +Date: July 2022 +KernelVersion: 5.20 +Contact: Vadim Pasternak +Description: This file shows 2-nd ASIC health status. The possible values are: + 0 - health failed, 2 - health OK, 3 - ASIC in booting state. + + The file is read only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic_reset +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/asic2_reset +Date: July 2022 +KernelVersion: 5.20 +Contact: Vadim Pasternak +Description: These files allow to each of ASICs by writing 1. + + The files are write only. + + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/comm_chnl_ready +Date: July 2022 +KernelVersion: 5.20 +Contact: Vadim Pasternak +Description: This file is used to indicate remote end (for example BMC) that system + host CPU is ready for sending telemetry data to remote end. + For indication the file should be written 1. + + The file is write only. + +What: /sys/devices/platform/mlxplat/mlxreg-io/hwmon/hwmon*/config3 +Date: January 2020 +KernelVersion: 5.6 +Contact: Vadim Pasternak +Description: The file indicates COME module hardware configuration. + The value is pushed by hardware through GPIO pins. + The purpose is to expose some minor BOM changes for the same system SKU. + + The file is read only. diff --git a/Documentation/ABI/stable/sysfs-module b/Documentation/ABI/stable/sysfs-module index 560b4a3278df..41b1f16e8795 100644 --- a/Documentation/ABI/stable/sysfs-module +++ b/Documentation/ABI/stable/sysfs-module @@ -38,7 +38,7 @@ What: /sys/module//srcversion Date: Jun 2005 Description: If the module source has MODULE_VERSION, this file will contain - the checksum of the the source code. + the checksum of the source code. What: /sys/module//version Date: Jun 2005 diff --git a/Documentation/ABI/testing/configfs-usb-gadget-mass-storage b/Documentation/ABI/testing/configfs-usb-gadget-mass-storage index c86b63a7bb43..fc0328069267 100644 --- a/Documentation/ABI/testing/configfs-usb-gadget-mass-storage +++ b/Documentation/ABI/testing/configfs-usb-gadget-mass-storage @@ -19,7 +19,7 @@ KernelVersion: 3.13 Description: The attributes: - =========== ============================================== + ============ ============================================== file The path to the backing file for the LUN. Required if LUN is not marked as removable. ro Flag specifying access to the LUN shall be @@ -32,4 +32,10 @@ Description: being a CD-ROM. nofua Flag specifying that FUA flag in SCSI WRITE(10,12) - =========== ============================================== + forced_eject This write-only file is useful only when + the function is active. It causes the backing + file to be forcibly detached from the LUN, + regardless of whether the host has allowed it. + Any non-zero number of bytes written will + result in ejection. + ============ ============================================== diff --git a/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub b/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub new file mode 100644 index 000000000000..42deb0552065 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub @@ -0,0 +1,8 @@ +What: /sys/bus/platform/devices//always_powered_in_suspend +Date: June 2022 +KernelVersion: 5.20 +Contact: Matthias Kaehlcke + linux-usb@vger.kernel.org +Description: + (RW) Controls whether the USB hub remains always powered + during system suspend or not. \ No newline at end of file diff --git a/Documentation/ABI/testing/sysfs-bus-surface_aggregator-tabletsw b/Documentation/ABI/testing/sysfs-bus-surface_aggregator-tabletsw new file mode 100644 index 000000000000..74cd9d754e60 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-bus-surface_aggregator-tabletsw @@ -0,0 +1,57 @@ +What: /sys/bus/surface_aggregator/devices/01:0e:01:00:01/state +Date: July 2022 +KernelVersion: 5.20 +Contact: Maximilian Luz +Description: + This attribute returns a string with the current type-cover + or device posture, as indicated by the embedded controller. + Currently returned posture states are: + + - "disconnected": The type-cover has been disconnected. + + - "closed": The type-cover has been folded closed and lies on + top of the display. + + - "laptop": The type-cover is open and in laptop-mode, i.e., + ready for normal use. + + - "folded-canvas": The type-cover has been folded back + part-ways, but does not lie flush with the back side of the + device. In general, this means that the kick-stand is used + and extended atop of the cover. + + - "folded-back": The type cover has been fully folded back and + lies flush with the back side of the device. + + - "": The current state is unknown to the driver, for + example due to newer as-of-yet unsupported hardware. + + New states may be introduced with new hardware. Users therefore + must not rely on this list of states being exhaustive and + gracefully handle unknown states. + +What: /sys/bus/surface_aggregator/devices/01:26:01:00:01/state +Date: July 2022 +KernelVersion: 5.20 +Contact: Maximilian Luz +Description: + This attribute returns a string with the current device posture, as indicated by the embedded controller. Currently + returned posture states are: + + - "closed": The lid of the device is closed. + + - "laptop": The lid of the device is opened and the device + operates as a normal laptop. + + - "slate": The screen covers the keyboard or has been flipped + back and the device operates mainly based on touch input. + + - "tablet": The device operates as tablet and exclusively + relies on touch input (or external peripherals). + + - "": The current state is unknown to the driver, for + example due to newer as-of-yet unsupported hardware. + + New states may be introduced with new hardware. Users therefore + must not rely on this list of states being exhaustive and + gracefully handle unknown states. diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb index 7efe31ed3a25..568103d3376e 100644 --- a/Documentation/ABI/testing/sysfs-bus-usb +++ b/Documentation/ABI/testing/sysfs-bus-usb @@ -253,6 +253,17 @@ Description: only if the system firmware is capable of describing the connection between a port and its connector. +What: /sys/bus/usb/devices/...//port/disable +Date: June 2022 +Contact: Michael Grzeschik +Description: + This file controls the state of a USB port, including + Vbus power output (but only on hubs that support + power switching -- most hubs don't support it). If + a port is disabled, the port is unusable: Devices + attached to the port will not be detected, initialized, + or enumerated. + What: /sys/bus/usb/devices/.../power/usb2_lpm_l1_timeout Date: May 2013 Contact: Mathias Nyman diff --git a/Documentation/ABI/testing/sysfs-class-pwm b/Documentation/ABI/testing/sysfs-class-pwm index 3d65285bcd5f..0638c94d01ef 100644 --- a/Documentation/ABI/testing/sysfs-class-pwm +++ b/Documentation/ABI/testing/sysfs-class-pwm @@ -81,7 +81,7 @@ Description: What: /sys/class/pwm/pwmchip/pwmX/capture Date: June 2016 KernelVersion: 4.8 -Contact: Lee Jones +Contact: Lee Jones Description: Capture information about a PWM signal. The output format is a pair unsigned integers (period and duty cycle), separated by a diff --git a/Documentation/ABI/testing/sysfs-class-rtrs-client b/Documentation/ABI/testing/sysfs-class-rtrs-client index 49a4157c7bf1..fecc59d1b96f 100644 --- a/Documentation/ABI/testing/sysfs-class-rtrs-client +++ b/Documentation/ABI/testing/sysfs-class-rtrs-client @@ -78,7 +78,7 @@ What: /sys/class/rtrs-client//paths//hca_name Date: Feb 2020 KernelVersion: 5.7 Contact: Jack Wang Danil Kipnis -Description: RO, Contains the the name of HCA the connection established on. +Description: RO, Contains the name of HCA the connection established on. What: /sys/class/rtrs-client//paths//hca_port Date: Feb 2020 diff --git a/Documentation/ABI/testing/sysfs-class-rtrs-server b/Documentation/ABI/testing/sysfs-class-rtrs-server index 3b6d5b067df0..b08601d80409 100644 --- a/Documentation/ABI/testing/sysfs-class-rtrs-server +++ b/Documentation/ABI/testing/sysfs-class-rtrs-server @@ -24,7 +24,7 @@ What: /sys/class/rtrs-server//paths//hca_name Date: Feb 2020 KernelVersion: 5.7 Contact: Jack Wang Danil Kipnis -Description: RO, Contains the the name of HCA the connection established on. +Description: RO, Contains the name of HCA the connection established on. What: /sys/class/rtrs-server//paths//hca_port Date: Feb 2020 diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 75088ecad202..281b995beb05 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -141,6 +141,14 @@ Description: - "reverse": CC2 orientation - "unknown": Orientation cannot be determined. +What: /sys/class/typec//select_usb_power_delivery +Date: May 2022 +Contact: Heikki Krogerus +Description: + Lists the USB Power Delivery Capabilities that the port can + advertise to the partner. The currently used capabilities are in + brackets. Selection happens by writing to the file. + USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) What: /sys/class/typec/-partner/accessory_mode diff --git a/Documentation/ABI/testing/sysfs-class-usb_power_delivery b/Documentation/ABI/testing/sysfs-class-usb_power_delivery new file mode 100644 index 000000000000..ce2b1b563cb3 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-usb_power_delivery @@ -0,0 +1,240 @@ +What: /sys/class/usb_power_delivery +Date: May 2022 +Contact: Heikki Krogerus +Description: + Directory for USB Power Delivery devices. + +What: /sys/class/usb_power_delivery/.../revision +Date: May 2022 +Contact: Heikki Krogerus +Description: + File showing the USB Power Delivery Specification Revision used + in communication. + +What: /sys/class/usb_power_delivery/.../version +Date: May 2022 +Contact: Heikki Krogerus +Description: + This is an optional attribute file showing the version of the + specific revision of the USB Power Delivery Specification. In + most cases the specification version is not known and the file + is not available. + +What: /sys/class/usb_power_delivery/.../source-capabilities +Date: May 2022 +Contact: Heikki Krogerus +Description: + The source capabilities message "Source_Capabilities" contains a + set of Power Data Objects (PDO), each representing a type of + power supply. The order of the PDO objects is defined in the USB + Power Delivery Specification. Each PDO - power supply - will + have its own device, and the PDO device name will start with the + object position number as the first character followed by the + power supply type name (":" as delimiter). + + /sys/class/usb_power_delivery/.../source_capabilities/: + +What: /sys/class/usb_power_delivery/.../sink-capabilities +Date: May 2022 +Contact: Heikki Krogerus +Description: + The sink capability message "Sink_Capabilities" contains a set + of Power Data Objects (PDO) just like with source capabilities, + but instead of describing the power capabilities, these objects + describe the power requirements. + + The order of the objects in the sink capability message is the + same as with the source capabilities message. + +Fixed Supplies + +What: /sys/class/usb_power_delivery/...//:fixed_supply +Date: May 2022 +Contact: Heikki Krogerus +Description: + Devices containing the attributes (the bit fields) defined for + Fixed Supplies. + + The device "1:fixed_supply" is special. USB Power Delivery + Specification dictates that the first PDO (at object position + 1), and the only mandatory PDO, is always the vSafe5V Fixed + Supply Object. vSafe5V Object has additional fields defined for + it that the other Fixed Supply Objects do not have and that are + related to the USB capabilities rather than power capabilities. + +What: /sys/class/usb_power_delivery/...//1:fixed_supply/dual_role_power +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file contains boolean value that tells does the device + support both source and sink power roles. + +What: /sys/class/usb_power_delivery/...//1:fixed_supply/usb_suspend_supported +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file shows the value of the USB Suspend Supported bit in + vSafe5V Fixed Supply Object. If the bit is set then the device + will follow the USB 2.0 and USB 3.2 rules for suspend and + resume. + +What: /sys/class/usb_power_delivery/...//1:fixed_supply/unconstrained_power +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file shows the value of the Unconstrained Power bit in + vSafe5V Fixed Supply Object. The bit is set when an external + source of power, powerful enough to power the entire system on + its own, is available for the device. + +What: /sys/class/usb_power_delivery/...//1:fixed_supply/usb_communication_capable +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file shows the value of the USB Communication Capable bit in + vSafe5V Fixed Supply Object. + +What: /sys/class/usb_power_delivery/...//1:fixed_supply/dual_role_data +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file shows the value of the Dual-Role Data bit in vSafe5V + Fixed Supply Object. Dual role data means ability act as both + USB host and USB device. + +What: /sys/class/usb_power_delivery/...//1:fixed_supply/unchunked_extended_messages_supported +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file shows the value of the Unchunked Extended Messages + Supported bit in vSafe5V Fixed Supply Object. + +What: /sys/class/usb_power_delivery/...//:fixed_supply/voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + The voltage the supply supports in millivolts. + +What: /sys/class/usb_power_delivery/.../source-capabilities/:fixed_supply/maximum_current +Date: May 2022 +Contact: Heikki Krogerus +Description: + Maximum current of the fixed source supply in milliamperes. + +What: /sys/class/usb_power_delivery/.../sink-capabilities/:fixed_supply/operational_current +Date: May 2022 +Contact: Heikki Krogerus +Description: + Operational current of the sink in milliamperes. + +What: /sys/class/usb_power_delivery/.../sink-capabilities/:fixed_supply/fast_role_swap_current +Date: May 2022 +Contact: Heikki Krogerus +Description: + This file contains the value of the "Fast Role Swap USB Type-C + Current" field that tells the current level the sink requires + after a Fast Role Swap. + 0 - Fast Swap not supported" + 1 - Default USB Power" + 2 - 1.5A@5V" + 3 - 3.0A@5V" + +Variable Supplies + +What: /sys/class/usb_power_delivery/...//:variable_supply +Date: May 2022 +Contact: Heikki Krogerus +Description: + Variable Power Supply PDO. + +What: /sys/class/usb_power_delivery/...//:variable_supply/maximum_voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + Maximum Voltage in millivolts. + +What: /sys/class/usb_power_delivery/...//:variable_supply/minimum_voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + Minimum Voltage in millivolts. + +What: /sys/class/usb_power_delivery/.../source-capabilities/:variable_supply/maximum_current +Date: May 2022 +Contact: Heikki Krogerus +Description: + The maximum current in milliamperes that the source can supply + at the given Voltage range. + +What: /sys/class/usb_power_delivery/.../sink-capabilities/:variable_supply/operational_current +Date: May 2022 +Contact: Heikki Krogerus +Description: + The operational current in milliamperes that the sink requires + at the given Voltage range. + +Battery Supplies + +What: /sys/class/usb_power_delivery/...//:battery +Date: May 2022 +Contact: Heikki Krogerus +Description: + Battery PDO. + +What: /sys/class/usb_power_delivery/...//:battery/maximum_voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + Maximum Voltage in millivolts. + +What: /sys/class/usb_power_delivery/...//:battery/minimum_voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + Minimum Voltage in millivolts. + +What: /sys/class/usb_power_delivery/.../source-capabilities/:battery/maximum_power +Date: May 2022 +Contact: Heikki Krogerus +Description: + Maximum allowable Power in milliwatts. + +What: /sys/class/usb_power_delivery/.../sink-capabilities/:battery/operational_power +Date: May 2022 +Contact: Heikki Krogerus +Description: + The operational power that the sink requires at the given + voltage range. + +Standard Power Range (SPR) Programmable Power Supplies + +What: /sys/class/usb_power_delivery/...//:programmable_supply +Date: May 2022 +Contact: Heikki Krogerus +Description: + Programmable Power Supply (PPS) Augmented PDO (APDO). + +What: /sys/class/usb_power_delivery/...//:programmable_supply/maximum_voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + Maximum Voltage in millivolts. + +What: /sys/class/usb_power_delivery/...//:programmable_supply/minimum_voltage +Date: May 2022 +Contact: Heikki Krogerus +Description: + Minimum Voltage in millivolts. + +What: /sys/class/usb_power_delivery/...//:programmable_supply/maximum_current +Date: May 2022 +Contact: Heikki Krogerus +Description: + Maximum Current in milliamperes. + +What: /sys/class/usb_power_delivery/.../source-capabilities/:programmable_supply/pps_power_limited +Date: May 2022 +Contact: Heikki Krogerus +Description: + The PPS Power Limited bit indicates whether or not the source + supply will exceed the rated output power if requested. diff --git a/Documentation/ABI/testing/sysfs-devices-platform-ACPI-TAD b/Documentation/ABI/testing/sysfs-devices-platform-ACPI-TAD index f7b360a61b21..bc44bc903bc8 100644 --- a/Documentation/ABI/testing/sysfs-devices-platform-ACPI-TAD +++ b/Documentation/ABI/testing/sysfs-devices-platform-ACPI-TAD @@ -74,7 +74,7 @@ Description: Reads also cause the AC alarm timer status to be reset. - Another way to reset the the status of the AC alarm timer is to + Another way to reset the status of the AC alarm timer is to write (the number) 0 to this file. If the status return value indicates that the timer has expired, diff --git a/Documentation/ABI/testing/sysfs-devices-power b/Documentation/ABI/testing/sysfs-devices-power index 1b2a2d41ff80..54195530e97a 100644 --- a/Documentation/ABI/testing/sysfs-devices-power +++ b/Documentation/ABI/testing/sysfs-devices-power @@ -303,5 +303,5 @@ Date: Apr 2010 Contact: Dominik Brodowski Description: Reports the runtime PM children usage count of a device, or - 0 if the the children will be ignored. + 0 if the children will be ignored. diff --git a/Documentation/ABI/testing/sysfs-devices-soc b/Documentation/ABI/testing/sysfs-devices-soc index ea999e292f11..5269808ec35f 100644 --- a/Documentation/ABI/testing/sysfs-devices-soc +++ b/Documentation/ABI/testing/sysfs-devices-soc @@ -1,6 +1,6 @@ What: /sys/devices/socX Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: The /sys/devices/ directory contains a sub-directory for each System-on-Chip (SoC) device on a running platform. Information @@ -14,14 +14,14 @@ Description: What: /sys/devices/socX/machine Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: Read-only attribute common to all SoCs. Contains the SoC machine name (e.g. Ux500). What: /sys/devices/socX/family Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: Read-only attribute common to all SoCs. Contains SoC family name (e.g. DB8500). @@ -59,7 +59,7 @@ Description: What: /sys/devices/socX/soc_id Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: Read-only attribute supported by most SoCs. In the case of ST-Ericsson's chips this contains the SoC serial number. @@ -72,21 +72,21 @@ Description: What: /sys/devices/socX/revision Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: Read-only attribute supported by most SoCs. Contains the SoC's manufacturing revision number. What: /sys/devices/socX/process Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: Read-only attribute supported ST-Ericsson's silicon. Contains the the process by which the silicon chip was manufactured. What: /sys/bus/soc Date: January 2012 -contact: Lee Jones +contact: Lee Jones Description: The /sys/bus/soc/ directory contains the usual sub-folders expected under most buses. /sys/bus/soc/devices is of particular diff --git a/Documentation/ABI/testing/sysfs-devices-system-cpu b/Documentation/ABI/testing/sysfs-devices-system-cpu index df79e129d097..5bf61881f012 100644 --- a/Documentation/ABI/testing/sysfs-devices-system-cpu +++ b/Documentation/ABI/testing/sysfs-devices-system-cpu @@ -67,8 +67,7 @@ Description: Discover NUMA node a CPU belongs to /sys/devices/system/cpu/cpu42/node2 -> ../../node/node2 -What: /sys/devices/system/cpu/cpuX/topology/core_id - /sys/devices/system/cpu/cpuX/topology/core_siblings +What: /sys/devices/system/cpu/cpuX/topology/core_siblings /sys/devices/system/cpu/cpuX/topology/core_siblings_list /sys/devices/system/cpu/cpuX/topology/physical_package_id /sys/devices/system/cpu/cpuX/topology/thread_siblings @@ -84,10 +83,6 @@ Description: CPU topology files that describe a logical CPU's relationship Briefly, the files above are: - core_id: the CPU core ID of cpuX. Typically it is the - hardware platform's identifier (rather than the kernel's). - The actual value is architecture and platform dependent. - core_siblings: internal kernel map of cpuX's hardware threads within the same physical_package_id. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index b200758e53c7..e1add192137b 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -2432,8 +2432,7 @@ the KVM_CLEAR_DIRTY ioctl, and only for the pages being cleared. - Eager page splitting currently only supports splitting - huge pages mapped by the TDP MMU. + Eager page splitting is only supported when kvm.tdp_mmu=Y. Default is Y (on). diff --git a/Documentation/arm/samsung-s3c24xx/cpufreq.rst b/Documentation/arm/samsung-s3c24xx/cpufreq.rst index 2ddc26c03b1f..cd22697cf606 100644 --- a/Documentation/arm/samsung-s3c24xx/cpufreq.rst +++ b/Documentation/arm/samsung-s3c24xx/cpufreq.rst @@ -1,3 +1,5 @@ +.. SPDX-License-Identifier: GPL-2.0-only + ======================= S3C24XX CPUfreq support ======================= @@ -73,4 +75,3 @@ Document Author --------------- Ben Dooks, Copyright 2009 Simtec Electronics -Licensed under GPLv2 diff --git a/Documentation/block/null_blk.rst b/Documentation/block/null_blk.rst index edbbab2f12f8..4dd78f24d10a 100644 --- a/Documentation/block/null_blk.rst +++ b/Documentation/block/null_blk.rst @@ -72,6 +72,28 @@ submit_queues=[1..nr_cpus]: Default: 1 hw_queue_depth=[0..qdepth]: Default: 64 The hardware queue depth of the device. +memory_backed=[0/1]: Default: 0 + Whether or not to use a memory buffer to respond to IO requests + + = ============================================= + 0 Transfer no data in response to IO requests + 1 Use a memory buffer to respond to IO requests + = ============================================= + +discard=[0/1]: Default: 0 + Support discard operations (requires memory-backed null_blk device). + + = ===================================== + 0 Do not support discard operations + 1 Enable support for discard operations + = ===================================== + +cache_size=[Size in MB]: Default: 0 + Cache size in MB for memory-backed device. + +mbps=[Maximum bandwidth in MB/s]: Default: 0 (no limit) + Bandwidth limit for device performance. + Multi-queue specific parameters ------------------------------- diff --git a/Documentation/devicetree/bindings/arm/cpus.yaml b/Documentation/devicetree/bindings/arm/cpus.yaml index 5c2e3a5f3789..a07c5bac7c46 100644 --- a/Documentation/devicetree/bindings/arm/cpus.yaml +++ b/Documentation/devicetree/bindings/arm/cpus.yaml @@ -138,6 +138,7 @@ properties: - arm,cortex-a76 - arm,cortex-a77 - arm,cortex-a78 + - arm,cortex-a78ae - arm,cortex-a510 - arm,cortex-a710 - arm,cortex-m0 diff --git a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt index 052a967c1f28..c83245065d44 100644 --- a/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt @@ -72,7 +72,7 @@ mpp19 19 gpio, uart0(rxd), sdio(pw_off) GPIO: ----- For common binding part and usage, refer to -Documentation/devicetree/bindings/gpio/gpio-mvebu.txt. +Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml. Required properties: diff --git a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt index 0705e765f432..d84105c7c935 100644 --- a/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt +++ b/Documentation/devicetree/bindings/arm/marvell/cp110-system-controller.txt @@ -156,7 +156,7 @@ GPIO: ----- For common binding part and usage, refer to -Documentation/devicetree/bindings/gpio/gpio-mvebu.txt. +Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml. Required properties: diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-sys-clock.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-sys-clock.yaml index 0886e2e335bb..661047d26e11 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-sys-clock.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8186-sys-clock.yaml @@ -39,6 +39,9 @@ properties: '#clock-cells': const: 1 + '#reset-cells': + const: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-clock.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-clock.yaml index c8c67c033f8c..b57cc2e69efb 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-clock.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-clock.yaml @@ -24,7 +24,6 @@ properties: - mediatek,mt8192-imp_iic_wrap_w - mediatek,mt8192-imp_iic_wrap_n - mediatek,mt8192-msdc_top - - mediatek,mt8192-msdc - mediatek,mt8192-mfgcfg - mediatek,mt8192-imgsys - mediatek,mt8192-imgsys2 @@ -107,13 +106,6 @@ examples: #clock-cells = <1>; }; - - | - msdc: clock-controller@11f60000 { - compatible = "mediatek,mt8192-msdc"; - reg = <0x11f60000 0x1000>; - #clock-cells = <1>; - }; - - | mfgcfg: clock-controller@13fbf000 { compatible = "mediatek,mt8192-mfgcfg"; diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-sys-clock.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-sys-clock.yaml index 5705bcf1fe47..27f79175c678 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-sys-clock.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8192-sys-clock.yaml @@ -29,6 +29,9 @@ properties: '#clock-cells': const: 1 + '#reset-cells': + const: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8195-sys-clock.yaml b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8195-sys-clock.yaml index 57a1503d95fe..95b6bdf99936 100644 --- a/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8195-sys-clock.yaml +++ b/Documentation/devicetree/bindings/arm/mediatek/mediatek,mt8195-sys-clock.yaml @@ -37,6 +37,9 @@ properties: '#clock-cells': const: 1 + '#reset-cells': + const: 1 + required: - compatible - reg diff --git a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt index 94d50a949be1..c0e3c3a42bea 100644 --- a/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt +++ b/Documentation/devicetree/bindings/arm/msm/qcom,saw2.txt @@ -10,7 +10,7 @@ system, notifying them when a low power state is entered or exited. Multiple revisions of the SAW hardware are supported using these Device Nodes. SAW2 revisions differ in the register offset and configuration data. Also, the same revision of the SAW in different SoCs may have different configuration -data due the the differences in hardware capabilities. Hence the SoC name, the +data due the differences in hardware capabilities. Hence the SoC name, the version of the SAW hardware in that SoC and the distinction between cpu (big or Little) or cache, may be needed to uniquely identify the SAW register configuration and initialization data. The compatible string is used to diff --git a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml index 564ae6aaccf7..7fd8d47b1be4 100644 --- a/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml +++ b/Documentation/devicetree/bindings/arm/tegra/nvidia,tegra20-pmc.yaml @@ -208,7 +208,7 @@ properties: "^[a-z0-9]+$": type: object - patternProperties: + properties: clocks: minItems: 1 maxItems: 8 diff --git a/Documentation/devicetree/bindings/arm/vexpress-sysreg.yaml b/Documentation/devicetree/bindings/arm/vexpress-sysreg.yaml index b5e26e41f88c..f04db802a732 100644 --- a/Documentation/devicetree/bindings/arm/vexpress-sysreg.yaml +++ b/Documentation/devicetree/bindings/arm/vexpress-sysreg.yaml @@ -29,6 +29,13 @@ properties: ranges: true + gpio-controller: + deprecated: true + + "#gpio-cells": + deprecated: true + const: 2 + additionalProperties: false patternProperties: @@ -67,8 +74,7 @@ patternProperties: required: - compatible - - "#address-cells" - - "#size-cells" + - reg examples: - | diff --git a/Documentation/devicetree/bindings/ata/ahci-ceva.txt b/Documentation/devicetree/bindings/ata/ahci-ceva.txt deleted file mode 100644 index bfb6da0281ec..000000000000 --- a/Documentation/devicetree/bindings/ata/ahci-ceva.txt +++ /dev/null @@ -1,63 +0,0 @@ -Binding for CEVA AHCI SATA Controller - -Required properties: - - reg: Physical base address and size of the controller's register area. - - compatible: Compatibility string. Must be 'ceva,ahci-1v84'. - - clocks: Input clock specifier. Refer to common clock bindings. - - interrupts: Interrupt specifier. Refer to interrupt binding. - - ceva,p0-cominit-params: OOB timing value for COMINIT parameter for port 0. - - ceva,p1-cominit-params: OOB timing value for COMINIT parameter for port 1. - The fields for the above parameter must be as shown below: - ceva,pN-cominit-params = /bits/ 8 ; - CINMP : COMINIT Negate Minimum Period. - CIBGN : COMINIT Burst Gap Nominal. - CIBGMX: COMINIT Burst Gap Maximum. - CIBGMN: COMINIT Burst Gap Minimum. - - ceva,p0-comwake-params: OOB timing value for COMWAKE parameter for port 0. - - ceva,p1-comwake-params: OOB timing value for COMWAKE parameter for port 1. - The fields for the above parameter must be as shown below: - ceva,pN-comwake-params = /bits/ 8 ; - CWBGMN: COMWAKE Burst Gap Minimum. - CWBGMX: COMWAKE Burst Gap Maximum. - CWBGN: COMWAKE Burst Gap Nominal. - CWNMP: COMWAKE Negate Minimum Period. - - ceva,p0-burst-params: Burst timing value for COM parameter for port 0. - - ceva,p1-burst-params: Burst timing value for COM parameter for port 1. - The fields for the above parameter must be as shown below: - ceva,pN-burst-params = /bits/ 8 ; - BMX: COM Burst Maximum. - BNM: COM Burst Nominal. - SFD: Signal Failure Detection value. - PTST: Partial to Slumber timer value. - - ceva,p0-retry-params: Retry interval timing value for port 0. - - ceva,p1-retry-params: Retry interval timing value for port 1. - The fields for the above parameter must be as shown below: - ceva,pN-retry-params = /bits/ 16 ; - RIT: Retry Interval Timer. - RCT: Rate Change Timer. - -Optional properties: - - ceva,broken-gen2: limit to gen1 speed instead of gen2. - - phys: phandle for the PHY device - - resets: phandle to the reset controller for the SATA IP - -Examples: - ahci@fd0c0000 { - compatible = "ceva,ahci-1v84"; - reg = <0xfd0c0000 0x200>; - interrupt-parent = <&gic>; - interrupts = <0 133 4>; - clocks = <&clkc SATA_CLK_ID>; - ceva,p0-cominit-params = /bits/ 8 <0x0F 0x25 0x18 0x29>; - ceva,p0-comwake-params = /bits/ 8 <0x04 0x0B 0x08 0x0F>; - ceva,p0-burst-params = /bits/ 8 <0x0A 0x08 0x4A 0x06>; - ceva,p0-retry-params = /bits/ 16 <0x0216 0x7F06>; - - ceva,p1-cominit-params = /bits/ 8 <0x0F 0x25 0x18 0x29>; - ceva,p1-comwake-params = /bits/ 8 <0x04 0x0B 0x08 0x0F>; - ceva,p1-burst-params = /bits/ 8 <0x0A 0x08 0x4A 0x06>; - ceva,p1-retry-params = /bits/ 16 <0x0216 0x7F06>; - ceva,broken-gen2; - phys = <&psgtr 1 PHY_TYPE_SATA 1 1>; - resets = <&zynqmp_reset ZYNQMP_RESET_SATA>; - }; diff --git a/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml b/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml new file mode 100644 index 000000000000..9b31f864e071 --- /dev/null +++ b/Documentation/devicetree/bindings/ata/ceva,ahci-1v84.yaml @@ -0,0 +1,189 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ata/ceva,ahci-1v84.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Ceva AHCI SATA Controller + +maintainers: + - Piyush Mehta + +description: | + The Ceva SATA controller mostly conforms to the AHCI interface with some + special extensions to add functionality, is a high-performance dual-port + SATA host controller with an AHCI compliant command layer which supports + advanced features such as native command queuing and frame information + structure (FIS) based switching for systems employing port multipliers. + +properties: + compatible: + const: ceva,ahci-1v84 + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + dma-coherent: true + + interrupts: + maxItems: 1 + + iommus: + maxItems: 1 + + power-domains: + maxItems: 1 + + ceva,p0-cominit-params: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: | + OOB timing value for COMINIT parameter for port 0. + The fields for the above parameter must be as shown below:- + ceva,p0-cominit-params = /bits/ 8 ; + items: + - description: CINMP - COMINIT Negate Minimum Period. + - description: CIBGN - COMINIT Burst Gap Nominal. + - description: CIBGMX - COMINIT Burst Gap Maximum. + - description: CIBGMN - COMINIT Burst Gap Minimum. + + ceva,p0-comwake-params: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: | + OOB timing value for COMWAKE parameter for port 0. + The fields for the above parameter must be as shown below:- + ceva,p0-comwake-params = /bits/ 8 ; + items: + - description: CWBGMN - COMWAKE Burst Gap Minimum. + - description: CWBGMX - COMWAKE Burst Gap Maximum. + - description: CWBGN - COMWAKE Burst Gap Nominal. + - description: CWNMP - COMWAKE Negate Minimum Period. + + ceva,p0-burst-params: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: | + Burst timing value for COM parameter for port 0. + The fields for the above parameter must be as shown below:- + ceva,p0-burst-params = /bits/ 8 ; + items: + - description: BMX - COM Burst Maximum. + - description: BNM - COM Burst Nominal. + - description: SFD - Signal Failure Detection value. + - description: PTST - Partial to Slumber timer value. + + ceva,p0-retry-params: + $ref: /schemas/types.yaml#/definitions/uint16-array + description: | + Retry interval timing value for port 0. + The fields for the above parameter must be as shown below:- + ceva,p0-retry-params = /bits/ 16 ; + items: + - description: RIT - Retry Interval Timer. + - description: RCT - Rate Change Timer. + + ceva,p1-cominit-params: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: | + OOB timing value for COMINIT parameter for port 1. + The fields for the above parameter must be as shown below:- + ceva,p1-cominit-params = /bits/ 8 ; + items: + - description: CINMP - COMINIT Negate Minimum Period. + - description: CIBGN - COMINIT Burst Gap Nominal. + - description: CIBGMX - COMINIT Burst Gap Maximum. + - description: CIBGMN - COMINIT Burst Gap Minimum. + + ceva,p1-comwake-params: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: | + OOB timing value for COMWAKE parameter for port 1. + The fields for the above parameter must be as shown below:- + ceva,p1-comwake-params = /bits/ 8 ; + items: + - description: CWBGMN - COMWAKE Burst Gap Minimum. + - description: CWBGMX - COMWAKE Burst Gap Maximum. + - description: CWBGN - COMWAKE Burst Gap Nominal. + - description: CWNMP - COMWAKE Negate Minimum Period. + + ceva,p1-burst-params: + $ref: /schemas/types.yaml#/definitions/uint8-array + description: | + Burst timing value for COM parameter for port 1. + The fields for the above parameter must be as shown below:- + ceva,p1-burst-params = /bits/ 8 ; + items: + - description: BMX - COM Burst Maximum. + - description: BNM - COM Burst Nominal. + - description: SFD - Signal Failure Detection value. + - description: PTST - Partial to Slumber timer value. + + ceva,p1-retry-params: + $ref: /schemas/types.yaml#/definitions/uint16-array + description: | + Retry interval timing value for port 1. + The fields for the above parameter must be as shown below:- + ceva,pN-retry-params = /bits/ 16 ; + items: + - description: RIT - Retry Interval Timer. + - description: RCT - Rate Change Timer. + + ceva,broken-gen2: + $ref: /schemas/types.yaml#/definitions/flag + description: | + limit to gen1 speed instead of gen2. + + phys: + maxItems: 1 + + phy-names: + items: + - const: sata-phy + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + - ceva,p0-cominit-params + - ceva,p0-comwake-params + - ceva,p0-burst-params + - ceva,p0-retry-params + - ceva,p1-cominit-params + - ceva,p1-comwake-params + - ceva,p1-burst-params + - ceva,p1-retry-params + +additionalProperties: false + +examples: + - | + #include + #include + #include + #include + #include + #include + + sata: ahci@fd0c0000 { + compatible = "ceva,ahci-1v84"; + reg = <0xfd0c0000 0x200>; + interrupt-parent = <&gic>; + interrupts = <0 133 IRQ_TYPE_LEVEL_HIGH>; + clocks = <&zynqmp_clk SATA_REF>; + ceva,p0-cominit-params = /bits/ 8 <0x0F 0x25 0x18 0x29>; + ceva,p0-comwake-params = /bits/ 8 <0x04 0x0B 0x08 0x0F>; + ceva,p0-burst-params = /bits/ 8 <0x0A 0x08 0x4A 0x06>; + ceva,p0-retry-params = /bits/ 16 <0x0216 0x7F06>; + ceva,p1-cominit-params = /bits/ 8 <0x0F 0x25 0x18 0x29>; + ceva,p1-comwake-params = /bits/ 8 <0x04 0x0B 0x08 0x0F>; + ceva,p1-burst-params = /bits/ 8 <0x0A 0x08 0x4A 0x06>; + ceva,p1-retry-params = /bits/ 16 <0x0216 0x7F06>; + ceva,broken-gen2; + phys = <&psgtr 1 PHY_TYPE_SATA 1 1>; + resets = <&zynqmp_reset ZYNQMP_RESET_SATA>; + }; diff --git a/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml b/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml index 5b9705079015..8e9e6ff35d7d 100644 --- a/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml +++ b/Documentation/devicetree/bindings/bus/qcom,ssc-block-bus.yaml @@ -28,11 +28,9 @@ properties: - const: qcom,ssc-block-bus reg: - description: | - Shall contain the addresses of the SSCAON_CONFIG0 and SSCAON_CONFIG1 - registers - minItems: 2 - maxItems: 2 + items: + - description: SSCAON_CONFIG0 registers + - description: SSCAON_CONFIG1 registers reg-names: items: @@ -48,7 +46,6 @@ properties: ranges: true clocks: - minItems: 6 maxItems: 6 clock-names: @@ -61,9 +58,9 @@ properties: - const: ssc_ahbs power-domains: - description: Power domain phandles for the ssc_cx and ssc_mx power domains - minItems: 2 - maxItems: 2 + items: + - description: CX power domain + - description: MX power domain power-domain-names: items: @@ -71,11 +68,11 @@ properties: - const: ssc_mx resets: - description: | - Reset phandles for the ssc_reset and ssc_bcr resets (note: ssc_bcr is the - branch control register associated with the ssc_xo and ssc_ahbs clocks) - minItems: 2 - maxItems: 2 + items: + - description: Main reset + - description: + SSC Branch Control Register reset (associated with the ssc_xo and + ssc_ahbs clocks) reset-names: items: diff --git a/Documentation/devicetree/bindings/chosen.txt b/Documentation/devicetree/bindings/chosen.txt deleted file mode 100644 index 1cc3aa10dcb1..000000000000 --- a/Documentation/devicetree/bindings/chosen.txt +++ /dev/null @@ -1,137 +0,0 @@ -The chosen node ---------------- - -The chosen node does not represent a real device, but serves as a place -for passing data between firmware and the operating system, like boot -arguments. Data in the chosen node does not represent the hardware. - -The following properties are recognized: - - -kaslr-seed ------------ - -This property is used when booting with CONFIG_RANDOMIZE_BASE as the -entropy used to randomize the kernel image base address location. Since -it is used directly, this value is intended only for KASLR, and should -not be used for other purposes (as it may leak information about KASLR -offsets). It is parsed as a u64 value, e.g. - -/ { - chosen { - kaslr-seed = <0xfeedbeef 0xc0def00d>; - }; -}; - -Note that if this property is set from UEFI (or a bootloader in EFI -mode) when EFI_RNG_PROTOCOL is supported, it will be overwritten by -the Linux EFI stub (which will populate the property itself, using -EFI_RNG_PROTOCOL). - -stdout-path ------------ - -Device trees may specify the device to be used for boot console output -with a stdout-path property under /chosen, as described in the Devicetree -Specification, e.g. - -/ { - chosen { - stdout-path = "/serial@f00:115200"; - }; - - serial@f00 { - compatible = "vendor,some-uart"; - reg = <0xf00 0x10>; - }; -}; - -If the character ":" is present in the value, this terminates the path. -The meaning of any characters following the ":" is device-specific, and -must be specified in the relevant binding documentation. - -For UART devices, the preferred binding is a string in the form: - - {{{}}} - -where - - baud - baud rate in decimal - parity - 'n' (none), 'o', (odd) or 'e' (even) - bits - number of data bits - flow - 'r' (rts) - -For example: 115200n8r - -Implementation note: Linux will look for the property "linux,stdout-path" or -on PowerPC "stdout" if "stdout-path" is not found. However, the -"linux,stdout-path" and "stdout" properties are deprecated. New platforms -should only use the "stdout-path" property. - -linux,booted-from-kexec ------------------------ - -This property is set (currently only on PowerPC, and only needed on -book3e) by some versions of kexec-tools to tell the new kernel that it -is being booted by kexec, as the booting environment may differ (e.g. -a different secondary CPU release mechanism) - -linux,usable-memory-range -------------------------- - -This property holds a base address and size, describing a limited region in -which memory may be considered available for use by the kernel. Memory outside -of this range is not available for use. - -This property describes a limitation: memory within this range is only -valid when also described through another mechanism that the kernel -would otherwise use to determine available memory (e.g. memory nodes -or the EFI memory map). Valid memory may be sparse within the range. -e.g. - -/ { - chosen { - linux,usable-memory-range = <0x9 0xf0000000 0x0 0x10000000>; - }; -}; - -The main usage is for crash dump kernel to identify its own usable -memory and exclude, at its boot time, any other memory areas that are -part of the panicked kernel's memory. - -While this property does not represent a real hardware, the address -and the size are expressed in #address-cells and #size-cells, -respectively, of the root node. - -linux,elfcorehdr ----------------- - -This property holds the memory range, the address and the size, of the elf -core header which mainly describes the panicked kernel's memory layout as -PT_LOAD segments of elf format. -e.g. - -/ { - chosen { - linux,elfcorehdr = <0x9 0xfffff000 0x0 0x800>; - }; -}; - -While this property does not represent a real hardware, the address -and the size are expressed in #address-cells and #size-cells, -respectively, of the root node. - -linux,initrd-start and linux,initrd-end ---------------------------------------- - -These properties hold the physical start and end address of an initrd that's -loaded by the bootloader. Note that linux,initrd-start is inclusive, but -linux,initrd-end is exclusive. -e.g. - -/ { - chosen { - linux,initrd-start = <0x82000000>; - linux,initrd-end = <0x82800000>; - }; -}; diff --git a/Documentation/devicetree/bindings/chrome/google,cros-kbd-led-backlight.yaml b/Documentation/devicetree/bindings/chrome/google,cros-kbd-led-backlight.yaml new file mode 100644 index 000000000000..5b875af6a95a --- /dev/null +++ b/Documentation/devicetree/bindings/chrome/google,cros-kbd-led-backlight.yaml @@ -0,0 +1,35 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/chrome/google,cros-kbd-led-backlight.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ChromeOS keyboard backlight LED driver. + +maintainers: + - Tzung-Bi Shih + +properties: + compatible: + const: google,cros-kbd-led-backlight + +required: + - compatible + +additionalProperties: false + +examples: + - | + spi0 { + #address-cells = <1>; + #size-cells = <0>; + + cros_ec: ec@0 { + compatible = "google,cros-ec-spi"; + reg = <0>; + + kbd-led-backlight { + compatible = "google,cros-kbd-led-backlight"; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/efm32-clock.txt b/Documentation/devicetree/bindings/clock/efm32-clock.txt deleted file mode 100644 index 263d293f6a10..000000000000 --- a/Documentation/devicetree/bindings/clock/efm32-clock.txt +++ /dev/null @@ -1,11 +0,0 @@ -* Clock bindings for Energy Micro efm32 Giant Gecko's Clock Management Unit - -Required properties: -- compatible: Should be "efm32gg,cmu" -- reg: Base address and length of the register set -- interrupts: Interrupt used by the CMU -- #clock-cells: Should be <1> - -The clock consumer should specify the desired clock by having the clock ID in -its "clocks" phandle cell. The header efm32-clk.h contains a list of available -IDs. diff --git a/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml b/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml index f415845b38dd..0b02378a3a0c 100644 --- a/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml +++ b/Documentation/devicetree/bindings/clock/fixed-factor-clock.yaml @@ -13,7 +13,6 @@ maintainers: properties: compatible: enum: - - allwinner,sun4i-a10-pll3-2x-clk - fixed-factor-clock "#clock-cells": diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-apq8064.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-apq8064.yaml index 9fafcb080069..3cf404c9325a 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-apq8064.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-apq8064.yaml @@ -4,7 +4,7 @@ $id: http://devicetree.org/schemas/clock/qcom,gcc-apq8064.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# -title: Qualcomm Global Clock & Reset Controller Binding for APQ8064 +title: Qualcomm Global Clock & Reset Controller Binding for APQ8064/MSM8960 allOf: - $ref: qcom,gcc.yaml# @@ -23,11 +23,25 @@ description: | properties: compatible: - const: qcom,gcc-apq8064 + oneOf: + - items: + - enum: + - qcom,gcc-apq8064 + - qcom,gcc-msm8960 + - const: syscon + - enum: + - qcom,gcc-apq8064 + - qcom,gcc-msm8960 + deprecated: true + + thermal-sensor: + description: child tsens device + $ref: /schemas/thermal/qcom-tsens.yaml# nvmem-cells: minItems: 1 maxItems: 2 + deprecated: true description: Qualcomm TSENS (thermal sensor device) on some devices can be part of GCC and hence the TSENS properties can also be part @@ -37,31 +51,39 @@ properties: nvmem-cell-names: minItems: 1 + deprecated: true items: - const: calib - const: calib_backup '#thermal-sensor-cells': const: 1 + deprecated: true required: - compatible - - nvmem-cells - - nvmem-cell-names - - '#thermal-sensor-cells' unevaluatedProperties: false examples: - | clock-controller@900000 { - compatible = "qcom,gcc-apq8064"; + compatible = "qcom,gcc-apq8064", "syscon"; reg = <0x00900000 0x4000>; - nvmem-cells = <&tsens_calib>, <&tsens_backup>; - nvmem-cell-names = "calib", "calib_backup"; #clock-cells = <1>; #reset-cells = <1>; #power-domain-cells = <1>; - #thermal-sensor-cells = <1>; + + thermal-sensor { + compatible = "qcom,msm8960-tsens"; + + nvmem-cells = <&tsens_calib>, <&tsens_backup>; + nvmem-cell-names = "calib", "calib_backup"; + interrupts = <0 178 4>; + interrupt-names = "uplow"; + + #qcom,sensors = <11>; + #thermal-sensor-cells = <1>; + }; }; ... diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml index 98572b4a9b60..21470f52ce36 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-ipq8074.yaml @@ -24,6 +24,9 @@ properties: '#clock-cells': const: 1 + '#power-domain-cells': + const: 1 + '#reset-cells': const: 1 @@ -38,6 +41,7 @@ required: - compatible - reg - '#clock-cells' + - '#power-domain-cells' - '#reset-cells' additionalProperties: false @@ -48,6 +52,7 @@ examples: compatible = "qcom,gcc-ipq8074"; reg = <0x01800000 0x80000>; #clock-cells = <1>; + #power-domain-cells = <1>; #reset-cells = <1>; }; ... diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml index 5a5b2214f0ca..005e0edd4609 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-msm8996.yaml @@ -22,16 +22,32 @@ properties: const: qcom,gcc-msm8996 clocks: + minItems: 3 items: - description: XO source - description: Second XO source - description: Sleep clock source + - description: PCIe 0 PIPE clock (optional) + - description: PCIe 1 PIPE clock (optional) + - description: PCIe 2 PIPE clock (optional) + - description: USB3 PIPE clock (optional) + - description: UFS RX symbol 0 clock (optional) + - description: UFS RX symbol 1 clock (optional) + - description: UFS TX symbol 0 clock (optional) clock-names: + minItems: 3 items: - const: cxo - const: cxo2 - const: sleep_clk + - const: pcie_0_pipe_clk_src + - const: pcie_1_pipe_clk_src + - const: pcie_2_pipe_clk_src + - const: usb3_phy_pipe_clk_src + - const: ufs_rx_symbol_0_clk_src + - const: ufs_rx_symbol_1_clk_src + - const: ufs_tx_symbol_0_clk_src '#clock-cells': const: 1 diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-other.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-other.yaml index 6c45e0f85494..6c78df0c46a9 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-other.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-other.yaml @@ -44,7 +44,6 @@ properties: - qcom,gcc-msm8916 - qcom,gcc-msm8939 - qcom,gcc-msm8953 - - qcom,gcc-msm8960 - qcom,gcc-msm8974 - qcom,gcc-msm8974pro - qcom,gcc-msm8974pro-ac @@ -58,10 +57,10 @@ required: unevaluatedProperties: false examples: - # Example for GCC for MSM8960: + # Example for GCC for MSM8974: - | clock-controller@900000 { - compatible = "qcom,gcc-msm8960"; + compatible = "qcom,gcc-msm8974"; reg = <0x900000 0x4000>; #clock-cells = <1>; #reset-cells = <1>; diff --git a/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml b/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml index d902f137ab17..daf7906ebc40 100644 --- a/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,gcc-sdm845.yaml @@ -43,6 +43,9 @@ properties: '#reset-cells': const: 1 + power-domains: + maxItems: 1 + '#power-domain-cells': const: 1 diff --git a/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml b/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml index 9d296b89a8d0..d63b45ad06e8 100644 --- a/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml +++ b/Documentation/devicetree/bindings/clock/qcom,rpmcc.yaml @@ -49,15 +49,86 @@ properties: const: 1 clocks: - maxItems: 1 + minItems: 1 + maxItems: 2 clock-names: - const: xo + minItems: 1 + maxItems: 2 required: - compatible - '#clock-cells' +allOf: + - if: + properties: + compatible: + contains: + enum: + - qcom,rpmcc-apq8060 + - qcom,rpmcc-ipq806x + - qcom,rpmcc-msm8660 + + then: + properties: + clocks: + items: + - description: pxo clock + + clock-names: + items: + - const: pxo + + - if: + properties: + compatible: + contains: + const: qcom,rpmcc-apq8064 + then: + properties: + clocks: + items: + - description: pxo clock + - description: cxo clock + + clock-names: + items: + - const: pxo + - const: cxo + + - if: + properties: + compatible: + contains: + enum: + - qcom,rpmcc-mdm9607 + - qcom,rpmcc-msm8226 + - qcom,rpmcc-msm8916 + - qcom,rpmcc-msm8936 + - qcom,rpmcc-msm8953 + - qcom,rpmcc-msm8974 + - qcom,rpmcc-msm8976 + - qcom,rpmcc-msm8992 + - qcom,rpmcc-msm8994 + - qcom,rpmcc-msm8996 + - qcom,rpmcc-msm8998 + - qcom,rpmcc-qcm2290 + - qcom,rpmcc-qcs404 + - qcom,rpmcc-sdm660 + - qcom,rpmcc-sm6115 + - qcom,rpmcc-sm6125 + + then: + properties: + clocks: + items: + - description: xo clock + + clock-names: + items: + - const: xo + additionalProperties: false examples: @@ -73,3 +144,13 @@ examples: }; }; }; + + - | + rpm { + clock-controller { + compatible = "qcom,rpmcc-ipq806x", "qcom,rpmcc"; + #clock-cells = <1>; + clocks = <&pxo_board>; + clock-names = "pxo"; + }; + }; diff --git a/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml b/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml index 8880b834f264..d036675e0779 100644 --- a/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml +++ b/Documentation/devicetree/bindings/clock/renesas,rzg2l-cpg.yaml @@ -45,10 +45,9 @@ properties: description: | - For CPG core clocks, the two clock specifier cells must be "CPG_CORE" and a core clock reference, as defined in - + , - For module clocks, the two clock specifier cells must be "CPG_MOD" and - a module number, as defined in the or - . + a module number, as defined in . const: 2 '#power-domain-cells': @@ -62,7 +61,7 @@ properties: '#reset-cells': description: The single reset specifier cell must be the module number, as defined in - the or . + . const: 1 required: diff --git a/Documentation/devicetree/bindings/clock/sprd,ums512-clk.yaml b/Documentation/devicetree/bindings/clock/sprd,ums512-clk.yaml new file mode 100644 index 000000000000..5f747b0471cf --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sprd,ums512-clk.yaml @@ -0,0 +1,71 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright 2022 Unisoc Inc. +%YAML 1.2 +--- +$id: "http://devicetree.org/schemas/clock/sprd,ums512-clk.yaml#" +$schema: "http://devicetree.org/meta-schemas/core.yaml#" + +title: UMS512 Soc clock controller + +maintainers: + - Orson Zhai + - Baolin Wang + - Chunyan Zhang + +properties: + compatible: + enum: + - sprd,ums512-apahb-gate + - sprd,ums512-ap-clk + - sprd,ums512-aonapb-clk + - sprd,ums512-pmu-gate + - sprd,ums512-g0-pll + - sprd,ums512-g2-pll + - sprd,ums512-g3-pll + - sprd,ums512-gc-pll + - sprd,ums512-aon-gate + - sprd,ums512-audcpapb-gate + - sprd,ums512-audcpahb-gate + - sprd,ums512-gpu-clk + - sprd,ums512-mm-clk + - sprd,ums512-mm-gate-clk + - sprd,ums512-apapb-gate + + "#clock-cells": + const: 1 + + clocks: + minItems: 1 + maxItems: 4 + description: | + The input parent clock(s) phandle for the clock, only list + fixed clocks which are declared in devicetree. + + clock-names: + minItems: 1 + items: + - const: ext-26m + - const: ext-32k + - const: ext-4m + - const: rco-100m + + reg: + maxItems: 1 + +required: + - compatible + - '#clock-cells' + - reg + +additionalProperties: false + +examples: + - | + ap_clk: clock-controller@20200000 { + compatible = "sprd,ums512-ap-clk"; + reg = <0x20200000 0x1000>; + clocks = <&ext_26m>; + clock-names = "ext-26m"; + #clock-cells = <1>; + }; +... diff --git a/Documentation/devicetree/bindings/clock/st/st,flexgen.txt b/Documentation/devicetree/bindings/clock/st/st,flexgen.txt index 55a18939bddd..c918075405ba 100644 --- a/Documentation/devicetree/bindings/clock/st/st,flexgen.txt +++ b/Documentation/devicetree/bindings/clock/st/st,flexgen.txt @@ -78,7 +78,7 @@ Required properties: - #clock-cells : from common clock binding; shall be set to 1 (multiple clock outputs). -- clocks : must be set to the parent's phandle. it's could be output clocks of +- clocks : must be set to the parent's phandle. it could be output clocks of a quadsfs or/and a pll or/and clk_sysin (up to 7 clocks) - clock-output-names : List of strings used to name the clock outputs. diff --git a/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt index 36998e184821..c9894538315b 100644 --- a/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt +++ b/Documentation/devicetree/bindings/clock/ti/davinci/pll.txt @@ -15,7 +15,7 @@ Required properties: - for "ti,da850-pll1", shall be "clksrc" Optional properties: -- ti,clkmode-square-wave: Indicates that the the board is supplying a square +- ti,clkmode-square-wave: Indicates that the board is supplying a square wave input on the OSCIN pin instead of using a crystal oscillator. This property is only valid when compatible = "ti,da850-pll0". diff --git a/Documentation/devicetree/bindings/clock/ti/dra7-atl.txt b/Documentation/devicetree/bindings/clock/ti/dra7-atl.txt index 21c002d28b9b..68504079f99f 100644 --- a/Documentation/devicetree/bindings/clock/ti/dra7-atl.txt +++ b/Documentation/devicetree/bindings/clock/ti/dra7-atl.txt @@ -6,7 +6,7 @@ functional clock but can be configured to provide different clocks. ATL can maintain a clock averages to some desired frequency based on the bws/aws signals - can compensate the drift between the two ws signal. -In order to provide the support for ATL and it's output clocks (which can be used +In order to provide the support for ATL and its output clocks (which can be used internally within the SoC or external components) two sets of bindings is needed: Clock tree binding: diff --git a/Documentation/devicetree/bindings/connector/usb-connector.yaml b/Documentation/devicetree/bindings/connector/usb-connector.yaml index 0420fa563532..ae515651fc6b 100644 --- a/Documentation/devicetree/bindings/connector/usb-connector.yaml +++ b/Documentation/devicetree/bindings/connector/usb-connector.yaml @@ -263,11 +263,11 @@ examples: # Micro-USB connector with HS lines routed via controller (MUIC). - | muic-max77843 { - usb_con1: connector { - compatible = "usb-b-connector"; - label = "micro-USB"; - type = "micro"; - }; + usb_con1: connector { + compatible = "usb-b-connector"; + label = "micro-USB"; + type = "micro"; + }; }; # USB-C connector attached to CC controller (s2mm005), HS lines routed @@ -275,34 +275,34 @@ examples: # DisplayPort video lines are routed to the connector via SS mux in USB3 PHY. - | ccic: s2mm005 { - usb_con2: connector { - compatible = "usb-c-connector"; - label = "USB-C"; + usb_con2: connector { + compatible = "usb-c-connector"; + label = "USB-C"; - ports { - #address-cells = <1>; - #size-cells = <0>; + ports { + #address-cells = <1>; + #size-cells = <0>; - port@0 { - reg = <0>; - usb_con_hs: endpoint { - remote-endpoint = <&max77865_usbc_hs>; + port@0 { + reg = <0>; + usb_con_hs: endpoint { + remote-endpoint = <&max77865_usbc_hs>; + }; + }; + port@1 { + reg = <1>; + usb_con_ss: endpoint { + remote-endpoint = <&usbdrd_phy_ss>; + }; + }; + port@2 { + reg = <2>; + usb_con_sbu: endpoint { + remote-endpoint = <&dp_aux>; + }; + }; }; - }; - port@1 { - reg = <1>; - usb_con_ss: endpoint { - remote-endpoint = <&usbdrd_phy_ss>; - }; - }; - port@2 { - reg = <2>; - usb_con_sbu: endpoint { - remote-endpoint = <&dp_aux>; - }; - }; }; - }; }; # USB-C connector attached to a typec port controller(ptn5110), which has @@ -310,16 +310,16 @@ examples: - | #include typec: ptn5110 { - usb_con3: connector { - compatible = "usb-c-connector"; - label = "USB-C"; - power-role = "dual"; - try-power-role = "sink"; - source-pdos = ; - sink-pdos = ; - op-sink-microwatt = <10000000>; - }; + usb_con3: connector { + compatible = "usb-c-connector"; + label = "USB-C"; + power-role = "dual"; + try-power-role = "sink"; + source-pdos = ; + sink-pdos = ; + op-sink-microwatt = <10000000>; + }; }; # USB-C connector attached to SoC and USB3 typec port controller(hd3ss3220) @@ -332,20 +332,20 @@ examples: data-role = "dual"; ports { - #address-cells = <1>; - #size-cells = <0>; - port@0 { - reg = <0>; - hs_ep: endpoint { - remote-endpoint = <&usb3_hs_ep>; - }; + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + hs_ep: endpoint { + remote-endpoint = <&usb3_hs_ep>; }; - port@1 { - reg = <1>; - ss_ep: endpoint { - remote-endpoint = <&hd3ss3220_in_ep>; - }; + }; + port@1 { + reg = <1>; + ss_ep: endpoint { + remote-endpoint = <&hd3ss3220_in_ep>; }; + }; }; }; @@ -354,12 +354,12 @@ examples: #include usb { - connector { - compatible = "gpio-usb-b-connector", "usb-b-connector"; - type = "micro"; - id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; - vbus-supply = <&usb_p0_vbus>; - }; + connector { + compatible = "gpio-usb-b-connector", "usb-b-connector"; + type = "micro"; + id-gpios = <&pio 12 GPIO_ACTIVE_HIGH>; + vbus-supply = <&usb_p0_vbus>; + }; }; # Micro-USB connector with HS lines routed via controller (MUIC) and MHL @@ -367,27 +367,27 @@ examples: # mobile phone - | muic-max77843 { - usb_con4: connector { - compatible = "samsung,usb-connector-11pin", "usb-b-connector"; - label = "micro-USB"; - type = "micro"; + usb_con4: connector { + compatible = "samsung,usb-connector-11pin", "usb-b-connector"; + label = "micro-USB"; + type = "micro"; - ports { - #address-cells = <1>; - #size-cells = <0>; + ports { + #address-cells = <1>; + #size-cells = <0>; - port@0 { - reg = <0>; - muic_to_usb: endpoint { - remote-endpoint = <&usb_to_muic>; + port@0 { + reg = <0>; + muic_to_usb: endpoint { + remote-endpoint = <&usb_to_muic>; + }; + }; + port@3 { + reg = <3>; + usb_con_mhl: endpoint { + remote-endpoint = <&sii8620_mhl>; + }; + }; }; - }; - port@3 { - reg = <3>; - usb_con_mhl: endpoint { - remote-endpoint = <&sii8620_mhl>; - }; - }; }; - }; }; diff --git a/Documentation/devicetree/bindings/display/arm,pl11x.yaml b/Documentation/devicetree/bindings/display/arm,pl11x.yaml index b545c6d20325..6cc9045e5c68 100644 --- a/Documentation/devicetree/bindings/display/arm,pl11x.yaml +++ b/Documentation/devicetree/bindings/display/arm,pl11x.yaml @@ -159,25 +159,12 @@ examples: }; panel { - compatible = "arm,rtsm-display", "panel-dpi"; - power-supply = <&vcc_supply>; + compatible = "arm,rtsm-display"; port { clcd_panel: endpoint { remote-endpoint = <&clcd_pads>; }; }; - - panel-timing { - clock-frequency = <25175000>; - hactive = <640>; - hback-porch = <40>; - hfront-porch = <24>; - hsync-len = <96>; - vactive = <480>; - vback-porch = <32>; - vfront-porch = <11>; - vsync-len = <2>; - }; }; ... diff --git a/Documentation/devicetree/bindings/display/atmel,lcdc.txt b/Documentation/devicetree/bindings/display/atmel,lcdc.txt index acb5a0132127..b5e355ada2fa 100644 --- a/Documentation/devicetree/bindings/display/atmel,lcdc.txt +++ b/Documentation/devicetree/bindings/display/atmel,lcdc.txt @@ -9,7 +9,6 @@ Required properties: "atmel,at91sam9g45-lcdc" , "atmel,at91sam9g45es-lcdc" , "atmel,at91sam9rl-lcdc" , - "atmel,at32ap-lcdc" - reg : Should contain 1 register ranges(address and length). Can contain an additional register range(address and length) for fixed framebuffer memory. Useful for dedicated memories. diff --git a/Documentation/devicetree/bindings/display/bridge/sii902x.txt b/Documentation/devicetree/bindings/display/bridge/sii902x.txt deleted file mode 100644 index 3bc760cc31cb..000000000000 --- a/Documentation/devicetree/bindings/display/bridge/sii902x.txt +++ /dev/null @@ -1,78 +0,0 @@ -sii902x HDMI bridge bindings - -Required properties: - - compatible: "sil,sii9022" - - reg: i2c address of the bridge - -Optional properties: - - interrupts: describe the interrupt line used to inform the host - about hotplug events. - - reset-gpios: OF device-tree gpio specification for RST_N pin. - - iovcc-supply: I/O Supply Voltage (1.8V or 3.3V) - - cvcc12-supply: Digital Core Supply Voltage (1.2V) - - HDMI audio properties: - - #sound-dai-cells: <0> or <1>. <0> if only i2s or spdif pin - is wired, <1> if the both are wired. HDMI audio is - configured only if this property is found. - - sil,i2s-data-lanes: Array of up to 4 integers with values of 0-3 - Each integer indicates which i2s pin is connected to which - audio fifo. The first integer selects i2s audio pin for the - first audio fifo#0 (HDMI channels 1&2), second for fifo#1 - (HDMI channels 3&4), and so on. There is 4 fifos and 4 i2s - pins (SD0 - SD3). Any i2s pin can be connected to any fifo, - but there can be no gaps. E.g. an i2s pin must be mapped to - fifo#0 and fifo#1 before mapping a channel to fifo#2. Default - value is <0>, describing SD0 pin beiging routed to hdmi audio - fifo #0. - - clocks: phandle and clock specifier for each clock listed in - the clock-names property - - clock-names: "mclk" - Describes SII902x MCLK input. MCLK can be used to produce - HDMI audio CTS values. This property follows - Documentation/devicetree/bindings/clock/clock-bindings.txt - consumer binding. - - If HDMI audio is configured the sii902x device becomes an I2S - and/or spdif audio codec component (e.g a digital audio sink), - that can be used in configuring a full audio devices with - simple-card or audio-graph-card binding. See their binding - documents on how to describe the way the sii902x device is - connected to the rest of the audio system: - Documentation/devicetree/bindings/sound/simple-card.yaml - Documentation/devicetree/bindings/sound/audio-graph-card.yaml - Note: In case of the audio-graph-card binding the used port - index should be 3. - -Optional subnodes: - - video input: this subnode can contain a video input port node - to connect the bridge to a display controller output (See this - documentation [1]). - -[1]: Documentation/devicetree/bindings/media/video-interfaces.txt - -Example: - hdmi-bridge@39 { - compatible = "sil,sii9022"; - reg = <0x39>; - reset-gpios = <&pioA 1 0>; - iovcc-supply = <&v3v3_hdmi>; - cvcc12-supply = <&v1v2_hdmi>; - - #sound-dai-cells = <0>; - sil,i2s-data-lanes = < 0 1 2 >; - clocks = <&mclk>; - clock-names = "mclk"; - - ports { - #address-cells = <1>; - #size-cells = <0>; - - port@0 { - reg = <0>; - bridge_in: endpoint { - remote-endpoint = <&dc_out>; - }; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml b/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml new file mode 100644 index 000000000000..5a69547ad3d7 --- /dev/null +++ b/Documentation/devicetree/bindings/display/bridge/sil,sii9022.yaml @@ -0,0 +1,131 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/bridge/sil,sii9022.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Silicon Image sii902x HDMI bridge + +maintainers: + - Boris Brezillon + +properties: + compatible: + oneOf: + - items: + - enum: + - sil,sii9022-cpi # CEC Programming Interface + - sil,sii9022-tpi # Transmitter Programming Interface + - const: sil,sii9022 + - const: sil,sii9022 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + description: Interrupt line used to inform the host about hotplug events. + + reset-gpios: + maxItems: 1 + + iovcc-supply: + description: I/O Supply Voltage (1.8V or 3.3V) + + cvcc12-supply: + description: Digital Core Supply Voltage (1.2V) + + '#sound-dai-cells': + enum: [ 0, 1 ] + description: | + <0> if only I2S or S/PDIF pin is wired, + <1> if both are wired. + HDMI audio is configured only if this property is found. + If HDMI audio is configured, the sii902x device becomes an I2S and/or + S/PDIF audio codec component (e.g. a digital audio sink), that can be + used in configuring full audio devices with simple-card or + audio-graph-card bindings. See their binding documents on how to describe + the way the + sii902x device is connected to the rest of the audio system: + Documentation/devicetree/bindings/sound/simple-card.yaml + Documentation/devicetree/bindings/sound/audio-graph-card.yaml + Note: In case of the audio-graph-card binding the used port index should + be 3. + + sil,i2s-data-lanes: + $ref: /schemas/types.yaml#/definitions/uint32-array + minItems: 1 + maxItems: 4 + uniqueItems: true + items: + enum: [ 0, 1, 2, 3 ] + description: + Each integer indicates which I2S pin is connected to which audio FIFO. + The first integer selects the I2S audio pin for the first audio FIFO#0 + (HDMI channels 1&2), the second for FIFO#1 (HDMI channels 3&4), and so + on. There are 4 FIFOs and 4 I2S pins (SD0 - SD3). Any I2S pin can be + connected to any FIFO, but there can be no gaps. E.g. an I2S pin must be + mapped to FIFO#0 and FIFO#1 before mapping a channel to FIFO#2. The + default value is <0>, describing SD0 pin being routed to HDMI audio + FIFO#0. + + clocks: + maxItems: 1 + description: MCLK input. MCLK can be used to produce HDMI audio CTS values. + + clock-names: + const: mclk + + ports: + $ref: /schemas/graph.yaml#/properties/ports + + properties: + port@0: + $ref: /schemas/graph.yaml#/properties/port + description: Parallel RGB input port + + port@1: + $ref: /schemas/graph.yaml#/properties/port + description: HDMI output port + + port@3: + $ref: /schemas/graph.yaml#/properties/port + description: Sound input port + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + hdmi-bridge@39 { + compatible = "sil,sii9022"; + reg = <0x39>; + reset-gpios = <&pioA 1 0>; + iovcc-supply = <&v3v3_hdmi>; + cvcc12-supply = <&v1v2_hdmi>; + + #sound-dai-cells = <0>; + sil,i2s-data-lanes = < 0 1 2 >; + clocks = <&mclk>; + clock-names = "mclk"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + + port@0 { + reg = <0>; + bridge_in: endpoint { + remote-endpoint = <&dc_out>; + }; + }; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/display/panel/arm,rtsm-display.yaml b/Documentation/devicetree/bindings/display/panel/arm,rtsm-display.yaml new file mode 100644 index 000000000000..4ad484f09ba3 --- /dev/null +++ b/Documentation/devicetree/bindings/display/panel/arm,rtsm-display.yaml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/display/panel/arm,rtsm-display.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Arm RTSM Virtual Platforms Display + +maintainers: + - Linus Walleij + +allOf: + - $ref: panel-common.yaml# + +properties: + compatible: + const: arm,rtsm-display + + port: true + +required: + - compatible + - port + +additionalProperties: false + +... diff --git a/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml b/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml index b4314ce7b411..ee357e139ac0 100644 --- a/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml +++ b/Documentation/devicetree/bindings/display/panel/lg,lg4573.yaml @@ -15,13 +15,13 @@ maintainers: allOf: - $ref: panel-common.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# properties: compatible: const: lg,lg4573 reg: true - spi-max-frequency: true required: - compatible diff --git a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml index 617aa8c8c03a..d62fd692bf10 100644 --- a/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml +++ b/Documentation/devicetree/bindings/display/panel/raydium,rm67191.yaml @@ -38,6 +38,7 @@ properties: 0 - burst-mode 1 - non-burst with sync event 2 - non-burst with sync pulse + $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1, 2] required: diff --git a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml index 157b1a7b18f9..53f181ef3670 100644 --- a/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml +++ b/Documentation/devicetree/bindings/display/sitronix,st7735r.yaml @@ -15,6 +15,7 @@ description: allOf: - $ref: panel/panel-common.yaml# + - $ref: /schemas/spi/spi-peripheral-props.yaml# properties: compatible: diff --git a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml index 3fbd87c2c120..669f70b1b4c4 100644 --- a/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml +++ b/Documentation/devicetree/bindings/display/solomon,ssd1307fb.yaml @@ -49,9 +49,6 @@ properties: vbat-supply: description: The supply for VBAT - # Only required for SPI - spi-max-frequency: true - solomon,height: $ref: /schemas/types.yaml#/definitions/uint32 default: 16 @@ -153,6 +150,8 @@ required: - reg allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + - if: properties: compatible: @@ -223,7 +222,7 @@ allOf: solomon,dclk-frq: default: 10 -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/dma/apple,admac.yaml b/Documentation/devicetree/bindings/dma/apple,admac.yaml new file mode 100644 index 000000000000..bdc8c129c4f5 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/apple,admac.yaml @@ -0,0 +1,80 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/apple,admac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Apple Audio DMA Controller (ADMAC) + +description: | + Apple's Audio DMA Controller (ADMAC) is used to fetch and store audio samples + on SoCs from the "Apple Silicon" family. + + The controller has been seen with up to 24 channels. Even-numbered channels + are TX-only, odd-numbered are RX-only. Individual channels are coupled to + fixed device endpoints. + +maintainers: + - Martin Povišer + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + compatible: + items: + - enum: + - apple,t6000-admac + - apple,t8103-admac + - const: apple,admac + + reg: + maxItems: 1 + + '#dma-cells': + const: 1 + description: + Clients specify a single cell with channel number. + + dma-channels: + maximum: 24 + + interrupts: + minItems: 4 + maxItems: 4 + description: + Interrupts that correspond to the 4 IRQ outputs of the controller. Usually + only one of the controller outputs will be connected as an usable interrupt + source. The remaining interrupts will be left without a valid value, e.g. + in an interrupts-extended list the disconnected positions will contain + an empty phandle reference <0>. + +required: + - compatible + - reg + - '#dma-cells' + - dma-channels + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + aic: interrupt-controller { + interrupt-controller; + #interrupt-cells = <3>; + }; + + admac: dma-controller@238200000 { + compatible = "apple,t8103-admac", "apple,admac"; + reg = <0x38200000 0x34000>; + dma-channels = <24>; + interrupts-extended = <0>, + <&aic AIC_IRQ 626 IRQ_TYPE_LEVEL_HIGH>, + <0>, + <0>; + #dma-cells = <1>; + }; diff --git a/Documentation/devicetree/bindings/dma/fsl,edma.yaml b/Documentation/devicetree/bindings/dma/fsl,edma.yaml new file mode 100644 index 000000000000..050e6cd57727 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/fsl,edma.yaml @@ -0,0 +1,155 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/fsl,edma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Freescale enhanced Direct Memory Access(eDMA) Controller + +description: | + The eDMA channels have multiplex capability by programmable + memory-mapped registers. channels are split into two groups, called + DMAMUX0 and DMAMUX1, specific DMA request source can only be multiplexed + by any channel of certain group, DMAMUX0 or DMAMUX1, but not both. + +maintainers: + - Peng Fan + +properties: + compatible: + oneOf: + - enum: + - fsl,vf610-edma + - fsl,imx7ulp-edma + - items: + - const: fsl,ls1028a-edma + - const: fsl,vf610-edma + + reg: + minItems: 2 + maxItems: 3 + + interrupts: + minItems: 2 + maxItems: 17 + + interrupt-names: + minItems: 2 + maxItems: 17 + + "#dma-cells": + const: 2 + + dma-channels: + const: 32 + + clocks: + maxItems: 2 + + clock-names: + maxItems: 2 + + big-endian: + description: | + If present registers and hardware scatter/gather descriptors of the + eDMA are implemented in big endian mode, otherwise in little mode. + type: boolean + +required: + - "#dma-cells" + - compatible + - reg + - interrupts + - clocks + - dma-channels + +allOf: + - $ref: "dma-controller.yaml#" + - if: + properties: + compatible: + contains: + const: fsl,vf610-edma + then: + properties: + clock-names: + items: + - const: dmamux0 + - const: dmamux1 + interrupts: + maxItems: 2 + interrupt-names: + items: + - const: edma-tx + - const: edma-err + reg: + maxItems: 3 + + - if: + properties: + compatible: + contains: + const: fsl,imx7ulp-edma + then: + properties: + clock-names: + items: + - const: dma + - const: dmamux0 + interrupts: + maxItems: 17 + reg: + maxItems: 2 + +unevaluatedProperties: false + +examples: + - | + #include + #include + + edma0: dma-controller@40018000 { + #dma-cells = <2>; + compatible = "fsl,vf610-edma"; + reg = <0x40018000 0x2000>, + <0x40024000 0x1000>, + <0x40025000 0x1000>; + interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>, + <0 9 IRQ_TYPE_LEVEL_HIGH>; + interrupt-names = "edma-tx", "edma-err"; + dma-channels = <32>; + clock-names = "dmamux0", "dmamux1"; + clocks = <&clks VF610_CLK_DMAMUX0>, <&clks VF610_CLK_DMAMUX1>; + }; + + - | + #include + #include + + edma1: dma-controller@40080000 { + #dma-cells = <2>; + compatible = "fsl,imx7ulp-edma"; + reg = <0x40080000 0x2000>, + <0x40210000 0x1000>; + dma-channels = <32>; + interrupts = , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + , + /* last is eDMA2-ERR interrupt */ + ; + clock-names = "dma", "dmamux0"; + clocks = <&pcc2 IMX7ULP_CLK_DMA1>, <&pcc2 IMX7ULP_CLK_DMA_MUX1>; + }; diff --git a/Documentation/devicetree/bindings/dma/fsl-edma.txt b/Documentation/devicetree/bindings/dma/fsl-edma.txt deleted file mode 100644 index ee1754739b4b..000000000000 --- a/Documentation/devicetree/bindings/dma/fsl-edma.txt +++ /dev/null @@ -1,111 +0,0 @@ -* Freescale enhanced Direct Memory Access(eDMA) Controller - - The eDMA channels have multiplex capability by programmble memory-mapped -registers. channels are split into two groups, called DMAMUX0 and DMAMUX1, -specific DMA request source can only be multiplexed by any channel of certain -group, DMAMUX0 or DMAMUX1, but not both. - -* eDMA Controller -Required properties: -- compatible : - - "fsl,vf610-edma" for eDMA used similar to that on Vybrid vf610 SoC - - "fsl,imx7ulp-edma" for eDMA2 used similar to that on i.mx7ulp - - "fsl,ls1028a-edma" followed by "fsl,vf610-edma" for eDMA used on the - LS1028A SoC. -- reg : Specifies base physical address(s) and size of the eDMA registers. - The 1st region is eDMA control register's address and size. - The 2nd and the 3rd regions are programmable channel multiplexing - control register's address and size. -- interrupts : A list of interrupt-specifiers, one for each entry in - interrupt-names on vf610 similar SoC. But for i.mx7ulp per channel - per transmission interrupt, total 16 channel interrupt and 1 - error interrupt(located in the last), no interrupt-names list on - i.mx7ulp for clean on dts. -- #dma-cells : Must be <2>. - The 1st cell specifies the DMAMUX(0 for DMAMUX0 and 1 for DMAMUX1). - Specific request source can only be multiplexed by specific channels - group called DMAMUX. - The 2nd cell specifies the request source(slot) ID. - See the SoC's reference manual for all the supported request sources. -- dma-channels : Number of channels supported by the controller -- clock-names : A list of channel group clock names. Should contain: - "dmamux0" - clock name of mux0 group - "dmamux1" - clock name of mux1 group - Note: No dmamux0 on i.mx7ulp, but another 'dma' clk added on i.mx7ulp. -- clocks : A list of phandle and clock-specifier pairs, one for each entry in - clock-names. - -Optional properties: -- big-endian: If present registers and hardware scatter/gather descriptors - of the eDMA are implemented in big endian mode, otherwise in little - mode. -- interrupt-names : Should contain the below on vf610 similar SoC but not used - on i.mx7ulp similar SoC: - "edma-tx" - the transmission interrupt - "edma-err" - the error interrupt - - -Examples: - -edma0: dma-controller@40018000 { - #dma-cells = <2>; - compatible = "fsl,vf610-edma"; - reg = <0x40018000 0x2000>, - <0x40024000 0x1000>, - <0x40025000 0x1000>; - interrupts = <0 8 IRQ_TYPE_LEVEL_HIGH>, - <0 9 IRQ_TYPE_LEVEL_HIGH>; - interrupt-names = "edma-tx", "edma-err"; - dma-channels = <32>; - clock-names = "dmamux0", "dmamux1"; - clocks = <&clks VF610_CLK_DMAMUX0>, - <&clks VF610_CLK_DMAMUX1>; -}; /* vf610 */ - -edma1: dma-controller@40080000 { - #dma-cells = <2>; - compatible = "fsl,imx7ulp-edma"; - reg = <0x40080000 0x2000>, - <0x40210000 0x1000>; - dma-channels = <32>; - interrupts = , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - , - /* last is eDMA2-ERR interrupt */ - ; - clock-names = "dma", "dmamux0"; - clocks = <&pcc2 IMX7ULP_CLK_DMA1>, - <&pcc2 IMX7ULP_CLK_DMA_MUX1>; -}; /* i.mx7ulp */ - -* DMA clients -DMA client drivers that uses the DMA function must use the format described -in the dma.txt file, using a two-cell specifier for each channel: the 1st -specifies the channel group(DMAMUX) in which this request can be multiplexed, -and the 2nd specifies the request source. - -Examples: - -sai2: sai@40031000 { - compatible = "fsl,vf610-sai"; - reg = <0x40031000 0x1000>; - interrupts = <0 86 IRQ_TYPE_LEVEL_HIGH>; - clock-names = "sai"; - clocks = <&clks VF610_CLK_SAI2>; - dma-names = "tx", "rx"; - dmas = <&edma0 0 21>, - <&edma0 0 20>; -}; diff --git a/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml b/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml index 54d68fc688b5..19ea8dcbcbce 100644 --- a/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml +++ b/Documentation/devicetree/bindings/dma/mediatek,uart-dma.yaml @@ -22,6 +22,7 @@ properties: - items: - enum: - mediatek,mt2712-uart-dma + - mediatek,mt8365-uart-dma - mediatek,mt8516-uart-dma - const: mediatek,mt6577-uart-dma - enum: diff --git a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml index 9dd1476d1849..7e575296df0c 100644 --- a/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml +++ b/Documentation/devicetree/bindings/dma/nvidia,tegra186-gpc-dma.yaml @@ -23,7 +23,9 @@ properties: oneOf: - const: nvidia,tegra186-gpcdma - items: - - const: nvidia,tegra194-gpcdma + - enum: + - nvidia,tegra234-gpcdma + - nvidia,tegra194-gpcdma - const: nvidia,tegra186-gpcdma "#dma-cells": diff --git a/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml new file mode 100644 index 000000000000..9bf3a1b164f1 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/qcom,bam-dma.yaml @@ -0,0 +1,100 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/qcom,bam-dma.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Technologies Inc BAM DMA controller + +maintainers: + - Andy Gross + - Bjorn Andersson + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + compatible: + enum: + # APQ8064, IPQ8064 and MSM8960 + - qcom,bam-v1.3.0 + # MSM8974, APQ8074 and APQ8084 + - qcom,bam-v1.4.0 + # MSM8916 + - qcom,bam-v1.7.0 + + clocks: + maxItems: 1 + + clock-names: + items: + - const: bam_clk + + "#dma-cells": + const: 1 + + interrupts: + maxItems: 1 + + iommus: + minItems: 1 + maxItems: 4 + + num-channels: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Indicates supported number of DMA channels in a remotely controlled bam. + + qcom,controlled-remotely: + type: boolean + description: + Indicates that the bam is controlled by remote proccessor i.e. execution + environment. + + qcom,ee: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + description: + Indicates the active Execution Environment identifier (0-7) used in the + secure world. + + qcom,num-ees: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + Indicates supported number of Execution Environments in a remotely + controlled bam. + + qcom,powered-remotely: + type: boolean + description: + Indicates that the bam is powered up by a remote processor but must be + initialized by the local processor. + + reg: + maxItems: 1 + +required: + - compatible + - "#dma-cells" + - interrupts + - qcom,ee + - reg + +additionalProperties: false + +examples: + - | + #include + #include + + dma-controller@f9944000 { + compatible = "qcom,bam-v1.4.0"; + reg = <0xf9944000 0x15000>; + interrupts = ; + clocks = <&gcc GCC_BLSP2_AHB_CLK>; + clock-names = "bam_clk"; + #dma-cells = <1>; + qcom,ee = <0>; + }; +... diff --git a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt b/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt deleted file mode 100644 index 6e9a5497b3f2..000000000000 --- a/Documentation/devicetree/bindings/dma/qcom_bam_dma.txt +++ /dev/null @@ -1,52 +0,0 @@ -QCOM BAM DMA controller - -Required properties: -- compatible: must be one of the following: - * "qcom,bam-v1.4.0" for MSM8974, APQ8074 and APQ8084 - * "qcom,bam-v1.3.0" for APQ8064, IPQ8064 and MSM8960 - * "qcom,bam-v1.7.0" for MSM8916 -- reg: Address range for DMA registers -- interrupts: Should contain the one interrupt shared by all channels -- #dma-cells: must be <1>, the cell in the dmas property of the client device - represents the channel number -- clocks: required clock -- clock-names: must contain "bam_clk" entry -- qcom,ee : indicates the active Execution Environment identifier (0-7) used in - the secure world. -- qcom,controlled-remotely : optional, indicates that the bam is controlled by - remote proccessor i.e. execution environment. -- qcom,powered-remotely : optional, indicates that the bam is powered up by - a remote processor but must be initialized by the local processor. -- num-channels : optional, indicates supported number of DMA channels in a - remotely controlled bam. -- qcom,num-ees : optional, indicates supported number of Execution Environments - in a remotely controlled bam. - -Example: - - uart-bam: dma@f9984000 = { - compatible = "qcom,bam-v1.4.0"; - reg = <0xf9984000 0x15000>; - interrupts = <0 94 0>; - clocks = <&gcc GCC_BAM_DMA_AHB_CLK>; - clock-names = "bam_clk"; - #dma-cells = <1>; - qcom,ee = <0>; - }; - -DMA clients must use the format described in the dma.txt file, using a two cell -specifier for each channel. - -Example: - serial@f991e000 { - compatible = "qcom,msm-uart"; - reg = <0xf991e000 0x1000> - <0xf9944000 0x19000>; - interrupts = <0 108 0>; - clocks = <&gcc GCC_BLSP1_UART2_APPS_CLK>, - <&gcc GCC_BLSP1_AHB_CLK>; - clock-names = "core", "iface"; - - dmas = <&uart-bam 0>, <&uart-bam 1>; - dma-names = "rx", "tx"; - }; diff --git a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml index 4324a94b26b2..67aa7bb6d36a 100644 --- a/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml +++ b/Documentation/devicetree/bindings/dma/snps,dw-axi-dmac.yaml @@ -34,7 +34,12 @@ properties: - const: axidma_apb_regs interrupts: - maxItems: 1 + description: + If the IP-core synthesis parameter DMAX_INTR_IO_TYPE is set to 1, this + will be per-channel interrupts. Otherwise, this is a single combined IRQ + for all channels. + minItems: 1 + maxItems: 8 clocks: items: diff --git a/Documentation/devicetree/bindings/dma/ste-dma40.txt b/Documentation/devicetree/bindings/dma/ste-dma40.txt deleted file mode 100644 index 99ab5c4d331e..000000000000 --- a/Documentation/devicetree/bindings/dma/ste-dma40.txt +++ /dev/null @@ -1,138 +0,0 @@ -* DMA40 DMA Controller - -Required properties: -- compatible: "stericsson,dma40" -- reg: Address range of the DMAC registers -- reg-names: Names of the above areas to use during resource look-up -- interrupt: Should contain the DMAC interrupt number -- #dma-cells: must be <3> -- memcpy-channels: Channels to be used for memcpy - -Optional properties: -- dma-channels: Number of channels supported by hardware - if not present - the driver will attempt to obtain the information from H/W -- disabled-channels: Channels which can not be used - -Example: - - dma: dma-controller@801c0000 { - compatible = "stericsson,db8500-dma40", "stericsson,dma40"; - reg = <0x801C0000 0x1000 0x40010000 0x800>; - reg-names = "base", "lcpa"; - interrupt-parent = <&intc>; - interrupts = <0 25 0x4>; - - #dma-cells = <2>; - memcpy-channels = <56 57 58 59 60>; - disabled-channels = <12>; - dma-channels = <8>; - }; - -Clients -Required properties: -- dmas: Comma separated list of dma channel requests -- dma-names: Names of the aforementioned requested channels - -Each dmas request consists of 4 cells: - 1. A phandle pointing to the DMA controller - 2. Device signal number, the signal line for single and burst requests - connected from the device to the DMA40 engine - 3. The DMA request line number (only when 'use fixed channel' is set) - 4. A 32bit mask specifying; mode, direction and endianness - [NB: This list will grow] - 0x00000001: Mode: - Logical channel when unset - Physical channel when set - 0x00000002: Direction: - Memory to Device when unset - Device to Memory when set - 0x00000004: Endianness: - Little endian when unset - Big endian when set - 0x00000008: Use fixed channel: - Use automatic channel selection when unset - Use DMA request line number when set - 0x00000010: Set channel as high priority: - Normal priority when unset - High priority when set - -Existing signal numbers for the DB8500 ASIC. Unless specified, the signals are -bidirectional, i.e. the same for RX and TX operations: - -0: SPI controller 0 -1: SD/MMC controller 0 (unused) -2: SD/MMC controller 1 (unused) -3: SD/MMC controller 2 (unused) -4: I2C port 1 -5: I2C port 3 -6: I2C port 2 -7: I2C port 4 -8: Synchronous Serial Port SSP0 -9: Synchronous Serial Port SSP1 -10: Multi-Channel Display Engine MCDE RX -11: UART port 2 -12: UART port 1 -13: UART port 0 -14: Multirate Serial Port MSP2 -15: I2C port 0 -16: USB OTG in/out endpoints 7 & 15 -17: USB OTG in/out endpoints 6 & 14 -18: USB OTG in/out endpoints 5 & 13 -19: USB OTG in/out endpoints 4 & 12 -20: SLIMbus or HSI channel 0 -21: SLIMbus or HSI channel 1 -22: SLIMbus or HSI channel 2 -23: SLIMbus or HSI channel 3 -24: Multimedia DSP SXA0 -25: Multimedia DSP SXA1 -26: Multimedia DSP SXA2 -27: Multimedia DSP SXA3 -28: SD/MM controller 2 -29: SD/MM controller 0 -30: MSP port 1 on DB8500 v1, MSP port 3 on DB8500 v2 -31: MSP port 0 or SLIMbus channel 0 -32: SD/MM controller 1 -33: SPI controller 2 -34: i2c3 RX2 TX2 -35: SPI controller 1 -36: USB OTG in/out endpoints 3 & 11 -37: USB OTG in/out endpoints 2 & 10 -38: USB OTG in/out endpoints 1 & 9 -39: USB OTG in/out endpoints 8 -40: SPI controller 3 -41: SD/MM controller 3 -42: SD/MM controller 4 -43: SD/MM controller 5 -44: Multimedia DSP SXA4 -45: Multimedia DSP SXA5 -46: SLIMbus channel 8 or Multimedia DSP SXA6 -47: SLIMbus channel 9 or Multimedia DSP SXA7 -48: Crypto Accelerator 1 -49: Crypto Accelerator 1 TX or Hash Accelerator 1 TX -50: Hash Accelerator 1 TX -51: memcpy TX (to be used by the DMA driver for memcpy operations) -52: SLIMbus or HSI channel 4 -53: SLIMbus or HSI channel 5 -54: SLIMbus or HSI channel 6 -55: SLIMbus or HSI channel 7 -56: memcpy (to be used by the DMA driver for memcpy operations) -57: memcpy (to be used by the DMA driver for memcpy operations) -58: memcpy (to be used by the DMA driver for memcpy operations) -59: memcpy (to be used by the DMA driver for memcpy operations) -60: memcpy (to be used by the DMA driver for memcpy operations) -61: Crypto Accelerator 0 -62: Crypto Accelerator 0 TX or Hash Accelerator 0 TX -63: Hash Accelerator 0 TX - -Example: - - uart@80120000 { - compatible = "arm,pl011", "arm,primecell"; - reg = <0x80120000 0x1000>; - interrupts = <0 11 0x4>; - - dmas = <&dma 13 0 0x2>, /* Logical - DevToMem */ - <&dma 13 0 0x0>; /* Logical - MemToDev */ - dma-names = "rx", "rx"; - - }; diff --git a/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml b/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml new file mode 100644 index 000000000000..8bddfb3b6fa0 --- /dev/null +++ b/Documentation/devicetree/bindings/dma/stericsson,dma40.yaml @@ -0,0 +1,159 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/dma/stericsson,dma40.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ST-Ericsson DMA40 DMA Engine + +maintainers: + - Linus Walleij + +allOf: + - $ref: "dma-controller.yaml#" + +properties: + "#dma-cells": + const: 3 + description: | + The first cell is the unique device channel number as indicated by this + table for DB8500 which is the only ASIC known to use DMA40: + + 0: SPI controller 0 + 1: SD/MMC controller 0 (unused) + 2: SD/MMC controller 1 (unused) + 3: SD/MMC controller 2 (unused) + 4: I2C port 1 + 5: I2C port 3 + 6: I2C port 2 + 7: I2C port 4 + 8: Synchronous Serial Port SSP0 + 9: Synchronous Serial Port SSP1 + 10: Multi-Channel Display Engine MCDE RX + 11: UART port 2 + 12: UART port 1 + 13: UART port 0 + 14: Multirate Serial Port MSP2 + 15: I2C port 0 + 16: USB OTG in/out endpoints 7 & 15 + 17: USB OTG in/out endpoints 6 & 14 + 18: USB OTG in/out endpoints 5 & 13 + 19: USB OTG in/out endpoints 4 & 12 + 20: SLIMbus or HSI channel 0 + 21: SLIMbus or HSI channel 1 + 22: SLIMbus or HSI channel 2 + 23: SLIMbus or HSI channel 3 + 24: Multimedia DSP SXA0 + 25: Multimedia DSP SXA1 + 26: Multimedia DSP SXA2 + 27: Multimedia DSP SXA3 + 28: SD/MMC controller 2 + 29: SD/MMC controller 0 + 30: MSP port 1 on DB8500 v1, MSP port 3 on DB8500 v2 + 31: MSP port 0 or SLIMbus channel 0 + 32: SD/MMC controller 1 + 33: SPI controller 2 + 34: i2c3 RX2 TX2 + 35: SPI controller 1 + 36: USB OTG in/out endpoints 3 & 11 + 37: USB OTG in/out endpoints 2 & 10 + 38: USB OTG in/out endpoints 1 & 9 + 39: USB OTG in/out endpoints 8 + 40: SPI controller 3 + 41: SD/MMC controller 3 + 42: SD/MMC controller 4 + 43: SD/MMC controller 5 + 44: Multimedia DSP SXA4 + 45: Multimedia DSP SXA5 + 46: SLIMbus channel 8 or Multimedia DSP SXA6 + 47: SLIMbus channel 9 or Multimedia DSP SXA7 + 48: Crypto Accelerator 1 + 49: Crypto Accelerator 1 TX or Hash Accelerator 1 TX + 50: Hash Accelerator 1 TX + 51: memcpy TX (to be used by the DMA driver for memcpy operations) + 52: SLIMbus or HSI channel 4 + 53: SLIMbus or HSI channel 5 + 54: SLIMbus or HSI channel 6 + 55: SLIMbus or HSI channel 7 + 56: memcpy (to be used by the DMA driver for memcpy operations) + 57: memcpy (to be used by the DMA driver for memcpy operations) + 58: memcpy (to be used by the DMA driver for memcpy operations) + 59: memcpy (to be used by the DMA driver for memcpy operations) + 60: memcpy (to be used by the DMA driver for memcpy operations) + 61: Crypto Accelerator 0 + 62: Crypto Accelerator 0 TX or Hash Accelerator 0 TX + 63: Hash Accelerator 0 TX + + The second cell is the DMA request line number. This is only used when + a fixed channel is allocated, and indicated by setting bit 3 in the + flags field (see below). + + The third cell is a 32bit flags bitfield with the following possible + bits set: + 0x00000001 (bit 0) - mode: + Logical channel when unset + Physical channel when set + 0x00000002 (bit 1) - direction: + Memory to Device when unset + Device to Memory when set + 0x00000004 (bit 2) - endianness: + Little endian when unset + Big endian when set + 0x00000008 (bit 3) - use fixed channel: + Use automatic channel selection when unset + Use DMA request line number when set + 0x00000010 (bit 4) - set channel as high priority: + Normal priority when unset + High priority when set + + compatible: + items: + - const: stericsson,db8500-dma40 + - const: stericsson,dma40 + + reg: + items: + - description: DMA40 memory base + - description: LCPA memory base + + reg-names: + items: + - const: base + - const: lcpa + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + memcpy-channels: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: Array of u32 elements indicating which channels on the DMA + engine are elegible for memcpy transfers + +required: + - "#dma-cells" + - compatible + - reg + - interrupts + - clocks + - memcpy-channels + +additionalProperties: false + +examples: + - | + #include + #include + #include + dma-controller@801C0000 { + compatible = "stericsson,db8500-dma40", "stericsson,dma40"; + reg = <0x801C0000 0x1000>, <0x40010000 0x800>; + reg-names = "base", "lcpa"; + interrupts = ; + #dma-cells = <3>; + memcpy-channels = <56 57 58 59 60>; + clocks = <&prcmu_clk PRCMU_DMACLK>; + }; +... diff --git a/Documentation/devicetree/bindings/eeprom/at25.yaml b/Documentation/devicetree/bindings/eeprom/at25.yaml index fbf99e346966..8b1c997caac1 100644 --- a/Documentation/devicetree/bindings/eeprom/at25.yaml +++ b/Documentation/devicetree/bindings/eeprom/at25.yaml @@ -44,8 +44,6 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - pagesize: $ref: /schemas/types.yaml#/definitions/uint32 enum: [1, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072] @@ -105,6 +103,7 @@ required: - spi-max-frequency allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# - if: properties: compatible: @@ -117,7 +116,7 @@ allOf: - size - address-width -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/misc/eeprom-93xx46.yaml b/Documentation/devicetree/bindings/eeprom/microchip,93lc46b.yaml similarity index 89% rename from Documentation/devicetree/bindings/misc/eeprom-93xx46.yaml rename to Documentation/devicetree/bindings/eeprom/microchip,93lc46b.yaml index 44fd2f6f0d8a..0c2f5ddb79c5 100644 --- a/Documentation/devicetree/bindings/misc/eeprom-93xx46.yaml +++ b/Documentation/devicetree/bindings/eeprom/microchip,93lc46b.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/misc/eeprom-93xx46.yaml# +$id: http://devicetree.org/schemas/eeprom/microchip,93lc46b.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Microchip 93xx46 SPI compatible EEPROM family dt bindings @@ -28,9 +28,6 @@ properties: description: chip select of EEPROM maxItems: 1 - spi-max-frequency: true - spi-cs-high: true - read-only: description: parameter-less property which disables writes to the EEPROM @@ -42,14 +39,16 @@ properties: of EEPROM (e.g. for SPI bus multiplexing) maxItems: 1 - required: - compatible - reg - data-size - spi-max-frequency -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/fpga/fpga-region.txt b/Documentation/devicetree/bindings/fpga/fpga-region.txt index 7d3515264838..6694ef29a267 100644 --- a/Documentation/devicetree/bindings/fpga/fpga-region.txt +++ b/Documentation/devicetree/bindings/fpga/fpga-region.txt @@ -330,7 +330,7 @@ succeeded. The Device Tree Overlay will contain: * "target-path" or "target" - The insertion point where the the contents of the overlay will go into the + The insertion point where the contents of the overlay will go into the live tree. target-path is a full path, while target is a phandle. * "ranges" The address space mapping from processor to FPGA bus(ses). diff --git a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml index f57d22d1ebd6..ae18603697d7 100644 --- a/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/fsl-imx-gpio.yaml @@ -37,6 +37,8 @@ properties: - fsl,imx8mp-gpio - fsl,imx8mq-gpio - fsl,imx8qxp-gpio + - fsl,imxrt1050-gpio + - fsl,imxrt1170-gpio - const: fsl,imx35-gpio reg: diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt b/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt deleted file mode 100644 index 0fc6700ed800..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +++ /dev/null @@ -1,93 +0,0 @@ -* Marvell EBU GPIO controller - -Required properties: - -- compatible : Should be "marvell,orion-gpio", "marvell,mv78200-gpio", - "marvell,armadaxp-gpio" or "marvell,armada-8k-gpio". - - "marvell,orion-gpio" should be used for Orion, Kirkwood, Dove, - Discovery (except MV78200) and Armada 370. "marvell,mv78200-gpio" - should be used for the Discovery MV78200. - - "marvel,armadaxp-gpio" should be used for all Armada XP SoCs - (MV78230, MV78260, MV78460). - - "marvell,armada-8k-gpio" should be used for the Armada 7K and 8K - SoCs (either from AP or CP), see - Documentation/devicetree/bindings/arm/marvell/ap80x-system-controller.txt - for specific details about the offset property. - -- reg: Address and length of the register set for the device. Only one - entry is expected, except for the "marvell,armadaxp-gpio" variant - for which two entries are expected: one for the general registers, - one for the per-cpu registers. Not used for marvell,armada-8k-gpio. - -- interrupts: The list of interrupts that are used for all the pins - managed by this GPIO bank. There can be more than one interrupt - (example: 1 interrupt per 8 pins on Armada XP, which means 4 - interrupts per bank of 32 GPIOs). - -- interrupt-controller: identifies the node as an interrupt controller - -- #interrupt-cells: specifies the number of cells needed to encode an - interrupt source. Should be two. - The first cell is the GPIO number. - The second cell is used to specify flags: - bits[3:0] trigger type and level flags: - 1 = low-to-high edge triggered. - 2 = high-to-low edge triggered. - 4 = active high level-sensitive. - 8 = active low level-sensitive. - -- gpio-controller: marks the device node as a gpio controller - -- ngpios: number of GPIOs this controller has - -- #gpio-cells: Should be two. The first cell is the pin number. The - second cell is reserved for flags, unused at the moment. - -Optional properties: - -In order to use the GPIO lines in PWM mode, some additional optional -properties are required. - -- compatible: Must contain "marvell,armada-370-gpio" - -- reg: an additional register set is needed, for the GPIO Blink - Counter on/off registers. - -- reg-names: Must contain an entry "pwm" corresponding to the - additional register range needed for PWM operation. - -- #pwm-cells: Should be two. The first cell is the GPIO line number. The - second cell is the period in nanoseconds. - -- clocks: Must be a phandle to the clock for the GPIO controller. - -Example: - - gpio0: gpio@d0018100 { - compatible = "marvell,armadaxp-gpio"; - reg = <0xd0018100 0x40>, - <0xd0018800 0x30>; - ngpios = <32>; - gpio-controller; - #gpio-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <16>, <17>, <18>, <19>; - }; - - gpio1: gpio@18140 { - compatible = "marvell,armada-370-gpio"; - reg = <0x18140 0x40>, <0x181c8 0x08>; - reg-names = "gpio", "pwm"; - ngpios = <17>; - gpio-controller; - #gpio-cells = <2>; - #pwm-cells = <2>; - interrupt-controller; - #interrupt-cells = <2>; - interrupts = <87>, <88>, <89>; - clocks = <&coreclk 0>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml new file mode 100644 index 000000000000..f1bd1e6b2e1f --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml @@ -0,0 +1,146 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-mvebu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Marvell EBU GPIO controller + +maintainers: + - Thomas Petazzoni + - Andrew Lunn + +properties: + compatible: + oneOf: + - enum: + - marvell,armada-8k-gpio + - marvell,orion-gpio + + - items: + - enum: + - marvell,mv78200-gpio + - marvell,armada-370-gpio + - const: marvell,orion-gpio + + - description: Deprecated binding + items: + - const: marvell,armadaxp-gpio + - const: marvell,orion-gpio + deprecated: true + + reg: + description: | + Address and length of the register set for the device. Not used for + marvell,armada-8k-gpio. + + A second entry can be provided, for the PWM function using the GPIO Blink + Counter on/off registers. + minItems: 1 + maxItems: 2 + + reg-names: + items: + - const: gpio + - const: pwm + minItems: 1 + + offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset in the register map for the gpio registers (in bytes) + + interrupts: + description: | + The list of interrupts that are used for all the pins managed by this + GPIO bank. There can be more than one interrupt (example: 1 interrupt + per 8 pins on Armada XP, which means 4 interrupts per bank of 32 + GPIOs). + minItems: 1 + maxItems: 4 + + interrupt-controller: true + + "#interrupt-cells": + const: 2 + + gpio-controller: true + + ngpios: + minimum: 1 + maximum: 32 + + "#gpio-cells": + const: 2 + + marvell,pwm-offset: + $ref: /schemas/types.yaml#/definitions/uint32 + description: Offset in the register map for the pwm registers (in bytes) + + "#pwm-cells": + description: + The first cell is the GPIO line number. The second cell is the period + in nanoseconds. + const: 2 + + clocks: + description: + Clock(s) used for PWM function. + items: + - description: Core clock + - description: AXI bus clock + minItems: 1 + + clock-names: + items: + - const: core + - const: axi + minItems: 1 + +required: + - compatible + - gpio-controller + - ngpios + - "#gpio-cells" + +allOf: + - if: + properties: + compatible: + contains: + const: marvell,armada-8k-gpio + then: + required: + - offset + else: + required: + - reg + +unevaluatedProperties: true + +examples: + - | + gpio@d0018100 { + compatible = "marvell,armadaxp-gpio", "marvell,orion-gpio"; + reg = <0xd0018100 0x40>, <0xd0018800 0x30>; + ngpios = <32>; + gpio-controller; + #gpio-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <16>, <17>, <18>, <19>; + }; + + - | + gpio@18140 { + compatible = "marvell,armada-370-gpio", "marvell,orion-gpio"; + reg = <0x18140 0x40>, <0x181c8 0x08>; + reg-names = "gpio", "pwm"; + ngpios = <17>; + gpio-controller; + #gpio-cells = <2>; + #pwm-cells = <2>; + interrupt-controller; + #interrupt-cells = <2>; + interrupts = <87>, <88>, <89>; + clocks = <&coreclk 0>; + }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml index 338c5312a106..1acaa0a3d35a 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml +++ b/Documentation/devicetree/bindings/gpio/gpio-pca9570.yaml @@ -13,6 +13,7 @@ properties: compatible: enum: - nxp,pca9570 + - nxp,pca9571 reg: maxItems: 1 diff --git a/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt b/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt index 414a01cdf715..fba3c61f6a5b 100644 --- a/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt +++ b/Documentation/devicetree/bindings/gpio/gpio-pisosr.txt @@ -14,7 +14,7 @@ Optional properties: - ngpios : Number of used GPIO lines (0..n-1), default is 8. - load-gpios : GPIO pin specifier attached to load enable, this pin is pulsed before reading from the device to - load input pin values into the the device. + load input pin values into the device. For other required and optional properties of SPI slave nodes please refer to ../spi/spi-bus.txt. diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt deleted file mode 100644 index 1afc2de7a537..000000000000 --- a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.txt +++ /dev/null @@ -1,16 +0,0 @@ -TPIC2810 GPIO controller bindings - -Required properties: - - compatible : Should be "ti,tpic2810". - - reg : The I2C address of the device - - gpio-controller : Marks the device node as a GPIO controller. - - #gpio-cells : Should be two. For consumer use see gpio.txt. - -Example: - - gpio@60 { - compatible = "ti,tpic2810"; - reg = <0x60>; - gpio-controller; - #gpio-cells = <2>; - }; diff --git a/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml new file mode 100644 index 000000000000..cb8a5c376e1e --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/gpio-tpic2810.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/gpio-tpic2810.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: TPIC2810 GPIO controller bindings + +maintainers: + - Aswath Govindraju + +properties: + compatible: + enum: + - ti,tpic2810 + + reg: + maxItems: 1 + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-line-names: + minItems: 1 + maxItems: 32 + +required: + - compatible + - reg + - gpio-controller + - "#gpio-cells" + +additionalProperties: false + +examples: + - | + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + gpio@60 { + compatible = "ti,tpic2810"; + reg = <0x60>; + gpio-controller; + #gpio-cells = <2>; + gpio-line-names = "LED A", "LED B", "LED C"; + }; + }; diff --git a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml index 0681a4790cd6..75e5da6a7cc0 100644 --- a/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml +++ b/Documentation/devicetree/bindings/gpio/renesas,rcar-gpio.yaml @@ -48,11 +48,9 @@ properties: - renesas,gpio-r8a77995 # R-Car D3 - const: renesas,rcar-gen3-gpio # R-Car Gen3 or RZ/G2 - - items: - - const: renesas,gpio-r8a779a0 # R-Car V3U - - items: - enum: + - renesas,gpio-r8a779a0 # R-Car V3U - renesas,gpio-r8a779f0 # R-Car S4-8 - const: renesas,rcar-gen4-gpio # R-Car Gen4 diff --git a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml index d4e42c2b995b..affd823c881d 100644 --- a/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml +++ b/Documentation/devicetree/bindings/gpio/rockchip,gpio-bank.yaml @@ -27,6 +27,8 @@ properties: - description: APB interface clock source - description: GPIO debounce reference clock source + gpio-ranges: true + gpio-controller: true gpio-line-names: true diff --git a/Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml b/Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml similarity index 92% rename from Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml rename to Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml index 4bb8efb83ac1..95cbdcb56efe 100644 --- a/Documentation/devicetree/bindings/soc/samsung/exynos-chipid.yaml +++ b/Documentation/devicetree/bindings/hwinfo/samsung,exynos-chipid.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: http://devicetree.org/schemas/soc/samsung/exynos-chipid.yaml# +$id: http://devicetree.org/schemas/hwinfo/samsung,exynos-chipid.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Samsung Exynos SoC series Chipid driver diff --git a/Documentation/devicetree/bindings/hwinfo/samsung,s5pv210-chipid.yaml b/Documentation/devicetree/bindings/hwinfo/samsung,s5pv210-chipid.yaml new file mode 100644 index 000000000000..563ded4fca83 --- /dev/null +++ b/Documentation/devicetree/bindings/hwinfo/samsung,s5pv210-chipid.yaml @@ -0,0 +1,30 @@ +# SPDX-License-Identifier: GPL-2.0 OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/hwinfo/samsung,s5pv210-chipid.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung S5PV210 SoC ChipID + +maintainers: + - Krzysztof Kozlowski + +properties: + compatible: + const: samsung,s5pv210-chipid + + reg: + maxItems: 1 + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + chipid@e0000000 { + compatible = "samsung,s5pv210-chipid"; + reg = <0xe0000000 0x1000>; + }; diff --git a/Documentation/devicetree/bindings/soc/ti/k3-socinfo.yaml b/Documentation/devicetree/bindings/hwinfo/ti,k3-socinfo.yaml similarity index 92% rename from Documentation/devicetree/bindings/soc/ti/k3-socinfo.yaml rename to Documentation/devicetree/bindings/hwinfo/ti,k3-socinfo.yaml index a1a8423b2e2e..dada28b47ea0 100644 --- a/Documentation/devicetree/bindings/soc/ti/k3-socinfo.yaml +++ b/Documentation/devicetree/bindings/hwinfo/ti,k3-socinfo.yaml @@ -1,7 +1,7 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: http://devicetree.org/schemas/soc/ti/k3-socinfo.yaml# +$id: http://devicetree.org/schemas/hwinfo/ti,k3-socinfo.yaml# $schema: http://devicetree.org/meta-schemas/core.yaml# title: Texas Instruments K3 Multicore SoC platforms chipid module diff --git a/Documentation/devicetree/bindings/hwmon/adt7475.yaml b/Documentation/devicetree/bindings/hwmon/adt7475.yaml index 56baf2e5c6d1..ea595102a86e 100644 --- a/Documentation/devicetree/bindings/hwmon/adt7475.yaml +++ b/Documentation/devicetree/bindings/hwmon/adt7475.yaml @@ -57,7 +57,7 @@ patternProperties: Configures bypassing the individual voltage input attenuator. If set to 1 the attenuator is bypassed if set to 0 the attenuator is not bypassed. If the property is absent then the attenuator - retains it's configuration from the bios/bootloader. + retains its configuration from the bios/bootloader. $ref: /schemas/types.yaml#/definitions/uint32 enum: [0, 1] diff --git a/Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt b/Documentation/devicetree/bindings/hwmon/ibm,p8-occ-hwmon.txt similarity index 100% rename from Documentation/devicetree/bindings/i2c/ibm,p8-occ-hwmon.txt rename to Documentation/devicetree/bindings/hwmon/ibm,p8-occ-hwmon.txt diff --git a/Documentation/devicetree/bindings/i2c/i2c-efm32.txt b/Documentation/devicetree/bindings/i2c/i2c-efm32.txt deleted file mode 100644 index 3b30e54ae3c7..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-efm32.txt +++ /dev/null @@ -1,33 +0,0 @@ -* Energymicro efm32 i2c controller - -Required properties : - - - reg : Offset and length of the register set for the device - - compatible : should be "energymicro,efm32-i2c" - - interrupts : the interrupt number - - clocks : reference to the module clock - -Recommended properties : - - - clock-frequency : maximal I2C bus clock frequency in Hz. - - energymicro,location : Decides the location of the USART I/O pins. - Allowed range : [0 .. 6] - -Example: - i2c0: i2c@4000a000 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "energymicro,efm32-i2c"; - reg = <0x4000a000 0x400>; - interrupts = <9>; - clocks = <&cmu clk_HFPERCLKI2C0>; - clock-frequency = <100000>; - energymicro,location = <3>; - - eeprom@50 { - compatible = "microchip,24c02"; - reg = <0x50>; - pagesize = <16>; - }; - }; - diff --git a/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml b/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml index a8f7720d1e3e..29bd16dab546 100644 --- a/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml +++ b/Documentation/devicetree/bindings/iio/dac/adi,ad5766.yaml @@ -22,6 +22,8 @@ properties: - adi,ad5767 output-range-microvolts: + $ref: /schemas/types.yaml#/definitions/int32-array + maxItems: 2 description: Select converter output range. reg: diff --git a/Documentation/devicetree/bindings/input/elan,ekth6915.yaml b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml new file mode 100644 index 000000000000..05e6f2df604c --- /dev/null +++ b/Documentation/devicetree/bindings/input/elan,ekth6915.yaml @@ -0,0 +1,65 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/input/elan,ekth6915.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Elan eKTH6915 touchscreen controller + +maintainers: + - Douglas Anderson + +description: + Supports the Elan eKTH6915 touchscreen controller. + This touchscreen controller uses the i2c-hid protocol with a reset GPIO. + +properties: + compatible: + items: + - const: elan,ekth6915 + + reg: + const: 0x10 + + interrupts: + maxItems: 1 + + reset-gpios: + description: Reset GPIO; not all touchscreens using eKTH6915 hook this up. + + vcc33-supply: + description: The 3.3V supply to the touchscreen. + + vccio-supply: + description: + The IO supply to the touchscreen. Need not be specified if this is the + same as the 3.3V supply. + +required: + - compatible + - reg + - interrupts + - vcc33-supply + +additionalProperties: false + +examples: + - | + #include + #include + + i2c { + #address-cells = <1>; + #size-cells = <0>; + + ap_ts: touchscreen@10 { + compatible = "elan,ekth6915"; + reg = <0x10>; + + interrupt-parent = <&tlmm>; + interrupts = <9 IRQ_TYPE_LEVEL_LOW>; + + reset-gpios = <&tlmm 8 GPIO_ACTIVE_LOW>; + vcc33-supply = <&pp3300_ts>; + }; + }; diff --git a/Documentation/devicetree/bindings/input/touchscreen/ektf2127.txt b/Documentation/devicetree/bindings/input/touchscreen/ektf2127.txt index 5eef5e7d6aae..c9f2c9f578e3 100644 --- a/Documentation/devicetree/bindings/input/touchscreen/ektf2127.txt +++ b/Documentation/devicetree/bindings/input/touchscreen/ektf2127.txt @@ -6,7 +6,7 @@ Required properties: - interrupts : interrupt specification for the ektf2127 interrupt - power-gpios : GPIO specification for the pin connected to the ektf2127's wake input. This needs to be driven high - to take ektf2127 out of it's low power state + to take ektf2127 out of its low power state For additional optional properties see: touchscreen.txt diff --git a/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt b/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt deleted file mode 100644 index e0062aebf025..000000000000 --- a/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt +++ /dev/null @@ -1,61 +0,0 @@ -RDA Micro RDA8810PL Interrupt Controller - -The interrupt controller in RDA8810PL SoC is a custom interrupt controller -which supports up to 32 interrupts. - -Required properties: - -- compatible: Should be "rda,8810pl-intc". -- reg: Specifies base physical address of the registers set. -- interrupt-controller: Identifies the node as an interrupt controller. -- #interrupt-cells: Specifies the number of cells needed to encode an - interrupt source. The value shall be 2. - -The interrupt sources are as follows: - -ID Name ------------- -0: PULSE_DUMMY -1: I2C -2: NAND_NFSC -3: SDMMC1 -4: SDMMC2 -5: SDMMC3 -6: SPI1 -7: SPI2 -8: SPI3 -9: UART1 -10: UART2 -11: UART3 -12: GPIO1 -13: GPIO2 -14: GPIO3 -15: KEYPAD -16: TIMER -17: TIMEROS -18: COMREG0 -19: COMREG1 -20: USB -21: DMC -22: DMA -23: CAMERA -24: GOUDA -25: GPU -26: VPU_JPG -27: VPU_HOST -28: VOC -29: AUIFC0 -30: AUIFC1 -31: L2CC - -Example: - apb@20800000 { - compatible = "simple-bus"; - ... - intc: interrupt-controller@0 { - compatible = "rda,8810pl-intc"; - reg = <0x0 0x1000>; - interrupt-controller; - #interrupt-cells = <2>; - }; - }; diff --git a/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.yaml b/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.yaml new file mode 100644 index 000000000000..96d6285d0087 --- /dev/null +++ b/Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.yaml @@ -0,0 +1,43 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/interrupt-controller/rda,8810pl-intc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: RDA Micro RDA8810PL interrupt controller + +maintainers: + - Manivannan Sadhasivam + +allOf: + - $ref: /schemas/interrupt-controller.yaml# + +properties: + compatible: + const: rda,8810pl-intc + + reg: + maxItems: 1 + + interrupt-controller: true + + '#interrupt-cells': + const: 2 + +required: + - compatible + - reg + - interrupt-controller + - '#interrupt-cells' + +additionalProperties: false + +examples: + - | + intc: interrupt-controller@0 { + compatible = "rda,8810pl-intc"; + reg = <0x0 0x1000>; + interrupt-controller; + #interrupt-cells = <2>; + }; +... diff --git a/Documentation/devicetree/bindings/leds/issi,is31fl319x.yaml b/Documentation/devicetree/bindings/leds/issi,is31fl319x.yaml new file mode 100644 index 000000000000..940333f2d69c --- /dev/null +++ b/Documentation/devicetree/bindings/leds/issi,is31fl319x.yaml @@ -0,0 +1,193 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/issi,is31fl319x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ISSI LED controllers bindings for IS31FL319{0,1,3,6,9} + +maintainers: + - Vincent Knecht + +description: | + The IS31FL319X are LED controllers with I2C interface. + Previously known as Si-En SN319{0,1,3,6,9}. + + For more product information please see the links below: + https://lumissil.com/assets/pdf/core/IS31FL3190_DS.pdf + https://lumissil.com/assets/pdf/core/IS31FL3191_DS.pdf + https://lumissil.com/assets/pdf/core/IS31FL3193_DS.pdf + https://lumissil.com/assets/pdf/core/IS31FL3196_DS.pdf + https://lumissil.com/assets/pdf/core/IS31FL3199_DS.pdf + +properties: + compatible: + enum: + - issi,is31fl3190 + - issi,is31fl3191 + - issi,is31fl3193 + - issi,is31fl3196 + - issi,is31fl3199 + - si-en,sn3190 + - si-en,sn3191 + - si-en,sn3193 + - si-en,sn3196 + - si-en,sn3199 + + reg: + maxItems: 1 + + shutdown-gpios: + maxItems: 1 + description: GPIO attached to the SDB pin. + + audio-gain-db: + $ref: /schemas/types.yaml#/definitions/uint32 + default: 0 + description: Audio gain selection for external analog modulation input. + enum: [0, 3, 6, 9, 12, 15, 18, 21] + + "#address-cells": + const: 1 + + "#size-cells": + const: 0 + +patternProperties: + "^led@[1-9]$": + type: object + $ref: common.yaml# + + properties: + reg: + description: Index of the LED. + minimum: 1 + maximum: 9 + + led-max-microamp: + description: + Note that a driver will take the lowest of all LED limits + since the chip has a single global setting. The lowest value + will be chosen due to the PWM specificity, where lower + brightness is achieved by reducing the duty-cycle of pulses + and not the current, which will always have its peak value + equal to led-max-microamp. + +allOf: + - if: + properties: + compatible: + contains: + enum: + - issi,is31fl3190 + - issi,is31fl3191 + - issi,is31fl3193 + - si-en,sn3190 + - si-en,sn3191 + - si-en,sn3193 + then: + properties: + reg: + enum: [0x68, 0x69, 0x6a, 0x6b] + + audio-gain-db: false + + patternProperties: + "^led@[1-9]$": + properties: + led-max-microamp: + default: 42000 + enum: [5000, 10000, 17500, 30000, 42000] + else: + properties: + reg: + enum: [0x64, 0x65, 0x66, 0x67] + + patternProperties: + "^led@[1-9]$": + properties: + led-max-microamp: + default: 20000 + enum: [5000, 10000, 15000, 20000, 25000, 30000, 35000, 40000] + - if: + properties: + compatible: + contains: + enum: + - issi,is31fl3190 + - issi,is31fl3191 + - si-en,sn3190 + - si-en,sn3191 + then: + patternProperties: + "^led@[1-9]$": + properties: + reg: + maximum: 1 + - if: + properties: + compatible: + contains: + enum: + - issi,is31fl3193 + - si-en,sn3193 + then: + patternProperties: + "^led@[1-9]$": + properties: + reg: + maximum: 3 + - if: + properties: + compatible: + contains: + enum: + - issi,is31fl3196 + - si-en,sn3196 + then: + patternProperties: + "^led@[1-9]$": + properties: + reg: + maximum: 6 + +required: + - compatible + - reg + - "#address-cells" + - "#size-cells" + +additionalProperties: false + +examples: + - | + #include + #include + + i2c0 { + #address-cells = <1>; + #size-cells = <0>; + + led-controller@65 { + compatible = "issi,is31fl3196"; + reg = <0x65>; + #address-cells = <1>; + #size-cells = <0>; + + shutdown-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; + + led@1 { + reg = <1>; + label = "red:aux"; + led-max-microamp = <10000>; + }; + + led@5 { + reg = <5>; + label = "green:power"; + linux,default-trigger = "default-on"; + }; + }; + }; +... + diff --git a/Documentation/devicetree/bindings/leds/leds-aat1290.txt b/Documentation/devicetree/bindings/leds/leds-aat1290.txt deleted file mode 100644 index 62ed17ec075b..000000000000 --- a/Documentation/devicetree/bindings/leds/leds-aat1290.txt +++ /dev/null @@ -1,77 +0,0 @@ -* Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs - -The device is controlled through two pins: FL_EN and EN_SET. The pins when, -asserted high, enable flash strobe and movie mode (max 1/2 of flash current) -respectively. In order to add a capability of selecting the strobe signal source -(e.g. CPU or camera sensor) there is an additional switch required, independent -of the flash chip. The switch is controlled with pin control. - -Required properties: - -- compatible : Must be "skyworks,aat1290". -- flen-gpios : Must be device tree identifier of the flash device FL_EN pin. -- enset-gpios : Must be device tree identifier of the flash device EN_SET pin. - -Optional properties: -- pinctrl-names : Must contain entries: "default", "host", "isp". Entries - "default" and "host" must refer to the same pin configuration - node, which sets the host as a strobe signal provider. Entry - "isp" must refer to the pin configuration node, which sets the - ISP as a strobe signal provider. - -A discrete LED element connected to the device must be represented by a child -node - see Documentation/devicetree/bindings/leds/common.txt. - -Required properties of the LED child node: -- led-max-microamp : see Documentation/devicetree/bindings/leds/common.txt -- flash-max-microamp : see Documentation/devicetree/bindings/leds/common.txt - Maximum flash LED supply current can be calculated using - following formula: I = 1A * 162kohm / Rset. -- flash-max-timeout-us : see Documentation/devicetree/bindings/leds/common.txt - Maximum flash timeout can be calculated using following - formula: T = 8.82 * 10^9 * Ct. - -Optional properties of the LED child node: -- function : see Documentation/devicetree/bindings/leds/common.txt -- color : see Documentation/devicetree/bindings/leds/common.txt -- label : see Documentation/devicetree/bindings/leds/common.txt (deprecated) - -Example (by Ct = 220nF, Rset = 160kohm and exynos4412-trats2 board with -a switch that allows for routing strobe signal either from the host or from -the camera sensor): - -#include "exynos4412.dtsi" -#include - -led-controller { - compatible = "skyworks,aat1290"; - flen-gpios = <&gpj1 1 GPIO_ACTIVE_HIGH>; - enset-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>; - - pinctrl-names = "default", "host", "isp"; - pinctrl-0 = <&camera_flash_host>; - pinctrl-1 = <&camera_flash_host>; - pinctrl-2 = <&camera_flash_isp>; - - camera_flash: led { - function = LED_FUNCTION_FLASH; - color = ; - led-max-microamp = <520833>; - flash-max-microamp = <1012500>; - flash-max-timeout-us = <1940000>; - }; -}; - -&pinctrl_0 { - camera_flash_host: camera-flash-host { - samsung,pins = "gpj1-0"; - samsung,pin-function = <1>; - samsung,pin-val = <0>; - }; - - camera_flash_isp: camera-flash-isp { - samsung,pins = "gpj1-0"; - samsung,pin-function = <1>; - samsung,pin-val = <1>; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt b/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt deleted file mode 100644 index 676d43ec8169..000000000000 --- a/Documentation/devicetree/bindings/leds/leds-is31fl319x.txt +++ /dev/null @@ -1,61 +0,0 @@ -LEDs connected to is31fl319x LED controller chip - -Required properties: -- compatible : Should be any of - "issi,is31fl3190" - "issi,is31fl3191" - "issi,is31fl3193" - "issi,is31fl3196" - "issi,is31fl3199" - "si-en,sn3199". -- #address-cells: Must be 1. -- #size-cells: Must be 0. -- reg: 0x64, 0x65, 0x66, or 0x67. - -Optional properties: -- audio-gain-db : audio gain selection for external analog modulation input. - Valid values: 0 - 21, step by 3 (rounded down) - Default: 0 -- shutdown-gpios : Specifier of the GPIO connected to SDB pin of the chip. - -Each led is represented as a sub-node of the issi,is31fl319x device. -There can be less leds subnodes than the chip can support but not more. - -Required led sub-node properties: -- reg : number of LED line - Valid values: 1 - number of leds supported by the chip variant. - -Optional led sub-node properties: -- label : see Documentation/devicetree/bindings/leds/common.txt. -- linux,default-trigger : - see Documentation/devicetree/bindings/leds/common.txt. -- led-max-microamp : (optional) - Valid values: 5000 - 40000, step by 5000 (rounded down) - Default: 20000 (20 mA) - Note: a driver will take the lowest of all led limits since the - chip has a single global setting. The lowest value will be chosen - due to the PWM specificity, where lower brightness is achieved - by reducing the dury-cycle of pulses and not the current, which - will always have its peak value equal to led-max-microamp. - -Examples: - -fancy_leds: leds@65 { - compatible = "issi,is31fl3196"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0x65>; - shutdown-gpios = <&gpio0 11 GPIO_ACTIVE_HIGH>; - - red_aux: led@1 { - label = "red:aux"; - reg = <1>; - led-max-microamp = <10000>; - }; - - green_power: led@5 { - label = "green:power"; - reg = <5>; - linux,default-trigger = "default-on"; - }; -}; diff --git a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml index f12fe5b53f30..d11898567313 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml +++ b/Documentation/devicetree/bindings/leds/leds-lp50xx.yaml @@ -78,60 +78,66 @@ additionalProperties: false examples: - | - #include - #include + #include + #include - i2c { - #address-cells = <1>; - #size-cells = <0>; + i2c { + #address-cells = <1>; + #size-cells = <0>; - led-controller@14 { - compatible = "ti,lp5009"; - reg = <0x14>; - #address-cells = <1>; - #size-cells = <0>; - enable-gpios = <&gpio1 16>; - - multi-led@1 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0x1>; - color = ; - function = LED_FUNCTION_CHARGING; - - led-0 { - color = ; - }; - - led-1 { - color = ; - }; - - led-2 { - color = ; - }; - }; - - multi-led@2 { + led-controller@14 { + compatible = "ti,lp5009"; + reg = <0x14>; #address-cells = <1>; - #size-cells = <2>; - reg = <0x2 0x3 0x5>; - color = ; - function = LED_FUNCTION_STANDBY; + #size-cells = <0>; + enable-gpios = <&gpio1 16>; - led-6 { - color = ; + multi-led@1 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x1>; + color = ; + function = LED_FUNCTION_CHARGING; + + led@0 { + reg = <0x0>; + color = ; + }; + + led@1 { + reg = <0x1>; + color = ; + }; + + led@2 { + reg = <0x2>; + color = ; + }; }; - led-7 { - color = ; - }; + multi-led@3 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x3>, <0x4>, <0x5>; + color = ; + function = LED_FUNCTION_STANDBY; - led-8 { - color = ; + led@3 { + reg = <0x3>; + color = ; + }; + + led@4 { + reg = <0x4>; + color = ; + }; + + led@5 { + reg = <0x5>; + color = ; + }; }; - }; - }; + }; }; ... diff --git a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml index f552cd143d5b..7ec676e53851 100644 --- a/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml +++ b/Documentation/devicetree/bindings/leds/leds-lp55xx.yaml @@ -108,119 +108,119 @@ additionalProperties: false examples: - | - #include + #include - i2c { - #address-cells = <1>; - #size-cells = <0>; + i2c { + #address-cells = <1>; + #size-cells = <0>; - led-controller@32 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "ti,lp8501"; - reg = <0x32>; - clock-mode = /bits/ 8 <2>; - pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */ + led-controller@32 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "ti,lp8501"; + reg = <0x32>; + clock-mode = /bits/ 8 <2>; + pwr-sel = /bits/ 8 <3>; /* D1~9 connected to VOUT */ - led@0 { - reg = <0>; - chan-name = "d1"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@0 { + reg = <0>; + chan-name = "d1"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@1 { - reg = <1>; - chan-name = "d2"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@1 { + reg = <1>; + chan-name = "d2"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@2 { - reg = <2>; - chan-name = "d3"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@2 { + reg = <2>; + chan-name = "d3"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@3 { - reg = <3>; - chan-name = "d4"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@3 { + reg = <3>; + chan-name = "d4"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@4 { - reg = <4>; - chan-name = "d5"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@4 { + reg = <4>; + chan-name = "d5"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@5 { - reg = <5>; - chan-name = "d6"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@5 { + reg = <5>; + chan-name = "d6"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@6 { - reg = <6>; - chan-name = "d7"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@6 { + reg = <6>; + chan-name = "d7"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@7 { - reg = <7>; - chan-name = "d8"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@7 { + reg = <7>; + chan-name = "d8"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; - led@8 { - reg = <8>; - chan-name = "d9"; - led-cur = /bits/ 8 <0x14>; - max-cur = /bits/ 8 <0x20>; - }; + led@8 { + reg = <8>; + chan-name = "d9"; + led-cur = /bits/ 8 <0x14>; + max-cur = /bits/ 8 <0x20>; + }; }; - led-controller@33 { - #address-cells = <1>; - #size-cells = <0>; - compatible = "national,lp5523"; - reg = <0x33>; - clock-mode = /bits/ 8 <0>; + led-controller@33 { + #address-cells = <1>; + #size-cells = <0>; + compatible = "national,lp5523"; + reg = <0x33>; + clock-mode = /bits/ 8 <0>; - multi-led@2 { - #address-cells = <1>; - #size-cells = <0>; - reg = <0x2>; - color = ; - function = LED_FUNCTION_STANDBY; - linux,default-trigger = "heartbeat"; + multi-led@2 { + #address-cells = <1>; + #size-cells = <0>; + reg = <0x2>; + color = ; + function = LED_FUNCTION_STANDBY; + linux,default-trigger = "heartbeat"; - led@0 { - led-cur = /bits/ 8 <50>; - max-cur = /bits/ 8 <100>; - reg = <0x0>; - color = ; - }; + led@0 { + led-cur = /bits/ 8 <50>; + max-cur = /bits/ 8 <100>; + reg = <0x0>; + color = ; + }; - led@1 { - led-cur = /bits/ 8 <50>; - max-cur = /bits/ 8 <100>; - reg = <0x1>; - color = ; - }; + led@1 { + led-cur = /bits/ 8 <50>; + max-cur = /bits/ 8 <100>; + reg = <0x1>; + color = ; + }; - led@6 { - led-cur = /bits/ 8 <50>; - max-cur = /bits/ 8 <100>; - reg = <0x6>; - color = ; - }; + led@6 { + led-cur = /bits/ 8 <50>; + max-cur = /bits/ 8 <100>; + reg = <0x6>; + color = ; + }; }; }; }; diff --git a/Documentation/devicetree/bindings/leds/leds-pwm-multicolor.yaml b/Documentation/devicetree/bindings/leds/leds-pwm-multicolor.yaml index 6625a528f727..fdaf04e03a8d 100644 --- a/Documentation/devicetree/bindings/leds/leds-pwm-multicolor.yaml +++ b/Documentation/devicetree/bindings/leds/leds-pwm-multicolor.yaml @@ -55,24 +55,24 @@ examples: compatible = "pwm-leds-multicolor"; multi-led { - color = ; - function = LED_FUNCTION_INDICATOR; - max-brightness = <65535>; + color = ; + function = LED_FUNCTION_INDICATOR; + max-brightness = <65535>; - led-red { - pwms = <&pwm1 0 1000000>; - color = ; - }; + led-red { + pwms = <&pwm1 0 1000000>; + color = ; + }; - led-green { - pwms = <&pwm2 0 1000000>; - color = ; - }; + led-green { + pwms = <&pwm2 0 1000000>; + color = ; + }; - led-blue { - pwms = <&pwm3 0 1000000>; - color = ; - }; + led-blue { + pwms = <&pwm3 0 1000000>; + color = ; + }; }; }; diff --git a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml index 409a4c7298e1..cd02811583ec 100644 --- a/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml +++ b/Documentation/devicetree/bindings/leds/leds-qcom-lpg.yaml @@ -17,6 +17,7 @@ description: > properties: compatible: enum: + - qcom,pm660l-lpg - qcom,pm8150b-lpg - qcom,pm8150l-lpg - qcom,pm8350c-pwm diff --git a/Documentation/devicetree/bindings/leds/skyworks,aat1290.yaml b/Documentation/devicetree/bindings/leds/skyworks,aat1290.yaml new file mode 100644 index 000000000000..a6aaa92dbccd --- /dev/null +++ b/Documentation/devicetree/bindings/leds/skyworks,aat1290.yaml @@ -0,0 +1,95 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/leds/skyworks,aat1290.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Skyworks Solutions, Inc. AAT1290 Current Regulator for Flash LEDs + +maintainers: + - Jacek Anaszewski + - Krzysztof Kozlowski + +description: | + The device is controlled through two pins:: FL_EN and EN_SET. The pins when, + asserted high, enable flash strobe and movie mode (max 1/2 of flash current) + respectively. In order to add a capability of selecting the strobe signal + source (e.g. CPU or camera sensor) there is an additional switch required, + independent of the flash chip. The switch is controlled with pin control. + +properties: + compatible: + const: skyworks,aat1290 + + enset-gpios: + maxItems: 1 + description: EN_SET pin + + flen-gpios: + maxItems: 1 + description: FL_EN pin + + led: + $ref: common.yaml# + unevaluatedProperties: false + + properties: + led-max-microamp: true + + flash-max-microamp: + description: | + Maximum flash LED supply current can be calculated using following + formula:: I = 1A * 162 kOhm / Rset. + + flash-max-timeout-us: + description: | + Maximum flash timeout can be calculated using following formula:: + T = 8.82 * 10^9 * Ct. + + required: + - flash-max-microamp + - flash-max-timeout-us + - led-max-microamp + + pinctrl-names: + items: + - const: default + - const: host + - const: isp + + pinctrl-0: true + pinctrl-1: true + pinctrl-2: true + +required: + - compatible + - enset-gpios + - flen-gpios + - led + +additionalProperties: false + +examples: + - | + #include + #include + + // Ct = 220 nF, Rset = 160 kOhm + led-controller { + compatible = "skyworks,aat1290"; + flen-gpios = <&gpj1 1 GPIO_ACTIVE_HIGH>; + enset-gpios = <&gpj1 2 GPIO_ACTIVE_HIGH>; + + pinctrl-names = "default", "host", "isp"; + pinctrl-0 = <&camera_flash_host>; + pinctrl-1 = <&camera_flash_host>; + pinctrl-2 = <&camera_flash_isp>; + + led { + function = LED_FUNCTION_FLASH; + color = ; + led-max-microamp = <520833>; + flash-max-microamp = <1012500>; + flash-max-timeout-us = <1940000>; + }; + }; diff --git a/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt b/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt deleted file mode 100644 index 108bf435b933..000000000000 --- a/Documentation/devicetree/bindings/media/gpio-ir-receiver.txt +++ /dev/null @@ -1,20 +0,0 @@ -Device-Tree bindings for GPIO IR receiver - -Required properties: - - compatible: should be "gpio-ir-receiver". - - gpios: specifies GPIO used for IR signal reception. - -Optional properties: - - linux,rc-map-name: see rc.txt file in the same - directory. - - linux,autosuspend-period: autosuspend delay time, - the unit is milisecond. - -Example node: - - ir: ir-receiver { - compatible = "gpio-ir-receiver"; - gpios = <&gpio0 19 1>; - linux,rc-map-name = "rc-rc6-mce"; - linux,autosuspend-period = <125>; - }; diff --git a/Documentation/devicetree/bindings/media/gpio-ir-receiver.yaml b/Documentation/devicetree/bindings/media/gpio-ir-receiver.yaml new file mode 100644 index 000000000000..61072745b983 --- /dev/null +++ b/Documentation/devicetree/bindings/media/gpio-ir-receiver.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/media/gpio-ir-receiver.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: GPIO Based IR receiver + +maintainers: + - Sebastian Hesselbarth + +allOf: + - $ref: rc.yaml# + +properties: + compatible: + const: gpio-ir-receiver + + gpios: + maxItems: 1 + + linux,autosuspend-period: + description: autosuspend delay time in milliseconds + $ref: /schemas/types.yaml#/definitions/uint32 + +required: + - compatible + - gpios + +unevaluatedProperties: false + +examples: + - | + ir-receiver { + compatible = "gpio-ir-receiver"; + gpios = <&gpio0 19 1>; + linux,rc-map-name = "rc-rc6-mce"; + linux,autosuspend-period = <125>; + }; +... diff --git a/Documentation/devicetree/bindings/media/rc.yaml b/Documentation/devicetree/bindings/media/rc.yaml index d4c541c4b164..b11d14ab89c4 100644 --- a/Documentation/devicetree/bindings/media/rc.yaml +++ b/Documentation/devicetree/bindings/media/rc.yaml @@ -12,7 +12,7 @@ maintainers: properties: $nodename: - pattern: "^ir(@[a-f0-9]+)?$" + pattern: "^ir(-receiver)?(@[a-f0-9]+)?$" linux,rc-map-name: description: diff --git a/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml b/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml index e25caf8ef9f4..04962bb29576 100644 --- a/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml +++ b/Documentation/devicetree/bindings/mfd/google,cros-ec.yaml @@ -90,6 +90,9 @@ properties: pwm: $ref: "/schemas/pwm/google,cros-ec-pwm.yaml#" + kbd-led-backlight: + $ref: "/schemas/chrome/google,cros-kbd-led-backlight.yaml#" + keyboard-controller: $ref: "/schemas/input/google,cros-ec-keyb.yaml#" diff --git a/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml b/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml index fe265bcab50d..fbface720678 100644 --- a/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml +++ b/Documentation/devicetree/bindings/mfd/rohm,bd71815-pmic.yaml @@ -74,7 +74,7 @@ properties: rohm,enable-hidden-gpo: description: | The BD71815 has undocumented GPO at pin E5. Pin is marked as GND at the - data-sheet as it's location in the middle of GND pins makes it hard to + data-sheet as its location in the middle of GND pins makes it hard to use on PCB. If your board has managed to use this pin you can enable the second GPO by defining this property. Dont enable this if you are unsure about how the E5 pin is connected on your board. diff --git a/Documentation/devicetree/bindings/mips/lantiq/rcu.txt b/Documentation/devicetree/bindings/mips/lantiq/rcu.txt index 58d51f480c9e..8ec6191c1712 100644 --- a/Documentation/devicetree/bindings/mips/lantiq/rcu.txt +++ b/Documentation/devicetree/bindings/mips/lantiq/rcu.txt @@ -2,7 +2,7 @@ Lantiq XWAY SoC RCU binding =========================== This binding describes the RCU (reset controller unit) multifunction device, -where each sub-device has it's own set of registers. +where each sub-device has its own set of registers. The RCU register range is used for multiple purposes. Mostly one device uses one or multiple register exclusively, but for some registers some diff --git a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml index 5ecdac9de484..dead421e17d6 100644 --- a/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml +++ b/Documentation/devicetree/bindings/mmc/brcm,sdhci-brcmstb.yaml @@ -10,9 +10,6 @@ maintainers: - Al Cooper - Florian Fainelli -allOf: - - $ref: mmc-controller.yaml# - properties: compatible: oneOf: @@ -42,23 +39,46 @@ properties: maxItems: 1 clocks: - maxItems: 1 - description: - handle to core clock for the sdhci controller. + minItems: 1 + items: + - description: handle to core clock for the sdhci controller + - description: handle to improved 150Mhz clock for sdhci controller (Optional clock) clock-names: + minItems: 1 items: - const: sw_sdio + - const: sdio_freq # Optional clock + + clock-frequency: + description: + Maximum operating frequency of sdio_freq sdhci controller clock + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 100000000 + maximum: 150000000 sdhci,auto-cmd12: type: boolean description: Specifies that controller should use auto CMD12 +allOf: + - $ref: mmc-controller.yaml# + - if: + properties: + clock-names: + contains: + const: sdio_freq + + then: + required: + - clock-frequency + required: - compatible - reg - interrupts - clocks + - clock-names unevaluatedProperties: false diff --git a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt b/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt deleted file mode 100644 index 753e9d7d8956..000000000000 --- a/Documentation/devicetree/bindings/mmc/exynos-dw-mshc.txt +++ /dev/null @@ -1,94 +0,0 @@ -* Samsung Exynos specific extensions to the Synopsys Designware Mobile - Storage Host Controller - -The Synopsys designware mobile storage host controller is used to interface -a SoC with storage medium such as eMMC or SD/MMC cards. This file documents -differences between the core Synopsys dw mshc controller properties described -by synopsys-dw-mshc.txt and the properties used by the Samsung Exynos specific -extensions to the Synopsys Designware Mobile Storage Host Controller. - -Required Properties: - -* compatible: should be - - "samsung,exynos4210-dw-mshc": for controllers with Samsung Exynos4210 - specific extensions. - - "samsung,exynos4412-dw-mshc": for controllers with Samsung Exynos4412 - specific extensions. - - "samsung,exynos5250-dw-mshc": for controllers with Samsung Exynos5250 - specific extensions. - - "samsung,exynos5420-dw-mshc": for controllers with Samsung Exynos5420 - specific extensions. - - "samsung,exynos7-dw-mshc": for controllers with Samsung Exynos7 - specific extensions. - - "samsung,exynos7-dw-mshc-smu": for controllers with Samsung Exynos7 - specific extensions having an SMU. - - "axis,artpec8-dw-mshc": for controllers with ARTPEC-8 specific - extensions. - -* samsung,dw-mshc-ciu-div: Specifies the divider value for the card interface - unit (ciu) clock. This property is applicable only for Exynos5 SoC's and - ignored for Exynos4 SoC's. The valid range of divider value is 0 to 7. - -* samsung,dw-mshc-sdr-timing: Specifies the value of CIU clock phase shift value - in transmit mode and CIU clock phase shift value in receive mode for single - data rate mode operation. Refer notes below for the order of the cells and the - valid values. - -* samsung,dw-mshc-ddr-timing: Specifies the value of CUI clock phase shift value - in transmit mode and CIU clock phase shift value in receive mode for double - data rate mode operation. Refer notes below for the order of the cells and the - valid values. -* samsung,dw-mshc-hs400-timing: Specifies the value of CIU TX and RX clock phase - shift value for hs400 mode operation. - - Notes for the sdr-timing and ddr-timing values: - - The order of the cells should be - - First Cell: CIU clock phase shift value for tx mode. - - Second Cell: CIU clock phase shift value for rx mode. - - Valid values for SDR and DDR CIU clock timing for Exynos5250: - - valid value for tx phase shift and rx phase shift is 0 to 7. - - when CIU clock divider value is set to 3, all possible 8 phase shift - values can be used. - - if CIU clock divider value is 0 (that is divide by 1), both tx and rx - phase shift clocks should be 0. - -* samsung,read-strobe-delay: RCLK (Data strobe) delay to control HS400 mode - (Latency value for delay line in Read path) - -Required properties for a slot (Deprecated - Recommend to use one slot per host): - -* gpios: specifies a list of gpios used for command, clock and data bus. The - first gpio is the command line and the second gpio is the clock line. The - rest of the gpios (depending on the bus-width property) are the data lines in - no particular order. The format of the gpio specifier depends on the gpio - controller. -(Deprecated - Refer to Documentation/devicetree/bindings/pinctrl/samsung-pinctrl.txt) - -Example: - - The MSHC controller node can be split into two portions, SoC specific and - board specific portions as listed below. - - dwmmc0@12200000 { - compatible = "samsung,exynos5250-dw-mshc"; - reg = <0x12200000 0x1000>; - interrupts = <0 75 0>; - #address-cells = <1>; - #size-cells = <0>; - }; - - dwmmc0@12200000 { - cap-mmc-highspeed; - cap-sd-highspeed; - broken-cd; - fifo-depth = <0x80>; - card-detect-delay = <200>; - samsung,dw-mshc-ciu-div = <3>; - samsung,dw-mshc-sdr-timing = <2 3>; - samsung,dw-mshc-ddr-timing = <1 2>; - samsung,dw-mshc-hs400-timing = <0 2>; - samsung,read-strobe-delay = <90>; - bus-width = <8>; - }; diff --git a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt deleted file mode 100644 index 5e74db69f581..000000000000 --- a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.txt +++ /dev/null @@ -1,29 +0,0 @@ -MMC/SD/SDIO slot directly connected to a SPI bus - -This file documents differences between the core properties described -by mmc.txt and the properties used by the mmc_spi driver. - -Required properties: -- spi-max-frequency : maximum frequency for this device (Hz). - -Optional properties: -- voltage-ranges : two cells are required, first cell specifies minimum - slot voltage (mV), second cell specifies maximum slot voltage (mV). - Several ranges could be specified. If not provided, 3.2v..3.4v is assumed. -- gpios : may specify GPIOs in this order: Card-Detect GPIO, - Write-Protect GPIO. Note that this does not follow the - binding from mmc.txt, for historical reasons. - -Example: - - mmc-slot@0 { - compatible = "fsl,mpc8323rdb-mmc-slot", - "mmc-spi-slot"; - reg = <0>; - gpios = <&qe_pio_d 14 1 - &qe_pio_d 15 0>; - voltage-ranges = <3300 3300>; - spi-max-frequency = <50000000>; - interrupts = <42>; - interrupt-parent = <&PIC>; - }; diff --git a/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml new file mode 100644 index 000000000000..c45b91099325 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/mmc-spi-slot.yaml @@ -0,0 +1,77 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/mmc-spi-slot.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: MMC/SD/SDIO slot directly connected to a SPI bus + +maintainers: + - Ulf Hansson + +allOf: + - $ref: "mmc-controller.yaml" + - $ref: /schemas/spi/spi-peripheral-props.yaml + +description: | + The extra properties used by an mmc connected via SPI. + +properties: + compatible: + const: mmc-spi-slot + + reg: + maxItems: 1 + + spi-max-frequency: true + + interrupts: + maxItems: 1 + + voltage-ranges: + $ref: /schemas/types.yaml#/definitions/uint32-array + description: | + Two cells are required, first cell specifies minimum slot voltage (mV), + second cell specifies maximum slot voltage (mV). + items: + - description: | + value for minimum slot voltage in mV + default: 3200 + - description: | + value for maximum slot voltage in mV + default: 3400 + + gpios: + description: | + For historical reasons, this does not follow the generic mmc-controller + binding. + minItems: 1 + items: + - description: Card-Detect GPIO + - description: Write-Protect GPIO + +required: + - compatible + - reg + - spi-max-frequency + +unevaluatedProperties: false + +examples: + - | + #include + spi { + #address-cells = <1>; + #size-cells = <0>; + mmc@0 { + compatible = "mmc-spi-slot"; + reg = <0>; + gpios = <&gpio 14 GPIO_ACTIVE_LOW>, <&gpio 15 GPIO_ACTIVE_HIGH>; + voltage-ranges = <3300 3300>; + spi-max-frequency = <50000000>; + interrupts = <42>; + interrupt-parent = <&PIC>; + }; + }; + +... diff --git a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml index 2a2e9fa8c188..083d1ec2f661 100644 --- a/Documentation/devicetree/bindings/mmc/mtk-sd.yaml +++ b/Documentation/devicetree/bindings/mmc/mtk-sd.yaml @@ -30,13 +30,11 @@ properties: - const: mediatek,mt7623-mmc - const: mediatek,mt2701-mmc - items: - - const: mediatek,mt8186-mmc - - const: mediatek,mt8183-mmc - - items: - - const: mediatek,mt8192-mmc - - const: mediatek,mt8183-mmc - - items: - - const: mediatek,mt8195-mmc + - enum: + - mediatek,mt8186-mmc + - mediatek,mt8188-mmc + - mediatek,mt8192-mmc + - mediatek,mt8195-mmc - const: mediatek,mt8183-mmc reg: @@ -72,12 +70,27 @@ properties: - const: ahb_cg interrupts: - maxItems: 1 + description: + Should at least contain MSDC GIC interrupt. To support SDIO in-band wakeup, an extended + interrupt is required and be configured as wakeup source irq. + minItems: 1 + maxItems: 2 + + interrupt-names: + items: + - const: msdc + - const: sdio_wakeup pinctrl-names: + description: + Should at least contain default and state_uhs. To support SDIO in-band wakeup, dat1 pin + will be switched between GPIO mode and SDIO DAT1 mode, state_eint is mandatory in this + scenario. + minItems: 2 items: - const: default - const: state_uhs + - const: state_eint pinctrl-0: description: @@ -89,6 +102,11 @@ properties: should contain uhs mode pin ctrl. maxItems: 1 + pinctrl-2: + description: + should switch dat1 pin to GPIO mode. + maxItems: 1 + assigned-clocks: description: PLL of the source clock. @@ -208,4 +226,32 @@ examples: mediatek,hs400-cmd-resp-sel-rising; }; + mmc3: mmc@11260000 { + compatible = "mediatek,mt8173-mmc"; + reg = <0x11260000 0x1000>; + clock-names = "source", "hclk"; + clocks = <&pericfg CLK_PERI_MSDC30_3>, + <&topckgen CLK_TOP_MSDC50_2_H_SEL>; + interrupt-names = "msdc", "sdio_wakeup"; + interrupts-extended = <&gic GIC_SPI 74 IRQ_TYPE_LEVEL_LOW 0>, + <&pio 23 IRQ_TYPE_LEVEL_LOW>; + pinctrl-names = "default", "state_uhs", "state_eint"; + pinctrl-0 = <&mmc2_pins_default>; + pinctrl-1 = <&mmc2_pins_uhs>; + pinctrl-2 = <&mmc2_pins_eint>; + bus-width = <4>; + max-frequency = <200000000>; + cap-sd-highspeed; + sd-uhs-sdr104; + keep-power-in-suspend; + wakeup-source; + cap-sdio-irq; + no-mmc; + no-sd; + non-removable; + vmmc-supply = <&sdio_fixed_3v3>; + vqmmc-supply = <&mt6397_vgp3_reg>; + mmc-pwrseq = <&wifi_pwrseq>; + }; + ... diff --git a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml index 9ac4986988c5..14945ebc31d2 100644 --- a/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml +++ b/Documentation/devicetree/bindings/mmc/renesas,sdhi.yaml @@ -56,11 +56,15 @@ properties: - renesas,sdhi-r8a77980 # R-Car V3H - renesas,sdhi-r8a77990 # R-Car E3 - renesas,sdhi-r8a77995 # R-Car D3 - - renesas,sdhi-r8a779a0 # R-Car V3U - renesas,sdhi-r9a07g043 # RZ/G2UL - renesas,sdhi-r9a07g044 # RZ/G2{L,LC} - renesas,sdhi-r9a07g054 # RZ/V2L - const: renesas,rcar-gen3-sdhi # R-Car Gen3 or RZ/G2 + - items: + - enum: + - renesas,sdhi-r8a779a0 # R-Car V3U + - renesas,sdhi-r8a779f0 # R-Car S4-8 + - const: renesas,rcar-gen4-sdhi # R-Car Gen4 reg: maxItems: 1 @@ -141,6 +145,7 @@ allOf: enum: - renesas,rcar-gen2-sdhi - renesas,rcar-gen3-sdhi + - renesas,rcar-gen4-sdhi then: properties: clocks: diff --git a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml index 54fb59820d2b..8d888b435817 100644 --- a/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml +++ b/Documentation/devicetree/bindings/mmc/rockchip-dw-mshc.yaml @@ -39,6 +39,7 @@ properties: - rockchip,rk3399-dw-mshc - rockchip,rk3568-dw-mshc - rockchip,rv1108-dw-mshc + - rockchip,rv1126-dw-mshc - const: rockchip,rk3288-dw-mshc reg: diff --git a/Documentation/devicetree/bindings/mmc/samsung,exynos-dw-mshc.yaml b/Documentation/devicetree/bindings/mmc/samsung,exynos-dw-mshc.yaml new file mode 100644 index 000000000000..fdaa18481aa0 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/samsung,exynos-dw-mshc.yaml @@ -0,0 +1,160 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/samsung,exynos-dw-mshc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: + Samsung Exynos SoC specific extensions to the Synopsys Designware Mobile + Storage Host Controller + +maintainers: + - Jaehoon Chung + - Krzysztof Kozlowski + +properties: + compatible: + enum: + - samsung,exynos4210-dw-mshc + - samsung,exynos4412-dw-mshc + - samsung,exynos5250-dw-mshc + - samsung,exynos5420-dw-mshc + - samsung,exynos5420-dw-mshc-smu + - samsung,exynos7-dw-mshc + - samsung,exynos7-dw-mshc-smu + - axis,artpec8-dw-mshc + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 2 + description: + Handle to "biu" and "ciu" clocks for the + bus interface unit clock and the card interface unit clock. + + clock-names: + items: + - const: biu + - const: ciu + + samsung,dw-mshc-ciu-div: + $ref: /schemas/types.yaml#/definitions/uint32 + minimum: 0 + maximum: 7 + description: + The divider value for the card interface unit (ciu) clock. + + samsung,dw-mshc-ddr-timing: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: CIU clock phase shift value for tx mode + minimum: 0 + maximum: 7 + - description: CIU clock phase shift value for rx mode + minimum: 0 + maximum: 7 + description: + The value of CUI clock phase shift value in transmit mode and CIU clock + phase shift value in receive mode for double data rate mode operation. + See also samsung,dw-mshc-hs400-timing property. + + samsung,dw-mshc-hs400-timing: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: CIU clock phase shift value for tx mode + minimum: 0 + maximum: 7 + - description: CIU clock phase shift value for rx mode + minimum: 0 + maximum: 7 + description: | + The value of CIU TX and RX clock phase shift value for HS400 mode + operation. + Valid values for SDR and DDR CIU clock timing:: + - valid value for tx phase shift and rx phase shift is 0 to 7. + - when CIU clock divider value is set to 3, all possible 8 phase shift + values can be used. + - if CIU clock divider value is 0 (that is divide by 1), both tx and rx + phase shift clocks should be 0. + If missing, values from samsung,dw-mshc-ddr-timing property are used. + + samsung,dw-mshc-sdr-timing: + $ref: /schemas/types.yaml#/definitions/uint32-array + items: + - description: CIU clock phase shift value for tx mode + minimum: 0 + maximum: 7 + - description: CIU clock phase shift value for rx mode + minimum: 0 + maximum: 7 + description: + The value of CIU clock phase shift value in transmit mode and CIU clock + phase shift value in receive mode for single data rate mode operation. + See also samsung,dw-mshc-hs400-timing property. + + samsung,read-strobe-delay: + $ref: /schemas/types.yaml#/definitions/uint32 + description: + RCLK (Data strobe) delay to control HS400 mode (Latency value for delay + line in Read path). If missing, default from hardware is used. + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - samsung,dw-mshc-ddr-timing + - samsung,dw-mshc-sdr-timing + +allOf: + - $ref: "synopsys-dw-mshc-common.yaml#" + - if: + properties: + compatible: + contains: + enum: + - samsung,exynos5250-dw-mshc + - samsung,exynos5420-dw-mshc + - samsung,exynos7-dw-mshc + - samsung,exynos7-dw-mshc-smu + - axis,artpec8-dw-mshc + then: + required: + - samsung,dw-mshc-ciu-div + +unevaluatedProperties: false + +examples: + - | + #include + #include + + mmc@12220000 { + compatible = "samsung,exynos5420-dw-mshc"; + interrupts = ; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x12220000 0x1000>; + clocks = <&clock CLK_MMC2>, <&clock CLK_SCLK_MMC2>; + clock-names = "biu", "ciu"; + fifo-depth = <0x40>; + card-detect-delay = <200>; + samsung,dw-mshc-ciu-div = <3>; + samsung,dw-mshc-sdr-timing = <0 4>; + samsung,dw-mshc-ddr-timing = <0 2>; + pinctrl-names = "default"; + pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_cd &sd2_wp &sd2_bus1 &sd2_bus4>; + bus-width = <4>; + cap-sd-highspeed; + max-frequency = <200000000>; + vmmc-supply = <&ldo19_reg>; + vqmmc-supply = <&ldo13_reg>; + sd-uhs-sdr50; + sd-uhs-sdr104; + sd-uhs-ddr50; + }; diff --git a/Documentation/devicetree/bindings/mmc/samsung,s3c6410-sdhci.yaml b/Documentation/devicetree/bindings/mmc/samsung,s3c6410-sdhci.yaml new file mode 100644 index 000000000000..5d873a60f650 --- /dev/null +++ b/Documentation/devicetree/bindings/mmc/samsung,s3c6410-sdhci.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mmc/samsung,s3c6410-sdhci.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Samsung SoC SDHCI Controller + +maintainers: + - Jaehoon Chung + - Krzysztof Kozlowski + +properties: + compatible: + enum: + - samsung,s3c6410-sdhci + - samsung,exynos4210-sdhci + + reg: + maxItems: 1 + + clocks: + minItems: 2 + maxItems: 5 + + clock-names: + minItems: 2 + items: + - const: hsmmc + - pattern: "^mmc_busclk.[0-3]$" + - pattern: "^mmc_busclk.[0-3]$" + - pattern: "^mmc_busclk.[0-3]$" + - pattern: "^mmc_busclk.[0-3]$" + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + +allOf: + - $ref: mmc-controller.yaml# + - if: + properties: + compatible: + contains: + enum: + - samsung,exynos4210-sdhci + then: + properties: + clocks: + maxItems: 2 + clock-names: + items: + - const: hsmmc + - const: mmc_busclk.2 + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + mmc@12510000 { + compatible = "samsung,exynos4210-sdhci"; + reg = <0x12510000 0x100>; + interrupts = ; + clocks = <&clock CLK_SDMMC0>, <&clock CLK_SCLK_MMC0>; + clock-names = "hsmmc", "mmc_busclk.2"; + bus-width = <4>; + cd-gpios = <&gpx3 4 GPIO_ACTIVE_LOW>; + pinctrl-0 = <&sd2_clk &sd2_cmd &sd2_bus4 &sdhci2_cd>; + pinctrl-names = "default"; + vmmc-supply = <&ldo21_reg>; + }; diff --git a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt b/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt deleted file mode 100644 index 42e0a9afa100..000000000000 --- a/Documentation/devicetree/bindings/mmc/samsung-sdhci.txt +++ /dev/null @@ -1,32 +0,0 @@ -* Samsung's SDHCI Controller device tree bindings - -Samsung's SDHCI controller is used as a connectivity interface with external -MMC, SD and eMMC storage mediums. This file documents differences between the -core mmc properties described by mmc.txt and the properties used by the -Samsung implementation of the SDHCI controller. - -Required SoC Specific Properties: -- compatible: should be one of the following - - "samsung,s3c6410-sdhci": For controllers compatible with s3c6410 sdhci - controller. - - "samsung,exynos4210-sdhci": For controllers compatible with Exynos4 sdhci - controller. - -Required Board Specific Properties: -- pinctrl-0: Should specify pin control groups used for this controller. -- pinctrl-names: Should contain only one value - "default". - -Example: - sdhci@12530000 { - compatible = "samsung,exynos4210-sdhci"; - reg = <0x12530000 0x100>; - interrupts = <0 75 0>; - bus-width = <4>; - cd-gpios = <&gpk2 2 0>; - pinctrl-names = "default"; - pinctrl-0 = <&sd0_clk &sd0_cmd &sd0_bus4>; - }; - - Note: This example shows both SoC specific and board specific properties - in a single device node. The properties can be actually be separated - into SoC specific node and board specific node. diff --git a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml index e4236334e748..b00578ae1dea 100644 --- a/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml +++ b/Documentation/devicetree/bindings/mmc/sdhci-msm.yaml @@ -17,6 +17,9 @@ description: properties: compatible: oneOf: + - enum: + - qcom,sdhci-msm-v4 + deprecated: true - items: - enum: - qcom,apq8084-sdhci @@ -27,6 +30,10 @@ properties: - qcom,msm8992-sdhci - qcom,msm8994-sdhci - qcom,msm8996-sdhci + - qcom,msm8998-sdhci + - const: qcom,sdhci-msm-v4 # for sdcc versions less than 5.0 + - items: + - enum: - qcom,qcs404-sdhci - qcom,sc7180-sdhci - qcom,sc7280-sdhci @@ -38,20 +45,16 @@ properties: - qcom,sm6350-sdhci - qcom,sm8150-sdhci - qcom,sm8250-sdhci - - enum: - - qcom,sdhci-msm-v4 # for sdcc versions less than 5.0 - - qcom,sdhci-msm-v5 # for sdcc version 5.0 - - items: - - const: qcom,sdhci-msm-v4 # Deprecated (only for backward compatibility) - # for sdcc versions less than 5.0 + - qcom,sm8450-sdhci + - const: qcom,sdhci-msm-v5 # for sdcc version 5.0 reg: minItems: 1 - items: - - description: Host controller register map - - description: SD Core register map - - description: CQE register map - - description: Inline Crypto Engine register map + maxItems: 4 + + reg-names: + minItems: 1 + maxItems: 4 clocks: minItems: 3 @@ -93,6 +96,9 @@ properties: description: Should specify pin control groups used for this controller. + resets: + maxItems: 1 + qcom,ddr-config: $ref: /schemas/types.yaml#/definitions/uint32 description: platform specific settings for DDR_CONFIG reg. @@ -121,6 +127,16 @@ properties: description: A phandle to sdhci power domain node maxItems: 1 + mmc-ddr-1_8v: true + + mmc-hs200-1_8v: true + + mmc-hs400-1_8v: true + + bus-width: true + + max-frequency: true + patternProperties: '^opp-table(-[a-z0-9]+)?$': if: @@ -140,7 +156,47 @@ required: - clock-names - interrupts -additionalProperties: true +allOf: + - $ref: mmc-controller.yaml# + + - if: + properties: + compatible: + contains: + enum: + - qcom,sdhci-msm-v4 + then: + properties: + reg: + minItems: 2 + items: + - description: Host controller register map + - description: SD Core register map + - description: CQE register map + - description: Inline Crypto Engine register map + reg-names: + minItems: 2 + items: + - const: hc + - const: core + - const: cqhci + - const: ice + else: + properties: + reg: + minItems: 1 + items: + - description: Host controller register map + - description: CQE register map + - description: Inline Crypto Engine register map + reg-names: + minItems: 1 + items: + - const: hc + - const: cqhci + - const: ice + +unevaluatedProperties: false examples: - | @@ -149,7 +205,7 @@ examples: #include #include - sdhc_2: sdhci@8804000 { + sdhc_2: mmc@8804000 { compatible = "qcom,sm8250-sdhci", "qcom,sdhci-msm-v5"; reg = <0 0x08804000 0 0x1000>; diff --git a/Documentation/devicetree/bindings/mtd/microchip,mchp48l640.yaml b/Documentation/devicetree/bindings/mtd/microchip,mchp48l640.yaml index 2cdf6bf3dc4a..8cc2a7ceb5fb 100644 --- a/Documentation/devicetree/bindings/mtd/microchip,mchp48l640.yaml +++ b/Documentation/devicetree/bindings/mtd/microchip,mchp48l640.yaml @@ -22,13 +22,14 @@ properties: reg: maxItems: 1 - spi-max-frequency: true - required: - compatible - reg -additionalProperties: false +allOf: + - $ref: /schemas/spi/spi-peripheral-props.yaml# + +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/mtd/partitions/arm,arm-firmware-suite.txt b/Documentation/devicetree/bindings/mtd/partitions/arm,arm-firmware-suite.txt deleted file mode 100644 index d5c5616f6db5..000000000000 --- a/Documentation/devicetree/bindings/mtd/partitions/arm,arm-firmware-suite.txt +++ /dev/null @@ -1,17 +0,0 @@ -ARM AFS - ARM Firmware Suite Partitions -======================================= - -The ARM Firmware Suite is a flash partitioning system found on the -ARM reference designs: Integrator AP, Integrator CP, Versatile AB, -Versatile PB, the RealView family, Versatile Express and Juno. - -Required properties: -- compatible : (required) must be "arm,arm-firmware-suite" - -Example: - -flash@0 { - partitions { - compatible = "arm,arm-firmware-suite"; - }; -}; diff --git a/Documentation/devicetree/bindings/mtd/partitions/arm,arm-firmware-suite.yaml b/Documentation/devicetree/bindings/mtd/partitions/arm,arm-firmware-suite.yaml new file mode 100644 index 000000000000..76c88027b6d2 --- /dev/null +++ b/Documentation/devicetree/bindings/mtd/partitions/arm,arm-firmware-suite.yaml @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/mtd/partitions/arm,arm-firmware-suite.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM Firmware Suite (AFS) Partitions + +maintainers: + - Linus Walleij + +description: | + The ARM Firmware Suite is a flash partitioning system found on the + ARM reference designs: Integrator AP, Integrator CP, Versatile AB, + Versatile PB, the RealView family, Versatile Express and Juno. + +properties: + compatible: + const: arm,arm-firmware-suite + +additionalProperties: false + +examples: + - | + partitions { + compatible = "arm,arm-firmware-suite"; + }; +... diff --git a/Documentation/devicetree/bindings/net/altera_tse.txt b/Documentation/devicetree/bindings/net/altera_tse.txt index 0b7d4d3758ea..1d9148ff5130 100644 --- a/Documentation/devicetree/bindings/net/altera_tse.txt +++ b/Documentation/devicetree/bindings/net/altera_tse.txt @@ -15,7 +15,7 @@ Required properties: "rx_desc": MSGDMA Rx dispatcher descriptor space region "rx_resp": MSGDMA Rx dispatcher response space region "s1": SGDMA descriptor memory -- interrupts: Should contain the TSE interrupts and it's mode. +- interrupts: Should contain the TSE interrupts and its mode. - interrupt-names: Should contain the interrupt names "rx_irq": xDMA Rx dispatcher interrupt "tx_irq": xDMA Tx dispatcher interrupt diff --git a/Documentation/devicetree/bindings/net/cdns,macb.yaml b/Documentation/devicetree/bindings/net/cdns,macb.yaml index dfb2860ca771..318f4efe7f6f 100644 --- a/Documentation/devicetree/bindings/net/cdns,macb.yaml +++ b/Documentation/devicetree/bindings/net/cdns,macb.yaml @@ -51,7 +51,6 @@ properties: - atmel,sama5d2-gem # GEM IP (10/100) on Atmel sama5d2 SoCs - atmel,sama5d3-gem # Gigabit IP on Atmel sama5d3 SoCs - atmel,sama5d4-gem # GEM IP (10/100) on Atmel sama5d4 SoCs - - cdns,at32ap7000-macb # Other 10/100 usage or use the generic form - cdns,np4-macb # NP4 SoC devices - microchip,sama7g5-emac # Microchip SAMA7G5 ethernet interface - microchip,sama7g5-gem # Microchip SAMA7G5 gigabit ethernet interface @@ -164,7 +163,7 @@ unevaluatedProperties: false examples: - | macb0: ethernet@fffc4000 { - compatible = "cdns,at32ap7000-macb"; + compatible = "cdns,macb"; reg = <0xfffc4000 0x4000>; interrupts = <21>; phy-mode = "rmii"; diff --git a/Documentation/devicetree/bindings/net/cpsw.txt b/Documentation/devicetree/bindings/net/cpsw.txt index 7c7ac5eb0313..ef655f386b2e 100644 --- a/Documentation/devicetree/bindings/net/cpsw.txt +++ b/Documentation/devicetree/bindings/net/cpsw.txt @@ -20,7 +20,7 @@ Required properties: - active_slave : Specifies the slave to use for time stamping, ethtool and SIOCGMIIPHY - cpsw-phy-sel : Specifies the phandle to the CPSW phy mode selection - device. See also cpsw-phy-sel.txt for it's binding. + device. See also cpsw-phy-sel.txt for its binding. Note that in legacy cases cpsw-phy-sel may be a child device instead of a phandle (DEPRECATED, use phys property instead). diff --git a/Documentation/devicetree/bindings/net/emac_rockchip.txt b/Documentation/devicetree/bindings/net/emac_rockchip.txt deleted file mode 100644 index 05bd7dafce17..000000000000 --- a/Documentation/devicetree/bindings/net/emac_rockchip.txt +++ /dev/null @@ -1,52 +0,0 @@ -* ARC EMAC 10/100 Ethernet platform driver for Rockchip RK3036/RK3066/RK3188 SoCs - -Required properties: -- compatible: should be "rockchip,-emac" - "rockchip,rk3036-emac": found on RK3036 SoCs - "rockchip,rk3066-emac": found on RK3066 SoCs - "rockchip,rk3188-emac": found on RK3188 SoCs -- reg: Address and length of the register set for the device -- interrupts: Should contain the EMAC interrupts -- rockchip,grf: phandle to the syscon grf used to control speed and mode - for emac. -- phy: see ethernet.txt file in the same directory. -- phy-mode: see ethernet.txt file in the same directory. - -Optional properties: -- phy-supply: phandle to a regulator if the PHY needs one - -Clock handling: -- clocks: Must contain an entry for each entry in clock-names. -- clock-names: Shall be "hclk" for the host clock needed to calculate and set - polling period of EMAC and "macref" for the reference clock needed to transfer - data to and from the phy. - -Child nodes of the driver are the individual PHY devices connected to the -MDIO bus. They must have a "reg" property given the PHY address on the MDIO bus. - -Examples: - -ethernet@10204000 { - compatible = "rockchip,rk3188-emac"; - reg = <0xc0fc2000 0x3c>; - interrupts = <6>; - mac-address = [ 00 11 22 33 44 55 ]; - - clocks = <&cru HCLK_EMAC>, <&cru SCLK_MAC>; - clock-names = "hclk", "macref"; - - pinctrl-names = "default"; - pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&phy_int>; - - rockchip,grf = <&grf>; - - phy = <&phy0>; - phy-mode = "rmii"; - phy-supply = <&vcc_rmii>; - - #address-cells = <1>; - #size-cells = <0>; - phy0: ethernet-phy@0 { - reg = <1>; - }; -}; diff --git a/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml b/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml index e381a3c14836..b2558421268a 100644 --- a/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml +++ b/Documentation/devicetree/bindings/net/nfc/nxp,nci.yaml @@ -7,7 +7,6 @@ $schema: http://devicetree.org/meta-schemas/core.yaml# title: NXP Semiconductors NCI NFC controller maintainers: - - Charles Gorand - Krzysztof Kozlowski properties: diff --git a/Documentation/devicetree/bindings/net/qcom-emac.txt b/Documentation/devicetree/bindings/net/qcom-emac.txt index 346e6c7f47b7..e6cb2291471c 100644 --- a/Documentation/devicetree/bindings/net/qcom-emac.txt +++ b/Documentation/devicetree/bindings/net/qcom-emac.txt @@ -14,7 +14,7 @@ MAC node: - mac-address : The 6-byte MAC address. If present, it is the default MAC address. - internal-phy : phandle to the internal PHY node -- phy-handle : phandle the the external PHY node +- phy-handle : phandle the external PHY node Internal PHY node: - compatible : Should be "qcom,fsm9900-emac-sgmii" or "qcom,qdf2432-emac-sgmii". diff --git a/Documentation/devicetree/bindings/net/rockchip,emac.yaml b/Documentation/devicetree/bindings/net/rockchip,emac.yaml new file mode 100644 index 000000000000..a6d4f14df442 --- /dev/null +++ b/Documentation/devicetree/bindings/net/rockchip,emac.yaml @@ -0,0 +1,115 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/net/rockchip,emac.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Rockchip RK3036/RK3066/RK3188 Ethernet Media Access Controller (EMAC) + +maintainers: + - Heiko Stuebner + +properties: + compatible: + enum: + - rockchip,rk3036-emac + - rockchip,rk3066-emac + - rockchip,rk3188-emac + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + minItems: 2 + items: + - description: host clock + - description: reference clock + - description: mac TX/RX clock + + clock-names: + minItems: 2 + items: + - const: hclk + - const: macref + - const: macclk + + rockchip,grf: + $ref: /schemas/types.yaml#/definitions/phandle + description: + Phandle to the syscon GRF used to control speed and mode for the EMAC. + + phy-supply: + description: + Phandle to a regulator if the PHY needs one. + + mdio: + $ref: mdio.yaml# + unevaluatedProperties: false + +required: + - compatible + - reg + - interrupts + - clocks + - clock-names + - rockchip,grf + - phy + - phy-mode + - mdio + +allOf: + - $ref: "ethernet-controller.yaml#" + - if: + properties: + compatible: + contains: + const: rockchip,rk3036-emac + + then: + properties: + clocks: + minItems: 3 + + clock-names: + minItems: 3 + + else: + properties: + clocks: + maxItems: 2 + + clock-names: + maxItems: 2 + +unevaluatedProperties: false + +examples: + - | + #include + #include + + ethernet@10204000 { + compatible = "rockchip,rk3188-emac"; + reg = <0xc0fc2000 0x3c>; + interrupts = ; + clocks = <&cru HCLK_EMAC>, <&cru SCLK_MAC>; + clock-names = "hclk", "macref"; + rockchip,grf = <&grf>; + pinctrl-0 = <&emac_xfer>, <&emac_mdio>, <&phy_int>; + pinctrl-names = "default"; + phy = <&phy0>; + phy-mode = "rmii"; + phy-supply = <&vcc_rmii>; + + mdio { + #address-cells = <1>; + #size-cells = <0>; + + phy0: ethernet-phy@0 { + reg = <1>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml index 252e5b72aee0..376e739bcad4 100644 --- a/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml +++ b/Documentation/devicetree/bindings/pci/fsl,imx6q-pcie.yaml @@ -144,6 +144,7 @@ properties: description: If present then the reset sequence using the GPIO specified in the "reset-gpio" property is reversed (H=reset state, L=operation state) (optional required). + type: boolean vpcie-supply: description: Should specify the regulator in charge of PCIe port power. diff --git a/Documentation/devicetree/bindings/pci/host-generic-pci.yaml b/Documentation/devicetree/bindings/pci/host-generic-pci.yaml index 6bcaa8f2c3cf..d25423aa7167 100644 --- a/Documentation/devicetree/bindings/pci/host-generic-pci.yaml +++ b/Documentation/devicetree/bindings/pci/host-generic-pci.yaml @@ -106,6 +106,9 @@ properties: maxItems: 3 dma-coherent: true + iommu-map: true + iommu-map-mask: true + msi-parent: true required: - compatible diff --git a/Documentation/devicetree/bindings/perf/arm,ccn.yaml b/Documentation/devicetree/bindings/perf/arm,ccn.yaml new file mode 100644 index 000000000000..0b0bb2091016 --- /dev/null +++ b/Documentation/devicetree/bindings/perf/arm,ccn.yaml @@ -0,0 +1,40 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/perf/arm,ccn.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ARM CCN (Cache Coherent Network) Performance Monitors + +maintainers: + - Robin Murphy + +properties: + compatible: + enum: + - arm,ccn-502 + - arm,ccn-504 + - arm,ccn-508 + - arm,ccn-512 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + +additionalProperties: false + +examples: + - | + ccn@20000000 { + compatible = "arm,ccn-504"; + reg = <0x20000000 0x1000000>; + interrupts = <0 181 4>; + }; +... diff --git a/Documentation/devicetree/bindings/perf/arm-ccn.txt b/Documentation/devicetree/bindings/perf/arm-ccn.txt deleted file mode 100644 index 1c53b5aa3317..000000000000 --- a/Documentation/devicetree/bindings/perf/arm-ccn.txt +++ /dev/null @@ -1,23 +0,0 @@ -* ARM CCN (Cache Coherent Network) - -Required properties: - -- compatible: (standard compatible string) should be one of: - "arm,ccn-502" - "arm,ccn-504" - "arm,ccn-508" - "arm,ccn-512" - -- reg: (standard registers property) physical address and size - (16MB) of the configuration registers block - -- interrupts: (standard interrupt property) single interrupt - generated by the control block - -Example: - - ccn@2000000000 { - compatible = "arm,ccn-504"; - reg = <0x20 0x00000000 0 0x1000000>; - interrupts = <0 181 4>; - }; diff --git a/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml b/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml index 4d01f3124e1c..a90fa1baadab 100644 --- a/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml +++ b/Documentation/devicetree/bindings/phy/amlogic,meson-axg-mipi-pcie-analog.yaml @@ -16,7 +16,7 @@ description: |+ - compatible: Should be the following: "amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon" - Refer to the the bindings described in + Refer to the bindings described in Documentation/devicetree/bindings/mfd/syscon.yaml properties: diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml index c689bea7ce6e..d3a8911728d0 100644 --- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2400-pinctrl.yaml @@ -16,7 +16,7 @@ description: |+ - compatible: Should be one of the following: "aspeed,ast2400-scu", "syscon", "simple-mfd" - Refer to the the bindings described in + Refer to the bindings described in Documentation/devicetree/bindings/mfd/syscon.yaml properties: diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml index 9db904a528ee..5d2c1b1fb7fd 100644 --- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2500-pinctrl.yaml @@ -17,7 +17,7 @@ description: |+ "aspeed,ast2500-scu", "syscon", "simple-mfd" "aspeed,g5-scu", "syscon", "simple-mfd" - Refer to the the bindings described in + Refer to the bindings described in Documentation/devicetree/bindings/mfd/syscon.yaml properties: diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml index 3666ac5b6518..e92686d2f062 100644 --- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2600-pinctrl.yaml @@ -16,7 +16,7 @@ description: |+ - compatible: Should be one of the following: "aspeed,ast2600-scu", "syscon", "simple-mfd" - Refer to the the bindings described in + Refer to the bindings described in Documentation/devicetree/bindings/mfd/syscon.yaml properties: diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml index f005abac7079..5390e988a934 100644 --- a/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml +++ b/Documentation/devicetree/bindings/power/amlogic,meson-ee-pwrc.yaml @@ -2,8 +2,8 @@ # Copyright 2019 BayLibre, SAS %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/amlogic,meson-ee-pwrc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/amlogic,meson-ee-pwrc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Amlogic Meson Everything-Else Power Domains @@ -17,7 +17,7 @@ description: |+ - compatible: Should be the following: "amlogic,meson-gx-hhi-sysctrl", "simple-mfd", "syscon" - Refer to the the bindings described in + Refer to the bindings described in Documentation/devicetree/bindings/mfd/syscon.yaml properties: diff --git a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml index 86e5f6513bb3..eab21bb2050a 100644 --- a/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml +++ b/Documentation/devicetree/bindings/power/amlogic,meson-sec-pwrc.yaml @@ -3,8 +3,8 @@ # Author: Jianxin Pan %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/amlogic,meson-sec-pwrc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/amlogic,meson-sec-pwrc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Amlogic Meson Secure Power Domains diff --git a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml index 19a194980142..94d369eb85de 100644 --- a/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml +++ b/Documentation/devicetree/bindings/power/apple,pmgr-pwrstate.yaml @@ -10,7 +10,7 @@ maintainers: - Hector Martin allOf: - - $ref: "power-domain.yaml#" + - $ref: power-domain.yaml# description: | Apple SoCs include PMGR blocks responsible for power management, diff --git a/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml b/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml index 63b15ac6dde4..d867bd6976d8 100644 --- a/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml +++ b/Documentation/devicetree/bindings/power/brcm,bcm63xx-power.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/brcm,bcm63xx-power.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/brcm,bcm63xx-power.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: BCM63xx power domain driver diff --git a/Documentation/devicetree/bindings/power/renesas,apmu.yaml b/Documentation/devicetree/bindings/power/renesas,apmu.yaml index d77fc88050c8..f2cc89e7f4e4 100644 --- a/Documentation/devicetree/bindings/power/renesas,apmu.yaml +++ b/Documentation/devicetree/bindings/power/renesas,apmu.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/renesas,apmu.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/renesas,apmu.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Renesas Advanced Power Management Unit diff --git a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.yaml b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.yaml index 8d56bedd3390..0720b54881c2 100644 --- a/Documentation/devicetree/bindings/power/renesas,rcar-sysc.yaml +++ b/Documentation/devicetree/bindings/power/renesas,rcar-sysc.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/renesas,rcar-sysc.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/renesas,rcar-sysc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Renesas R-Car and RZ/G System Controller diff --git a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml index 353f155df0f4..e7b436d2e757 100644 --- a/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml +++ b/Documentation/devicetree/bindings/power/reset/qcom,pon.yaml @@ -30,11 +30,15 @@ properties: pwrkey: type: object - $ref: "../../input/qcom,pm8941-pwrkey.yaml#" + $ref: /schemas/input/qcom,pm8941-pwrkey.yaml# resin: type: object - $ref: "../../input/qcom,pm8941-pwrkey.yaml#" + $ref: /schemas/input/qcom,pm8941-pwrkey.yaml# + + watchdog: + type: object + $ref: /schemas/watchdog/qcom,pm8916-wdt.yaml required: - compatible diff --git a/Documentation/devicetree/bindings/power/reset/regulator-poweroff.yaml b/Documentation/devicetree/bindings/power/reset/regulator-poweroff.yaml index 03bd1fa5a623..e9417557cd30 100644 --- a/Documentation/devicetree/bindings/power/reset/regulator-poweroff.yaml +++ b/Documentation/devicetree/bindings/power/reset/regulator-poweroff.yaml @@ -16,7 +16,7 @@ description: | properties: compatible: - const: "regulator-poweroff" + const: regulator-poweroff cpu-supply: description: diff --git a/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.yaml b/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.yaml index 68d7c14a7163..46de35861738 100644 --- a/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.yaml +++ b/Documentation/devicetree/bindings/power/reset/xlnx,zynqmp-power.yaml @@ -15,7 +15,7 @@ description: | properties: compatible: - const: "xlnx,zynqmp-power" + const: xlnx,zynqmp-power interrupts: maxItems: 1 diff --git a/Documentation/devicetree/bindings/power/supply/active-semi,act8945a-charger.yaml b/Documentation/devicetree/bindings/power/supply/active-semi,act8945a-charger.yaml index 3f74bc19415d..5220d9cb16d8 100644 --- a/Documentation/devicetree/bindings/power/supply/active-semi,act8945a-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/active-semi,act8945a-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/active-semi,act8945a-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/active-semi,act8945a-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Active-semi ACT8945A Charger Function diff --git a/Documentation/devicetree/bindings/power/supply/bq2415x.yaml b/Documentation/devicetree/bindings/power/supply/bq2415x.yaml index 118cf484cc69..a3c00e078918 100644 --- a/Documentation/devicetree/bindings/power/supply/bq2415x.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq2415x.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq2415x.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq2415x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for TI bq2415x Li-Ion Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq24190.yaml b/Documentation/devicetree/bindings/power/supply/bq24190.yaml index 0d7cbbdf808b..21a9dadc1c6a 100644 --- a/Documentation/devicetree/bindings/power/supply/bq24190.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq24190.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq24190.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq24190.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for TI BQ2419x Li-Ion Battery Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq24257.yaml b/Documentation/devicetree/bindings/power/supply/bq24257.yaml index 3a0f6cd9015a..c7406bef0fa8 100644 --- a/Documentation/devicetree/bindings/power/supply/bq24257.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq24257.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq24257.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq24257.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for bq24250, bq24251 and bq24257 Li-Ion Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq24735.yaml b/Documentation/devicetree/bindings/power/supply/bq24735.yaml index 131be6782c4b..dd9176ce71b3 100644 --- a/Documentation/devicetree/bindings/power/supply/bq24735.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq24735.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq24735.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq24735.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for TI BQ24735 Li-Ion Battery Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml index 813d6afde606..27db38577822 100644 --- a/Documentation/devicetree/bindings/power/supply/bq2515x.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq2515x.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2020 Texas Instruments Incorporated %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq2515x.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq2515x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TI bq2515x 500-mA Linear charger family diff --git a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml index 92ec7ed25668..91abe5733c41 100644 --- a/Documentation/devicetree/bindings/power/supply/bq256xx.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq256xx.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2020 Texas Instruments Incorporated %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq256xx.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq256xx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TI bq256xx Switch Mode Buck Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq25890.yaml b/Documentation/devicetree/bindings/power/supply/bq25890.yaml index bf823b615439..204c0147188f 100644 --- a/Documentation/devicetree/bindings/power/supply/bq25890.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq25890.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq25890.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq25890.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for bq25890, bq25892, bq25895 and bq25896 Li-Ion Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq25980.yaml b/Documentation/devicetree/bindings/power/supply/bq25980.yaml index 8367a1fd4057..4883527ab5c7 100644 --- a/Documentation/devicetree/bindings/power/supply/bq25980.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq25980.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2020 Texas Instruments Incorporated %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq25980.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq25980.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TI BQ25980 Flash Charger diff --git a/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml b/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml index 6af41da3e055..65fc6049efc1 100644 --- a/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml +++ b/Documentation/devicetree/bindings/power/supply/bq27xxx.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2020 Texas Instruments Incorporated %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/bq27xxx.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/bq27xxx.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TI BQ27XXX fuel gauge family diff --git a/Documentation/devicetree/bindings/power/supply/charger-manager.yaml b/Documentation/devicetree/bindings/power/supply/charger-manager.yaml index fbb2204769aa..5af1e0beaf29 100644 --- a/Documentation/devicetree/bindings/power/supply/charger-manager.yaml +++ b/Documentation/devicetree/bindings/power/supply/charger-manager.yaml @@ -50,6 +50,7 @@ properties: cm-battery-stat: description: battery status + $ref: /schemas/types.yaml#/definitions/uint32 enum: - 0 # battery always present - 1 # no battery diff --git a/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml b/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml index 7153fd4ce55f..694bfdb5815c 100644 --- a/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/cpcap-battery.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/cpcap-battery.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/cpcap-battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Motorola CPCAP PMIC battery diff --git a/Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml b/Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml index cb6353683d7b..7e6bf30a0107 100644 --- a/Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/cpcap-charger.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/cpcap-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/cpcap-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Motorola CPCAP PMIC charger diff --git a/Documentation/devicetree/bindings/power/supply/dlg,da9150-charger.yaml b/Documentation/devicetree/bindings/power/supply/dlg,da9150-charger.yaml index 96336b05d76d..b289388952bf 100644 --- a/Documentation/devicetree/bindings/power/supply/dlg,da9150-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/dlg,da9150-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/dlg,da9150-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/dlg,da9150-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Dialog Semiconductor DA9150 Charger Power Supply bindings diff --git a/Documentation/devicetree/bindings/power/supply/dlg,da9150-fuel-gauge.yaml b/Documentation/devicetree/bindings/power/supply/dlg,da9150-fuel-gauge.yaml index 30c2fff7cf92..d47caf59d204 100644 --- a/Documentation/devicetree/bindings/power/supply/dlg,da9150-fuel-gauge.yaml +++ b/Documentation/devicetree/bindings/power/supply/dlg,da9150-fuel-gauge.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/dlg,da9150-fuel-gauge.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/dlg,da9150-fuel-gauge.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Dialog Semiconductor DA9150 Fuel-Gauge Power Supply bindings diff --git a/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml b/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml index 76c227a7cd5c..46527038bf22 100644 --- a/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/ingenic,battery.yaml @@ -2,8 +2,8 @@ # Copyright 2019-2020 Artur Rojek %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/ingenic,battery.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/ingenic,battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Ingenic JZ47xx battery bindings diff --git a/Documentation/devicetree/bindings/power/supply/isp1704.yaml b/Documentation/devicetree/bindings/power/supply/isp1704.yaml index 4c91da70011d..7e3449ed70d4 100644 --- a/Documentation/devicetree/bindings/power/supply/isp1704.yaml +++ b/Documentation/devicetree/bindings/power/supply/isp1704.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/isp1704.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/isp1704.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for NXP ISP1704 USB Charger Detection diff --git a/Documentation/devicetree/bindings/power/supply/lego,ev3-battery.yaml b/Documentation/devicetree/bindings/power/supply/lego,ev3-battery.yaml index 518eabb63588..a99d989f1450 100644 --- a/Documentation/devicetree/bindings/power/supply/lego,ev3-battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/lego,ev3-battery.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/lego,ev3-battery.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/lego,ev3-battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: LEGO MINDSTORMS EV3 Battery diff --git a/Documentation/devicetree/bindings/power/supply/lltc,lt3651-charger.yaml b/Documentation/devicetree/bindings/power/supply/lltc,lt3651-charger.yaml index e2d8d2aebb73..76cedf95a12c 100644 --- a/Documentation/devicetree/bindings/power/supply/lltc,lt3651-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/lltc,lt3651-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/lltc,lt3651-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/lltc,lt3651-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Analog Devices LT3651 Charger Power Supply bindings diff --git a/Documentation/devicetree/bindings/power/supply/lltc,ltc294x.yaml b/Documentation/devicetree/bindings/power/supply/lltc,ltc294x.yaml index 043bf378040f..109b41a0d56c 100644 --- a/Documentation/devicetree/bindings/power/supply/lltc,ltc294x.yaml +++ b/Documentation/devicetree/bindings/power/supply/lltc,ltc294x.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/lltc,ltc294x.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/lltc,ltc294x.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for LTC2941, LTC2942, LTC2943 and LTC2944 battery fuel gauges diff --git a/Documentation/devicetree/bindings/power/supply/ltc4162-l.yaml b/Documentation/devicetree/bindings/power/supply/ltc4162-l.yaml index 6d7aa97a6475..cfffaeef8b09 100644 --- a/Documentation/devicetree/bindings/power/supply/ltc4162-l.yaml +++ b/Documentation/devicetree/bindings/power/supply/ltc4162-l.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2020 Topic Embedded Products %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/ltc4162-l.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/ltc4162-l.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Linear Technology (Analog Devices) LTC4162-L Charger diff --git a/Documentation/devicetree/bindings/power/supply/maxim,ds2760.yaml b/Documentation/devicetree/bindings/power/supply/maxim,ds2760.yaml index 818647edf63d..c838efcf7e16 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,ds2760.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,ds2760.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/maxim,ds2760.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/maxim,ds2760.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Maxim DS2760 DT bindings diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max14656.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max14656.yaml index 0a41078ebd99..070ef6f96e60 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max14656.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max14656.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/maxim,max14656.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/maxim,max14656.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Maxim MAX14656 DT bindings diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml index 6b4588a3253b..3a529326ecbd 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17040.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/maxim,max17040.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/maxim,max17040.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Maxim 17040 fuel gauge series diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml index 971b53c58cc6..aff5d0792e0f 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max17042.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/maxim,max17042.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/maxim,max17042.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Maxim 17042 fuel gauge series diff --git a/Documentation/devicetree/bindings/power/supply/maxim,max8903.yaml b/Documentation/devicetree/bindings/power/supply/maxim,max8903.yaml index 4828ca0842ae..a8d625f285f1 100644 --- a/Documentation/devicetree/bindings/power/supply/maxim,max8903.yaml +++ b/Documentation/devicetree/bindings/power/supply/maxim,max8903.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/maxim,max8903.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/maxim,max8903.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Maxim Semiconductor MAX8903 Battery Charger diff --git a/Documentation/devicetree/bindings/power/supply/nokia,n900-battery.yaml b/Documentation/devicetree/bindings/power/supply/nokia,n900-battery.yaml index 4a1489f2b28d..5178e6207271 100644 --- a/Documentation/devicetree/bindings/power/supply/nokia,n900-battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/nokia,n900-battery.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/nokia,n900-battery.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/nokia,n900-battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Nokia N900 battery diff --git a/Documentation/devicetree/bindings/power/supply/olpc-battery.yaml b/Documentation/devicetree/bindings/power/supply/olpc-battery.yaml index 0bd7bf3b8e1b..dd89e2532a07 100644 --- a/Documentation/devicetree/bindings/power/supply/olpc-battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/olpc-battery.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/olpc-battery.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/olpc-battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: OLPC Battery diff --git a/Documentation/devicetree/bindings/power/supply/power-supply.yaml b/Documentation/devicetree/bindings/power/supply/power-supply.yaml index 9a490fbd32e1..2f672e6e8d72 100644 --- a/Documentation/devicetree/bindings/power/supply/power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/power-supply.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/power-supply.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/power-supply.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Power Supply Core Support diff --git a/Documentation/devicetree/bindings/power/supply/richtek,rt5033-battery.yaml b/Documentation/devicetree/bindings/power/supply/richtek,rt5033-battery.yaml index ae647d3355a2..756c16d1727d 100644 --- a/Documentation/devicetree/bindings/power/supply/richtek,rt5033-battery.yaml +++ b/Documentation/devicetree/bindings/power/supply/richtek,rt5033-battery.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/richtek,rt5033-battery.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/richtek,rt5033-battery.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Richtek RT5033 PMIC Fuel Gauge diff --git a/Documentation/devicetree/bindings/power/supply/richtek,rt9455.yaml b/Documentation/devicetree/bindings/power/supply/richtek,rt9455.yaml index e1c233462f29..bce15101318e 100644 --- a/Documentation/devicetree/bindings/power/supply/richtek,rt9455.yaml +++ b/Documentation/devicetree/bindings/power/supply/richtek,rt9455.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/richtek,rt9455.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/richtek,rt9455.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Binding for Richtek rt9455 battery charger diff --git a/Documentation/devicetree/bindings/power/supply/sc2731-charger.yaml b/Documentation/devicetree/bindings/power/supply/sc2731-charger.yaml index b62c2431f94e..eeb043f9bb4f 100644 --- a/Documentation/devicetree/bindings/power/supply/sc2731-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/sc2731-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/sc2731-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/sc2731-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Spreadtrum SC2731 PMICs battery charger binding diff --git a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml index e019cffd1f38..d90a838a1744 100644 --- a/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml +++ b/Documentation/devicetree/bindings/power/supply/sc27xx-fg.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/sc27xx-fg.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/sc27xx-fg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Spreadtrum SC27XX PMICs Fuel Gauge Unit Power Supply Bindings diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml index 4b8a00cec39c..525abdfb3e2d 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-btemp.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/stericsson,ab8500-btemp.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/stericsson,ab8500-btemp.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AB8500 Battery Temperature Monitor diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml index 6799224f7fb4..10bbdcfc87b6 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-chargalg.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/stericsson,ab8500-chargalg.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/stericsson,ab8500-chargalg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AB8500 Charging Algorithm diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml index 9518eb7289d0..e33329b3af61 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-charger.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/stericsson,ab8500-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/stericsson,ab8500-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AB8500 Charger diff --git a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml index 2ce408a7c0ae..6a724ca90e99 100644 --- a/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml +++ b/Documentation/devicetree/bindings/power/supply/stericsson,ab8500-fg.yaml @@ -2,8 +2,8 @@ # Copyright (C) 2021 Sebastian Reichel %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/stericsson,ab8500-fg.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/stericsson,ab8500-fg.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AB8500 Fuel Gauge diff --git a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml index 20862cdfc116..0581497448ce 100644 --- a/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/summit,smb347-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/summit,smb347-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/summit,smb347-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: Battery charger driver for SMB345, SMB347 and SMB358 diff --git a/Documentation/devicetree/bindings/power/supply/tps65090-charger.yaml b/Documentation/devicetree/bindings/power/supply/tps65090-charger.yaml index f2dd38bf078c..586745426341 100644 --- a/Documentation/devicetree/bindings/power/supply/tps65090-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/tps65090-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/tps65090-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/tps65090-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TPS65090 Frontend PMU with Switchmode Charger diff --git a/Documentation/devicetree/bindings/power/supply/tps65217-charger.yaml b/Documentation/devicetree/bindings/power/supply/tps65217-charger.yaml index 2c2fe883bb48..7ccf0cdffd3e 100644 --- a/Documentation/devicetree/bindings/power/supply/tps65217-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/tps65217-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/tps65217-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/tps65217-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TPS65217 Charger diff --git a/Documentation/devicetree/bindings/power/supply/twl4030-charger.yaml b/Documentation/devicetree/bindings/power/supply/twl4030-charger.yaml index fe3f32a0ea79..d8d3154f9cb1 100644 --- a/Documentation/devicetree/bindings/power/supply/twl4030-charger.yaml +++ b/Documentation/devicetree/bindings/power/supply/twl4030-charger.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/twl4030-charger.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/twl4030-charger.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: TWL4030 BCI (Battery Charger Interface) diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml index de6a23aee977..5c8369fd3ef7 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-ac-power-supply.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/x-powers,axp20x-ac-power-supply.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/x-powers,axp20x-ac-power-supply.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AXP20x AC power-supply diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml index d055428ae39f..e0b95ecbbebd 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-battery-power-supply.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/x-powers,axp20x-battery-power-supply.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/x-powers,axp20x-battery-power-supply.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AXP20x Battery power-supply diff --git a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml index 0c371b55c9e1..3ce648dd91bd 100644 --- a/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml +++ b/Documentation/devicetree/bindings/power/supply/x-powers,axp20x-usb-power-supply.yaml @@ -1,8 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 %YAML 1.2 --- -$id: "http://devicetree.org/schemas/power/supply/x-powers,axp20x-usb-power-supply.yaml#" -$schema: "http://devicetree.org/meta-schemas/core.yaml#" +$id: http://devicetree.org/schemas/power/supply/x-powers,axp20x-usb-power-supply.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# title: AXP20x USB power-supply diff --git a/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt b/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt index d63ab1dec16d..801c66069121 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/cpus.txt @@ -5,7 +5,7 @@ Copyright 2013 Freescale Semiconductor Inc. Power Architecture CPUs in Freescale SOCs are represented in device trees as per the definition in the Devicetree Specification. -In addition to the the Devicetree Specification definitions, the properties +In addition to the Devicetree Specification definitions, the properties defined below may be present on CPU nodes. PROPERTIES diff --git a/Documentation/devicetree/bindings/powerpc/fsl/mpc5200.txt b/Documentation/devicetree/bindings/powerpc/fsl/mpc5200.txt index d096cf461d81..4571c857dbe5 100644 --- a/Documentation/devicetree/bindings/powerpc/fsl/mpc5200.txt +++ b/Documentation/devicetree/bindings/powerpc/fsl/mpc5200.txt @@ -172,7 +172,7 @@ Interrupt controller (fsl,mpc5200-pic) node The mpc5200 pic binding splits hardware IRQ numbers into two levels. The split reflects the layout of the PIC hardware itself, which groups interrupts into one of three groups; CRIT, MAIN or PERP. Also, the -Bestcomm dma engine has it's own set of interrupt sources which are +Bestcomm dma engine has its own set of interrupt sources which are cascaded off of peripheral interrupt 0, which the driver interprets as a fourth group, SDMA. diff --git a/Documentation/devicetree/bindings/powerpc/opal/power-mgt.txt b/Documentation/devicetree/bindings/powerpc/opal/power-mgt.txt index 9d619e955576..d6658d3dd15e 100644 --- a/Documentation/devicetree/bindings/powerpc/opal/power-mgt.txt +++ b/Documentation/devicetree/bindings/powerpc/opal/power-mgt.txt @@ -39,7 +39,7 @@ otherwise. The length of all the property arrays must be the same. - ibm,cpu-idle-state-flags: Array of unsigned 32-bit values containing the values of the - flags associated with the the aforementioned idle-states. The + flags associated with the aforementioned idle-states. The flag bits are as follows: 0x00000001 /* Decrementer would stop */ 0x00000002 /* Needs timebase restore */ diff --git a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt index b677900b3aae..658f96fbc4fe 100644 --- a/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt +++ b/Documentation/devicetree/bindings/remoteproc/qcom,q6v5.txt @@ -37,7 +37,7 @@ on the Qualcomm Hexagon core. - interrupt-names: Usage: required Value type: - Definition: The interrupts needed depends on the the compatible + Definition: The interrupts needed depends on the compatible string: qcom,q6v5-pil: qcom,ipq8074-wcss-pil: diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt b/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt deleted file mode 100644 index 36f610bb051e..000000000000 --- a/Documentation/devicetree/bindings/rtc/rtc-ds1307.txt +++ /dev/null @@ -1,52 +0,0 @@ -Dallas DS1307 and compatible RTC - -Required properties: -- compatible: should be one of: - "dallas,ds1307", - "dallas,ds1308", - "dallas,ds1337", - "dallas,ds1338", - "dallas,ds1339", - "dallas,ds1388", - "dallas,ds1340", - "dallas,ds1341", - "maxim,ds3231", - "st,m41t0", - "st,m41t00", - "st,m41t11", - "microchip,mcp7940x", - "microchip,mcp7941x", - "pericom,pt7c4338", - "epson,rx8025", - "isil,isl12057" - "epson,rx8130" -- reg: I2C bus address of the device - -Optional properties: -- interrupts: rtc alarm interrupt. -- clock-output-names: From common clock binding to override the default output - clock name -- wakeup-source: Enables wake up of host system on alarm -- trickle-resistor-ohms : ds1339, ds1340 and ds 1388 only - Selected resistor for trickle charger - Possible values are 250, 2000, 4000 - Should be given if trickle charger should be enabled -- aux-voltage-chargeable: ds1339, ds1340, ds1388 and rx8130 only - Tells whether the battery/supercap of the RTC (if any) is - chargeable or not. - Possible values are 0 (not chargeable), 1 (chargeable) - -Deprecated properties: -- trickle-diode-disable : ds1339, ds1340 and ds1388 only - Do not use internal trickle charger diode - Should be given if internal trickle charger diode should be disabled - (superseded by aux-voltage-chargeable) - -Example: - ds1339: rtc@68 { - compatible = "dallas,ds1339"; - reg = <0x68>; - interrupt-parent = <&gpio4>; - interrupts = <20 0>; - trickle-resistor-ohms = <250>; - }; diff --git a/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml b/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml new file mode 100644 index 000000000000..98d10e680144 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/rtc-ds1307.yaml @@ -0,0 +1,102 @@ +# SPDX-License-Identifier: GPL-2.0 +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/rtc/rtc-ds1307.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Dallas DS1307 and compatible RTC + +maintainers: + - Alexandre Belloni + +properties: + compatible: + oneOf: + - enum: + - dallas,ds1307 + - dallas,ds1308 + - dallas,ds1337 + - dallas,ds1338 + - dallas,ds1339 + - dallas,ds1388 + - dallas,ds1340 + - dallas,ds1341 + - maxim,ds3231 + - st,m41t0 + - st,m41t00 + - st,m41t11 + - microchip,mcp7940x + - microchip,mcp7941x + - pericom,pt7c4338 + - epson,rx8025 + - isil,isl12057 + - epson,rx8130 + + - items: + - enum: + - st,m41t00 + - const: dallas,ds1338 + + reg: + maxItems: 1 + + interrupts: + minItems: 1 + maxItems: 2 + + interrupt-names: + maxItems: 2 + + "#clock-cells": + const: 1 + + clock-output-names: + description: From common clock binding to override the default output clock name. + + wakeup-source: + description: Enables wake up of host system on alarm. + + vcc-supply: true + +allOf: + - $ref: rtc.yaml + - if: + properties: + compatible: + contains: + enum: + - dallas,ds1339 + - dallas,ds1340 + - dallas,ds1388 + then: + properties: + trickle-resistor-ohms: + description: Selected resistor for trickle charger. Should be specified if trickle + charger should be enabled. + enum: [ 250, 2000, 4000 ] + + trickle-diode-disable: + description: Do not use internal trickle charger diode. Should be given if internal + trickle charger diode should be disabled (superseded by aux-voltage-chargeable) + deprecated: true + +unevaluatedProperties: false + +required: + - compatible + - reg + +examples: + - | + i2c { + #address-cells = <1>; + #size-cells = <0>; + + rtc@68 { + compatible = "dallas,ds1337"; + reg = <0x68>; + interrupt-parent = <&gpio4>; + interrupts = <20 0>; + trickle-resistor-ohms = <250>; + }; + }; diff --git a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml index 13925bb78ec7..d9fc120c61cc 100644 --- a/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml +++ b/Documentation/devicetree/bindings/rtc/trivial-rtc.yaml @@ -30,6 +30,8 @@ properties: - dallas,ds1672 # Extremely Accurate I²C RTC with Integrated Crystal and SRAM - dallas,ds3232 + # EM Microelectronic EM3027 RTC + - emmicro,em3027 # I2C-BUS INTERFACE REAL TIME CLOCK MODULE - epson,rx8010 # I2C-BUS INTERFACE REAL TIME CLOCK MODULE diff --git a/Documentation/devicetree/bindings/serial/efm32-uart.txt b/Documentation/devicetree/bindings/serial/efm32-uart.txt deleted file mode 100644 index 4f8d8fde0c1c..000000000000 --- a/Documentation/devicetree/bindings/serial/efm32-uart.txt +++ /dev/null @@ -1,20 +0,0 @@ -* Energymicro efm32 UART - -Required properties: -- compatible : Should be "energymicro,efm32-uart" -- reg : Address and length of the register set -- interrupts : Should contain uart interrupt - -Optional properties: -- energymicro,location : Decides the location of the USART I/O pins. - Allowed range : [0 .. 5] - Default: 0 - -Example: - -uart@4000c400 { - compatible = "energymicro,efm32-uart"; - reg = <0x4000c400 0x400>; - interrupts = <15>; - energymicro,location = <0>; -}; diff --git a/Documentation/devicetree/bindings/serio/ps2-gpio.txt b/Documentation/devicetree/bindings/serio/ps2-gpio.txt deleted file mode 100644 index 7b7bc9cdf986..000000000000 --- a/Documentation/devicetree/bindings/serio/ps2-gpio.txt +++ /dev/null @@ -1,23 +0,0 @@ -Device-Tree binding for ps/2 gpio device - -Required properties: - - compatible = "ps2-gpio" - - data-gpios: the data pin - - clk-gpios: the clock pin - - interrupts: Should trigger on the falling edge of the clock line. - -Optional properties: - - write-enable: Indicates whether write function is provided - to serio device. Possibly providing the write fn will not work, because - of the tough timing requirements. - -Example nodes: - -ps2@0 { - compatible = "ps2-gpio"; - interrupt-parent = <&gpio>; - interrupts = <23 IRQ_TYPE_EDGE_FALLING>; - data-gpios = <&gpio 24 GPIO_ACTIVE_HIGH>; - clk-gpios = <&gpio 23 GPIO_ACTIVE_HIGH>; - write-enable; -}; diff --git a/Documentation/devicetree/bindings/serio/ps2-gpio.yaml b/Documentation/devicetree/bindings/serio/ps2-gpio.yaml new file mode 100644 index 000000000000..a63d9172346f --- /dev/null +++ b/Documentation/devicetree/bindings/serio/ps2-gpio.yaml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/serio/ps2-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Bindings for GPIO based PS/2 + +maintainers: + - Danilo Krummrich + +properties: + compatible: + const: ps2-gpio + + data-gpios: + description: + the gpio used for the data signal - this should be flagged as + active high using open drain with (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN) + from since the signal is open drain by + definition + maxItems: 1 + + clk-gpios: + description: + the gpio used for the clock signal - this should be flagged as + active high using open drain with (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN) + from since the signal is open drain by + definition + maxItems: 1 + + interrupts: + description: + The given interrupt should trigger on the falling edge of the clock line. + maxItems: 1 + + write-enable: + type: boolean + description: + Indicates whether write function is provided to serio device. Possibly + providing the write function will not work, because of the tough timing + requirements. + +required: + - compatible + - data-gpios + - clk-gpios + - interrupts + +additionalProperties: false + +examples: + - | + #include + #include + + ps2 { + compatible = "ps2-gpio"; + interrupt-parent = <&gpio>; + interrupts = <23 IRQ_TYPE_EDGE_FALLING>; + data-gpios = <&gpio 24 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + clk-gpios = <&gpio 23 (GPIO_ACTIVE_HIGH | GPIO_OPEN_DRAIN)>; + write-enable; + }; diff --git a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml index d01e98768153..a4eeb7e158e5 100644 --- a/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml +++ b/Documentation/devicetree/bindings/soc/qcom/qcom,aoss-qmp.yaml @@ -13,7 +13,7 @@ description: This binding describes the hardware component responsible for side channel requests to the always-on subsystem (AOSS), used for certain power management requests that is not handled by the standard RPMh interface. Each client in the - SoC has it's own block of message RAM and IRQ for communication with the AOSS. + SoC has its own block of message RAM and IRQ for communication with the AOSS. The protocol used to communicate in the message RAM is known as Qualcomm Messaging Protocol (QMP) diff --git a/Documentation/devicetree/bindings/sound/da9055.txt b/Documentation/devicetree/bindings/sound/da9055.txt index ed1b7cc6f249..75c6338b6ae2 100644 --- a/Documentation/devicetree/bindings/sound/da9055.txt +++ b/Documentation/devicetree/bindings/sound/da9055.txt @@ -2,7 +2,7 @@ DA9055 provides Audio CODEC support (I2C only). -The Audio CODEC device in DA9055 has it's own I2C address which is configurable, +The Audio CODEC device in DA9055 has its own I2C address which is configurable, so the device is instantiated separately from the PMIC (MFD) device. For details on accompanying PMIC I2C device, see the following: diff --git a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml index 2ad17b361db0..bc2fb1a80ed7 100644 --- a/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml +++ b/Documentation/devicetree/bindings/sound/tlv320adcx140.yaml @@ -68,9 +68,9 @@ properties: array is defined as . 0 - (default) Odd channel is latched on the negative edge and even - channel is latched on the the positive edge. + channel is latched on the positive edge. 1 - Odd channel is latched on the positive edge and even channel is - latched on the the negative edge. + latched on the negative edge. PDMIN1 - PDMCLK latching edge used for channel 1 and 2 data PDMIN2 - PDMCLK latching edge used for channel 3 and 4 data diff --git a/Documentation/devicetree/bindings/spi/efm32-spi.txt b/Documentation/devicetree/bindings/spi/efm32-spi.txt deleted file mode 100644 index e0fa61a1be0c..000000000000 --- a/Documentation/devicetree/bindings/spi/efm32-spi.txt +++ /dev/null @@ -1,39 +0,0 @@ -* Energy Micro EFM32 SPI - -Required properties: -- #address-cells: see spi-bus.txt -- #size-cells: see spi-bus.txt -- compatible: should be "energymicro,efm32-spi" -- reg: Offset and length of the register set for the controller -- interrupts: pair specifying rx and tx irq -- clocks: phandle to the spi clock -- cs-gpios: see spi-bus.txt - -Recommended properties : -- energymicro,location: Value to write to the ROUTE register's LOCATION - bitfield to configure the pinmux for the device, see - datasheet for values. - If this property is not provided, keeping what is - already configured in the hardware, so its either the - reset default 0 or whatever the bootloader did. - -Example: - -spi1: spi@4000c400 { /* USART1 */ - #address-cells = <1>; - #size-cells = <0>; - compatible = "energymicro,efm32-spi"; - reg = <0x4000c400 0x400>; - interrupts = <15 16>; - clocks = <&cmu 20>; - cs-gpios = <&gpio 51 1>; // D3 - energymicro,location = <1>; - - ks8851@0 { - compatible = "ks8851"; - spi-max-frequency = <6000000>; - reg = <0>; - interrupt-parent = <&boardfpga>; - interrupts = <4>; - }; -}; diff --git a/Documentation/devicetree/bindings/sram/qcom,imem.yaml b/Documentation/devicetree/bindings/sram/qcom,imem.yaml new file mode 100644 index 000000000000..e9199190198d --- /dev/null +++ b/Documentation/devicetree/bindings/sram/qcom,imem.yaml @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/sram/qcom,imem.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm IMEM memory region + +maintainers: + - Bjorn Andersson + +description: + Qualcomm IMEM is dedicated memory region for various debug features and DMA + transactions. + +properties: + compatible: + items: + - enum: + - qcom,apq8064-imem + - qcom,msm8974-imem + - qcom,qcs404-imem + - qcom,sc7180-imem + - qcom,sc7280-imem + - qcom,sdm630-imem + - qcom,sdm845-imem + - qcom,sdx55-imem + - const: syscon + - const: simple-mfd + + reg: + maxItems: 1 + + ranges: true + + '#address-cells': + const: 1 + + '#size-cells': + const: 1 + + reboot-mode: + $ref: /schemas/power/reset/syscon-reboot-mode.yaml# + +patternProperties: + "^pil-reloc@[0-9a-f]+$": + $ref: /schemas/remoteproc/qcom,pil-info.yaml# + description: Peripheral image loader relocation region + +required: + - compatible + - reg + +additionalProperties: false + +examples: + - | + soc { + #address-cells = <2>; + #size-cells = <2>; + + sram@146bf000 { + compatible = "qcom,sdm845-imem", "syscon", "simple-mfd"; + reg = <0 0x146bf000 0 0x1000>; + ranges = <0 0 0x146bf000 0x1000>; + + #address-cells = <1>; + #size-cells = <1>; + + pil-reloc@94c { + compatible = "qcom,pil-reloc-info"; + reg = <0x94c 0xc8>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/sram/qcom,ocmem.yaml b/Documentation/devicetree/bindings/sram/qcom,ocmem.yaml index 930188bc5e6a..071f2d676196 100644 --- a/Documentation/devicetree/bindings/sram/qcom,ocmem.yaml +++ b/Documentation/devicetree/bindings/sram/qcom,ocmem.yaml @@ -72,10 +72,10 @@ patternProperties: examples: - | - #include - #include + #include + #include - ocmem: ocmem@fdd00000 { + sram@fdd00000 { compatible = "qcom,msm8974-ocmem"; reg = <0xfdd00000 0x2000>, @@ -93,6 +93,6 @@ examples: ranges = <0 0xfec00000 0x100000>; gmu-sram@0 { - reg = <0x0 0x100000>; + reg = <0x0 0x100000>; }; - }; + }; diff --git a/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml b/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml index 1ab5070c751d..89a2c32c0ab2 100644 --- a/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/brcm,avs-ro-thermal.yaml @@ -16,7 +16,7 @@ description: |+ - compatible: Should be one of the following: "brcm,bcm2711-avs-monitor", "syscon", "simple-mfd" - Refer to the the bindings described in + Refer to the bindings described in Documentation/devicetree/bindings/mfd/syscon.yaml properties: diff --git a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt index db880e7ed713..aea4a2a178b9 100644 --- a/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt +++ b/Documentation/devicetree/bindings/thermal/nvidia,tegra124-soctherm.txt @@ -96,7 +96,7 @@ critical trip point is reported back to the thermal framework to implement software shutdown. - the "hot" type trip points will be set to SOC_THERM hardware as the throttle -temperature. Once the the temperature of this thermal zone is higher +temperature. Once the temperature of this thermal zone is higher than it, it will trigger the HW throttle event. Example : diff --git a/Documentation/devicetree/bindings/thermal/rcar-thermal.yaml b/Documentation/devicetree/bindings/thermal/rcar-thermal.yaml index 927de79ab4b5..00dcbdd36144 100644 --- a/Documentation/devicetree/bindings/thermal/rcar-thermal.yaml +++ b/Documentation/devicetree/bindings/thermal/rcar-thermal.yaml @@ -42,7 +42,7 @@ properties: description: Address ranges of the thermal registers. If more then one range is given the first one must be the common registers followed by each sensor - according the the datasheet. + according the datasheet. minItems: 1 maxItems: 4 diff --git a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml index dcd32c10205a..f2d6298d926c 100644 --- a/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/qcom,ufs.yaml @@ -26,6 +26,7 @@ properties: - qcom,msm8994-ufshc - qcom,msm8996-ufshc - qcom,msm8998-ufshc + - qcom,sc8280xp-ufshc - qcom,sdm845-ufshc - qcom,sm6350-ufshc - qcom,sm8150-ufshc @@ -98,6 +99,7 @@ allOf: contains: enum: - qcom,msm8998-ufshc + - qcom,sc8280xp-ufshc - qcom,sm8250-ufshc - qcom,sm8350-ufshc - qcom,sm8450-ufshc diff --git a/Documentation/devicetree/bindings/ufs/renesas,ufs.yaml b/Documentation/devicetree/bindings/ufs/renesas,ufs.yaml new file mode 100644 index 000000000000..f04f9f61fa9f --- /dev/null +++ b/Documentation/devicetree/bindings/ufs/renesas,ufs.yaml @@ -0,0 +1,61 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/ufs/renesas,ufs.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Renesas R-Car UFS Host Controller + +maintainers: + - Yoshihiro Shimoda + +allOf: + - $ref: ufs-common.yaml + +properties: + compatible: + const: renesas,r8a779f0-ufs + + reg: + maxItems: 1 + + clocks: + maxItems: 2 + + clock-names: + items: + - const: fck + - const: ref_clk + + power-domains: + maxItems: 1 + + resets: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - power-domains + - resets + +unevaluatedProperties: false + +examples: + - | + #include + #include + #include + + ufs: ufs@e686000 { + compatible = "renesas,r8a779f0-ufs"; + reg = <0xe6860000 0x100>; + interrupts = ; + clocks = <&cpg CPG_MOD 1514>, <&ufs30_clk>; + clock-names = "fck", "ref_clk"; + freq-table-hz = <200000000 200000000>, <38400000 38400000>; + power-domains = <&sysc R8A779F0_PD_ALWAYS_ON>; + resets = <&cpg 1514>; + }; diff --git a/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml b/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml index c949eb617313..2c715eec48b8 100644 --- a/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml +++ b/Documentation/devicetree/bindings/ufs/samsung,exynos-ufs.yaml @@ -21,6 +21,7 @@ properties: - samsung,exynos7-ufs - samsung,exynosautov9-ufs - samsung,exynosautov9-ufs-vh + - tesla,fsd-ufs reg: items: diff --git a/Documentation/devicetree/bindings/usb/analogix,anx7411.yaml b/Documentation/devicetree/bindings/usb/analogix,anx7411.yaml new file mode 100644 index 000000000000..ee436308e5dc --- /dev/null +++ b/Documentation/devicetree/bindings/usb/analogix,anx7411.yaml @@ -0,0 +1,81 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/analogix,anx7411.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Analogix ANX7411 Type-C controller bindings + +maintainers: + - Xin Ji + +properties: + compatible: + enum: + - analogix,anx7411 + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + connector: + type: object + $ref: ../connector/usb-connector.yaml + description: + Properties for usb c connector. + + properties: + compatible: + const: usb-c-connector + + power-role: true + + data-role: true + + try-power-role: true + + required: + - compatible + +required: + - compatible + - reg + - connector + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec@2c { + compatible = "analogix,anx7411"; + reg = <0x2c>; + interrupts = <8 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpio0>; + + typec_con: connector { + compatible = "usb-c-connector"; + power-role = "dual"; + data-role = "dual"; + try-power-role = "source"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + typec_con_ep: endpoint { + remote-endpoint = <&usbotg_hs_ep>; + }; + }; + }; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/aspeed,ast2600-udc.yaml b/Documentation/devicetree/bindings/usb/aspeed,ast2600-udc.yaml new file mode 100644 index 000000000000..c3b6be3d8002 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/aspeed,ast2600-udc.yaml @@ -0,0 +1,52 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +# Copyright (c) 2020 Facebook Inc. +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/aspeed,ast2600-udc.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: ASPEED USB 2.0 Device Controller + +maintainers: + - Neal Liu + +description: |+ + The ASPEED USB 2.0 Device Controller implements 1 control endpoint and + 4 generic endpoints for AST260x. + + Supports independent DMA channel for each generic endpoint. + Supports 32/256 stages descriptor mode for all generic endpoints. + +properties: + compatible: + enum: + - aspeed,ast2600-udc + + reg: + maxItems: 1 + + clocks: + maxItems: 1 + + interrupts: + maxItems: 1 + +required: + - compatible + - reg + - clocks + - interrupts + +additionalProperties: false + +examples: + - | + #include + udc: usb@1e6a2000 { + compatible = "aspeed,ast2600-udc"; + reg = <0x1e6a2000 0x300>; + interrupts = <9>; + clocks = <&syscon ASPEED_CLK_GATE_USBPORT2CLK>; + pinctrl-names = "default"; + pinctrl-0 = <&pinctrl_usb2bd_default>; + }; diff --git a/Documentation/devicetree/bindings/usb/dwc2.yaml b/Documentation/devicetree/bindings/usb/dwc2.yaml index 8d22a9843ba5..1bfbc6ef16eb 100644 --- a/Documentation/devicetree/bindings/usb/dwc2.yaml +++ b/Documentation/devicetree/bindings/usb/dwc2.yaml @@ -11,6 +11,7 @@ maintainers: allOf: - $ref: usb-drd.yaml# + - $ref: usb-hcd.yaml# properties: compatible: @@ -161,6 +162,8 @@ properties: property is used. $ref: /schemas/graph.yaml#/properties/port + tpl-support: true + dependencies: port: [ usb-role-switch ] role-switch-default-mode: [ usb-role-switch ] diff --git a/Documentation/devicetree/bindings/usb/generic-ehci.yaml b/Documentation/devicetree/bindings/usb/generic-ehci.yaml index 25a6c14618e1..079f7cff0c24 100644 --- a/Documentation/devicetree/bindings/usb/generic-ehci.yaml +++ b/Documentation/devicetree/bindings/usb/generic-ehci.yaml @@ -131,11 +131,6 @@ properties: Set this flag to indicate that the hardware sometimes turns on the OC bit when an over-current isn't actually present. - companion: - $ref: /schemas/types.yaml#/definitions/phandle - description: - Phandle of a companion. - phys: minItems: 1 maxItems: 3 @@ -156,7 +151,7 @@ required: - reg - interrupts -additionalProperties: false +unevaluatedProperties: false examples: - | diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml index 084d7135b2d9..b0e58b15b9ae 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtk-xhci.yaml @@ -31,6 +31,7 @@ properties: - mediatek,mt8173-xhci - mediatek,mt8183-xhci - mediatek,mt8186-xhci + - mediatek,mt8188-xhci - mediatek,mt8192-xhci - mediatek,mt8195-xhci - const: mediatek,mtk-xhci @@ -57,6 +58,7 @@ properties: - description: optional, wakeup interrupt used to support runtime PM interrupt-names: + minItems: 1 items: - const: host - const: wakeup @@ -113,6 +115,9 @@ properties: vbus-supply: description: Regulator of USB VBUS5v + resets: + maxItems: 1 + usb3-lpm-capable: true usb2-lpm-disable: true diff --git a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml index 37b02a841dc4..e63b66545317 100644 --- a/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml +++ b/Documentation/devicetree/bindings/usb/mediatek,mtu3.yaml @@ -107,6 +107,9 @@ properties: maximum-speed: enum: [super-speed-plus, super-speed, high-speed, full-speed] + resets: + maxItems: 1 + "#address-cells": enum: [1, 2] diff --git a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml index 749e1963ddbb..fea3e7092ace 100644 --- a/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/qcom,dwc3.yaml @@ -24,6 +24,7 @@ properties: - qcom,qcs404-dwc3 - qcom,sc7180-dwc3 - qcom,sc7280-dwc3 + - qcom,sc8280xp-dwc3 - qcom,sdm660-dwc3 - qcom,sdm845-dwc3 - qcom,sdx55-dwc3 @@ -66,11 +67,11 @@ properties: - mock_utmi:: Mock utmi clock needed for ITP/SOF generation in host mode. Its frequency should be 19.2MHz. minItems: 1 - maxItems: 6 + maxItems: 9 clock-names: minItems: 1 - maxItems: 6 + maxItems: 9 assigned-clocks: items: @@ -93,20 +94,12 @@ properties: - const: apps-usb interrupts: - items: - - description: The interrupt that is asserted - when a wakeup event is received on USB2 bus. - - description: The interrupt that is asserted - when a wakeup event is received on USB3 bus. - - description: Wakeup event on DM line. - - description: Wakeup event on DP line. + minItems: 1 + maxItems: 4 interrupt-names: - items: - - const: hs_phy_irq - - const: ss_phy_irq - - const: dm_hs_phy_irq - - const: dp_hs_phy_irq + minItems: 1 + maxItems: 4 qcom,select-utmi-as-pipe-clk: description: @@ -249,6 +242,28 @@ allOf: - const: sleep - const: mock_utmi + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-dwc3 + then: + properties: + clocks: + maxItems: 9 + clock-names: + items: + - const: cfg_noc + - const: core + - const: iface + - const: sleep + - const: mock_utmi + - const: noc_aggr + - const: noc_aggr_north + - const: noc_aggr_south + - const: noc_sys + - if: properties: compatible: @@ -311,6 +326,115 @@ allOf: - const: mock_utmi - const: xo + - if: + properties: + compatible: + contains: + enum: + - qcom,ipq4019-dwc3 + - qcom,ipq6018-dwc3 + - qcom,ipq8064-dwc3 + - qcom,ipq8074-dwc3 + - qcom,msm8994-dwc3 + - qcom,qcs404-dwc3 + - qcom,sc7180-dwc3 + - qcom,sdm845-dwc3 + - qcom,sdx55-dwc3 + - qcom,sdx65-dwc3 + - qcom,sm4250-dwc3 + - qcom,sm6115-dwc3 + - qcom,sm6125-dwc3 + - qcom,sm6350-dwc3 + - qcom,sm8150-dwc3 + - qcom,sm8250-dwc3 + - qcom,sm8350-dwc3 + - qcom,sm8450-dwc3 + then: + properties: + interrupts: + items: + - description: The interrupt that is asserted + when a wakeup event is received on USB2 bus. + - description: The interrupt that is asserted + when a wakeup event is received on USB3 bus. + - description: Wakeup event on DM line. + - description: Wakeup event on DP line. + interrupt-names: + items: + - const: hs_phy_irq + - const: ss_phy_irq + - const: dm_hs_phy_irq + - const: dp_hs_phy_irq + + - if: + properties: + compatible: + contains: + enum: + - qcom,msm8953-dwc3 + - qcom,msm8996-dwc3 + - qcom,msm8998-dwc3 + then: + properties: + interrupts: + maxItems: 2 + interrupt-names: + items: + - const: hs_phy_irq + - const: ss_phy_irq + + - if: + properties: + compatible: + contains: + enum: + - qcom,sdm660-dwc3 + then: + properties: + interrupts: + minItems: 1 + maxItems: 2 + interrupt-names: + minItems: 1 + items: + - const: hs_phy_irq + - const: ss_phy_irq + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc7280-dwc3 + then: + properties: + interrupts: + minItems: 3 + maxItems: 4 + interrupt-names: + minItems: 3 + items: + - const: hs_phy_irq + - const: dp_hs_phy_irq + - const: dm_hs_phy_irq + - const: ss_phy_irq + + - if: + properties: + compatible: + contains: + enum: + - qcom,sc8280xp-dwc3 + then: + properties: + interrupts: + maxItems: 4 + interrupt-names: + items: + - const: pwr_event + - const: dp_hs_phy_irq + - const: dm_hs_phy_irq + - const: ss_phy_irq additionalProperties: false diff --git a/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml b/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml index 04ee255eb4f0..50f2b505bdeb 100644 --- a/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml +++ b/Documentation/devicetree/bindings/usb/realtek,rts5411.yaml @@ -25,13 +25,13 @@ properties: description: phandle to the regulator that provides power to the hub. - companion-hub: + peer-hub: $ref: '/schemas/types.yaml#/definitions/phandle' description: - phandle to the companion hub on the controller. + phandle to the peer hub on the controller. required: - - companion-hub + - peer-hub - compatible - reg @@ -49,7 +49,7 @@ examples: compatible = "usbbda,5411"; reg = <1>; vdd-supply = <&pp3300_hub>; - companion-hub = <&hub_3_0>; + peer-hub = <&hub_3_0>; }; /* 3.0 hub on port 2 */ @@ -57,6 +57,6 @@ examples: compatible = "usbbda,411"; reg = <2>; vdd-supply = <&pp3300_hub>; - companion-hub = <&hub_2_0>; + peer-hub = <&hub_2_0>; }; }; diff --git a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml index d41265ba8ce2..1779d08ba1c0 100644 --- a/Documentation/devicetree/bindings/usb/snps,dwc3.yaml +++ b/Documentation/devicetree/bindings/usb/snps,dwc3.yaml @@ -343,6 +343,11 @@ properties: This port is used with the 'usb-role-switch' property to connect the dwc3 to type C connector. + wakeup-source: + $ref: /schemas/types.yaml#/definitions/flag + description: + Enable USB remote wakeup. + unevaluatedProperties: false required: diff --git a/Documentation/devicetree/bindings/usb/st,typec-stm32g0.yaml b/Documentation/devicetree/bindings/usb/st,typec-stm32g0.yaml new file mode 100644 index 000000000000..1cb68cabe17d --- /dev/null +++ b/Documentation/devicetree/bindings/usb/st,typec-stm32g0.yaml @@ -0,0 +1,91 @@ +# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/st,typec-stm32g0.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: STMicroelectronics STM32G0 USB Type-C PD controller + +description: | + The STM32G0 MCU can be programmed to control Type-C connector(s) through I2C + typically using the UCSI protocol over I2C, with a dedicated alert + (interrupt) pin. + +maintainers: + - Fabrice Gasnier + +properties: + compatible: + const: st,stm32g0-typec + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + connector: + type: object + $ref: /schemas/connector/usb-connector.yaml# + unevaluatedProperties: false + + firmware-name: + description: | + Should contain the name of the default firmware image + file located on the firmware search path + + wakeup-source: true + + power-domains: + maxItems: 1 + +required: + - compatible + - reg + - interrupts + - connector + +additionalProperties: false + +examples: + - | + #include + i2c { + #address-cells = <1>; + #size-cells = <0>; + + typec@53 { + compatible = "st,stm32g0-typec"; + reg = <0x53>; + /* Alert pin on GPIO PE12 */ + interrupts = <12 IRQ_TYPE_EDGE_FALLING>; + interrupt-parent = <&gpioe>; + + /* Example with one type-C connector */ + connector { + compatible = "usb-c-connector"; + label = "USB-C"; + + ports { + #address-cells = <1>; + #size-cells = <0>; + port@0 { + reg = <0>; + con_usb_c_ep: endpoint { + remote-endpoint = <&usb_ep>; + }; + }; + }; + }; + }; + }; + + usb { + usb-role-switch; + port { + usb_ep: endpoint { + remote-endpoint = <&con_usb_c_ep>; + }; + }; + }; +... diff --git a/Documentation/devicetree/bindings/usb/ti,usb8041.yaml b/Documentation/devicetree/bindings/usb/ti,usb8041.yaml new file mode 100644 index 000000000000..e04fbd8ab0b7 --- /dev/null +++ b/Documentation/devicetree/bindings/usb/ti,usb8041.yaml @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: GPL-2.0-only or BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/usb/ti,usb8041.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Binding for the TI USB8041 USB 3.0 hub controller + +maintainers: + - Alexander Stein + +allOf: + - $ref: usb-device.yaml# + +properties: + compatible: + enum: + - usb451,8140 + - usb451,8142 + + reg: true + + reset-gpios: + items: + - description: GPIO specifier for GRST# pin. + + vdd-supply: + description: + VDD power supply to the hub + + peer-hub: + $ref: /schemas/types.yaml#/definitions/phandle + description: + phandle to the peer hub on the controller. + +required: + - compatible + - reg + - peer-hub + +additionalProperties: false + +examples: + - | + #include + + usb { + dr_mode = "host"; + #address-cells = <1>; + #size-cells = <0>; + + /* 2.0 hub on port 1 */ + hub_2_0: hub@1 { + compatible = "usb451,8142"; + reg = <1>; + peer-hub = <&hub_3_0>; + reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + }; + + /* 3.0 hub on port 2 */ + hub_3_0: hub@2 { + compatible = "usb451,8140"; + reg = <2>; + peer-hub = <&hub_2_0>; + reset-gpios = <&gpio1 11 GPIO_ACTIVE_LOW>; + }; + }; diff --git a/Documentation/devicetree/bindings/vendor-prefixes.yaml b/Documentation/devicetree/bindings/vendor-prefixes.yaml index 5a3bc64773a9..2f0151e9f6be 100644 --- a/Documentation/devicetree/bindings/vendor-prefixes.yaml +++ b/Documentation/devicetree/bindings/vendor-prefixes.yaml @@ -141,6 +141,8 @@ patternProperties: description: ASIX Electronics Corporation "^aspeed,.*": description: ASPEED Technology Inc. + "^asrock,.*": + description: ASRock Inc. "^asus,.*": description: AsusTek Computer Inc. "^atheros,.*": @@ -198,12 +200,14 @@ patternProperties: description: Broadcom Corporation "^bsh,.*": description: BSH Hausgeraete GmbH + "^bticino,.*": + description: Bticino International "^buffalo,.*": description: Buffalo, Inc. "^bur,.*": description: B&R Industrial Automation GmbH - "^bticino,.*": - description: Bticino International + "^bytedance,.*": + description: ByteDance Ltd. "^calamp,.*": description: CalAmp Corp. "^calaosystems,.*": @@ -308,6 +312,8 @@ patternProperties: description: Dell Inc. "^delta,.*": description: Delta Electronics, Inc. + "^densitron,.*": + description: Densitron Technologies Ltd "^denx,.*": description: Denx Software Engineering "^devantech,.*": @@ -551,6 +557,8 @@ patternProperties: description: Shenzhen Hugsun Technology Co. Ltd. "^hwacom,.*": description: HwaCom Systems Inc. + "^hxt,.*": + description: HXT Semiconductor "^hycon,.*": description: Hycon Technology Corp. "^hydis,.*": @@ -585,6 +593,8 @@ patternProperties: description: Infineon Technologies "^inforce,.*": description: Inforce Computing + "^ingrasys,.*": + description: Ingrasys Technology Inc. "^ivo,.*": description: InfoVision Optoelectronics Kunshan Co. Ltd. "^ingenic,.*": @@ -605,6 +615,8 @@ patternProperties: description: Inter Control Group "^invensense,.*": description: InvenSense Inc. + "^inventec,.*": + description: Inventec "^inversepath,.*": description: Inverse Path "^iom,.*": @@ -1019,6 +1031,8 @@ patternProperties: description: Shenzhen QiShenglong Industrialist Co., Ltd. "^qnap,.*": description: QNAP Systems, Inc. + "^quanta,.*": + description: Quanta Computer Inc. "^radxa,.*": description: Radxa "^raidsonic,.*": diff --git a/Documentation/devicetree/bindings/watchdog/faraday,ftwdt010.yaml b/Documentation/devicetree/bindings/watchdog/faraday,ftwdt010.yaml index ca9e1beff76b..6ecd429f76b5 100644 --- a/Documentation/devicetree/bindings/watchdog/faraday,ftwdt010.yaml +++ b/Documentation/devicetree/bindings/watchdog/faraday,ftwdt010.yaml @@ -55,7 +55,7 @@ examples: compatible = "faraday,ftwdt010"; reg = <0x41000000 0x1000>; interrupts = <3 IRQ_TYPE_LEVEL_HIGH>; - timeout-secs = <5>; + timeout-sec = <5>; }; - | watchdog: watchdog@98500000 { diff --git a/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.txt b/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.txt deleted file mode 100644 index 6fb984f31982..000000000000 --- a/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.txt +++ /dev/null @@ -1,28 +0,0 @@ -QCOM PM8916 watchdog timer controller - -This pm8916 watchdog timer controller must be under pm8916-pon node. - -Required properties: -- compatible: should be "qcom,pm8916-wdt" - -Optional properties : -- interrupts : Watchdog pre-timeout (bark) interrupt. -- timeout-sec : Watchdog timeout value in seconds. - -Example: - - pm8916_0: pm8916@0 { - compatible = "qcom,pm8916", "qcom,spmi-pmic"; - reg = <0x0 SPMI_USID>; - - pon@800 { - compatible = "qcom,pm8916-pon"; - reg = <0x800>; - - watchdog { - compatible = "qcom,pm8916-wdt"; - interrupts = <0x0 0x8 6 IRQ_TYPE_EDGE_RISING>; - timeout-sec = <10>; - }; - }; - }; diff --git a/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml b/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml new file mode 100644 index 000000000000..568eb8480fc3 --- /dev/null +++ b/Documentation/devicetree/bindings/watchdog/qcom,pm8916-wdt.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/watchdog/qcom,pm8916-wdt.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm PM8916 watchdog timer controller + +maintainers: + - Krzysztof Kozlowski + +allOf: + - $ref: watchdog.yaml# + +properties: + compatible: + const: qcom,pm8916-wdt + + interrupts: + maxItems: 1 + +required: + - compatible + - interrupts + +unevaluatedProperties: false + +examples: + - | + #include + #include + + pmic@0 { + compatible = "qcom,pm8916", "qcom,spmi-pmic"; + reg = <0x0 SPMI_USID>; + #address-cells = <1>; + #size-cells = <0>; + + pon@800 { + compatible = "qcom,pm8916-pon"; + reg = <0x800>; + mode-bootloader = <0x2>; + mode-recovery = <0x1>; + + watchdog { + compatible = "qcom,pm8916-wdt"; + interrupts = <0x0 0x8 6 IRQ_TYPE_EDGE_RISING>; + timeout-sec = <60>; + }; + }; + }; diff --git a/Documentation/devicetree/bindings/writing-bindings.rst b/Documentation/devicetree/bindings/writing-bindings.rst index 5465eced2af1..1ad081de2dd0 100644 --- a/Documentation/devicetree/bindings/writing-bindings.rst +++ b/Documentation/devicetree/bindings/writing-bindings.rst @@ -53,7 +53,7 @@ Properties - DO use common property unit suffixes for properties with scientific units. Recommended suffixes are listed at - https://github.com/devicetree-org/dt-schema/blob/master/schemas/property-units.yaml + https://github.com/devicetree-org/dt-schema/blob/main/dtschema/schemas/property-units.yaml - DO define properties in terms of constraints. How many entries? What are possible values? What is the order? diff --git a/Documentation/driver-api/dmaengine/provider.rst b/Documentation/driver-api/dmaengine/provider.rst index 1e0f1f85d10e..ceac2a300e32 100644 --- a/Documentation/driver-api/dmaengine/provider.rst +++ b/Documentation/driver-api/dmaengine/provider.rst @@ -162,16 +162,6 @@ Currently, the types available are: - The device is able to do memory to memory copies -- - DMA_MEMCPY_SG - - - The device supports memory to memory scatter-gather transfers. - - - Even though a plain memcpy can look like a particular case of a - scatter-gather transfer, with a single chunk to copy, it's a distinct - transaction type in the mem2mem transfer case. This is because some very - simple devices might be able to do contiguous single-chunk memory copies, - but have no support for more complex SG transfers. - - No matter what the overall size of the combined chunks for source and destination is, only as many bytes as the smallest of the two will be transmitted. That means the number and size of the scatter-gather buffers in diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 2d39967bafcc..55272942e721 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -277,7 +277,6 @@ GPIO devm_gpiochip_add_data() devm_gpio_request() devm_gpio_request_one() - devm_gpio_free() I2C devm_i2c_new_dummy_device() diff --git a/Documentation/driver-api/firmware/core.rst b/Documentation/driver-api/firmware/core.rst index 1d1688cbc078..803cd574bbd7 100644 --- a/Documentation/driver-api/firmware/core.rst +++ b/Documentation/driver-api/firmware/core.rst @@ -13,4 +13,5 @@ documents these features. direct-fs-lookup fallback-mechanisms lookup-order + firmware-usage-guidelines diff --git a/Documentation/driver-api/firmware/firmware-usage-guidelines.rst b/Documentation/driver-api/firmware/firmware-usage-guidelines.rst new file mode 100644 index 000000000000..fdcfce42c6d2 --- /dev/null +++ b/Documentation/driver-api/firmware/firmware-usage-guidelines.rst @@ -0,0 +1,44 @@ +=================== +Firmware Guidelines +=================== + +Users switching to a newer kernel should *not* have to install newer +firmware files to keep their hardware working. At the same time updated +firmware files must not cause any regressions for users of older kernel +releases. + +Drivers that use firmware from linux-firmware should follow the rules in +this guide. (Where there is limited control of the firmware, +i.e. company doesn't support Linux, firmwares sourced from misc places, +then of course these rules will not apply strictly.) + +* Firmware files shall be designed in a way that it allows checking for + firmware ABI version changes. It is recommended that firmware files be + versioned with at least a major/minor version. It is suggested that + the firmware files in linux-firmware be named with some device + specific name, and just the major version. The firmware version should + be stored in the firmware header, or as an exception, as part of the + firmware file name, in order to let the driver detact any non-ABI + fixes/changes. The firmware files in linux-firmware should be + overwritten with the newest compatible major version. Newer major + version firmware shall remain compatible with all kernels that load + that major number. + +* If the kernel support for the hardware is normally inactive, or the + hardware isn't available for public consumption, this can + be ignored, until the first kernel release that enables that hardware. + This means no major version bumps without the kernel retaining + backwards compatibility for the older major versions. Minor version + bumps should not introduce new features that newer kernels depend on + non-optionally. + +* If a security fix needs lockstep firmware and kernel fixes in order to + be successful, then all supported major versions in the linux-firmware + repo that are required by currently supported stable/LTS kernels, + should be updated with the security fix. The kernel patches should + detect if the firmware is new enough to declare if the security issue + is fixed. All communications around security fixes should point at + both the firmware and kernel fixes. If a security fix requires + deprecating old major versions, then this should only be done as a + last option, and be stated clearly in all communications. + diff --git a/Documentation/driver-api/surface_aggregator/client.rst b/Documentation/driver-api/surface_aggregator/client.rst index e519d374c378..27f95abdbe99 100644 --- a/Documentation/driver-api/surface_aggregator/client.rst +++ b/Documentation/driver-api/surface_aggregator/client.rst @@ -17,6 +17,8 @@ .. |SSAM_DEVICE| replace:: :c:func:`SSAM_DEVICE` .. |ssam_notifier_register| replace:: :c:func:`ssam_notifier_register` .. |ssam_notifier_unregister| replace:: :c:func:`ssam_notifier_unregister` +.. |ssam_device_notifier_register| replace:: :c:func:`ssam_device_notifier_register` +.. |ssam_device_notifier_unregister| replace:: :c:func:`ssam_device_notifier_unregister` .. |ssam_request_sync| replace:: :c:func:`ssam_request_sync` .. |ssam_event_mask| replace:: :c:type:`enum ssam_event_mask ` @@ -312,7 +314,9 @@ Handling Events To receive events from the SAM EC, an event notifier must be registered for the desired event via |ssam_notifier_register|. The notifier must be unregistered via |ssam_notifier_unregister| once it is not required any -more. +more. For |ssam_device| type clients, the |ssam_device_notifier_register| and +|ssam_device_notifier_unregister| wrappers should be preferred as they properly +handle hot-removal of client devices. Event notifiers are registered by providing (at minimum) a callback to call in case an event has been received, the registry specifying how the event diff --git a/Documentation/driver-api/vfio-mediated-device.rst b/Documentation/driver-api/vfio-mediated-device.rst index eded8719180f..66bd00d7aecf 100644 --- a/Documentation/driver-api/vfio-mediated-device.rst +++ b/Documentation/driver-api/vfio-mediated-device.rst @@ -1,3 +1,4 @@ +.. SPDX-License-Identifier: GPL-2.0-only .. include:: ===================== @@ -8,9 +9,6 @@ VFIO Mediated devices :Author: Neo Jia :Author: Kirti Wankhede -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU General Public License version 2 as -published by the Free Software Foundation. Virtual Function I/O (VFIO) Mediated devices[1] diff --git a/Documentation/driver-api/vme.rst b/Documentation/driver-api/vme.rst index def139c13410..c0b475369de0 100644 --- a/Documentation/driver-api/vme.rst +++ b/Documentation/driver-api/vme.rst @@ -290,8 +290,8 @@ The function :c:func:`vme_bus_num` returns the bus ID of the provided bridge. VME API ------- -.. kernel-doc:: include/linux/vme.h +.. kernel-doc:: drivers/staging/vme_user/vme.h :internal: -.. kernel-doc:: drivers/vme/vme.c +.. kernel-doc:: drivers/staging/vme_user/vme.c :export: diff --git a/Documentation/features/debug/gcov-profile-all/arch-support.txt b/Documentation/features/debug/gcov-profile-all/arch-support.txt index 502c1d409648..0b3ba2415fac 100644 --- a/Documentation/features/debug/gcov-profile-all/arch-support.txt +++ b/Documentation/features/debug/gcov-profile-all/arch-support.txt @@ -27,5 +27,5 @@ | sparc: | TODO | | um: | ok | | x86: | ok | - | xtensa: | TODO | + | xtensa: | ok | ----------------------- diff --git a/Documentation/features/debug/kcov/arch-support.txt b/Documentation/features/debug/kcov/arch-support.txt index afb90bebded2..0a91f5ce34a9 100644 --- a/Documentation/features/debug/kcov/arch-support.txt +++ b/Documentation/features/debug/kcov/arch-support.txt @@ -27,5 +27,5 @@ | sparc: | TODO | | um: | ok | | x86: | ok | - | xtensa: | TODO | + | xtensa: | ok | ----------------------- diff --git a/Documentation/filesystems/ext4/blockmap.rst b/Documentation/filesystems/ext4/blockmap.rst index 2bd990402a5c..cc596541ce79 100644 --- a/Documentation/filesystems/ext4/blockmap.rst +++ b/Documentation/filesystems/ext4/blockmap.rst @@ -1,7 +1,7 @@ .. SPDX-License-Identifier: GPL-2.0 +---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ -| i.i_block Offset | Where It Points | +| i.i_block Offset | Where It Points | +=====================+==============================================================================================================================================================================================================================+ | 0 to 11 | Direct map to file blocks 0 to 11. | +---------------------+------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ diff --git a/Documentation/filesystems/xfs-delayed-logging-design.rst b/Documentation/filesystems/xfs-delayed-logging-design.rst index 464405d2801e..4ef419f54663 100644 --- a/Documentation/filesystems/xfs-delayed-logging-design.rst +++ b/Documentation/filesystems/xfs-delayed-logging-design.rst @@ -1,29 +1,314 @@ .. SPDX-License-Identifier: GPL-2.0 -========================== -XFS Delayed Logging Design -========================== +================== +XFS Logging Design +================== -Introduction to Re-logging in XFS -================================= +Preamble +======== -XFS logging is a combination of logical and physical logging. Some objects, -such as inodes and dquots, are logged in logical format where the details -logged are made up of the changes to in-core structures rather than on-disk -structures. Other objects - typically buffers - have their physical changes -logged. The reason for these differences is to reduce the amount of log space -required for objects that are frequently logged. Some parts of inodes are more -frequently logged than others, and inodes are typically more frequently logged -than any other object (except maybe the superblock buffer) so keeping the -amount of metadata logged low is of prime importance. +This document describes the design and algorithms that the XFS journalling +subsystem is based on. This document describes the design and algorithms that +the XFS journalling subsystem is based on so that readers may familiarize +themselves with the general concepts of how transaction processing in XFS works. -The reason that this is such a concern is that XFS allows multiple separate -modifications to a single object to be carried in the log at any given time. -This allows the log to avoid needing to flush each change to disk before -recording a new change to the object. XFS does this via a method called -"re-logging". Conceptually, this is quite simple - all it requires is that any -new change to the object is recorded with a *new copy* of all the existing -changes in the new transaction that is written to the log. +We begin with an overview of transactions in XFS, followed by describing how +transaction reservations are structured and accounted, and then move into how we +guarantee forwards progress for long running transactions with finite initial +reservations bounds. At this point we need to explain how relogging works. With +the basic concepts covered, the design of the delayed logging mechanism is +documented. + + +Introduction +============ + +XFS uses Write Ahead Logging for ensuring changes to the filesystem metadata +are atomic and recoverable. For reasons of space and time efficiency, the +logging mechanisms are varied and complex, combining intents, logical and +physical logging mechanisms to provide the necessary recovery guarantees the +filesystem requires. + +Some objects, such as inodes and dquots, are logged in logical format where the +details logged are made up of the changes to in-core structures rather than +on-disk structures. Other objects - typically buffers - have their physical +changes logged. Long running atomic modifications have individual changes +chained together by intents, ensuring that journal recovery can restart and +finish an operation that was only partially done when the system stopped +functioning. + +The reason for these differences is to keep the amount of log space and CPU time +required to process objects being modified as small as possible and hence the +logging overhead as low as possible. Some items are very frequently modified, +and some parts of objects are more frequently modified than others, so keeping +the overhead of metadata logging low is of prime importance. + +The method used to log an item or chain modifications together isn't +particularly important in the scope of this document. It suffices to know that +the method used for logging a particular object or chaining modifications +together are different and are dependent on the object and/or modification being +performed. The logging subsystem only cares that certain specific rules are +followed to guarantee forwards progress and prevent deadlocks. + + +Transactions in XFS +=================== + +XFS has two types of high level transactions, defined by the type of log space +reservation they take. These are known as "one shot" and "permanent" +transactions. Permanent transaction reservations can take reservations that span +commit boundaries, whilst "one shot" transactions are for a single atomic +modification. + +The type and size of reservation must be matched to the modification taking +place. This means that permanent transactions can be used for one-shot +modifications, but one-shot reservations cannot be used for permanent +transactions. + +In the code, a one-shot transaction pattern looks somewhat like this:: + + tp = xfs_trans_alloc() + + + + xfs_trans_commit(tp); + +As items are modified in the transaction, the dirty regions in those items are +tracked via the transaction handle. Once the transaction is committed, all +resources joined to it are released, along with the remaining unused reservation +space that was taken at the transaction allocation time. + +In contrast, a permanent transaction is made up of multiple linked individual +transactions, and the pattern looks like this:: + + tp = xfs_trans_alloc() + xfs_ilock(ip, XFS_ILOCK_EXCL) + + loop { + xfs_trans_ijoin(tp, 0); + + xfs_trans_log_inode(tp, ip); + xfs_trans_roll(&tp); + } + + xfs_trans_commit(tp); + xfs_iunlock(ip, XFS_ILOCK_EXCL); + +While this might look similar to a one-shot transaction, there is an important +difference: xfs_trans_roll() performs a specific operation that links two +transactions together:: + + ntp = xfs_trans_dup(tp); + xfs_trans_commit(tp); + xfs_log_reserve(ntp); + +This results in a series of "rolling transactions" where the inode is locked +across the entire chain of transactions. Hence while this series of rolling +transactions is running, nothing else can read from or write to the inode and +this provides a mechanism for complex changes to appear atomic from an external +observer's point of view. + +It is important to note that a series of rolling transactions in a permanent +transaction does not form an atomic change in the journal. While each +individual modification is atomic, the chain is *not atomic*. If we crash half +way through, then recovery will only replay up to the last transactional +modification the loop made that was committed to the journal. + +This affects long running permanent transactions in that it is not possible to +predict how much of a long running operation will actually be recovered because +there is no guarantee of how much of the operation reached stale storage. Hence +if a long running operation requires multiple transactions to fully complete, +the high level operation must use intents and deferred operations to guarantee +recovery can complete the operation once the first transactions is persisted in +the on-disk journal. + + +Transactions are Asynchronous +============================= + +In XFS, all high level transactions are asynchronous by default. This means that +xfs_trans_commit() does not guarantee that the modification has been committed +to stable storage when it returns. Hence when a system crashes, not all the +completed transactions will be replayed during recovery. + +However, the logging subsystem does provide global ordering guarantees, such +that if a specific change is seen after recovery, all metadata modifications +that were committed prior to that change will also be seen. + +For single shot operations that need to reach stable storage immediately, or +ensuring that a long running permanent transaction is fully committed once it is +complete, we can explicitly tag a transaction as synchronous. This will trigger +a "log force" to flush the outstanding committed transactions to stable storage +in the journal and wait for that to complete. + +Synchronous transactions are rarely used, however, because they limit logging +throughput to the IO latency limitations of the underlying storage. Instead, we +tend to use log forces to ensure modifications are on stable storage only when +a user operation requires a synchronisation point to occur (e.g. fsync). + + +Transaction Reservations +======================== + +It has been mentioned a number of times now that the logging subsystem needs to +provide a forwards progress guarantee so that no modification ever stalls +because it can't be written to the journal due to a lack of space in the +journal. This is achieved by the transaction reservations that are made when +a transaction is first allocated. For permanent transactions, these reservations +are maintained as part of the transaction rolling mechanism. + +A transaction reservation provides a guarantee that there is physical log space +available to write the modification into the journal before we start making +modifications to objects and items. As such, the reservation needs to be large +enough to take into account the amount of metadata that the change might need to +log in the worst case. This means that if we are modifying a btree in the +transaction, we have to reserve enough space to record a full leaf-to-root split +of the btree. As such, the reservations are quite complex because we have to +take into account all the hidden changes that might occur. + +For example, a user data extent allocation involves allocating an extent from +free space, which modifies the free space trees. That's two btrees. Inserting +the extent into the inode's extent map might require a split of the extent map +btree, which requires another allocation that can modify the free space trees +again. Then we might have to update reverse mappings, which modifies yet +another btree which might require more space. And so on. Hence the amount of +metadata that a "simple" operation can modify can be quite large. + +This "worst case" calculation provides us with the static "unit reservation" +for the transaction that is calculated at mount time. We must guarantee that the +log has this much space available before the transaction is allowed to proceed +so that when we come to write the dirty metadata into the log we don't run out +of log space half way through the write. + +For one-shot transactions, a single unit space reservation is all that is +required for the transaction to proceed. For permanent transactions, however, we +also have a "log count" that affects the size of the reservation that is to be +made. + +While a permanent transaction can get by with a single unit of space +reservation, it is somewhat inefficient to do this as it requires the +transaction rolling mechanism to re-reserve space on every transaction roll. We +know from the implementation of the permanent transactions how many transaction +rolls are likely for the common modifications that need to be made. + +For example, and inode allocation is typically two transactions - one to +physically allocate a free inode chunk on disk, and another to allocate an inode +from an inode chunk that has free inodes in it. Hence for an inode allocation +transaction, we might set the reservation log count to a value of 2 to indicate +that the common/fast path transaction will commit two linked transactions in a +chain. Each time a permanent transaction rolls, it consumes an entire unit +reservation. + +Hence when the permanent transaction is first allocated, the log space +reservation is increases from a single unit reservation to multiple unit +reservations. That multiple is defined by the reservation log count, and this +means we can roll the transaction multiple times before we have to re-reserve +log space when we roll the transaction. This ensures that the common +modifications we make only need to reserve log space once. + +If the log count for a permanent transaction reaches zero, then it needs to +re-reserve physical space in the log. This is somewhat complex, and requires +an understanding of how the log accounts for space that has been reserved. + + +Log Space Accounting +==================== + +The position in the log is typically referred to as a Log Sequence Number (LSN). +The log is circular, so the positions in the log are defined by the combination +of a cycle number - the number of times the log has been overwritten - and the +offset into the log. A LSN carries the cycle in the upper 32 bits and the +offset in the lower 32 bits. The offset is in units of "basic blocks" (512 +bytes). Hence we can do realtively simple LSN based math to keep track of +available space in the log. + +Log space accounting is done via a pair of constructs called "grant heads". The +position of the grant heads is an absolute value, so the amount of space +available in the log is defined by the distance between the position of the +grant head and the current log tail. That is, how much space can be +reserved/consumed before the grant heads would fully wrap the log and overtake +the tail position. + +The first grant head is the "reserve" head. This tracks the byte count of the +reservations currently held by active transactions. It is a purely in-memory +accounting of the space reservation and, as such, actually tracks byte offsets +into the log rather than basic blocks. Hence it technically isn't using LSNs to +represent the log position, but it is still treated like a split {cycle,offset} +tuple for the purposes of tracking reservation space. + +The reserve grant head is used to accurately account for exact transaction +reservations amounts and the exact byte count that modifications actually make +and need to write into the log. The reserve head is used to prevent new +transactions from taking new reservations when the head reaches the current +tail. It will block new reservations in a FIFO queue and as the log tail moves +forward it will wake them in order once sufficient space is available. This FIFO +mechanism ensures no transaction is starved of resources when log space +shortages occur. + +The other grant head is the "write" head. Unlike the reserve head, this grant +head contains an LSN and it tracks the physical space usage in the log. While +this might sound like it is accounting the same state as the reserve grant head +- and it mostly does track exactly the same location as the reserve grant head - +there are critical differences in behaviour between them that provides the +forwards progress guarantees that rolling permanent transactions require. + +These differences when a permanent transaction is rolled and the internal "log +count" reaches zero and the initial set of unit reservations have been +exhausted. At this point, we still require a log space reservation to continue +the next transaction in the sequeunce, but we have none remaining. We cannot +sleep during the transaction commit process waiting for new log space to become +available, as we may end up on the end of the FIFO queue and the items we have +locked while we sleep could end up pinning the tail of the log before there is +enough free space in the log to fulfil all of the pending reservations and +then wake up transaction commit in progress. + +To take a new reservation without sleeping requires us to be able to take a +reservation even if there is no reservation space currently available. That is, +we need to be able to *overcommit* the log reservation space. As has already +been detailed, we cannot overcommit physical log space. However, the reserve +grant head does not track physical space - it only accounts for the amount of +reservations we currently have outstanding. Hence if the reserve head passes +over the tail of the log all it means is that new reservations will be throttled +immediately and remain throttled until the log tail is moved forward far enough +to remove the overcommit and start taking new reservations. In other words, we +can overcommit the reserve head without violating the physical log head and tail +rules. + +As a result, permanent transactions only "regrant" reservation space during +xfs_trans_commit() calls, while the physical log space reservation - tracked by +the write head - is then reserved separately by a call to xfs_log_reserve() +after the commit completes. Once the commit completes, we can sleep waiting for +physical log space to be reserved from the write grant head, but only if one +critical rule has been observed:: + + Code using permanent reservations must always log the items they hold + locked across each transaction they roll in the chain. + +"Re-logging" the locked items on every transaction roll ensures that the items +attached to the transaction chain being rolled are always relocated to the +physical head of the log and so do not pin the tail of the log. If a locked item +pins the tail of the log when we sleep on the write reservation, then we will +deadlock the log as we cannot take the locks needed to write back that item and +move the tail of the log forwards to free up write grant space. Re-logging the +locked items avoids this deadlock and guarantees that the log reservation we are +making cannot self-deadlock. + +If all rolling transactions obey this rule, then they can all make forwards +progress independently because nothing will block the progress of the log +tail moving forwards and hence ensuring that write grant space is always +(eventually) made available to permanent transactions no matter how many times +they roll. + + +Re-logging Explained +==================== + +XFS allows multiple separate modifications to a single object to be carried in +the log at any given time. This allows the log to avoid needing to flush each +change to disk before recording a new change to the object. XFS does this via a +method called "re-logging". Conceptually, this is quite simple - all it requires +is that any new change to the object is recorded with a *new copy* of all the +existing changes in the new transaction that is written to the log. That is, if we have a sequence of changes A through to F, and the object was written to disk after change D, we would see in the log the following series @@ -42,16 +327,13 @@ transaction:: In other words, each time an object is relogged, the new transaction contains the aggregation of all the previous changes currently held only in the log. -This relogging technique also allows objects to be moved forward in the log so -that an object being relogged does not prevent the tail of the log from ever -moving forward. This can be seen in the table above by the changing -(increasing) LSN of each subsequent transaction - the LSN is effectively a -direct encoding of the location in the log of the transaction. +This relogging technique allows objects to be moved forward in the log so that +an object being relogged does not prevent the tail of the log from ever moving +forward. This can be seen in the table above by the changing (increasing) LSN +of each subsequent transaction, and it's the technique that allows us to +implement long-running, multiple-commit permanent transactions. -This relogging is also used to implement long-running, multiple-commit -transactions. These transaction are known as rolling transactions, and require -a special log reservation known as a permanent transaction reservation. A -typical example of a rolling transaction is the removal of extents from an +A typical example of a rolling transaction is the removal of extents from an inode which can only be done at a rate of two extents per transaction because of reservation size limitations. Hence a rolling extent removal transaction keeps relogging the inode and btree buffers as they get modified in each @@ -67,12 +349,13 @@ the log over and over again. Worse is the fact that objects tend to get dirtier as they get relogged, so each subsequent transaction is writing more metadata into the log. -Another feature of the XFS transaction subsystem is that most transactions are -asynchronous. That is, they don't commit to disk until either a log buffer is -filled (a log buffer can hold multiple transactions) or a synchronous operation -forces the log buffers holding the transactions to disk. This means that XFS is -doing aggregation of transactions in memory - batching them, if you like - to -minimise the impact of the log IO on transaction throughput. +It should now also be obvious how relogging and asynchronous transactions go +hand in hand. That is, transactions don't get written to the physical journal +until either a log buffer is filled (a log buffer can hold multiple +transactions) or a synchronous operation forces the log buffers holding the +transactions to disk. This means that XFS is doing aggregation of transactions +in memory - batching them, if you like - to minimise the impact of the log IO on +transaction throughput. The limitation on asynchronous transaction throughput is the number and size of log buffers made available by the log manager. By default there are 8 log diff --git a/Documentation/process/embargoed-hardware-issues.rst b/Documentation/process/embargoed-hardware-issues.rst index 95999302d279..b6b4481e2474 100644 --- a/Documentation/process/embargoed-hardware-issues.rst +++ b/Documentation/process/embargoed-hardware-issues.rst @@ -244,7 +244,7 @@ disclosure of a particular issue, unless requested by a response team or by an involved disclosed party. The current ambassadors list: ============= ======================================================== - AMD Tom Lendacky + AMD Tom Lendacky Ampere Darren Hart ARM Catalin Marinas IBM Power Anton Blanchard @@ -264,6 +264,9 @@ an involved disclosed party. The current ambassadors list: Amazon Google Kees Cook + + GCC + LLVM Nick Desaulniers ============= ======================================================== If you want your organization to be added to the ambassadors list, please diff --git a/Documentation/scsi/ufs.rst b/Documentation/scsi/ufs.rst index fbac745b783c..885b1a736e3f 100644 --- a/Documentation/scsi/ufs.rst +++ b/Documentation/scsi/ufs.rst @@ -17,6 +17,8 @@ Universal Flash Storage 3.2 UTP Transfer requests 3.3 UFS error handling 3.4 SCSI Error handling + 4. BSG Support + 5. UFS Reference Clock Frequency configuration 1. Overview @@ -193,3 +195,16 @@ UFS specifications can be found at: - UFS - http://www.jedec.org/sites/default/files/docs/JESD220.pdf - UFSHCI - http://www.jedec.org/sites/default/files/docs/JESD223.pdf + +5. UFS Reference Clock Frequency configuration +============================================== + +Devicetree can define a clock named "ref_clk" under the UFS controller node +to specify the intended reference clock frequency for the UFS storage +parts. ACPI-based system can specify the frequency using ACPI +Device-Specific Data property named "ref-clk-freq". In both ways the value +is interpreted as frequency in Hz and must match one of the values given in +the UFS specification. UFS subsystem will attempt to read the value when +executing common controller initialization. If the value is available, UFS +subsytem will ensure the bRefClkFreq attribute of the UFS storage device is +set accordingly and will modify it if there is a mismatch. diff --git a/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst b/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst index 88273ebe7823..cf5f1fca3d92 100644 --- a/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/zh_CN/process/embargoed-hardware-issues.rst @@ -174,7 +174,7 @@ CVE分配 ============= ======================================================== ARM - AMD Tom Lendacky + AMD Tom Lendacky IBM Intel Tony Luck Qualcomm Trilok Soni diff --git a/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst b/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst index 6c76fc96131a..fbde3e26eda5 100644 --- a/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst +++ b/Documentation/translations/zh_TW/process/embargoed-hardware-issues.rst @@ -177,7 +177,7 @@ CVE分配 ============= ======================================================== ARM - AMD Tom Lendacky + AMD Tom Lendacky IBM Intel Tony Luck Qualcomm Trilok Soni diff --git a/Documentation/usb/gadget-testing.rst b/Documentation/usb/gadget-testing.rst index 1c37159fa171..2278c9ffb74a 100644 --- a/Documentation/usb/gadget-testing.rst +++ b/Documentation/usb/gadget-testing.rst @@ -333,6 +333,12 @@ In each lun directory there are the following attribute files: being a CD-ROM. nofua Flag specifying that FUA flag in SCSI WRITE(10,12) + forced_eject This write-only file is useful only when + the function is active. It causes the backing + file to be forcibly detached from the LUN, + regardless of whether the host has allowed it. + Any non-zero number of bytes written will + result in ejection. =============== ============================================== Testing the MASS STORAGE function diff --git a/Documentation/usb/mass-storage.rst b/Documentation/usb/mass-storage.rst index d181b47c3cb6..f399ec631599 100644 --- a/Documentation/usb/mass-storage.rst +++ b/Documentation/usb/mass-storage.rst @@ -181,6 +181,15 @@ sysfs entries Reflects the state of nofua flag for given logical unit. It can be read and written. + - forced_eject + + When written into, it causes the backing file to be forcibly + detached from the LUN, regardless of whether the host has allowed + it. The content doesn't matter, any non-zero number of bytes + written will result in ejection. + + Can not be read. + Other then those, as usual, the values of module parameters can be read from /sys/module/g_mass_storage/parameters/* files. diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index fcab013e47c9..3b985b19f39d 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -120,7 +120,7 @@ Code Seq# Include File Comments 'C' 01-2F linux/capi.h conflict! 'C' F0-FF drivers/net/wan/cosa.h conflict! 'D' all arch/s390/include/asm/dasd.h -'D' 40-5F drivers/scsi/dpt/dtpi_ioctl.h +'D' 40-5F drivers/scsi/dpt/dtpi_ioctl.h Dead since 2022 'D' 05 drivers/scsi/pmcraid.h 'E' all linux/input.h conflict! 'E' 00-0F xen/evtchn.h conflict! diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst index dca926762f1f..9788b19f9ff7 100644 --- a/Documentation/virt/kvm/api.rst +++ b/Documentation/virt/kvm/api.rst @@ -1150,6 +1150,10 @@ The following bits are defined in the flags field: fields contain a valid state. This bit will be set whenever KVM_CAP_EXCEPTION_PAYLOAD is enabled. +- KVM_VCPUEVENT_VALID_TRIPLE_FAULT may be set to signal that the + triple_fault_pending field contains a valid state. This bit will + be set whenever KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled. + ARM64: ^^^^^^ @@ -1245,6 +1249,10 @@ can be set in the flags field to signal that the exception_has_payload, exception_payload, and exception.pending fields contain a valid state and shall be written into the VCPU. +If KVM_CAP_X86_TRIPLE_FAULT_EVENT is enabled, KVM_VCPUEVENT_VALID_TRIPLE_FAULT +can be set in flags field to signal that the triple_fault field contains +a valid state and shall be written into the VCPU. + ARM64: ^^^^^^ @@ -2998,7 +3006,9 @@ KVM_CREATE_PIT2. The state is returned in the following structure:: Valid flags are:: /* disable PIT in HPET legacy mode */ - #define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 + #define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 + /* speaker port data bit enabled */ + #define KVM_PIT_FLAGS_SPEAKER_DATA_ON 0x00000002 This IOCTL replaces the obsolete KVM_GET_PIT. @@ -5127,7 +5137,15 @@ into ESA mode. This reset is a superset of the initial reset. __u32 reserved[3]; }; -cmd values: +**Ultravisor return codes** +The Ultravisor return (reason) codes are provided by the kernel if a +Ultravisor call has been executed to achieve the results expected by +the command. Therefore they are independent of the IOCTL return +code. If KVM changes `rc`, its value will always be greater than 0 +hence setting it to 0 before issuing a PV command is advised to be +able to detect a change of `rc`. + +**cmd values:** KVM_PV_ENABLE Allocate memory and register the VM with the Ultravisor, thereby @@ -5143,7 +5161,6 @@ KVM_PV_ENABLE ===== ============================= KVM_PV_DISABLE - Deregister the VM from the Ultravisor and reclaim the memory that had been donated to the Ultravisor, making it usable by the kernel again. All registered VCPUs are converted back to non-protected @@ -5160,6 +5177,117 @@ KVM_PV_VM_VERIFY Verify the integrity of the unpacked image. Only if this succeeds, KVM is allowed to start protected VCPUs. +KVM_PV_INFO + :Capability: KVM_CAP_S390_PROTECTED_DUMP + + Presents an API that provides Ultravisor related data to userspace + via subcommands. len_max is the size of the user space buffer, + len_written is KVM's indication of how much bytes of that buffer + were actually written to. len_written can be used to determine the + valid fields if more response fields are added in the future. + + :: + + enum pv_cmd_info_id { + KVM_PV_INFO_VM, + KVM_PV_INFO_DUMP, + }; + + struct kvm_s390_pv_info_header { + __u32 id; + __u32 len_max; + __u32 len_written; + __u32 reserved; + }; + + struct kvm_s390_pv_info { + struct kvm_s390_pv_info_header header; + struct kvm_s390_pv_info_dump dump; + struct kvm_s390_pv_info_vm vm; + }; + +**subcommands:** + + KVM_PV_INFO_VM + This subcommand provides basic Ultravisor information for PV + hosts. These values are likely also exported as files in the sysfs + firmware UV query interface but they are more easily available to + programs in this API. + + The installed calls and feature_indication members provide the + installed UV calls and the UV's other feature indications. + + The max_* members provide information about the maximum number of PV + vcpus, PV guests and PV guest memory size. + + :: + + struct kvm_s390_pv_info_vm { + __u64 inst_calls_list[4]; + __u64 max_cpus; + __u64 max_guests; + __u64 max_guest_addr; + __u64 feature_indication; + }; + + + KVM_PV_INFO_DUMP + This subcommand provides information related to dumping PV guests. + + :: + + struct kvm_s390_pv_info_dump { + __u64 dump_cpu_buffer_len; + __u64 dump_config_mem_buffer_per_1m; + __u64 dump_config_finalize_len; + }; + +KVM_PV_DUMP + :Capability: KVM_CAP_S390_PROTECTED_DUMP + + Presents an API that provides calls which facilitate dumping a + protected VM. + + :: + + struct kvm_s390_pv_dmp { + __u64 subcmd; + __u64 buff_addr; + __u64 buff_len; + __u64 gaddr; /* For dump storage state */ + }; + + **subcommands:** + + KVM_PV_DUMP_INIT + Initializes the dump process of a protected VM. If this call does + not succeed all other subcommands will fail with -EINVAL. This + subcommand will return -EINVAL if a dump process has not yet been + completed. + + Not all PV vms can be dumped, the owner needs to set `dump + allowed` PCF bit 34 in the SE header to allow dumping. + + KVM_PV_DUMP_CONFIG_STOR_STATE + Stores `buff_len` bytes of tweak component values starting with + the 1MB block specified by the absolute guest address + (`gaddr`). `buff_len` needs to be `conf_dump_storage_state_len` + aligned and at least >= the `conf_dump_storage_state_len` value + provided by the dump uv_info data. buff_user might be written to + even if an error rc is returned. For instance if we encounter a + fault after writing the first page of data. + + KVM_PV_DUMP_COMPLETE + If the subcommand succeeds it completes the dump process and lets + KVM_PV_DUMP_INIT be called again. + + On success `conf_dump_finalize_len` bytes of completion data will be + stored to the `buff_addr`. The completion data contains a key + derivation seed, IV, tweak nonce and encryption keys as well as an + authentication tag all of which are needed to decrypt the dump at a + later time. + + 4.126 KVM_X86_SET_MSR_FILTER ---------------------------- @@ -5811,6 +5939,78 @@ of CPUID leaf 0xD on the host. This ioctl injects an event channel interrupt directly to the guest vCPU. +4.136 KVM_S390_PV_CPU_COMMAND +----------------------------- + +:Capability: KVM_CAP_S390_PROTECTED_DUMP +:Architectures: s390 +:Type: vcpu ioctl +:Parameters: none +:Returns: 0 on success, < 0 on error + +This ioctl closely mirrors `KVM_S390_PV_COMMAND` but handles requests +for vcpus. It re-uses the kvm_s390_pv_dmp struct and hence also shares +the command ids. + +**command:** + +KVM_PV_DUMP + Presents an API that provides calls which facilitate dumping a vcpu + of a protected VM. + +**subcommand:** + +KVM_PV_DUMP_CPU + Provides encrypted dump data like register values. + The length of the returned data is provided by uv_info.guest_cpu_stor_len. + +4.137 KVM_S390_ZPCI_OP +---------------------- + +:Capability: KVM_CAP_S390_ZPCI_OP +:Architectures: s390 +:Type: vm ioctl +:Parameters: struct kvm_s390_zpci_op (in) +:Returns: 0 on success, <0 on error + +Used to manage hardware-assisted virtualization features for zPCI devices. + +Parameters are specified via the following structure:: + + struct kvm_s390_zpci_op { + /* in */ + __u32 fh; /* target device */ + __u8 op; /* operation to perform */ + __u8 pad[3]; + union { + /* for KVM_S390_ZPCIOP_REG_AEN */ + struct { + __u64 ibv; /* Guest addr of interrupt bit vector */ + __u64 sb; /* Guest addr of summary bit */ + __u32 flags; + __u32 noi; /* Number of interrupts */ + __u8 isc; /* Guest interrupt subclass */ + __u8 sbo; /* Offset of guest summary bit vector */ + __u16 pad; + } reg_aen; + __u64 reserved[8]; + } u; + }; + +The type of operation is specified in the "op" field. +KVM_S390_ZPCIOP_REG_AEN is used to register the VM for adapter event +notification interpretation, which will allow firmware delivery of adapter +events directly to the vm, with KVM providing a backup delivery mechanism; +KVM_S390_ZPCIOP_DEREG_AEN is used to subsequently disable interpretation of +adapter event notifications. + +The target zPCI function must also be specified via the "fh" field. For the +KVM_S390_ZPCIOP_REG_AEN operation, additional information to establish firmware +delivery must be provided via the "reg_aen" struct. + +The "pad" and "reserved" fields may be used for future extensions and should be +set to 0s by userspace. + 5. The kvm_run structure ======================== @@ -6414,6 +6614,26 @@ array field represents return values. The userspace should update the return values of SBI call before resuming the VCPU. For more details on RISC-V SBI spec refer, https://github.com/riscv/riscv-sbi-doc. +:: + + /* KVM_EXIT_NOTIFY */ + struct { + #define KVM_NOTIFY_CONTEXT_INVALID (1 << 0) + __u32 flags; + } notify; + +Used on x86 systems. When the VM capability KVM_CAP_X86_NOTIFY_VMEXIT is +enabled, a VM exit generated if no event window occurs in VM non-root mode +for a specified amount of time. Once KVM_X86_NOTIFY_VMEXIT_USER is set when +enabling the cap, it would exit to userspace with the exit reason +KVM_EXIT_NOTIFY for further handling. The "flags" field contains more +detailed info. + +The valid value for 'flags' is: + + - KVM_NOTIFY_CONTEXT_INVALID -- the VM context is corrupted and not valid + in VMCS. It would run into unknown result if resume the target VM. + :: /* Fix the size of the union. */ @@ -7357,8 +7577,71 @@ The valid bits in cap.args[0] are: hypercall instructions. Executing the incorrect hypercall instruction will generate a #UD within the guest. + +KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS By default, KVM emulates MONITOR/MWAIT (if + they are intercepted) as NOPs regardless of + whether or not MONITOR/MWAIT are supported + according to guest CPUID. When this quirk + is disabled and KVM_X86_DISABLE_EXITS_MWAIT + is not set (MONITOR/MWAIT are intercepted), + KVM will inject a #UD on MONITOR/MWAIT if + they're unsupported per guest CPUID. Note, + KVM will modify MONITOR/MWAIT support in + guest CPUID on writes to MISC_ENABLE if + KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT is + disabled. =================================== ============================================ +7.32 KVM_CAP_MAX_VCPU_ID +------------------------ + +:Architectures: x86 +:Target: VM +:Parameters: args[0] - maximum APIC ID value set for current VM +:Returns: 0 on success, -EINVAL if args[0] is beyond KVM_MAX_VCPU_IDS + supported in KVM or if it has been set. + +This capability allows userspace to specify maximum possible APIC ID +assigned for current VM session prior to the creation of vCPUs, saving +memory for data structures indexed by the APIC ID. Userspace is able +to calculate the limit to APIC ID values from designated +CPU topology. + +The value can be changed only until KVM_ENABLE_CAP is set to a nonzero +value or until a vCPU is created. Upon creation of the first vCPU, +if the value was set to zero or KVM_ENABLE_CAP was not invoked, KVM +uses the return value of KVM_CHECK_EXTENSION(KVM_CAP_MAX_VCPU_ID) as +the maximum APIC ID. + +7.33 KVM_CAP_X86_NOTIFY_VMEXIT +------------------------------ + +:Architectures: x86 +:Target: VM +:Parameters: args[0] is the value of notify window as well as some flags +:Returns: 0 on success, -EINVAL if args[0] contains invalid flags or notify + VM exit is unsupported. + +Bits 63:32 of args[0] are used for notify window. +Bits 31:0 of args[0] are for some flags. Valid bits are:: + + #define KVM_X86_NOTIFY_VMEXIT_ENABLED (1 << 0) + #define KVM_X86_NOTIFY_VMEXIT_USER (1 << 1) + +This capability allows userspace to configure the notify VM exit on/off +in per-VM scope during VM creation. Notify VM exit is disabled by default. +When userspace sets KVM_X86_NOTIFY_VMEXIT_ENABLED bit in args[0], VMM will +enable this feature with the notify window provided, which will generate +a VM exit if no event window occurs in VM non-root mode for a specified of +time (notify window). + +If KVM_X86_NOTIFY_VMEXIT_USER is set in args[0], upon notify VM exits happen, +KVM would exit to userspace for handling. + +This capability is aimed to mitigate the threat that malicious VMs can +cause CPU stuck (due to event windows don't open up) and make the CPU +unavailable to host or other VMs. + 8. Other capabilities. ====================== @@ -7965,6 +8248,61 @@ should adjust CPUID leaf 0xA to reflect that the PMU is disabled. When enabled, KVM will exit to userspace with KVM_EXIT_SYSTEM_EVENT of type KVM_SYSTEM_EVENT_SUSPEND to process the guest suspend request. +8.37 KVM_CAP_S390_PROTECTED_DUMP +-------------------------------- + +:Capability: KVM_CAP_S390_PROTECTED_DUMP +:Architectures: s390 +:Type: vm + +This capability indicates that KVM and the Ultravisor support dumping +PV guests. The `KVM_PV_DUMP` command is available for the +`KVM_S390_PV_COMMAND` ioctl and the `KVM_PV_INFO` command provides +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 +: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. + +This capability disables the NX huge pages mitigation for iTLB MULTIHIT. + +The capability has no effect if the nx_huge_pages module parameter is not set. + +This capability may only be set before any vCPUs are created. + +8.39 KVM_CAP_S390_CPU_TOPOLOGY +------------------------------ + +:Capability: KVM_CAP_S390_CPU_TOPOLOGY +:Architectures: s390 +:Type: vm + +This capability indicates that KVM will provide the S390 CPU Topology +facility which consist of the interpretation of the PTF instruction for +the function code 2 along with interception and forwarding of both the +PTF instruction with function codes 0 or 1 and the STSI(15,1,x) +instruction to the userland hypervisor. + +The stfle facility 11, CPU Topology facility, should not be indicated +to the guest without this capability. + +When this capability is present, KVM provides a new attribute group +on vm fd, KVM_S390_VM_CPU_TOPOLOGY. +This new attribute allows to get, set or clear the Modified Change +Topology Report (MTCR) bit of the SCA through the kvm_device_attr +structure. + +When getting the Modified Change Topology Report value, the attr->addr +must point to a byte where the value will be stored or retrieved from. + 9. Known KVM API problems ========================= diff --git a/Documentation/virt/kvm/s390/index.rst b/Documentation/virt/kvm/s390/index.rst index 605f488f0cc5..44ec9ab14b59 100644 --- a/Documentation/virt/kvm/s390/index.rst +++ b/Documentation/virt/kvm/s390/index.rst @@ -10,3 +10,4 @@ KVM for s390 systems s390-diag s390-pv s390-pv-boot + s390-pv-dump diff --git a/Documentation/virt/kvm/s390/s390-pv-dump.rst b/Documentation/virt/kvm/s390/s390-pv-dump.rst new file mode 100644 index 000000000000..e542f06048f3 --- /dev/null +++ b/Documentation/virt/kvm/s390/s390-pv-dump.rst @@ -0,0 +1,64 @@ +.. SPDX-License-Identifier: GPL-2.0 + +=========================================== +s390 (IBM Z) Protected Virtualization dumps +=========================================== + +Summary +------- + +Dumping a VM is an essential tool for debugging problems inside +it. This is especially true when a protected VM runs into trouble as +there's no way to access its memory and registers from the outside +while it's running. + +However when dumping a protected VM we need to maintain its +confidentiality until the dump is in the hands of the VM owner who +should be the only one capable of analysing it. + +The confidentiality of the VM dump is ensured by the Ultravisor who +provides an interface to KVM over which encrypted CPU and memory data +can be requested. The encryption is based on the Customer +Communication Key which is the key that's used to encrypt VM data in a +way that the customer is able to decrypt. + + +Dump process +------------ + +A dump is done in 3 steps: + +**Initiation** + +This step initializes the dump process, generates cryptographic seeds +and extracts dump keys with which the VM dump data will be encrypted. + +**Data gathering** + +Currently there are two types of data that can be gathered from a VM: +the memory and the vcpu state. + +The vcpu state contains all the important registers, general, floating +point, vector, control and tod/timers of a vcpu. The vcpu dump can +contain incomplete data if a vcpu is dumped while an instruction is +emulated with help of the hypervisor. This is indicated by a flag bit +in the dump data. For the same reason it is very important to not only +write out the encrypted vcpu state, but also the unencrypted state +from the hypervisor. + +The memory state is further divided into the encrypted memory and its +metadata comprised of the encryption tweaks and status flags. The +encrypted memory can simply be read once it has been exported. The +time of the export does not matter as no re-encryption is +needed. Memory that has been swapped out and hence was exported can be +read from the swap and written to the dump target without need for any +special actions. + +The tweaks / status flags for the exported pages need to be requested +from the Ultravisor. + +**Finalization** + +The finalization step will provide the data needed to be able to +decrypt the vcpu and memory data and end the dump process. When this +step completes successfully a new dump initiation can be started. diff --git a/MAINTAINERS b/MAINTAINERS index 416b2ccc83d8..ba213b20508f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -736,6 +736,14 @@ S: Maintained F: Documentation/i2c/busses/i2c-ali1563.rst F: drivers/i2c/busses/i2c-ali1563.c +ALIBABA ELASTIC RDMA DRIVER +M: Cheng Xu +M: Kai Shen +L: linux-rdma@vger.kernel.org +S: Supported +F: drivers/infiniband/hw/erdma +F: include/uapi/rdma/erdma-abi.h + ALIENWARE WMI DRIVER L: Dell.Client.Kernel@dell.com S: Maintained @@ -1007,7 +1015,7 @@ AMD PMC DRIVER M: Shyam Sundar S K L: platform-driver-x86@vger.kernel.org S: Maintained -F: drivers/platform/x86/amd-pmc.* +F: drivers/platform/x86/amd/pmc.c AMD HSMP DRIVER M: Naveen Krishna Chatradhi @@ -1017,7 +1025,7 @@ S: Maintained F: Documentation/x86/amd_hsmp.rst F: arch/x86/include/asm/amd_hsmp.h F: arch/x86/include/uapi/asm/amd_hsmp.h -F: drivers/platform/x86/amd_hsmp.c +F: drivers/platform/x86/amd/hsmp.c AMD POWERPLAY AND SWSMU M: Evan Quan @@ -1851,6 +1859,7 @@ T: git https://github.com/AsahiLinux/linux.git F: Documentation/devicetree/bindings/arm/apple.yaml F: Documentation/devicetree/bindings/arm/apple/* F: Documentation/devicetree/bindings/clock/apple,nco.yaml +F: Documentation/devicetree/bindings/dma/apple,admac.yaml F: Documentation/devicetree/bindings/i2c/apple,i2c.yaml F: Documentation/devicetree/bindings/interrupt-controller/apple,* F: Documentation/devicetree/bindings/iommu/apple,dart.yaml @@ -1864,6 +1873,7 @@ F: Documentation/devicetree/bindings/power/apple* F: Documentation/devicetree/bindings/watchdog/apple,wdt.yaml F: arch/arm64/boot/dts/apple/ F: drivers/clk/clk-apple-nco.c +F: drivers/dma/apple-admac.c F: drivers/i2c/busses/i2c-pasemi-core.c F: drivers/i2c/busses/i2c-pasemi-platform.c F: drivers/iommu/apple-dart.c @@ -2609,7 +2619,7 @@ L: linux-unisoc@lists.infradead.org (moderated for non-subscribers) S: Maintained F: Documentation/devicetree/bindings/arm/rda.yaml F: Documentation/devicetree/bindings/gpio/gpio-rda.yaml -F: Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.txt +F: Documentation/devicetree/bindings/interrupt-controller/rda,8810pl-intc.yaml F: Documentation/devicetree/bindings/serial/rda,8810pl-uart.yaml F: Documentation/devicetree/bindings/timer/rda,8810pl-timer.yaml F: arch/arm/boot/dts/rda8810pl-* @@ -2689,6 +2699,7 @@ B: mailto:linux-samsung-soc@vger.kernel.org T: git git://git.kernel.org/pub/scm/linux/kernel/git/krzk/linux.git F: Documentation/arm/samsung/ F: Documentation/devicetree/bindings/arm/samsung/ +F: Documentation/devicetree/bindings/hwinfo/samsung,* F: Documentation/devicetree/bindings/power/pd-samsung.yaml F: Documentation/devicetree/bindings/soc/samsung/ F: arch/arm/boot/dts/exynos* @@ -2941,6 +2952,7 @@ M: Tero Kristo L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) S: Supported F: Documentation/devicetree/bindings/arm/ti/k3.yaml +F: Documentation/devicetree/bindings/hwinfo/ti,k3-socinfo.yaml F: arch/arm64/boot/dts/ti/Makefile F: arch/arm64/boot/dts/ti/k3-* F: include/dt-bindings/pinctrl/k3.h @@ -3179,6 +3191,13 @@ S: Maintained F: Documentation/devicetree/bindings/media/aspeed-video.txt F: drivers/media/platform/aspeed/ +ASPEED USB UDC DRIVER +M: Neal Liu +L: linux-aspeed@lists.ozlabs.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/usb/aspeed,ast2600-udc.yaml +F: drivers/usb/gadget/udc/aspeed_udc.c + ASUS NOTEBOOKS AND EEEPC ACPI/WMI EXTRAS DRIVERS M: Corentin Chary L: acpi4asus-user@lists.sourceforge.net @@ -5500,7 +5519,7 @@ W: http://www.chelsio.com F: drivers/net/ethernet/chelsio/cxgb3/ CXGB3 ISCSI DRIVER (CXGB3I) -M: Karen Xie +M: Varun Prakash L: linux-scsi@vger.kernel.org S: Supported W: http://www.chelsio.com @@ -5532,7 +5551,7 @@ W: http://www.chelsio.com F: drivers/net/ethernet/chelsio/cxgb4/ CXGB4 ISCSI DRIVER (CXGB4I) -M: Karen Xie +M: Varun Prakash L: linux-scsi@vger.kernel.org S: Supported W: http://www.chelsio.com @@ -6093,6 +6112,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git F: Documentation/devicetree/bindings/dma/ F: Documentation/driver-api/dmaengine/ F: drivers/dma/ +F: include/dt-bindings/dma/ F: include/linux/dma/ F: include/linux/dmaengine.h F: include/linux/of_dma.h @@ -6263,14 +6283,6 @@ F: Documentation/networking/device_drivers/ethernet/freescale/dpaa2/switch-drive F: drivers/net/ethernet/freescale/dpaa2/dpaa2-switch* F: drivers/net/ethernet/freescale/dpaa2/dpsw* -DPT_I2O SCSI RAID DRIVER -M: Adaptec OEM Raid Solutions -L: linux-scsi@vger.kernel.org -S: Maintained -W: http://www.adaptec.com/ -F: drivers/scsi/dpt* -F: drivers/scsi/dpt/ - DRBD DRIVER M: Philipp Reisner M: Lars Ellenberg @@ -7494,7 +7506,7 @@ F: Documentation/admin-guide/media/em28xx* F: drivers/media/usb/em28xx/ EMBEDDED LINUX -M: Matt Mackall +M: Olivia Mackall M: David Woodhouse L: linux-embedded@vger.kernel.org S: Maintained @@ -8902,7 +8914,7 @@ F: include/trace/events/hwmon*.h K: (devm_)?hwmon_device_(un)?register(|_with_groups|_with_info) HARDWARE RANDOM NUMBER GENERATOR CORE -M: Matt Mackall +M: Olivia Mackall M: Herbert Xu L: linux-crypto@vger.kernel.org S: Odd fixes @@ -10043,6 +10055,13 @@ L: linux-fbdev@vger.kernel.org S: Maintained F: drivers/video/fbdev/i810/ +INTEL 8255 GPIO DRIVER +M: William Breathitt Gray +L: linux-gpio@vger.kernel.org +S: Maintained +F: drivers/gpio/gpio-i8255.c +F: drivers/gpio/gpio-i8255.h + INTEL ASoC DRIVERS M: Cezary Rojewski M: Pierre-Louis Bossart @@ -10179,7 +10198,8 @@ S: Supported Q: https://patchwork.kernel.org/project/linux-dmaengine/list/ F: drivers/dma/ioat* -INTEL IADX DRIVER +INTEL IDXD DRIVER +M: Fenghua Yu M: Dave Jiang L: dmaengine@vger.kernel.org S: Supported @@ -13465,6 +13485,12 @@ F: drivers/scsi/smartpqi/smartpqi*.[ch] F: include/linux/cciss*.h F: include/uapi/linux/cciss*.h +MICROSOFT SURFACE AGGREGATOR TABLET-MODE SWITCH +M: Maximilian Luz +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/surface/surface_aggregator_tabletsw.c + MICROSOFT SURFACE BATTERY AND AC DRIVERS M: Maximilian Luz L: linux-pm@vger.kernel.org @@ -13536,6 +13562,12 @@ F: include/linux/surface_acpi_notify.h F: include/linux/surface_aggregator/ F: include/uapi/linux/surface_aggregator/ +MICROSOFT SURFACE SYSTEM AGGREGATOR HUB DRIVER +M: Maximilian Luz +L: platform-driver-x86@vger.kernel.org +S: Maintained +F: drivers/platform/surface/surface_aggregator_hub.c + MICROTEK X6 SCANNER M: Oliver Neukum S: Maintained @@ -14482,7 +14514,8 @@ S: Supported W: http://git.infradead.org/nvme.git T: git://git.infradead.org/nvme.git F: drivers/nvme/host/ -F: include/linux/nvme.h +F: drivers/nvme/common/ +F: include/linux/nvme* F: include/uapi/linux/nvme_ioctl.h NVM EXPRESS FC TRANSPORT DRIVERS @@ -15073,6 +15106,13 @@ S: Maintained T: git git://linuxtv.org/media_tree.git F: drivers/media/i2c/ov9734.c +ONBOARD USB HUB DRIVER +M: Matthias Kaehlcke +L: linux-usb@vger.kernel.org +S: Maintained +F: Documentation/ABI/testing/sysfs-bus-platform-onboard-usb-hub +F: drivers/usb/misc/onboard_usb_hub.c + ONENAND FLASH DRIVER M: Kyungmin Park L: linux-mtd@lists.infradead.org @@ -16122,14 +16162,6 @@ S: Maintained F: Documentation/devicetree/bindings/iio/chemical/plantower,pms7003.yaml F: drivers/iio/chemical/pms7003.c -PLATFORM FEATURE INFRASTRUCTURE -M: Juergen Gross -S: Maintained -F: arch/*/include/asm/platform-feature.h -F: include/asm-generic/platform-feature.h -F: include/linux/platform-feature.h -F: kernel/platform-feature.c - PLDMFW LIBRARY M: Jacob Keller S: Maintained @@ -16498,7 +16530,7 @@ L: linux-pwm@vger.kernel.org S: Maintained Q: https://patchwork.ozlabs.org/project/linux-pwm/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/thierry.reding/linux-pwm.git -F: Documentation/devicetree/bindings/gpio/gpio-mvebu.txt +F: Documentation/devicetree/bindings/gpio/gpio-mvebu.yaml F: Documentation/devicetree/bindings/pwm/ F: Documentation/driver-api/pwm.rst F: drivers/gpio/gpio-mvebu.c @@ -17473,6 +17505,7 @@ F: drivers/mailbox/mailbox-mpfs.c F: drivers/pci/controller/pcie-microchip-host.c F: drivers/soc/microchip/ F: drivers/spi/spi-microchip-core.c +F: drivers/usb/musb/mpfs.c F: include/soc/microchip/mpfs.h RNBD BLOCK DRIVERS @@ -17784,6 +17817,7 @@ M: Eric Farman L: linux-s390@vger.kernel.org L: kvm@vger.kernel.org S: Supported +F: arch/s390/kvm/pci* F: drivers/vfio/pci/vfio_pci_zdev.c F: include/uapi/linux/vfio_zdev.h @@ -18812,6 +18846,7 @@ SOFTWARE RAID (Multiple Disks) SUPPORT M: Song Liu L: linux-raid@vger.kernel.org S: Supported +Q: https://patchwork.kernel.org/project/linux-raid/list/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/song/md.git F: drivers/md/Kconfig F: drivers/md/Makefile @@ -19261,6 +19296,7 @@ F: drivers/staging/olpc_dcon/ STAGING - REALTEK RTL8188EU DRIVERS M: Larry Finger M: Phillip Potter +R: Pavel Skripkin S: Supported F: drivers/staging/r8188eu/ @@ -20784,6 +20820,13 @@ L: linux-mediatek@lists.infradead.org (moderated for non-subscribers) S: Maintained F: drivers/ufs/host/ufs-mediatek* +UNIVERSAL FLASH STORAGE HOST CONTROLLER DRIVER RENESAS HOOKS +M: Yoshihiro Shimoda +L: linux-renesas-soc@vger.kernel.org +L: linux-scsi@vger.kernel.org +S: Maintained +F: drivers/ufs/host/ufs-renesas.c + UNSORTED BLOCK IMAGES (UBI) M: Richard Weinberger L: linux-mtd@lists.infradead.org @@ -21580,12 +21623,10 @@ M: Martyn Welch M: Manohar Vanga M: Greg Kroah-Hartman L: linux-kernel@vger.kernel.org -S: Maintained +S: Odd fixes T: git git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc.git F: Documentation/driver-api/vme.rst F: drivers/staging/vme_user/ -F: drivers/vme/ -F: include/linux/vme* VM SOCKETS (AF_VSOCK) M: Stefano Garzarella diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 4294c0123857..53e6a1da9af5 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -75,6 +75,7 @@ config ARM select HAVE_ARCH_KFENCE if MMU && !XIP_KERNEL select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL + select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_PFN_VALID select HAVE_ARCH_SECCOMP @@ -1419,6 +1420,7 @@ config HW_PERF_EVENTS config ARM_MODULE_PLTS bool "Use PLTs to allow module memory to spill over into vmalloc area" depends on MODULES + select KASAN_VMALLOC if KASAN default y help Allocate PLTs when loading modules so that jumps and calls whose diff --git a/arch/arm/boot/bootp/bootp.lds b/arch/arm/boot/bootp/bootp.lds index fc54394f4340..160128186bf8 100644 --- a/arch/arm/boot/bootp/bootp.lds +++ b/arch/arm/boot/bootp/bootp.lds @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * linux/arch/arm/boot/bootp/bootp.lds * * Copyright (C) 2000-2002 Russell King - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ OUTPUT_ARCH(arm) ENTRY(_start) diff --git a/arch/arm/boot/dts/am33xx.dtsi b/arch/arm/boot/dts/am33xx.dtsi index 9a8698bd2852..32d397b3950b 100644 --- a/arch/arm/boot/dts/am33xx.dtsi +++ b/arch/arm/boot/dts/am33xx.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for AM33XX SoC * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/am3517.dtsi b/arch/arm/boot/dts/am3517.dtsi index de33c4f89f33..cb316135bc7c 100644 --- a/arch/arm/boot/dts/am3517.dtsi +++ b/arch/arm/boot/dts/am3517.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for am3517 SoC * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include "omap3.dtsi" diff --git a/arch/arm/boot/dts/am4372.dtsi b/arch/arm/boot/dts/am4372.dtsi index 61a1d88f9df6..8613355bbd5e 100644 --- a/arch/arm/boot/dts/am4372.dtsi +++ b/arch/arm/boot/dts/am4372.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for AM4372 SoC * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/artpec6-devboard.dts b/arch/arm/boot/dts/artpec6-devboard.dts index d20d95359b28..042a9cc920c6 100644 --- a/arch/arm/boot/dts/artpec6-devboard.dts +++ b/arch/arm/boot/dts/artpec6-devboard.dts @@ -1,10 +1,5 @@ -/* - * Axis ARTPEC-6 development board. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Axis ARTPEC-6 development board. /dts-v1/; #include "artpec6.dtsi" diff --git a/arch/arm/boot/dts/bcm11351.dtsi b/arch/arm/boot/dts/bcm11351.dtsi index 6197e7d80e3b..53696078bbf0 100644 --- a/arch/arm/boot/dts/bcm11351.dtsi +++ b/arch/arm/boot/dts/bcm11351.dtsi @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2012-2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2012-2013 Broadcom Corporation #include #include diff --git a/arch/arm/boot/dts/bcm21664-garnet.dts b/arch/arm/boot/dts/bcm21664-garnet.dts index be468f4adc37..1854cd907a1b 100644 --- a/arch/arm/boot/dts/bcm21664-garnet.dts +++ b/arch/arm/boot/dts/bcm21664-garnet.dts @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation /dts-v1/; diff --git a/arch/arm/boot/dts/bcm21664.dtsi b/arch/arm/boot/dts/bcm21664.dtsi index cc58f2b926b9..2684c37cb3a0 100644 --- a/arch/arm/boot/dts/bcm21664.dtsi +++ b/arch/arm/boot/dts/bcm21664.dtsi @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/arch/arm/boot/dts/bcm28155-ap.dts b/arch/arm/boot/dts/bcm28155-ap.dts index 78465ad37c5f..fce3d5260b00 100644 --- a/arch/arm/boot/dts/bcm28155-ap.dts +++ b/arch/arm/boot/dts/bcm28155-ap.dts @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Broadcom Corporation /dts-v1/; diff --git a/arch/arm/boot/dts/dm814x.dtsi b/arch/arm/boot/dts/dm814x.dtsi index a92630113f57..8104969c67c1 100644 --- a/arch/arm/boot/dts/dm814x.dtsi +++ b/arch/arm/boot/dts/dm814x.dtsi @@ -1,8 +1,4 @@ -/* - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only #include #include diff --git a/arch/arm/boot/dts/dm816x.dtsi b/arch/arm/boot/dts/dm816x.dtsi index eb0a95da94b2..649b33194455 100644 --- a/arch/arm/boot/dts/dm816x.dtsi +++ b/arch/arm/boot/dts/dm816x.dtsi @@ -1,8 +1,4 @@ -/* - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only #include #include diff --git a/arch/arm/boot/dts/dra62x.dtsi b/arch/arm/boot/dts/dra62x.dtsi index cc4878aaa8ea..cfefa670515c 100644 --- a/arch/arm/boot/dts/dra62x.dtsi +++ b/arch/arm/boot/dts/dra62x.dtsi @@ -1,8 +1,4 @@ -/* - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only #include "dm814x.dtsi" diff --git a/arch/arm/boot/dts/dra7-dspeve-thermal.dtsi b/arch/arm/boot/dts/dra7-dspeve-thermal.dtsi index e75569383dd8..747ff0db90c7 100644 --- a/arch/arm/boot/dts/dra7-dspeve-thermal.dtsi +++ b/arch/arm/boot/dts/dra7-dspeve-thermal.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for DRA7x SoC DSPEVE thermal * * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/dra7-iva-thermal.dtsi b/arch/arm/boot/dts/dra7-iva-thermal.dtsi index a7077321613f..0a31313065df 100644 --- a/arch/arm/boot/dts/dra7-iva-thermal.dtsi +++ b/arch/arm/boot/dts/dra7-iva-thermal.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for DRA7x SoC IVA thermal * * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/dra72x-mmc-iodelay.dtsi b/arch/arm/boot/dts/dra72x-mmc-iodelay.dtsi index a9dce919d443..34eea3c048bd 100644 --- a/arch/arm/boot/dts/dra72x-mmc-iodelay.dtsi +++ b/arch/arm/boot/dts/dra72x-mmc-iodelay.dtsi @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MMC IOdelay values for TI's DRA72x, DRA71x and AM571x SoCs. * * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* diff --git a/arch/arm/boot/dts/dra74x-mmc-iodelay.dtsi b/arch/arm/boot/dts/dra74x-mmc-iodelay.dtsi index e86da7a970b6..b9d040135c5f 100644 --- a/arch/arm/boot/dts/dra74x-mmc-iodelay.dtsi +++ b/arch/arm/boot/dts/dra74x-mmc-iodelay.dtsi @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MMC IOdelay values for TI's DRA74x, DRA75x and AM572x SoCs. * * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ /* diff --git a/arch/arm/boot/dts/imx23-pinfunc.h b/arch/arm/boot/dts/imx23-pinfunc.h index 5c0f32ca3a93..468c079f3c2b 100644 --- a/arch/arm/boot/dts/imx23-pinfunc.h +++ b/arch/arm/boot/dts/imx23-pinfunc.h @@ -1,14 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Header providing constants for i.MX23 pinctrl bindings. * * Copyright (C) 2013 Lothar Waßmann - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ #ifndef __DT_BINDINGS_MX23_PINCTRL_H__ diff --git a/arch/arm/boot/dts/imx28-pinfunc.h b/arch/arm/boot/dts/imx28-pinfunc.h index e11f69ba0fe4..d427e6c2fa78 100644 --- a/arch/arm/boot/dts/imx28-pinfunc.h +++ b/arch/arm/boot/dts/imx28-pinfunc.h @@ -1,14 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Header providing constants for i.MX28 pinctrl bindings. * * Copyright (C) 2013 Lothar Waßmann - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ #ifndef __DT_BINDINGS_MX28_PINCTRL_H__ diff --git a/arch/arm/boot/dts/imx6q-gk802.dts b/arch/arm/boot/dts/imx6q-gk802.dts index ccc2487d47ca..2fda68f9d3f6 100644 --- a/arch/arm/boot/dts/imx6q-gk802.dts +++ b/arch/arm/boot/dts/imx6q-gk802.dts @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2013 Philipp Zabel - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Philipp Zabel /dts-v1/; #include diff --git a/arch/arm/boot/dts/mxs-pinfunc.h b/arch/arm/boot/dts/mxs-pinfunc.h index c6da987b20cb..31297abcbc71 100644 --- a/arch/arm/boot/dts/mxs-pinfunc.h +++ b/arch/arm/boot/dts/mxs-pinfunc.h @@ -1,14 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Header providing constants for i.MX28 pinctrl bindings. * * Copyright (C) 2013 Lothar Waßmann - * - * The code contained herein is licensed under the GNU General Public - * License. You may obtain a copy of the GNU General Public License - * Version 2 at the following locations: - * - * http://www.opensource.org/licenses/gpl-license.html - * http://www.gnu.org/copyleft/gpl.html */ #ifndef __DT_BINDINGS_MXS_PINCTRL_H__ diff --git a/arch/arm/boot/dts/omap2.dtsi b/arch/arm/boot/dts/omap2.dtsi index 5750ca1233cc..afabb36a8ac1 100644 --- a/arch/arm/boot/dts/omap2.dtsi +++ b/arch/arm/boot/dts/omap2.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP2 SoC * * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap2420.dtsi b/arch/arm/boot/dts/omap2420.dtsi index bb529a2a295d..821da51cb870 100644 --- a/arch/arm/boot/dts/omap2420.dtsi +++ b/arch/arm/boot/dts/omap2420.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP2420 SoC * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include "omap2.dtsi" diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index 23115ba61bc0..b9a9e6e45266 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP243x SoC * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include "omap2.dtsi" diff --git a/arch/arm/boot/dts/omap3-cpu-thermal.dtsi b/arch/arm/boot/dts/omap3-cpu-thermal.dtsi index a9069cca5888..0da759f8e2c2 100644 --- a/arch/arm/boot/dts/omap3-cpu-thermal.dtsi +++ b/arch/arm/boot/dts/omap3-cpu-thermal.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP3 SoC CPU thermal * * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap3.dtsi b/arch/arm/boot/dts/omap3.dtsi index 64b7e6fddd1b..825075ff0e34 100644 --- a/arch/arm/boot/dts/omap3.dtsi +++ b/arch/arm/boot/dts/omap3.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP3 SoC * * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap34xx.dtsi b/arch/arm/boot/dts/omap34xx.dtsi index 8b8451399784..2eb73ae7ef3e 100644 --- a/arch/arm/boot/dts/omap34xx.dtsi +++ b/arch/arm/boot/dts/omap34xx.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP34xx/OMAP35xx SoC * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap36xx.dtsi b/arch/arm/boot/dts/omap36xx.dtsi index 22b33098b1a2..32ac7924a130 100644 --- a/arch/arm/boot/dts/omap36xx.dtsi +++ b/arch/arm/boot/dts/omap36xx.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP3 SoC * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap4-cpu-thermal.dtsi b/arch/arm/boot/dts/omap4-cpu-thermal.dtsi index 03d054b2bf9a..4d7eeb133dad 100644 --- a/arch/arm/boot/dts/omap4-cpu-thermal.dtsi +++ b/arch/arm/boot/dts/omap4-cpu-thermal.dtsi @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP4/5 SoC CPU thermal * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ * Contact: Eduardo Valentin - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap443x.dtsi b/arch/arm/boot/dts/omap443x.dtsi index 8466161197ae..238aceb799f8 100644 --- a/arch/arm/boot/dts/omap443x.dtsi +++ b/arch/arm/boot/dts/omap443x.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP443x SoC * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include "omap4.dtsi" diff --git a/arch/arm/boot/dts/omap4460.dtsi b/arch/arm/boot/dts/omap4460.dtsi index 3d6db1db94e0..1b27a862ae81 100644 --- a/arch/arm/boot/dts/omap4460.dtsi +++ b/arch/arm/boot/dts/omap4460.dtsi @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP4460 SoC * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include "omap4.dtsi" diff --git a/arch/arm/boot/dts/omap5-core-thermal.dtsi b/arch/arm/boot/dts/omap5-core-thermal.dtsi index 02e76338bfbc..e0d8e39a0014 100644 --- a/arch/arm/boot/dts/omap5-core-thermal.dtsi +++ b/arch/arm/boot/dts/omap5-core-thermal.dtsi @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP543x SoC CORE thermal * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ * Contact: Eduardo Valentin - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/omap5-gpu-thermal.dtsi b/arch/arm/boot/dts/omap5-gpu-thermal.dtsi index bf8fa9372e57..1b4b7d9136c8 100644 --- a/arch/arm/boot/dts/omap5-gpu-thermal.dtsi +++ b/arch/arm/boot/dts/omap5-gpu-thermal.dtsi @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree Source for OMAP543x SoC GPU thermal * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ * Contact: Eduardo Valentin - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/arm/boot/dts/orion5x-lacie-d2-network.dts b/arch/arm/boot/dts/orion5x-lacie-d2-network.dts index 422958d13d42..03471d30bfd9 100644 --- a/arch/arm/boot/dts/orion5x-lacie-d2-network.dts +++ b/arch/arm/boot/dts/orion5x-lacie-d2-network.dts @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Thomas Petazzoni * Copyright (C) 2009 Simon Guinot - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ /dts-v1/; diff --git a/arch/arm/boot/dts/orion5x-lacie-ethernet-disk-mini-v2.dts b/arch/arm/boot/dts/orion5x-lacie-ethernet-disk-mini-v2.dts index 0043e0040153..f17e25ac98dd 100644 --- a/arch/arm/boot/dts/orion5x-lacie-ethernet-disk-mini-v2.dts +++ b/arch/arm/boot/dts/orion5x-lacie-ethernet-disk-mini-v2.dts @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2012 Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2012 Thomas Petazzoni /* * TODO: add Orion USB device port init when kernel.org support is added. diff --git a/arch/arm/boot/dts/orion5x-maxtor-shared-storage-2.dts b/arch/arm/boot/dts/orion5x-maxtor-shared-storage-2.dts index 0ca6208a267d..d57859998350 100644 --- a/arch/arm/boot/dts/orion5x-maxtor-shared-storage-2.dts +++ b/arch/arm/boot/dts/orion5x-maxtor-shared-storage-2.dts @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Thomas Petazzoni * Copyright (C) Sylver Bruneau - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ /dts-v1/; diff --git a/arch/arm/boot/dts/orion5x-mv88f5181.dtsi b/arch/arm/boot/dts/orion5x-mv88f5181.dtsi index f667012b26ca..819f9efb7058 100644 --- a/arch/arm/boot/dts/orion5x-mv88f5181.dtsi +++ b/arch/arm/boot/dts/orion5x-mv88f5181.dtsi @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2016 Jamie Lentin - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Jamie Lentin #include "orion5x.dtsi" diff --git a/arch/arm/boot/dts/orion5x-mv88f5182.dtsi b/arch/arm/boot/dts/orion5x-mv88f5182.dtsi index d1ed71c60209..86b87fb26dc9 100644 --- a/arch/arm/boot/dts/orion5x-mv88f5182.dtsi +++ b/arch/arm/boot/dts/orion5x-mv88f5182.dtsi @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2014 Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Thomas Petazzoni #include "orion5x.dtsi" diff --git a/arch/arm/boot/dts/orion5x-netgear-wnr854t.dts b/arch/arm/boot/dts/orion5x-netgear-wnr854t.dts index ea081afa469d..4f4888ec9138 100644 --- a/arch/arm/boot/dts/orion5x-netgear-wnr854t.dts +++ b/arch/arm/boot/dts/orion5x-netgear-wnr854t.dts @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2016 Jamie Lentin - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Jamie Lentin /dts-v1/; diff --git a/arch/arm/boot/dts/orion5x-rd88f5182-nas.dts b/arch/arm/boot/dts/orion5x-rd88f5182-nas.dts index 487324f7c54e..fd78aa02a3c5 100644 --- a/arch/arm/boot/dts/orion5x-rd88f5182-nas.dts +++ b/arch/arm/boot/dts/orion5x-rd88f5182-nas.dts @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2014 Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Thomas Petazzoni /dts-v1/; diff --git a/arch/arm/boot/dts/orion5x.dtsi b/arch/arm/boot/dts/orion5x.dtsi index 61e631b3fd8b..2d41f5c166ee 100644 --- a/arch/arm/boot/dts/orion5x.dtsi +++ b/arch/arm/boot/dts/orion5x.dtsi @@ -1,10 +1,5 @@ -/* - * Copyright (C) 2012 Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2012 Thomas Petazzoni #define MBUS_ID(target,attributes) (((target) << 24) | ((attributes) << 16)) diff --git a/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi b/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi index 333c2af97130..8b48d3c89a04 100644 --- a/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi +++ b/arch/arm/boot/dts/stm32mp15xx-dkx.dtsi @@ -677,6 +677,14 @@ &usbh_ehci { phys = <&usbphyc_port0>; status = "okay"; + #address-cells = <1>; + #size-cells = <0>; + /* onboard HUB */ + hub@1 { + compatible = "usb424,2514"; + reg = <1>; + vdd-supply = <&v3v3>; + }; }; &usbotg_hs { diff --git a/arch/arm/configs/multi_v7_defconfig b/arch/arm/configs/multi_v7_defconfig index bcd7a431c66e..12b35008571f 100644 --- a/arch/arm/configs/multi_v7_defconfig +++ b/arch/arm/configs/multi_v7_defconfig @@ -865,6 +865,7 @@ CONFIG_USB_CHIPIDEA_UDC=y CONFIG_USB_CHIPIDEA_HOST=y CONFIG_USB_ISP1760=y CONFIG_USB_HSIC_USB3503=y +CONFIG_USB_ONBOARD_HUB=m CONFIG_AB8500_USB=y CONFIG_KEYSTONE_USB_PHY=m CONFIG_NOP_USB_XCEIV=y diff --git a/arch/arm/include/asm/hardware/cache-aurora-l2.h b/arch/arm/include/asm/hardware/cache-aurora-l2.h index 39769ffa0051..9694808ee97c 100644 --- a/arch/arm/include/asm/hardware/cache-aurora-l2.h +++ b/arch/arm/include/asm/hardware/cache-aurora-l2.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * AURORA shared L2 cache controller support * @@ -5,10 +6,6 @@ * * Yehuda Yitschak * Gregory CLEMENT - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ASM_ARM_HARDWARE_AURORA_L2_H diff --git a/arch/arm/include/asm/hardware/cache-feroceon-l2.h b/arch/arm/include/asm/hardware/cache-feroceon-l2.h index 12e1588dc4f1..eb2e7b7f70a8 100644 --- a/arch/arm/include/asm/hardware/cache-feroceon-l2.h +++ b/arch/arm/include/asm/hardware/cache-feroceon-l2.h @@ -1,13 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/include/asm/hardware/cache-feroceon-l2.h * * Copyright (C) 2008 Marvell Semiconductor - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ extern void __init feroceon_l2_init(int l2_wt_override); extern int __init feroceon_of_init(void); - diff --git a/arch/arm/include/asm/hardware/cache-tauros2.h b/arch/arm/include/asm/hardware/cache-tauros2.h index 295e2e40151b..4e493facaa31 100644 --- a/arch/arm/include/asm/hardware/cache-tauros2.h +++ b/arch/arm/include/asm/hardware/cache-tauros2.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/include/asm/hardware/cache-tauros2.h * * Copyright (C) 2008 Marvell Semiconductor - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define CACHE_TAUROS2_PREFETCH_ON (1 << 0) diff --git a/arch/arm/include/asm/irq_work.h b/arch/arm/include/asm/irq_work.h index 8895999834cc..3149e4dc1b54 100644 --- a/arch/arm/include/asm/irq_work.h +++ b/arch/arm/include/asm/irq_work.h @@ -9,4 +9,6 @@ static inline bool arch_irq_work_has_interrupt(void) return is_smp(); } +extern void arch_irq_work_raise(void); + #endif /* _ASM_ARM_IRQ_WORK_H */ diff --git a/arch/arm/include/debug/brcmstb.S b/arch/arm/include/debug/brcmstb.S index f684e3a815f6..f6175e6e28cd 100644 --- a/arch/arm/include/debug/brcmstb.S +++ b/arch/arm/include/debug/brcmstb.S @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2016 Broadcom */ #include #include diff --git a/arch/arm/kernel/reboot.c b/arch/arm/kernel/reboot.c index 2cb943422554..3f0d5c3dae11 100644 --- a/arch/arm/kernel/reboot.c +++ b/arch/arm/kernel/reboot.c @@ -10,6 +10,7 @@ #include #include #include +#include #include "reboot.h" diff --git a/arch/arm/mach-bcm/Makefile b/arch/arm/mach-bcm/Makefile index 020075eb38af..2e523f29ec3b 100644 --- a/arch/arm/mach-bcm/Makefile +++ b/arch/arm/mach-bcm/Makefile @@ -1,14 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Copyright (C) 2012-2015 Broadcom Corporation # -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License as -# published by the Free Software Foundation version 2. -# -# This program is distributed "as is" WITHOUT ANY WARRANTY of any -# kind, whether express or implied; without even the implied warranty -# of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. # Cygnus obj-$(CONFIG_ARCH_BCM_CYGNUS) += bcm_cygnus.o diff --git a/arch/arm/mach-bcm/bcm_cygnus.c b/arch/arm/mach-bcm/bcm_cygnus.c index 7ae894c7849b..2469b66cc59e 100644 --- a/arch/arm/mach-bcm/bcm_cygnus.c +++ b/arch/arm/mach-bcm/bcm_cygnus.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include diff --git a/arch/arm/mach-bcm/bcm_hr2.c b/arch/arm/mach-bcm/bcm_hr2.c index c104f28995d7..a19cc206ec76 100644 --- a/arch/arm/mach-bcm/bcm_hr2.c +++ b/arch/arm/mach-bcm/bcm_hr2.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2017 Broadcom #include diff --git a/arch/arm/mach-bcm/bcm_kona_smc.c b/arch/arm/mach-bcm/bcm_kona_smc.c index 347bfb7f03e2..185335843bbd 100644 --- a/arch/arm/mach-bcm/bcm_kona_smc.c +++ b/arch/arm/mach-bcm/bcm_kona_smc.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Broadcom Corporation #include #include #include diff --git a/arch/arm/mach-bcm/bcm_kona_smc.h b/arch/arm/mach-bcm/bcm_kona_smc.h index 2e29ec67e414..17f3811fa543 100644 --- a/arch/arm/mach-bcm/bcm_kona_smc.h +++ b/arch/arm/mach-bcm/bcm_kona_smc.h @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2013 Broadcom Corporation */ #ifndef BCM_KONA_SMC_H #define BCM_KONA_SMC_H diff --git a/arch/arm/mach-bcm/bcm_nsp.c b/arch/arm/mach-bcm/bcm_nsp.c index a1101a3d318e..65ed9f8a518a 100644 --- a/arch/arm/mach-bcm/bcm_nsp.c +++ b/arch/arm/mach-bcm/bcm_nsp.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015 Broadcom Corporation #include diff --git a/arch/arm/mach-bcm/board_bcm21664.c b/arch/arm/mach-bcm/board_bcm21664.c index c5bf01641172..9ce9ed092b09 100644 --- a/arch/arm/mach-bcm/board_bcm21664.c +++ b/arch/arm/mach-bcm/board_bcm21664.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include diff --git a/arch/arm/mach-bcm/board_bcm23550.c b/arch/arm/mach-bcm/board_bcm23550.c index 0ac01debd077..dd6e9cb785e0 100644 --- a/arch/arm/mach-bcm/board_bcm23550.c +++ b/arch/arm/mach-bcm/board_bcm23550.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include diff --git a/arch/arm/mach-bcm/board_bcm281xx.c b/arch/arm/mach-bcm/board_bcm281xx.c index 1238ac801530..91f6d633ee8e 100644 --- a/arch/arm/mach-bcm/board_bcm281xx.c +++ b/arch/arm/mach-bcm/board_bcm281xx.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2012-2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2012-2014 Broadcom Corporation #include #include diff --git a/arch/arm/mach-bcm/brcmstb.c b/arch/arm/mach-bcm/brcmstb.c index 5f127d5f1045..2e3385ced82a 100644 --- a/arch/arm/mach-bcm/brcmstb.c +++ b/arch/arm/mach-bcm/brcmstb.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013-2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013-2014 Broadcom Corporation #include #include diff --git a/arch/arm/mach-bcm/kona_l2_cache.c b/arch/arm/mach-bcm/kona_l2_cache.c index 59ad86304092..457c66e1d7ae 100644 --- a/arch/arm/mach-bcm/kona_l2_cache.c +++ b/arch/arm/mach-bcm/kona_l2_cache.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2012-2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2012-2014 Broadcom Corporation #include diff --git a/arch/arm/mach-bcm/kona_l2_cache.h b/arch/arm/mach-bcm/kona_l2_cache.h index 46f84a95ab1c..77c5dc033910 100644 --- a/arch/arm/mach-bcm/kona_l2_cache.h +++ b/arch/arm/mach-bcm/kona_l2_cache.h @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2012-2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2012-2014 Broadcom Corporation */ #ifdef CONFIG_ARCH_BCM_MOBILE_L2_CACHE void kona_l2_cache_init(void); diff --git a/arch/arm/mach-bcm/platsmp-brcmstb.c b/arch/arm/mach-bcm/platsmp-brcmstb.c index 9b457714a41c..8989299ebdd6 100644 --- a/arch/arm/mach-bcm/platsmp-brcmstb.c +++ b/arch/arm/mach-bcm/platsmp-brcmstb.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Broadcom STB CPU SMP and hotplug support for ARM * * Copyright (C) 2013-2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-davinci/board-da830-evm.c b/arch/arm/mach-davinci/board-da830-evm.c index a0aaab5c7aa6..6299e5c8f4ea 100644 --- a/arch/arm/mach-davinci/board-da830-evm.c +++ b/arch/arm/mach-davinci/board-da830-evm.c @@ -1,13 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DA830/OMAP L137 EVM board * * Author: Mark A. Greer * Derived from: arch/arm/mach-davinci/board-dm644x-evm.c * - * 2007, 2009 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007, 2009 (c) MontaVista Software, Inc. */ #include #include diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c index efc26b472ef8..92d74bc71967 100644 --- a/arch/arm/mach-davinci/board-da850-evm.c +++ b/arch/arm/mach-davinci/board-da850-evm.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DA850/OMAP-L138 EVM board * @@ -6,10 +7,7 @@ * Derived from: arch/arm/mach-davinci/board-da830-evm.c * Original Copyrights follow: * - * 2007, 2009 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007, 2009 (c) MontaVista Software, Inc. */ #include #include diff --git a/arch/arm/mach-davinci/board-dm355-evm.c b/arch/arm/mach-davinci/board-dm355-evm.c index f7c56f662d4c..b48ab1c3e48b 100644 --- a/arch/arm/mach-davinci/board-dm355-evm.c +++ b/arch/arm/mach-davinci/board-dm355-evm.c @@ -1,12 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DaVinci EVM board support * * Author: Kevin Hilman, Deep Root Systems, LLC * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) MontaVista Software, Inc. */ #include #include diff --git a/arch/arm/mach-davinci/board-dm355-leopard.c b/arch/arm/mach-davinci/board-dm355-leopard.c index 0f2b61266197..32b9d607d025 100644 --- a/arch/arm/mach-davinci/board-dm355-leopard.c +++ b/arch/arm/mach-davinci/board-dm355-leopard.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * DM355 leopard board support * * Based on board-dm355-evm.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-davinci/board-dm365-evm.c b/arch/arm/mach-davinci/board-dm365-evm.c index 9adcb5879d14..d8c6c360818b 100644 --- a/arch/arm/mach-davinci/board-dm365-evm.c +++ b/arch/arm/mach-davinci/board-dm365-evm.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DaVinci DM365 EVM board support * * Copyright (C) 2009 Texas Instruments Incorporated - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include @@ -861,4 +853,3 @@ MACHINE_START(DAVINCI_DM365_EVM, "DaVinci DM365 EVM") .init_late = davinci_init_late, .dma_zone_size = SZ_128M, MACHINE_END - diff --git a/arch/arm/mach-davinci/board-mityomapl138.c b/arch/arm/mach-davinci/board-mityomapl138.c index 3f084bdb9bc5..a46e7b9ff8e0 100644 --- a/arch/arm/mach-davinci/board-mityomapl138.c +++ b/arch/arm/mach-davinci/board-mityomapl138.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Critical Link MityOMAP-L138 SoM * * Copyright (C) 2010 Critical Link LLC - https://www.criticallink.com - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of - * any kind, whether express or implied. */ #define pr_fmt(fmt) "MityOMAPL138: " fmt diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c index 20f71856bf7e..8a80115999ad 100644 --- a/arch/arm/mach-davinci/board-omapl138-hawk.c +++ b/arch/arm/mach-davinci/board-omapl138-hawk.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Hawkboard.org based on TI's OMAP-L138 Platform * * Initial code: Syed Mohammed Khasim * * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of - * any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-davinci/common.c b/arch/arm/mach-davinci/common.c index 0a6f826ff136..c1ce6b2a8d48 100644 --- a/arch/arm/mach-davinci/common.c +++ b/arch/arm/mach-davinci/common.c @@ -1,12 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Code commons to all DaVinci SoCs. * * Author: Mark A. Greer * - * 2009 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2009 (c) MontaVista Software, Inc. */ #include #include diff --git a/arch/arm/mach-davinci/common.h b/arch/arm/mach-davinci/common.h index 139b83de011d..772b51e0ac5e 100644 --- a/arch/arm/mach-davinci/common.h +++ b/arch/arm/mach-davinci/common.h @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Header for code common to all DaVinci machines. * * Author: Kevin Hilman, MontaVista Software, Inc. * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) MontaVista Software, Inc. */ #ifndef __ARCH_ARM_MACH_DAVINCI_COMMON_H diff --git a/arch/arm/mach-davinci/cpuidle.h b/arch/arm/mach-davinci/cpuidle.h index 0d9193aefab5..976d43073597 100644 --- a/arch/arm/mach-davinci/cpuidle.h +++ b/arch/arm/mach-davinci/cpuidle.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI DaVinci cpuidle platform support * * 2009 (C) Texas Instruments, Inc. https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _MACH_DAVINCI_CPUIDLE_H #define _MACH_DAVINCI_CPUIDLE_H diff --git a/arch/arm/mach-davinci/cputype.h b/arch/arm/mach-davinci/cputype.h index 1fe9f84d5ee6..4590afdbe449 100644 --- a/arch/arm/mach-davinci/cputype.h +++ b/arch/arm/mach-davinci/cputype.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * DaVinci CPU type detection * @@ -8,10 +9,7 @@ * compiled in to the kernel, the macros return 0 so that * resulting code can be optimized out. * - * 2009 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2009 (c) Deep Root Systems, LLC. */ #ifndef _ASM_ARCH_CPU_H #define _ASM_ARCH_CPU_H diff --git a/arch/arm/mach-davinci/da830.c b/arch/arm/mach-davinci/da830.c index 1b86657c6e9d..eab5fac18806 100644 --- a/arch/arm/mach-davinci/da830.c +++ b/arch/arm/mach-davinci/da830.c @@ -1,12 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DA830/OMAP L137 chip specific setup * * Author: Mark A. Greer * - * 2009 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2009 (c) MontaVista Software, Inc. */ #include #include diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c index cd514c136de0..635e88daf5dd 100644 --- a/arch/arm/mach-davinci/da850.c +++ b/arch/arm/mach-davinci/da850.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DA850/OMAP-L138 chip specific setup * @@ -6,10 +7,7 @@ * Derived from: arch/arm/mach-davinci/da830.c * Original Copyrights follow: * - * 2009 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2009 (c) MontaVista Software, Inc. */ #include diff --git a/arch/arm/mach-davinci/da8xx.h b/arch/arm/mach-davinci/da8xx.h index 699df08714ba..382811dbbc3b 100644 --- a/arch/arm/mach-davinci/da8xx.h +++ b/arch/arm/mach-davinci/da8xx.h @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Chip specific defines for DA8XX/OMAP L1XX SoC * * Author: Mark A. Greer * - * 2007, 2009-2010 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007, 2009-2010 (c) MontaVista Software, Inc. */ #ifndef __ASM_ARCH_DAVINCI_DA8XX_H #define __ASM_ARCH_DAVINCI_DA8XX_H diff --git a/arch/arm/mach-davinci/davinci.h b/arch/arm/mach-davinci/davinci.h index e895eaf9c7cc..b7172797692b 100644 --- a/arch/arm/mach-davinci/davinci.h +++ b/arch/arm/mach-davinci/davinci.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This file contains the processor specific definitions * of the TI DM644x, DM355, DM365, and DM646x. * * Copyright (C) 2011 Texas Instruments Incorporated * Copyright (c) 2007 Deep Root Systems, LLC - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DAVINCI_H #define __DAVINCI_H diff --git a/arch/arm/mach-davinci/dm355.c b/arch/arm/mach-davinci/dm355.c index 1d7443c59b14..a12ba859beca 100644 --- a/arch/arm/mach-davinci/dm355.c +++ b/arch/arm/mach-davinci/dm355.c @@ -1,12 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DaVinci DM355 chip specific setup * * Author: Kevin Hilman, Deep Root Systems, LLC * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) Deep Root Systems, LLC. */ #include diff --git a/arch/arm/mach-davinci/dm365.c b/arch/arm/mach-davinci/dm365.c index 73ccc916f60a..7538bb87f373 100644 --- a/arch/arm/mach-davinci/dm365.c +++ b/arch/arm/mach-davinci/dm365.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI DaVinci DM365 chip specific setup * * Copyright (C) 2009 Texas Instruments - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-davinci/hardware.h b/arch/arm/mach-davinci/hardware.h index 16bb42291d39..7848b6a240b4 100644 --- a/arch/arm/mach-davinci/hardware.h +++ b/arch/arm/mach-davinci/hardware.h @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Hardware definitions common to all DaVinci family processors * * Author: Kevin Hilman, Deep Root Systems, LLC * - * 2007 (c) Deep Root Systems, LLC. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) Deep Root Systems, LLC. */ #ifndef __ASM_ARCH_HARDWARE_H #define __ASM_ARCH_HARDWARE_H diff --git a/arch/arm/mach-davinci/mux.c b/arch/arm/mach-davinci/mux.c index bab1eea7fca6..814a6b714010 100644 --- a/arch/arm/mach-davinci/mux.c +++ b/arch/arm/mach-davinci/mux.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Utility to set the DAVINCI MUX register from a table in mux.h * @@ -8,10 +9,7 @@ * * Written by Tony Lindgren * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) MontaVista Software, Inc. * * Copyright (C) 2008 Texas Instruments. */ diff --git a/arch/arm/mach-davinci/mux.h b/arch/arm/mach-davinci/mux.h index b0d1c4fb78dc..b5effe16402c 100644 --- a/arch/arm/mach-davinci/mux.h +++ b/arch/arm/mach-davinci/mux.h @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Pin-multiplex helper macros for TI DaVinci family devices * * Author: Vladimir Barinov, MontaVista Software, Inc. * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) MontaVista Software, Inc. * * Copyright (C) 2008 Texas Instruments. */ diff --git a/arch/arm/mach-davinci/pm.h b/arch/arm/mach-davinci/pm.h index 5a5f0ecc0704..6f50d6eb8da8 100644 --- a/arch/arm/mach-davinci/pm.h +++ b/arch/arm/mach-davinci/pm.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI DaVinci platform support for power management. * * Copyright (C) 2009 Texas Instruments, Inc. https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _MACH_DAVINCI_PM_H #define _MACH_DAVINCI_PM_H diff --git a/arch/arm/mach-davinci/pm_domain.c b/arch/arm/mach-davinci/pm_domain.c index e251fd593bfd..6b21d5bd999c 100644 --- a/arch/arm/mach-davinci/pm_domain.c +++ b/arch/arm/mach-davinci/pm_domain.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Runtime PM support code for DaVinci * * Author: Kevin Hilman * * Copyright (C) 2012 Texas Instruments, Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-davinci/serial.h b/arch/arm/mach-davinci/serial.h index 14473cb19852..c4a4ba553d45 100644 --- a/arch/arm/mach-davinci/serial.h +++ b/arch/arm/mach-davinci/serial.h @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * DaVinci serial device definitions * * Author: Kevin Hilman, MontaVista Software, Inc. * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2007 (c) MontaVista Software, Inc. */ #ifndef __ASM_ARCH_SERIAL_H #define __ASM_ARCH_SERIAL_H diff --git a/arch/arm/mach-dove/bridge-regs.h b/arch/arm/mach-dove/bridge-regs.h index ace0b0bfbf11..6fbc152d0950 100644 --- a/arch/arm/mach-dove/bridge-regs.h +++ b/arch/arm/mach-dove/bridge-regs.h @@ -1,10 +1,5 @@ -/* - * Mbus-L to Mbus Bridge Registers - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Mbus-L to Mbus Bridge Registers */ #ifndef __ASM_ARCH_BRIDGE_REGS_H #define __ASM_ARCH_BRIDGE_REGS_H diff --git a/arch/arm/mach-dove/cm-a510.c b/arch/arm/mach-dove/cm-a510.c index 9f25c993d863..beb532537c22 100644 --- a/arch/arm/mach-dove/cm-a510.c +++ b/arch/arm/mach-dove/cm-a510.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-dove/cm-a510.c * @@ -5,10 +6,6 @@ * Konstantin Sinyuk * * Based on Marvell DB-MV88AP510-BP Development Board Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-dove/common.c b/arch/arm/mach-dove/common.c index dbe970e37895..cd4ae7e4768d 100644 --- a/arch/arm/mach-dove/common.c +++ b/arch/arm/mach-dove/common.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-dove/common.c * * Core functions for Marvell Dove 88AP510 System On Chip - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-dove/common.h b/arch/arm/mach-dove/common.h index 1d725224d146..57ebc413d68c 100644 --- a/arch/arm/mach-dove/common.h +++ b/arch/arm/mach-dove/common.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/mach-dove/common.h * * Core functions for Marvell Dove 88AP510 System On Chip - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ARCH_DOVE_COMMON_H diff --git a/arch/arm/mach-dove/dove-db-setup.c b/arch/arm/mach-dove/dove-db-setup.c index 418ab21b9d9b..d5bf54040577 100644 --- a/arch/arm/mach-dove/dove-db-setup.c +++ b/arch/arm/mach-dove/dove-db-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-dove/dove-db-setup.c * * Marvell DB-MV88AP510-BP Development Board Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-dove/dove.h b/arch/arm/mach-dove/dove.h index 320ed1696abd..e5054e3b0b78 100644 --- a/arch/arm/mach-dove/dove.h +++ b/arch/arm/mach-dove/dove.h @@ -1,10 +1,5 @@ -/* - * Generic definitions for Marvell Dove 88AP510 SoC - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Generic definitions for Marvell Dove 88AP510 SoC */ #ifndef __ASM_ARCH_DOVE_H #define __ASM_ARCH_DOVE_H diff --git a/arch/arm/mach-dove/irq.c b/arch/arm/mach-dove/irq.c index d36f6b8269c2..027a8f87bc2e 100644 --- a/arch/arm/mach-dove/irq.c +++ b/arch/arm/mach-dove/irq.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-dove/irq.c * * Dove IRQ handling. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-dove/irqs.h b/arch/arm/mach-dove/irqs.h index a0742179faff..5467098c7042 100644 --- a/arch/arm/mach-dove/irqs.h +++ b/arch/arm/mach-dove/irqs.h @@ -1,10 +1,5 @@ -/* - * IRQ definitions for Marvell Dove 88AP510 SoC - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* IRQ definitions for Marvell Dove 88AP510 SoC */ #ifndef __ASM_ARCH_IRQS_H #define __ASM_ARCH_IRQS_H diff --git a/arch/arm/mach-dove/mpp.c b/arch/arm/mach-dove/mpp.c index 6acd8488bb05..93cb137da5f8 100644 --- a/arch/arm/mach-dove/mpp.c +++ b/arch/arm/mach-dove/mpp.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-dove/mpp.c * * MPP functions for Marvell Dove SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-dove/pcie.c b/arch/arm/mach-dove/pcie.c index f90f42fc495e..754ca381f600 100644 --- a/arch/arm/mach-dove/pcie.c +++ b/arch/arm/mach-dove/pcie.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-dove/pcie.c * * PCIe functions for Marvell Dove 88AP510 SoC - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-dove/pm.h b/arch/arm/mach-dove/pm.h index 01267746d707..a4c3aba1e2d0 100644 --- a/arch/arm/mach-dove/pm.h +++ b/arch/arm/mach-dove/pm.h @@ -1,8 +1,4 @@ -/* - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef __ASM_ARCH_PM_H #define __ASM_ARCH_PM_H diff --git a/arch/arm/mach-lpc18xx/board-dt.c b/arch/arm/mach-lpc18xx/board-dt.c index fdcee78d1bc4..4729eb83401a 100644 --- a/arch/arm/mach-lpc18xx/board-dt.c +++ b/arch/arm/mach-lpc18xx/board-dt.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree board file for NXP LPC18xx/43xx * * Copyright (C) 2015 Joachim Eastwood - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-lpc32xx/pm.c b/arch/arm/mach-lpc32xx/pm.c index b27fa1b9f56c..2572bd89a5e8 100644 --- a/arch/arm/mach-lpc32xx/pm.c +++ b/arch/arm/mach-lpc32xx/pm.c @@ -1,13 +1,11 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-lpc32xx/pm.c * * Original authors: Vitaly Wool, Dmitry Chigirev * Modified by Kevin Wells * - * 2005 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2005 (c) MontaVista Software, Inc. */ /* diff --git a/arch/arm/mach-lpc32xx/suspend.S b/arch/arm/mach-lpc32xx/suspend.S index 3f0a8282ef6f..a95c5e0e4038 100644 --- a/arch/arm/mach-lpc32xx/suspend.S +++ b/arch/arm/mach-lpc32xx/suspend.S @@ -1,13 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/mach-lpc32xx/suspend.S * * Original authors: Dmitry Chigirev, Vitaly Wool * Modified by Kevin Wells * - * 2005 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2005 (c) MontaVista Software, Inc. */ #include #include diff --git a/arch/arm/mach-mv78xx0/bridge-regs.h b/arch/arm/mach-mv78xx0/bridge-regs.h index 2f54e1753d45..d57ac967c4b3 100644 --- a/arch/arm/mach-mv78xx0/bridge-regs.h +++ b/arch/arm/mach-mv78xx0/bridge-regs.h @@ -1,8 +1,4 @@ -/* - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ #ifndef __ASM_ARCH_BRIDGE_REGS_H #define __ASM_ARCH_BRIDGE_REGS_H diff --git a/arch/arm/mach-mv78xx0/buffalo-wxl-setup.c b/arch/arm/mach-mv78xx0/buffalo-wxl-setup.c index e112f2e7cc9a..9aa765d4cdc8 100644 --- a/arch/arm/mach-mv78xx0/buffalo-wxl-setup.c +++ b/arch/arm/mach-mv78xx0/buffalo-wxl-setup.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78xx0/buffalo-wxl-setup.c * * Buffalo WXL (Terastation Duo) Setup routines * * sebastien requiem - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mv78xx0/common.c b/arch/arm/mach-mv78xx0/common.c index dd762d1b083f..461a68945c26 100644 --- a/arch/arm/mach-mv78xx0/common.c +++ b/arch/arm/mach-mv78xx0/common.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78xx0/common.c * * Core functions for Marvell MV78xx0 SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mv78xx0/common.h b/arch/arm/mach-mv78xx0/common.h index 6889af26077d..d8c6c2400e27 100644 --- a/arch/arm/mach-mv78xx0/common.h +++ b/arch/arm/mach-mv78xx0/common.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/mach-mv78xx0/common.h * * Core functions for Marvell MV78xx0 SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ARCH_MV78XX0_COMMON_H diff --git a/arch/arm/mach-mv78xx0/db78x00-bp-setup.c b/arch/arm/mach-mv78xx0/db78x00-bp-setup.c index cf16e08d4cf5..da633a33a0c1 100644 --- a/arch/arm/mach-mv78xx0/db78x00-bp-setup.c +++ b/arch/arm/mach-mv78xx0/db78x00-bp-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78xx0/db78x00-bp-setup.c * * Marvell DB-78x00-BP Development Board Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mv78xx0/irq.c b/arch/arm/mach-mv78xx0/irq.c index 0b5f055ca1c3..a34b6855fb19 100644 --- a/arch/arm/mach-mv78xx0/irq.c +++ b/arch/arm/mach-mv78xx0/irq.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78xx0/irq.c * * MV78xx0 IRQ handling. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-mv78xx0/irqs.h b/arch/arm/mach-mv78xx0/irqs.h index 67e0fe730a13..12b357d383d8 100644 --- a/arch/arm/mach-mv78xx0/irqs.h +++ b/arch/arm/mach-mv78xx0/irqs.h @@ -1,10 +1,5 @@ -/* - * IRQ definitions for Marvell MV78xx0 SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* IRQ definitions for Marvell MV78xx0 SoCs */ #ifndef __ASM_ARCH_IRQS_H #define __ASM_ARCH_IRQS_H diff --git a/arch/arm/mach-mv78xx0/mpp.c b/arch/arm/mach-mv78xx0/mpp.c index 72843c02e95a..aff0e612cbba 100644 --- a/arch/arm/mach-mv78xx0/mpp.c +++ b/arch/arm/mach-mv78xx0/mpp.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78x00/mpp.c * * MPP functions for Marvell MV78x00 SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-mv78xx0/mpp.h b/arch/arm/mach-mv78xx0/mpp.h index 3752302ae2ee..47db52f45546 100644 --- a/arch/arm/mach-mv78xx0/mpp.h +++ b/arch/arm/mach-mv78xx0/mpp.h @@ -1,12 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * linux/arch/arm/mach-mv78xx0/mpp.h -- Multi Purpose Pins * - * * sebastien requiem - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MV78X00_MPP_H diff --git a/arch/arm/mach-mv78xx0/mv78xx0.h b/arch/arm/mach-mv78xx0/mv78xx0.h index c1a9a1d1b295..3f19bef7d7ac 100644 --- a/arch/arm/mach-mv78xx0/mv78xx0.h +++ b/arch/arm/mach-mv78xx0/mv78xx0.h @@ -1,10 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Generic definitions for Marvell MV78xx0 SoC flavors: * MV781x0 and MV782x0. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ASM_ARCH_MV78XX0_H diff --git a/arch/arm/mach-mv78xx0/pcie.c b/arch/arm/mach-mv78xx0/pcie.c index 4f1847babef2..6190f538a124 100644 --- a/arch/arm/mach-mv78xx0/pcie.c +++ b/arch/arm/mach-mv78xx0/pcie.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78xx0/pcie.c * * PCIe functions for Marvell MV78xx0 SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mv78xx0/rd78x00-masa-setup.c b/arch/arm/mach-mv78xx0/rd78x00-masa-setup.c index 308ab71ec822..80ca8b1a81de 100644 --- a/arch/arm/mach-mv78xx0/rd78x00-masa-setup.c +++ b/arch/arm/mach-mv78xx0/rd78x00-masa-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mv78x00/rd78x00-masa-setup.c * * Marvell RD-78x00-mASA Development Board Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/armada-370-xp.h b/arch/arm/mach-mvebu/armada-370-xp.h index 09413b678409..c96ecdafe31f 100644 --- a/arch/arm/mach-mvebu/armada-370-xp.h +++ b/arch/arm/mach-mvebu/armada-370-xp.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Generic definitions for Marvell Armada_370_XP SoCs * @@ -6,10 +7,6 @@ * Lior Amsalem * Gregory CLEMENT * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MACH_ARMADA_370_XP_H diff --git a/arch/arm/mach-mvebu/board-v7.c b/arch/arm/mach-mvebu/board-v7.c index d2df5ef9382b..fd5d0c8ff695 100644 --- a/arch/arm/mach-mvebu/board-v7.c +++ b/arch/arm/mach-mvebu/board-v7.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Device Tree support for Armada 370 and XP platforms. * @@ -6,10 +7,6 @@ * Lior Amsalem * Gregory CLEMENT * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/coherency.c b/arch/arm/mach-mvebu/coherency.c index 49e3c8d20c2f..883dab1b54f3 100644 --- a/arch/arm/mach-mvebu/coherency.c +++ b/arch/arm/mach-mvebu/coherency.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Coherency fabric (Aurora) support for Armada 370, 375, 38x and XP * platforms. @@ -8,10 +9,6 @@ * Gregory Clement * Thomas Petazzoni * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The Armada 370, 375, 38x and XP SOCs have a coherency fabric which is * responsible for ensuring hardware coherency between all CPUs and between * CPUs and I/O masters. This file initializes the coherency fabric and diff --git a/arch/arm/mach-mvebu/coherency.h b/arch/arm/mach-mvebu/coherency.h index 6067f14263f7..cae866ab4867 100644 --- a/arch/arm/mach-mvebu/coherency.h +++ b/arch/arm/mach-mvebu/coherency.h @@ -1,14 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/mach-mvebu/include/mach/coherency.h * - * * Coherency fabric (Aurora) support for Armada 370 and XP platforms. * * Copyright (C) 2012 Marvell - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MACH_370_XP_COHERENCY_H diff --git a/arch/arm/mach-mvebu/coherency_ll.S b/arch/arm/mach-mvebu/coherency_ll.S index a3a64bf97250..eb81656e32d4 100644 --- a/arch/arm/mach-mvebu/coherency_ll.S +++ b/arch/arm/mach-mvebu/coherency_ll.S @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Coherency fabric: low level functions * @@ -5,10 +6,6 @@ * * Gregory CLEMENT * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * This file implements the assembly function to add a CPU to the * coherency fabric. This function is called by each of the secondary * CPUs during their early boot in an SMP kernel, this why this diff --git a/arch/arm/mach-mvebu/common.h b/arch/arm/mach-mvebu/common.h index 6b775492cfad..fbfa3c4f30df 100644 --- a/arch/arm/mach-mvebu/common.h +++ b/arch/arm/mach-mvebu/common.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Core functions for Marvell System On Chip * @@ -6,10 +7,6 @@ * Lior Amsalem * Gregory CLEMENT * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ARCH_MVEBU_COMMON_H diff --git a/arch/arm/mach-mvebu/cpu-reset.c b/arch/arm/mach-mvebu/cpu-reset.c index f33a31c6aff8..66b6c0c6ce1d 100644 --- a/arch/arm/mach-mvebu/cpu-reset.c +++ b/arch/arm/mach-mvebu/cpu-reset.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Marvell * * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "mvebu-cpureset: " fmt diff --git a/arch/arm/mach-mvebu/dove.c b/arch/arm/mach-mvebu/dove.c index d076c5771adc..c938ba725d3e 100644 --- a/arch/arm/mach-mvebu/dove.c +++ b/arch/arm/mach-mvebu/dove.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-mvebu/dove.c * * Marvell Dove 88AP510 System On Chip FDT Board - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/headsmp-a9.S b/arch/arm/mach-mvebu/headsmp-a9.S index b093a196e801..df723cf85cae 100644 --- a/arch/arm/mach-mvebu/headsmp-a9.S +++ b/arch/arm/mach-mvebu/headsmp-a9.S @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * SMP support: Entry point for secondary CPUs of Marvell EBU * Cortex-A9 based SOCs (Armada 375 and Armada 38x). @@ -6,10 +7,6 @@ * * Gregory CLEMENT * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/headsmp.S b/arch/arm/mach-mvebu/headsmp.S index 2c4032e368ba..f05c59dad32a 100644 --- a/arch/arm/mach-mvebu/headsmp.S +++ b/arch/arm/mach-mvebu/headsmp.S @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * SMP support: Entry point for secondary CPUs * @@ -7,10 +8,6 @@ * Gregory CLEMENT * Thomas Petazzoni * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * This file implements the assembly entry point for secondary CPUs in * an SMP kernel. The only thing we need to do is to add the CPU to * the coherency fabric by writing to 2 registers. Currently the base diff --git a/arch/arm/mach-mvebu/kirkwood.c b/arch/arm/mach-mvebu/kirkwood.c index 06b1706595f4..8ff34753e760 100644 --- a/arch/arm/mach-mvebu/kirkwood.c +++ b/arch/arm/mach-mvebu/kirkwood.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2012 (C), Jason Cooper * * arch/arm/mach-mvebu/kirkwood.c * * Flattened Device Tree board initialization - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/kirkwood.h b/arch/arm/mach-mvebu/kirkwood.h index 89f3d1f51643..15135994ce2f 100644 --- a/arch/arm/mach-mvebu/kirkwood.h +++ b/arch/arm/mach-mvebu/kirkwood.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/mach-mvebu/kirkwood.h * * Generic definitions for Marvell Kirkwood SoC flavors: * 88F6180, 88F6192 and 88F6281. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define KIRKWOOD_REGS_PHYS_BASE 0xf1000000 diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.c b/arch/arm/mach-mvebu/mvebu-soc-id.c index a99434bcee84..f436c7b8c7ae 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.c +++ b/arch/arm/mach-mvebu/mvebu-soc-id.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ID and revision information for mvebu SoCs * @@ -5,10 +6,6 @@ * * Gregory CLEMENT * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * All the mvebu SoCs have information related to their variant and * revision that can be read from the PCI control register. This is * done before the PCI initialization to avoid any conflict. Once the diff --git a/arch/arm/mach-mvebu/mvebu-soc-id.h b/arch/arm/mach-mvebu/mvebu-soc-id.h index e124a0b82a3e..225649b2288a 100644 --- a/arch/arm/mach-mvebu/mvebu-soc-id.h +++ b/arch/arm/mach-mvebu/mvebu-soc-id.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Marvell EBU SoC ID and revision definitions. * * Copyright (C) 2014 Marvell Semiconductor - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __LINUX_MVEBU_SOC_ID_H diff --git a/arch/arm/mach-mvebu/platsmp-a9.c b/arch/arm/mach-mvebu/platsmp-a9.c index d715dec1c197..785ee2af5baa 100644 --- a/arch/arm/mach-mvebu/platsmp-a9.c +++ b/arch/arm/mach-mvebu/platsmp-a9.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Symmetric Multi Processing (SMP) support for Marvell EBU Cortex-A9 * based SOCs (Armada 375/38x). @@ -6,10 +7,6 @@ * * Gregory CLEMENT * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/platsmp.c b/arch/arm/mach-mvebu/platsmp.c index c130497dc6cc..18384ea6862c 100644 --- a/arch/arm/mach-mvebu/platsmp.c +++ b/arch/arm/mach-mvebu/platsmp.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Symmetric Multi Processing (SMP) support for Armada XP * @@ -8,10 +9,6 @@ * Gregory CLEMENT * Thomas Petazzoni * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The Armada XP SoC has 4 ARMv7 PJ4B CPUs running in full HW coherency * This file implements the routines for preparing the SMP infrastructure * and waking up the secondary CPUs diff --git a/arch/arm/mach-mvebu/pm-board.c b/arch/arm/mach-mvebu/pm-board.c index 070552511699..7fa1806acd65 100644 --- a/arch/arm/mach-mvebu/pm-board.c +++ b/arch/arm/mach-mvebu/pm-board.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Board-level suspend/resume support. * * Copyright (C) 2014-2015 Marvell * * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/pm.c b/arch/arm/mach-mvebu/pm.c index c487be61d6d8..b149d9b77505 100644 --- a/arch/arm/mach-mvebu/pm.c +++ b/arch/arm/mach-mvebu/pm.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Suspend/resume support. Currently supporting Armada XP only. * * Copyright (C) 2014 Marvell * * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/pmsu.c b/arch/arm/mach-mvebu/pmsu.c index 73d5d72dfc3e..af27a7156675 100644 --- a/arch/arm/mach-mvebu/pmsu.c +++ b/arch/arm/mach-mvebu/pmsu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Power Management Service Unit(PMSU) support for Armada 370/XP platforms. * @@ -7,10 +8,6 @@ * Gregory Clement * Thomas Petazzoni * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The Armada 370 and Armada XP SOCs have a power management service * unit which is responsible for powering down and waking up CPUs and * other SOC units diff --git a/arch/arm/mach-mvebu/pmsu.h b/arch/arm/mach-mvebu/pmsu.h index ea79269c2702..1e847388e8dd 100644 --- a/arch/arm/mach-mvebu/pmsu.h +++ b/arch/arm/mach-mvebu/pmsu.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Power Management Service Unit (PMSU) support for Armada 370/XP platforms. * * Copyright (C) 2012 Marvell - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MACH_MVEBU_PMSU_H diff --git a/arch/arm/mach-mvebu/pmsu_ll.S b/arch/arm/mach-mvebu/pmsu_ll.S index 7aae9a25cfeb..f7d21385fd88 100644 --- a/arch/arm/mach-mvebu/pmsu_ll.S +++ b/arch/arm/mach-mvebu/pmsu_ll.S @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2014 Marvell * * Thomas Petazzoni * Gregory Clement - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-mvebu/system-controller.c b/arch/arm/mach-mvebu/system-controller.c index 04d9ebe6a90a..48224b6ed6dc 100644 --- a/arch/arm/mach-mvebu/system-controller.c +++ b/arch/arm/mach-mvebu/system-controller.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * System controller support for Armada 370, 375 and XP platforms. * @@ -7,10 +8,6 @@ * Gregory CLEMENT * Thomas Petazzoni * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The Armada 370, 375 and Armada XP SoCs have a range of * miscellaneous registers, that do not belong to a particular device, * but rather provide system-level features. This basic diff --git a/arch/arm/mach-omap1/board-sx1.h b/arch/arm/mach-omap1/board-sx1.h index 355adbdaae33..fafe54a2e444 100644 --- a/arch/arm/mach-omap1/board-sx1.h +++ b/arch/arm/mach-omap1/board-sx1.h @@ -1,15 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Siemens SX1 board definitions * * Copyright: Vovan888 at gmail com - * - * This package is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * THIS PACKAGE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR - * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ #ifndef __ASM_ARCH_SX1_I2C_CHIPS_H diff --git a/arch/arm/mach-omap1/gpio15xx.c b/arch/arm/mach-omap1/gpio15xx.c index 3faf2b7c2d09..c675f11de99d 100644 --- a/arch/arm/mach-omap1/gpio15xx.c +++ b/arch/arm/mach-omap1/gpio15xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP15xx specific gpio init * @@ -5,15 +6,6 @@ * * Author: * Charulatha V - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap1/gpio16xx.c b/arch/arm/mach-omap1/gpio16xx.c index 2a25751007d4..cf052714b3f8 100644 --- a/arch/arm/mach-omap1/gpio16xx.c +++ b/arch/arm/mach-omap1/gpio16xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP16xx specific gpio init * @@ -5,15 +6,6 @@ * * Author: * Charulatha V - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap1/gpio7xx.c b/arch/arm/mach-omap1/gpio7xx.c index 46b7d5d4d255..c372b357eab4 100644 --- a/arch/arm/mach-omap1/gpio7xx.c +++ b/arch/arm/mach-omap1/gpio7xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP7xx specific gpio init * @@ -5,15 +6,6 @@ * * Author: * Charulatha V - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap1/mtd-xip.h b/arch/arm/mach-omap1/mtd-xip.h index b675d501b13d..5ae312ff08a1 100644 --- a/arch/arm/mach-omap1/mtd-xip.h +++ b/arch/arm/mach-omap1/mtd-xip.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * MTD primitives for XIP support. Architecture specific functions. * @@ -5,10 +6,7 @@ * * Author: Vladimir Barinov * - * (c) 2005 MontaVista Software, Inc. This file is licensed under the - * terms of the GNU General Public License version 2. This program is - * licensed "as is" without any warranty of any kind, whether express or - * implied. + * (c) 2005 MontaVista Software, Inc. */ #ifndef __ARCH_OMAP_MTD_XIP_H__ diff --git a/arch/arm/mach-omap1/pm_bus.c b/arch/arm/mach-omap1/pm_bus.c index 667c1637ff91..c04619ac0631 100644 --- a/arch/arm/mach-omap1/pm_bus.c +++ b/arch/arm/mach-omap1/pm_bus.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Runtime PM support code for OMAP1 * * Author: Kevin Hilman, Deep Root Systems, LLC * * Copyright (C) 2010 Texas Instruments, Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include @@ -43,4 +40,3 @@ static int __init omap1_pm_runtime_init(void) return 0; } core_initcall(omap1_pm_runtime_init); - diff --git a/arch/arm/mach-omap1/timer.c b/arch/arm/mach-omap1/timer.c index 9ed64345f06e..f5cd4bbf7566 100644 --- a/arch/arm/mach-omap1/timer.c +++ b/arch/arm/mach-omap1/timer.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP1 Dual-Mode Timers - platform device registration * @@ -9,15 +10,6 @@ * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ * Tarun Kanti DebBarma * Thara Gopinath - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/am33xx.h b/arch/arm/mach-omap2/am33xx.h index bf2b5f87e404..32bcfcf34817 100644 --- a/arch/arm/mach-omap2/am33xx.h +++ b/arch/arm/mach-omap2/am33xx.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This file contains the address info for various AM33XX modules. * * Copyright (C) 2011 Texas Instruments, Inc. - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ASM_ARCH_AM33XX_H diff --git a/arch/arm/mach-omap2/clockdomains33xx_data.c b/arch/arm/mach-omap2/clockdomains33xx_data.c index b4d5144df445..87f4e927eb18 100644 --- a/arch/arm/mach-omap2/clockdomains33xx_data.c +++ b/arch/arm/mach-omap2/clockdomains33xx_data.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * AM33XX Clock Domain data. * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ * Vaibhav Hiremath - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/clockdomains81xx_data.c b/arch/arm/mach-omap2/clockdomains81xx_data.c index 127dc7ace71f..549cf61487a5 100644 --- a/arch/arm/mach-omap2/clockdomains81xx_data.c +++ b/arch/arm/mach-omap2/clockdomains81xx_data.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI81XX Clock Domain data. * * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ * Copyright (C) 2013 SKTB SKiT, http://www.skitlab.ru/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ARCH_ARM_MACH_OMAP2_CLOCKDOMAINS_81XX_H diff --git a/arch/arm/mach-omap2/cm-regbits-33xx.h b/arch/arm/mach-omap2/cm-regbits-33xx.h index e7ae2bb515e3..1b97219aaba4 100644 --- a/arch/arm/mach-omap2/cm-regbits-33xx.h +++ b/arch/arm/mach-omap2/cm-regbits-33xx.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * AM33XX Power Management register bits * @@ -5,15 +6,6 @@ * Vaibhav Hiremath * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ diff --git a/arch/arm/mach-omap2/cm33xx.c b/arch/arm/mach-omap2/cm33xx.c index ac4882ebdca3..d61fa06117b4 100644 --- a/arch/arm/mach-omap2/cm33xx.c +++ b/arch/arm/mach-omap2/cm33xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * AM33XX CM functions * @@ -5,15 +6,6 @@ * Vaibhav Hiremath * * Reference taken from from OMAP4 cminst44xx.c - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/cm33xx.h b/arch/arm/mach-omap2/cm33xx.h index af63d1892c33..456267a7af71 100644 --- a/arch/arm/mach-omap2/cm33xx.h +++ b/arch/arm/mach-omap2/cm33xx.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * AM33XX CM offset macros * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ * Vaibhav Hiremath - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ARCH_ARM_MACH_OMAP2_CM_33XX_H diff --git a/arch/arm/mach-omap2/cm81xx.h b/arch/arm/mach-omap2/cm81xx.h index bd91223e838e..ffcde1812c6c 100644 --- a/arch/arm/mach-omap2/cm81xx.h +++ b/arch/arm/mach-omap2/cm81xx.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Clock domain register offsets for TI81XX. * * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ * Copyright (C) 2013 SKTB SKiT, http://www.skitlab.ru/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ARCH_ARM_MACH_OMAP2_CM_TI81XX_H diff --git a/arch/arm/mach-omap2/display.c b/arch/arm/mach-omap2/display.c index 8d829f3dafe7..dbec3bb9fbf4 100644 --- a/arch/arm/mach-omap2/display.c +++ b/arch/arm/mach-omap2/display.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP2plus display device setup / initialization. * * Copyright (C) 2010 Texas Instruments Incorporated - https://www.ti.com/ * Senthilvadivu Guruswamy * Sumit Semwal - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c index 450ab990c66a..9b5c728fb7da 100644 --- a/arch/arm/mach-omap2/omap_hwmod_81xx_data.c +++ b/arch/arm/mach-omap2/omap_hwmod_81xx_data.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * DM81xx hwmod data. * * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ * Copyright (C) 2013 SKTB SKiT, http://www.skitlab.ru/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include diff --git a/arch/arm/mach-omap2/omap_opp_data.h b/arch/arm/mach-omap2/omap_opp_data.h index 533dd643069a..88375ab38e31 100644 --- a/arch/arm/mach-omap2/omap_opp_data.h +++ b/arch/arm/mach-omap2/omap_opp_data.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * OMAP SoC specific OPP Data helpers * @@ -6,15 +7,6 @@ * Kevin Hilman * Copyright (C) 2010 Nokia Corporation. * Eduardo Valentin - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H #define __ARCH_ARM_MACH_OMAP2_OMAP_OPP_DATA_H diff --git a/arch/arm/mach-omap2/opp3xxx_data.c b/arch/arm/mach-omap2/opp3xxx_data.c index b610c5fb423b..90257e2fb3d6 100644 --- a/arch/arm/mach-omap2/opp3xxx_data.c +++ b/arch/arm/mach-omap2/opp3xxx_data.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP3 OPP table definitions. * @@ -7,15 +8,6 @@ * Copyright (C) 2010-2011 Nokia Corporation. * Eduardo Valentin * Paul Walmsley - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/opp4xxx_data.c b/arch/arm/mach-omap2/opp4xxx_data.c index d937c5ef41c6..a9851886017d 100644 --- a/arch/arm/mach-omap2/opp4xxx_data.c +++ b/arch/arm/mach-omap2/opp4xxx_data.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP4 OPP table definitions. * @@ -8,15 +9,6 @@ * Copyright (C) 2010-2011 Nokia Corporation. * Eduardo Valentin * Paul Walmsley - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/powerdomains33xx_data.c b/arch/arm/mach-omap2/powerdomains33xx_data.c index 626055e59aed..1d58fd1a2dce 100644 --- a/arch/arm/mach-omap2/powerdomains33xx_data.c +++ b/arch/arm/mach-omap2/powerdomains33xx_data.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * AM33XX Power domain data * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/prcm43xx.h b/arch/arm/mach-omap2/prcm43xx.h index b65cccab6ad9..38ed69b150cb 100644 --- a/arch/arm/mach-omap2/prcm43xx.h +++ b/arch/arm/mach-omap2/prcm43xx.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * AM43x PRCM defines * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef __ARCH_ARM_MACH_OMAP2_PRCM_43XX_H diff --git a/arch/arm/mach-omap2/prm-regbits-33xx.h b/arch/arm/mach-omap2/prm-regbits-33xx.h index 7dfdff09ddeb..3748c5266ae1 100644 --- a/arch/arm/mach-omap2/prm-regbits-33xx.h +++ b/arch/arm/mach-omap2/prm-regbits-33xx.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * AM33XX PRM_XXX register bits * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ARCH_ARM_MACH_OMAP2_PRM_REGBITS_33XX_H diff --git a/arch/arm/mach-omap2/prm33xx.c b/arch/arm/mach-omap2/prm33xx.c index 9144cc0479af..4b65a0f9cf7d 100644 --- a/arch/arm/mach-omap2/prm33xx.c +++ b/arch/arm/mach-omap2/prm33xx.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * AM33XX PRM functions * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mach-omap2/prm33xx.h b/arch/arm/mach-omap2/prm33xx.h index a469a36c00d8..3081f3deb650 100644 --- a/arch/arm/mach-omap2/prm33xx.h +++ b/arch/arm/mach-omap2/prm33xx.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * AM33XX PRM instance offset macros * * Copyright (C) 2011-2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ARCH_ARM_MACH_OMAP2_PRM33XX_H diff --git a/arch/arm/mach-omap2/ti81xx.h b/arch/arm/mach-omap2/ti81xx.h index 192b0e7d3eb4..fe9f7f388cbd 100644 --- a/arch/arm/mach-omap2/ti81xx.h +++ b/arch/arm/mach-omap2/ti81xx.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This file contains the address data for various TI81XX modules. * * Copyright (C) 2010 Texas Instruments, Inc. - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __ASM_ARCH_TI81XX_H diff --git a/arch/arm/mach-omap2/vc.c b/arch/arm/mach-omap2/vc.c index 86f1ac4c2412..ea02d40405c4 100644 --- a/arch/arm/mach-omap2/vc.c +++ b/arch/arm/mach-omap2/vc.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP Voltage Controller (VC) interface * * Copyright (C) 2011 Texas Instruments, Inc. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include @@ -895,4 +892,3 @@ void __init omap_vc_init_channel(struct voltagedomain *voltdm) else if (cpu_is_omap44xx()) omap4_vc_init_channel(voltdm); } - diff --git a/arch/arm/mach-orion5x/board-d2net.c b/arch/arm/mach-orion5x/board-d2net.c index a89376a5cd92..0297e302d7bc 100644 --- a/arch/arm/mach-orion5x/board-d2net.c +++ b/arch/arm/mach-orion5x/board-d2net.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/board-d2net.c * * LaCie d2Network and Big Disk Network NAS setup * * Copyright (C) 2009 Simon Guinot - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-orion5x/board-dt.c b/arch/arm/mach-orion5x/board-dt.c index 3d36f1d95196..e3736ffc8347 100644 --- a/arch/arm/mach-orion5x/board-dt.c +++ b/arch/arm/mach-orion5x/board-dt.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2012 (C), Thomas Petazzoni * * arch/arm/mach-orion5x/board-dt.c * * Flattened Device Tree board initialization - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-orion5x/board-rd88f5182.c b/arch/arm/mach-orion5x/board-rd88f5182.c index b7b0f52f4c0a..596601367989 100644 --- a/arch/arm/mach-orion5x/board-rd88f5182.c +++ b/arch/arm/mach-orion5x/board-rd88f5182.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/rd88f5182-setup.c * * Marvell Orion-NAS Reference Design Setup * * Maintainer: Ronen Shitrit - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/bridge-regs.h b/arch/arm/mach-orion5x/bridge-regs.h index 305598eaaee1..fe85bc5b131f 100644 --- a/arch/arm/mach-orion5x/bridge-regs.h +++ b/arch/arm/mach-orion5x/bridge-regs.h @@ -1,10 +1,5 @@ -/* - * Orion CPU Bridge Registers - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Orion CPU Bridge Registers */ #ifndef __ASM_ARCH_BRIDGE_REGS_H #define __ASM_ARCH_BRIDGE_REGS_H diff --git a/arch/arm/mach-orion5x/common.c b/arch/arm/mach-orion5x/common.c index 7bcb41137bbf..2e711b7252c6 100644 --- a/arch/arm/mach-orion5x/common.c +++ b/arch/arm/mach-orion5x/common.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/common.c * * Core functions for Marvell Orion 5x SoCs * * Maintainer: Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-orion5x/db88f5281-setup.c b/arch/arm/mach-orion5x/db88f5281-setup.c index 39eae10ac8de..fe1a4cef1ba2 100644 --- a/arch/arm/mach-orion5x/db88f5281-setup.c +++ b/arch/arm/mach-orion5x/db88f5281-setup.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/db88f5281-setup.c * * Marvell Orion-2 Development Board Setup * * Maintainer: Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/irq.c b/arch/arm/mach-orion5x/irq.c index 1ae775d02d90..e17727e53cb4 100644 --- a/arch/arm/mach-orion5x/irq.c +++ b/arch/arm/mach-orion5x/irq.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/irq.c * * Core IRQ functions for Marvell Orion System On Chip * * Maintainer: Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/irqs.h b/arch/arm/mach-orion5x/irqs.h index 506c8e0b30c4..a70c47cfa6bc 100644 --- a/arch/arm/mach-orion5x/irqs.h +++ b/arch/arm/mach-orion5x/irqs.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * IRQ definitions for Orion SoC * * Maintainer: Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ASM_ARCH_IRQS_H diff --git a/arch/arm/mach-orion5x/kurobox_pro-setup.c b/arch/arm/mach-orion5x/kurobox_pro-setup.c index 83d43cff4bd7..acba06618080 100644 --- a/arch/arm/mach-orion5x/kurobox_pro-setup.c +++ b/arch/arm/mach-orion5x/kurobox_pro-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/kurobox_pro-setup.c * * Maintainer: Ronen Shitrit - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/ls_hgl-setup.c b/arch/arm/mach-orion5x/ls_hgl-setup.c index 47ba6e0502f5..af07f617465f 100644 --- a/arch/arm/mach-orion5x/ls_hgl-setup.c +++ b/arch/arm/mach-orion5x/ls_hgl-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/ls_hgl-setup.c * * Maintainer: Zhu Qingsen - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-orion5x/mpp.c b/arch/arm/mach-orion5x/mpp.c index 19ef18594415..b9855dce6ba0 100644 --- a/arch/arm/mach-orion5x/mpp.c +++ b/arch/arm/mach-orion5x/mpp.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/mpp.c * * MPP functions for Marvell Orion 5x SoCs - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-orion5x/net2big-setup.c b/arch/arm/mach-orion5x/net2big-setup.c index bf6be4cfd238..695cc683cd83 100644 --- a/arch/arm/mach-orion5x/net2big-setup.c +++ b/arch/arm/mach-orion5x/net2big-setup.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/net2big-setup.c * * LaCie 2Big Network NAS setup * * Copyright (C) 2009 Simon Guinot - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include @@ -432,4 +429,3 @@ MACHINE_START(NET2BIG, "LaCie 2Big Network") .fixup = tag_fixup_mem32, .restart = orion5x_restart, MACHINE_END - diff --git a/arch/arm/mach-orion5x/orion5x.h b/arch/arm/mach-orion5x/orion5x.h index 2b66120fba86..26f1ccb8cb28 100644 --- a/arch/arm/mach-orion5x/orion5x.h +++ b/arch/arm/mach-orion5x/orion5x.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Generic definitions of Orion SoC flavors: * Orion-1, Orion-VoIP, Orion-NAS, Orion-2, and Orion-1-90. * * Maintainer: Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __ASM_ARCH_ORION5X_H diff --git a/arch/arm/mach-orion5x/pci.c b/arch/arm/mach-orion5x/pci.c index 9574c73f3c03..888fdc9099c5 100644 --- a/arch/arm/mach-orion5x/pci.c +++ b/arch/arm/mach-orion5x/pci.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/pci.c * * PCI and PCIe functions for Marvell Orion System On Chip * * Maintainer: Tzachi Perelstein - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c index c65ab7db36ad..432fc8357d9e 100644 --- a/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c +++ b/arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/rd88f5181l-fxo-setup.c * * Marvell Orion-VoIP FXO Reference Design Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c index 76b8138d9d79..d4b1a9c3cd36 100644 --- a/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c +++ b/arch/arm/mach-orion5x/rd88f5181l-ge-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/rd88f5181l-ge-setup.c * * Marvell Orion-VoIP GE Reference Design Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/rd88f5182-setup.c b/arch/arm/mach-orion5x/rd88f5182-setup.c index fe3e67c81fb8..6ffcfc6445e2 100644 --- a/arch/arm/mach-orion5x/rd88f5182-setup.c +++ b/arch/arm/mach-orion5x/rd88f5182-setup.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/rd88f5182-setup.c * * Marvell Orion-NAS Reference Design Setup * * Maintainer: Ronen Shitrit - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c b/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c index 5f388a1ed1e4..93f74fd6b4da 100644 --- a/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c +++ b/arch/arm/mach-orion5x/rd88f6183ap-ge-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/rd88f6183-ap-ge-setup.c * * Marvell Orion-1-90 AP GE Reference Design Setup - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-orion5x/ts78xx-setup.c b/arch/arm/mach-orion5x/ts78xx-setup.c index a39764faf2a0..af810e7ccd79 100644 --- a/arch/arm/mach-orion5x/ts78xx-setup.c +++ b/arch/arm/mach-orion5x/ts78xx-setup.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-orion5x/ts78xx-setup.c * * Maintainer: Alexander Clouter - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/arch/arm/mach-orion5x/wnr854t-setup.c b/arch/arm/mach-orion5x/wnr854t-setup.c index 83589a28a491..e5f327054dd3 100644 --- a/arch/arm/mach-orion5x/wnr854t-setup.c +++ b/arch/arm/mach-orion5x/wnr854t-setup.c @@ -1,10 +1,5 @@ -/* - * arch/arm/mach-orion5x/wnr854t-setup.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// arch/arm/mach-orion5x/wnr854t-setup.c #include #include #include diff --git a/arch/arm/mach-orion5x/wrt350n-v2-setup.c b/arch/arm/mach-orion5x/wrt350n-v2-setup.c index cea08d4a2597..e6a2da6662df 100644 --- a/arch/arm/mach-orion5x/wrt350n-v2-setup.c +++ b/arch/arm/mach-orion5x/wrt350n-v2-setup.c @@ -1,10 +1,5 @@ -/* - * arch/arm/mach-orion5x/wrt350n-v2-setup.c - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ +// SPDX-License-Identifier: GPL-2.0-only +// arch/arm/mach-orion5x/wrt350n-v2-setup.c #include #include #include diff --git a/arch/arm/mach-pxa/eseries.c b/arch/arm/mach-pxa/eseries.c index 08f8737aa8fd..2c1e7bc5bac5 100644 --- a/arch/arm/mach-pxa/eseries.c +++ b/arch/arm/mach-pxa/eseries.c @@ -1,13 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Hardware definitions for the Toshiba eseries PDAs * * Copyright (c) 2003 Ian Molton - * - * This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * */ #include diff --git a/arch/arm/mach-pxa/standby.S b/arch/arm/mach-pxa/standby.S index 938310b708a0..ad32b9c0ebce 100644 --- a/arch/arm/mach-pxa/standby.S +++ b/arch/arm/mach-pxa/standby.S @@ -1,12 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * PXA27x standby mode * * Author: David Burrage * - * 2005 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2005 (c) MontaVista Software, Inc. */ #include diff --git a/arch/arm/mach-spear/generic.h b/arch/arm/mach-spear/generic.h index 8ec2b92dca19..43b7996ab754 100644 --- a/arch/arm/mach-spear/generic.h +++ b/arch/arm/mach-spear/generic.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * spear machine family generic header file * * Copyright (C) 2009-2012 ST Microelectronics * Rajeev Kumar * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MACH_GENERIC_H diff --git a/arch/arm/mach-spear/misc_regs.h b/arch/arm/mach-spear/misc_regs.h index 53cd74301f58..72aa801a3a89 100644 --- a/arch/arm/mach-spear/misc_regs.h +++ b/arch/arm/mach-spear/misc_regs.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Miscellaneous registers definitions for SPEAr3xx machine family * * Copyright (C) 2009 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MACH_MISC_REGS_H diff --git a/arch/arm/mach-spear/pl080.c b/arch/arm/mach-spear/pl080.c index 38b479f413dc..d6b8627d2544 100644 --- a/arch/arm/mach-spear/pl080.c +++ b/arch/arm/mach-spear/pl080.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/plat-spear/pl080.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-spear/pl080.h b/arch/arm/mach-spear/pl080.h index 608dec6725ae..3732d940dbfb 100644 --- a/arch/arm/mach-spear/pl080.h +++ b/arch/arm/mach-spear/pl080.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * arch/arm/plat-spear/include/plat/pl080.h * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __PLAT_PL080_H diff --git a/arch/arm/mach-spear/restart.c b/arch/arm/mach-spear/restart.c index 43417d0db6d9..76fb16cc8132 100644 --- a/arch/arm/mach-spear/restart.c +++ b/arch/arm/mach-spear/restart.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/plat-spear/restart.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2009 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/arch/arm/mach-spear/spear.h b/arch/arm/mach-spear/spear.h index 5ed841ccf8a3..432efd407c76 100644 --- a/arch/arm/mach-spear/spear.h +++ b/arch/arm/mach-spear/spear.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * SPEAr3xx/6xx Machine family specific definition * * Copyright (C) 2009,2012 ST Microelectronics * Rajeev Kumar * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __MACH_SPEAR_H diff --git a/arch/arm/mach-spear/spear1310.c b/arch/arm/mach-spear/spear1310.c index 549ab2be1b74..89d388388e02 100644 --- a/arch/arm/mach-spear/spear1310.c +++ b/arch/arm/mach-spear/spear1310.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear13xx/spear1310.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr1310: " fmt diff --git a/arch/arm/mach-spear/spear1340.c b/arch/arm/mach-spear/spear1340.c index a212af90c0bc..a391f154eff9 100644 --- a/arch/arm/mach-spear/spear1340.c +++ b/arch/arm/mach-spear/spear1340.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear13xx/spear1340.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr1340: " fmt diff --git a/arch/arm/mach-spear/spear13xx.c b/arch/arm/mach-spear/spear13xx.c index 9d4bdce2e865..ac5b76bbeab5 100644 --- a/arch/arm/mach-spear/spear13xx.c +++ b/arch/arm/mach-spear/spear13xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear13xx/spear13xx.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr13xx: " fmt diff --git a/arch/arm/mach-spear/spear300.c b/arch/arm/mach-spear/spear300.c index 1f2a6bbcf096..1d6b6e10fcf6 100644 --- a/arch/arm/mach-spear/spear300.c +++ b/arch/arm/mach-spear/spear300.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear3xx/spear300.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2009-2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr300: " fmt diff --git a/arch/arm/mach-spear/spear310.c b/arch/arm/mach-spear/spear310.c index f0fc140a59cf..da4643b9f3bb 100644 --- a/arch/arm/mach-spear/spear310.c +++ b/arch/arm/mach-spear/spear310.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear3xx/spear310.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2009-2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr310: " fmt diff --git a/arch/arm/mach-spear/spear320.c b/arch/arm/mach-spear/spear320.c index cb464ae7c08a..12aa82b987ac 100644 --- a/arch/arm/mach-spear/spear320.c +++ b/arch/arm/mach-spear/spear320.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear3xx/spear320.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2009-2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr320: " fmt diff --git a/arch/arm/mach-spear/spear3xx.c b/arch/arm/mach-spear/spear3xx.c index fab3d6df69ff..2ba406e92c41 100644 --- a/arch/arm/mach-spear/spear3xx.c +++ b/arch/arm/mach-spear/spear3xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear3xx/spear3xx.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2009-2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "SPEAr3xx: " fmt diff --git a/arch/arm/mach-spear/spear6xx.c b/arch/arm/mach-spear/spear6xx.c index d061080b191f..58183493e06d 100644 --- a/arch/arm/mach-spear/spear6xx.c +++ b/arch/arm/mach-spear/spear6xx.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear6xx/spear6xx.c * @@ -7,10 +8,6 @@ * Rajeev Kumar * * Copyright 2012 Stefan Roese - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-spear/time.c b/arch/arm/mach-spear/time.c index c7c17c0f936c..e979e2197f8e 100644 --- a/arch/arm/mach-spear/time.c +++ b/arch/arm/mach-spear/time.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/plat-spear/time.c * * Copyright (C) 2010 ST Microelectronics * Shiraz Hashim - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/arch/arm/mach-versatile/spc.c b/arch/arm/mach-versatile/spc.c index 6e6985e756af..5e44170e1a9a 100644 --- a/arch/arm/mach-versatile/spc.c +++ b/arch/arm/mach-versatile/spc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Versatile Express Serial Power Controller (SPC) support * @@ -6,15 +7,6 @@ * Authors: Sudeep KarkadaNagesha * Achin Gupta * Lorenzo Pieralisi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c index 5c1b7a7b9af6..25dbd84a1aaf 100644 --- a/arch/arm/mm/cache-feroceon-l2.c +++ b/arch/arm/mm/cache-feroceon-l2.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mm/cache-feroceon-l2.c - Feroceon L2 cache controller support * * Copyright (C) 2008 Marvell Semiconductor * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * References: * - Unified Layer 2 Cache for Feroceon CPU Cores, * Document ID MV-S104858-00, Rev. A, October 23 2007. diff --git a/arch/arm/mm/cache-tauros2.c b/arch/arm/mm/cache-tauros2.c index 88255bea65e4..b1e1aba602f7 100644 --- a/arch/arm/mm/cache-tauros2.c +++ b/arch/arm/mm/cache-tauros2.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mm/cache-tauros2.c - Tauros2 L2 cache controller support * * Copyright (C) 2008 Marvell Semiconductor * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * References: * - PJ1 CPU Core Datasheet, * Document ID MV-S104837-01, Rev 0.7, January 24 2008. diff --git a/arch/arm/mm/dma-mapping.c b/arch/arm/mm/dma-mapping.c index 059cce018570..1483b6a4319d 100644 --- a/arch/arm/mm/dma-mapping.c +++ b/arch/arm/mm/dma-mapping.c @@ -709,7 +709,7 @@ static void *__dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, *handle = DMA_MAPPING_ERROR; allowblock = gfpflags_allow_blocking(gfp); - cma = allowblock ? dev_get_cma_area(dev) : false; + cma = allowblock ? dev_get_cma_area(dev) : NULL; if (cma) buf->allocator = &cma_allocator; diff --git a/arch/arm/mm/kasan_init.c b/arch/arm/mm/kasan_init.c index 5ad0d6c56d56..29caee9c79ce 100644 --- a/arch/arm/mm/kasan_init.c +++ b/arch/arm/mm/kasan_init.c @@ -236,7 +236,11 @@ void __init kasan_init(void) clear_pgds(KASAN_SHADOW_START, KASAN_SHADOW_END); - kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START), + if (!IS_ENABLED(CONFIG_KASAN_VMALLOC)) + kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_START), + kasan_mem_to_shadow((void *)VMALLOC_END)); + + kasan_populate_early_shadow(kasan_mem_to_shadow((void *)VMALLOC_END), kasan_mem_to_shadow((void *)-1UL) + 1); for_each_mem_range(i, &pa_start, &pa_end) { diff --git a/arch/arm/xen/enlighten.c b/arch/arm/xen/enlighten.c index 1f9c3ba32833..93c8ccbf2982 100644 --- a/arch/arm/xen/enlighten.c +++ b/arch/arm/xen/enlighten.c @@ -34,6 +34,7 @@ #include #include #include +#include #include @@ -443,7 +444,8 @@ static int __init xen_guest_init(void) if (!xen_domain()) return 0; - xen_set_restricted_virtio_memory_access(); + if (IS_ENABLED(CONFIG_XEN_VIRTIO)) + virtio_set_mem_acc_cb(xen_virtio_mem_acc); if (!acpi_disabled) xen_acpi_guest_init(); diff --git a/arch/arm64/boot/dts/mediatek/mt6755.dtsi b/arch/arm64/boot/dts/mediatek/mt6755.dtsi index 01ba77669717..b55d3fac9bd4 100644 --- a/arch/arm64/boot/dts/mediatek/mt6755.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt6755.dtsi @@ -1,14 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2016 MediaTek Inc. * Author: Mars.C - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm64/boot/dts/mediatek/mt6795.dtsi b/arch/arm64/boot/dts/mediatek/mt6795.dtsi index d3bce9429e9b..d4842b4a4eb7 100644 --- a/arch/arm64/boot/dts/mediatek/mt6795.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt6795.dtsi @@ -1,14 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2015 MediaTek Inc. * Author: Mars.C - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm64/boot/dts/mediatek/mt8173.dtsi b/arch/arm64/boot/dts/mediatek/mt8173.dtsi index f35111724363..6d9513c1f5bf 100644 --- a/arch/arm64/boot/dts/mediatek/mt8173.dtsi +++ b/arch/arm64/boot/dts/mediatek/mt8173.dtsi @@ -1,14 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (c) 2014 MediaTek Inc. * Author: Eddie Huang - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/arch/arm64/configs/gki_defconfig b/arch/arm64/configs/gki_defconfig index 0d866cbeb03a..5695c91ad395 100644 --- a/arch/arm64/configs/gki_defconfig +++ b/arch/arm64/configs/gki_defconfig @@ -520,7 +520,6 @@ CONFIG_VHOST_VSOCK=y CONFIG_STAGING=y CONFIG_ASHMEM=y CONFIG_COMMON_CLK_SCPI=y -# CONFIG_CLK_SUNXI is not set # CONFIG_SUNXI_CCU is not set CONFIG_HWSPINLOCK=y # CONFIG_SUN50I_ERRATUM_UNKNOWN1 is not set diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h index 2e277f2ed671..53035763e48e 100644 --- a/arch/arm64/include/asm/kvm_asm.h +++ b/arch/arm64/include/asm/kvm_asm.h @@ -176,6 +176,22 @@ struct kvm_nvhe_init_params { unsigned long vtcr; }; +/* + * Used by the host in EL1 to dump the nVHE hypervisor backtrace on + * hyp_panic() in non-protected mode. + * + * @stack_base: hyp VA of the hyp_stack base. + * @overflow_stack_base: hyp VA of the hyp_overflow_stack base. + * @fp: hyp FP where the backtrace begins. + * @pc: hyp PC where the backtrace begins. + */ +struct kvm_nvhe_stacktrace_info { + unsigned long stack_base; + unsigned long overflow_stack_base; + unsigned long fp; + unsigned long pc; +}; + /* Translate a kernel address @ptr into its equivalent linear mapping */ #define kvm_ksym_ref(ptr) \ ({ \ diff --git a/arch/arm64/include/asm/kvm_emulate.h b/arch/arm64/include/asm/kvm_emulate.h index 0e66edd3aff2..9bdba47f7e14 100644 --- a/arch/arm64/include/asm/kvm_emulate.h +++ b/arch/arm64/include/asm/kvm_emulate.h @@ -473,9 +473,18 @@ static inline unsigned long vcpu_data_host_to_guest(struct kvm_vcpu *vcpu, static __always_inline void kvm_incr_pc(struct kvm_vcpu *vcpu) { - vcpu->arch.flags |= KVM_ARM64_INCREMENT_PC; + WARN_ON(vcpu_get_flag(vcpu, PENDING_EXCEPTION)); + vcpu_set_flag(vcpu, INCREMENT_PC); } +#define kvm_pend_exception(v, e) \ + do { \ + WARN_ON(vcpu_get_flag((v), INCREMENT_PC)); \ + vcpu_set_flag((v), PENDING_EXCEPTION); \ + vcpu_set_flag((v), e); \ + } while (0) + + static inline bool vcpu_has_feature(struct kvm_vcpu *vcpu, int feature) { return test_bit(feature, vcpu->arch.features); diff --git a/arch/arm64/include/asm/kvm_host.h b/arch/arm64/include/asm/kvm_host.h index de32152cea04..f38ef299f13b 100644 --- a/arch/arm64/include/asm/kvm_host.h +++ b/arch/arm64/include/asm/kvm_host.h @@ -325,8 +325,30 @@ struct kvm_vcpu_arch { /* Exception Information */ struct kvm_vcpu_fault_info fault; - /* Miscellaneous vcpu state flags */ - u64 flags; + /* Ownership of the FP regs */ + enum { + FP_STATE_FREE, + FP_STATE_HOST_OWNED, + FP_STATE_GUEST_OWNED, + } fp_state; + + /* Configuration flags, set once and for all before the vcpu can run */ + u8 cflags; + + /* Input flags to the hypervisor code, potentially cleared after use */ + u8 iflags; + + /* State flags for kernel bookkeeping, unused by the hypervisor code */ + u8 sflags; + + /* + * Don't run the guest (internal implementation need). + * + * Contrary to the flags above, this is set/cleared outside of + * a vcpu context, and thus cannot be mixed with the flags + * themselves (or the flag accesses need to be made atomic). + */ + bool pause; /* * We maintain more than a single set of debug registers to support @@ -376,9 +398,6 @@ struct kvm_vcpu_arch { /* vcpu power state */ struct kvm_mp_state mp_state; - /* Don't run the guest (internal implementation need) */ - bool pause; - /* Cache some mmu pages needed inside spinlock regions */ struct kvm_mmu_memory_cache mmu_page_cache; @@ -392,10 +411,6 @@ struct kvm_vcpu_arch { /* Additional reset state */ struct vcpu_reset_state reset_state; - /* True when deferrable sysregs are loaded on the physical CPU, - * see kvm_vcpu_load_sysregs_vhe and kvm_vcpu_put_sysregs_vhe. */ - bool sysregs_loaded_on_cpu; - /* Guest PV state */ struct { u64 last_steal; @@ -403,6 +418,124 @@ struct kvm_vcpu_arch { } steal; }; +/* + * Each 'flag' is composed of a comma-separated triplet: + * + * - the flag-set it belongs to in the vcpu->arch structure + * - the value for that flag + * - the mask for that flag + * + * __vcpu_single_flag() builds such a triplet for a single-bit flag. + * unpack_vcpu_flag() extract the flag value from the triplet for + * direct use outside of the flag accessors. + */ +#define __vcpu_single_flag(_set, _f) _set, (_f), (_f) + +#define __unpack_flag(_set, _f, _m) _f +#define unpack_vcpu_flag(...) __unpack_flag(__VA_ARGS__) + +#define __build_check_flag(v, flagset, f, m) \ + do { \ + typeof(v->arch.flagset) *_fset; \ + \ + /* Check that the flags fit in the mask */ \ + BUILD_BUG_ON(HWEIGHT(m) != HWEIGHT((f) | (m))); \ + /* Check that the flags fit in the type */ \ + BUILD_BUG_ON((sizeof(*_fset) * 8) <= __fls(m)); \ + } while (0) + +#define __vcpu_get_flag(v, flagset, f, m) \ + ({ \ + __build_check_flag(v, flagset, f, m); \ + \ + v->arch.flagset & (m); \ + }) + +#define __vcpu_set_flag(v, flagset, f, m) \ + do { \ + typeof(v->arch.flagset) *fset; \ + \ + __build_check_flag(v, flagset, f, m); \ + \ + fset = &v->arch.flagset; \ + if (HWEIGHT(m) > 1) \ + *fset &= ~(m); \ + *fset |= (f); \ + } while (0) + +#define __vcpu_clear_flag(v, flagset, f, m) \ + do { \ + typeof(v->arch.flagset) *fset; \ + \ + __build_check_flag(v, flagset, f, m); \ + \ + fset = &v->arch.flagset; \ + *fset &= ~(m); \ + } while (0) + +#define vcpu_get_flag(v, ...) __vcpu_get_flag((v), __VA_ARGS__) +#define vcpu_set_flag(v, ...) __vcpu_set_flag((v), __VA_ARGS__) +#define vcpu_clear_flag(v, ...) __vcpu_clear_flag((v), __VA_ARGS__) + +/* SVE exposed to guest */ +#define GUEST_HAS_SVE __vcpu_single_flag(cflags, BIT(0)) +/* SVE config completed */ +#define VCPU_SVE_FINALIZED __vcpu_single_flag(cflags, BIT(1)) +/* PTRAUTH exposed to guest */ +#define GUEST_HAS_PTRAUTH __vcpu_single_flag(cflags, BIT(2)) + +/* Exception pending */ +#define PENDING_EXCEPTION __vcpu_single_flag(iflags, BIT(0)) +/* + * PC increment. Overlaps with EXCEPT_MASK on purpose so that it can't + * be set together with an exception... + */ +#define INCREMENT_PC __vcpu_single_flag(iflags, BIT(1)) +/* Target EL/MODE (not a single flag, but let's abuse the macro) */ +#define EXCEPT_MASK __vcpu_single_flag(iflags, GENMASK(3, 1)) + +/* Helpers to encode exceptions with minimum fuss */ +#define __EXCEPT_MASK_VAL unpack_vcpu_flag(EXCEPT_MASK) +#define __EXCEPT_SHIFT __builtin_ctzl(__EXCEPT_MASK_VAL) +#define __vcpu_except_flags(_f) iflags, (_f << __EXCEPT_SHIFT), __EXCEPT_MASK_VAL + +/* + * When PENDING_EXCEPTION is set, EXCEPT_MASK can take the following + * values: + * + * For AArch32 EL1: + */ +#define EXCEPT_AA32_UND __vcpu_except_flags(0) +#define EXCEPT_AA32_IABT __vcpu_except_flags(1) +#define EXCEPT_AA32_DABT __vcpu_except_flags(2) +/* For AArch64: */ +#define EXCEPT_AA64_EL1_SYNC __vcpu_except_flags(0) +#define EXCEPT_AA64_EL1_IRQ __vcpu_except_flags(1) +#define EXCEPT_AA64_EL1_FIQ __vcpu_except_flags(2) +#define EXCEPT_AA64_EL1_SERR __vcpu_except_flags(3) +/* For AArch64 with NV (one day): */ +#define EXCEPT_AA64_EL2_SYNC __vcpu_except_flags(4) +#define EXCEPT_AA64_EL2_IRQ __vcpu_except_flags(5) +#define EXCEPT_AA64_EL2_FIQ __vcpu_except_flags(6) +#define EXCEPT_AA64_EL2_SERR __vcpu_except_flags(7) +/* Guest debug is live */ +#define DEBUG_DIRTY __vcpu_single_flag(iflags, BIT(4)) +/* Save SPE context if active */ +#define DEBUG_STATE_SAVE_SPE __vcpu_single_flag(iflags, BIT(5)) +/* Save TRBE context if active */ +#define DEBUG_STATE_SAVE_TRBE __vcpu_single_flag(iflags, BIT(6)) + +/* SVE enabled for host EL0 */ +#define HOST_SVE_ENABLED __vcpu_single_flag(sflags, BIT(0)) +/* SME enabled for EL0 */ +#define HOST_SME_ENABLED __vcpu_single_flag(sflags, BIT(1)) +/* Physical CPU not in supported_cpus */ +#define ON_UNSUPPORTED_CPU __vcpu_single_flag(sflags, BIT(2)) +/* WFIT instruction trapped */ +#define IN_WFIT __vcpu_single_flag(sflags, BIT(3)) +/* vcpu system registers loaded on physical CPU */ +#define SYSREGS_ON_CPU __vcpu_single_flag(sflags, BIT(4)) + /* Pointer to the vcpu's SVE FFR for sve_{save,load}_state() */ #define vcpu_sve_pffr(vcpu) (kern_hyp_va((vcpu)->arch.sve_state) + \ sve_ffr_offset((vcpu)->arch.sve_max_vl)) @@ -423,70 +556,31 @@ struct kvm_vcpu_arch { __size_ret; \ }) -/* vcpu_arch flags field values: */ -#define KVM_ARM64_DEBUG_DIRTY (1 << 0) -#define KVM_ARM64_FP_ENABLED (1 << 1) /* guest FP regs loaded */ -#define KVM_ARM64_FP_HOST (1 << 2) /* host FP regs loaded */ -#define KVM_ARM64_HOST_SVE_ENABLED (1 << 4) /* SVE enabled for EL0 */ -#define KVM_ARM64_GUEST_HAS_SVE (1 << 5) /* SVE exposed to guest */ -#define KVM_ARM64_VCPU_SVE_FINALIZED (1 << 6) /* SVE config completed */ -#define KVM_ARM64_GUEST_HAS_PTRAUTH (1 << 7) /* PTRAUTH exposed to guest */ -#define KVM_ARM64_PENDING_EXCEPTION (1 << 8) /* Exception pending */ -/* - * Overlaps with KVM_ARM64_EXCEPT_MASK on purpose so that it can't be - * set together with an exception... - */ -#define KVM_ARM64_INCREMENT_PC (1 << 9) /* Increment PC */ -#define KVM_ARM64_EXCEPT_MASK (7 << 9) /* Target EL/MODE */ -/* - * When KVM_ARM64_PENDING_EXCEPTION is set, KVM_ARM64_EXCEPT_MASK can - * take the following values: - * - * For AArch32 EL1: - */ -#define KVM_ARM64_EXCEPT_AA32_UND (0 << 9) -#define KVM_ARM64_EXCEPT_AA32_IABT (1 << 9) -#define KVM_ARM64_EXCEPT_AA32_DABT (2 << 9) -/* For AArch64: */ -#define KVM_ARM64_EXCEPT_AA64_ELx_SYNC (0 << 9) -#define KVM_ARM64_EXCEPT_AA64_ELx_IRQ (1 << 9) -#define KVM_ARM64_EXCEPT_AA64_ELx_FIQ (2 << 9) -#define KVM_ARM64_EXCEPT_AA64_ELx_SERR (3 << 9) -#define KVM_ARM64_EXCEPT_AA64_EL1 (0 << 11) -#define KVM_ARM64_EXCEPT_AA64_EL2 (1 << 11) - -#define KVM_ARM64_DEBUG_STATE_SAVE_SPE (1 << 12) /* Save SPE context if active */ -#define KVM_ARM64_DEBUG_STATE_SAVE_TRBE (1 << 13) /* Save TRBE context if active */ -#define KVM_ARM64_FP_FOREIGN_FPSTATE (1 << 14) -#define KVM_ARM64_ON_UNSUPPORTED_CPU (1 << 15) /* Physical CPU not in supported_cpus */ -#define KVM_ARM64_HOST_SME_ENABLED (1 << 16) /* SME enabled for EL0 */ -#define KVM_ARM64_WFIT (1 << 17) /* WFIT instruction trapped */ - #define KVM_GUESTDBG_VALID_MASK (KVM_GUESTDBG_ENABLE | \ KVM_GUESTDBG_USE_SW_BP | \ KVM_GUESTDBG_USE_HW | \ KVM_GUESTDBG_SINGLESTEP) #define vcpu_has_sve(vcpu) (system_supports_sve() && \ - ((vcpu)->arch.flags & KVM_ARM64_GUEST_HAS_SVE)) + vcpu_get_flag(vcpu, GUEST_HAS_SVE)) #ifdef CONFIG_ARM64_PTR_AUTH #define vcpu_has_ptrauth(vcpu) \ ((cpus_have_final_cap(ARM64_HAS_ADDRESS_AUTH) || \ cpus_have_final_cap(ARM64_HAS_GENERIC_AUTH)) && \ - (vcpu)->arch.flags & KVM_ARM64_GUEST_HAS_PTRAUTH) + vcpu_get_flag(vcpu, GUEST_HAS_PTRAUTH)) #else #define vcpu_has_ptrauth(vcpu) false #endif #define vcpu_on_unsupported_cpu(vcpu) \ - ((vcpu)->arch.flags & KVM_ARM64_ON_UNSUPPORTED_CPU) + vcpu_get_flag(vcpu, ON_UNSUPPORTED_CPU) #define vcpu_set_on_unsupported_cpu(vcpu) \ - ((vcpu)->arch.flags |= KVM_ARM64_ON_UNSUPPORTED_CPU) + vcpu_set_flag(vcpu, ON_UNSUPPORTED_CPU) #define vcpu_clear_on_unsupported_cpu(vcpu) \ - ((vcpu)->arch.flags &= ~KVM_ARM64_ON_UNSUPPORTED_CPU) + vcpu_clear_flag(vcpu, ON_UNSUPPORTED_CPU) #define vcpu_gp_regs(v) (&(v)->arch.ctxt.regs) @@ -620,8 +714,6 @@ int kvm_arm_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg); unsigned long kvm_arm_num_sys_reg_descs(struct kvm_vcpu *vcpu); int kvm_arm_copy_sys_reg_indices(struct kvm_vcpu *vcpu, u64 __user *uindices); -int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); -int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); int __kvm_arm_vcpu_get_events(struct kvm_vcpu *vcpu, struct kvm_vcpu_events *events); @@ -831,8 +923,7 @@ void kvm_init_protected_traps(struct kvm_vcpu *vcpu); int kvm_arm_vcpu_finalize(struct kvm_vcpu *vcpu, int feature); bool kvm_arm_vcpu_is_finalized(struct kvm_vcpu *vcpu); -#define kvm_arm_vcpu_sve_finalized(vcpu) \ - ((vcpu)->arch.flags & KVM_ARM64_VCPU_SVE_FINALIZED) +#define kvm_arm_vcpu_sve_finalized(vcpu) vcpu_get_flag(vcpu, VCPU_SVE_FINALIZED) #define kvm_has_mte(kvm) \ (system_supports_mte() && \ diff --git a/arch/arm64/include/asm/memory.h b/arch/arm64/include/asm/memory.h index 227d256cd4b9..4e4c171f070b 100644 --- a/arch/arm64/include/asm/memory.h +++ b/arch/arm64/include/asm/memory.h @@ -113,6 +113,14 @@ #define OVERFLOW_STACK_SIZE SZ_4K +/* + * With the minimum frame size of [x29, x30], exactly half the combined + * sizes of the hyp and overflow stacks is the maximum size needed to + * save the unwinded stacktrace; plus an additional entry to delimit the + * end. + */ +#define NVHE_STACKTRACE_SIZE ((OVERFLOW_STACK_SIZE + PAGE_SIZE) / 2 + sizeof(long)) + /* * Alignment of kernel segments (e.g. .text, .data). * diff --git a/arch/arm64/include/asm/stacktrace.h b/arch/arm64/include/asm/stacktrace.h index aec9315bf156..6ebdcdff77f5 100644 --- a/arch/arm64/include/asm/stacktrace.h +++ b/arch/arm64/include/asm/stacktrace.h @@ -8,52 +8,20 @@ #include #include #include -#include #include #include +#include #include #include -enum stack_type { - STACK_TYPE_UNKNOWN, - STACK_TYPE_TASK, - STACK_TYPE_IRQ, - STACK_TYPE_OVERFLOW, - STACK_TYPE_SDEI_NORMAL, - STACK_TYPE_SDEI_CRITICAL, - __NR_STACK_TYPES -}; - -struct stack_info { - unsigned long low; - unsigned long high; - enum stack_type type; -}; +#include extern void dump_backtrace(struct pt_regs *regs, struct task_struct *tsk, const char *loglvl); DECLARE_PER_CPU(unsigned long *, irq_stack_ptr); -static inline bool on_stack(unsigned long sp, unsigned long size, - unsigned long low, unsigned long high, - enum stack_type type, struct stack_info *info) -{ - if (!low) - return false; - - if (sp < low || sp + size < sp || sp + size > high) - return false; - - if (info) { - info->low = low; - info->high = high; - info->type = type; - } - return true; -} - static inline bool on_irq_stack(unsigned long sp, unsigned long size, struct stack_info *info) { @@ -89,30 +57,4 @@ static inline bool on_overflow_stack(unsigned long sp, unsigned long size, struct stack_info *info) { return false; } #endif - -/* - * We can only safely access per-cpu stacks from current in a non-preemptible - * context. - */ -static inline bool on_accessible_stack(const struct task_struct *tsk, - unsigned long sp, unsigned long size, - struct stack_info *info) -{ - if (info) - info->type = STACK_TYPE_UNKNOWN; - - if (on_task_stack(tsk, sp, size, info)) - return true; - if (tsk != current || preemptible()) - return false; - if (on_irq_stack(sp, size, info)) - return true; - if (on_overflow_stack(sp, size, info)) - return true; - if (on_sdei_stack(sp, size, info)) - return true; - - return false; -} - #endif /* __ASM_STACKTRACE_H */ diff --git a/arch/arm64/include/asm/stacktrace/common.h b/arch/arm64/include/asm/stacktrace/common.h new file mode 100644 index 000000000000..f58eb944c46f --- /dev/null +++ b/arch/arm64/include/asm/stacktrace/common.h @@ -0,0 +1,199 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Common arm64 stack unwinder code. + * + * To implement a new arm64 stack unwinder: + * 1) Include this header + * + * 2) Call into unwind_next_common() from your top level unwind + * function, passing it the validation and translation callbacks + * (though the later can be NULL if no translation is required). + * + * See: arch/arm64/kernel/stacktrace.c for the reference implementation. + * + * Copyright (C) 2012 ARM Ltd. + */ +#ifndef __ASM_STACKTRACE_COMMON_H +#define __ASM_STACKTRACE_COMMON_H + +#include +#include +#include +#include + +enum stack_type { + STACK_TYPE_UNKNOWN, + STACK_TYPE_TASK, + STACK_TYPE_IRQ, + STACK_TYPE_OVERFLOW, + STACK_TYPE_SDEI_NORMAL, + STACK_TYPE_SDEI_CRITICAL, + STACK_TYPE_HYP, + __NR_STACK_TYPES +}; + +struct stack_info { + unsigned long low; + unsigned long high; + enum stack_type type; +}; + +/* + * A snapshot of a frame record or fp/lr register values, along with some + * accounting information necessary for robust unwinding. + * + * @fp: The fp value in the frame record (or the real fp) + * @pc: The lr value in the frame record (or the real lr) + * + * @stacks_done: Stacks which have been entirely unwound, for which it is no + * longer valid to unwind to. + * + * @prev_fp: The fp that pointed to this frame record, or a synthetic value + * of 0. This is used to ensure that within a stack, each + * subsequent frame record is at an increasing address. + * @prev_type: The type of stack this frame record was on, or a synthetic + * value of STACK_TYPE_UNKNOWN. This is used to detect a + * transition from one stack to another. + * + * @kr_cur: When KRETPROBES is selected, holds the kretprobe instance + * associated with the most recently encountered replacement lr + * value. + * + * @task: The task being unwound. + */ +struct unwind_state { + unsigned long fp; + unsigned long pc; + DECLARE_BITMAP(stacks_done, __NR_STACK_TYPES); + unsigned long prev_fp; + enum stack_type prev_type; +#ifdef CONFIG_KRETPROBES + struct llist_node *kr_cur; +#endif + struct task_struct *task; +}; + +static inline bool on_stack(unsigned long sp, unsigned long size, + unsigned long low, unsigned long high, + enum stack_type type, struct stack_info *info) +{ + if (!low) + return false; + + if (sp < low || sp + size < sp || sp + size > high) + return false; + + if (info) { + info->low = low; + info->high = high; + info->type = type; + } + return true; +} + +static inline void unwind_init_common(struct unwind_state *state, + struct task_struct *task) +{ + state->task = task; +#ifdef CONFIG_KRETPROBES + state->kr_cur = NULL; +#endif + + /* + * Prime the first unwind. + * + * In unwind_next() we'll check that the FP points to a valid stack, + * which can't be STACK_TYPE_UNKNOWN, and the first unwind will be + * treated as a transition to whichever stack that happens to be. The + * prev_fp value won't be used, but we set it to 0 such that it is + * definitely not an accessible stack address. + */ + bitmap_zero(state->stacks_done, __NR_STACK_TYPES); + state->prev_fp = 0; + state->prev_type = STACK_TYPE_UNKNOWN; +} + +/* + * stack_trace_translate_fp_fn() - Translates a non-kernel frame pointer to + * a kernel address. + * + * @fp: the frame pointer to be updated to its kernel address. + * @type: the stack type associated with frame pointer @fp + * + * Returns true and success and @fp is updated to the corresponding + * kernel virtual address; otherwise returns false. + */ +typedef bool (*stack_trace_translate_fp_fn)(unsigned long *fp, + enum stack_type type); + +/* + * on_accessible_stack_fn() - Check whether a stack range is on any + * of the possible stacks. + * + * @tsk: task whose stack is being unwound + * @sp: stack address being checked + * @size: size of the stack range being checked + * @info: stack unwinding context + */ +typedef bool (*on_accessible_stack_fn)(const struct task_struct *tsk, + unsigned long sp, unsigned long size, + struct stack_info *info); + +static inline int unwind_next_common(struct unwind_state *state, + struct stack_info *info, + on_accessible_stack_fn accessible, + stack_trace_translate_fp_fn translate_fp) +{ + unsigned long fp = state->fp, kern_fp = fp; + struct task_struct *tsk = state->task; + + if (fp & 0x7) + return -EINVAL; + + if (!accessible(tsk, fp, 16, info)) + return -EINVAL; + + if (test_bit(info->type, state->stacks_done)) + return -EINVAL; + + /* + * If fp is not from the current address space perform the necessary + * translation before dereferencing it to get the next fp. + */ + if (translate_fp && !translate_fp(&kern_fp, info->type)) + return -EINVAL; + + /* + * As stacks grow downward, any valid record on the same stack must be + * at a strictly higher address than the prior record. + * + * Stacks can nest in several valid orders, e.g. + * + * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL + * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW + * HYP -> OVERFLOW + * + * ... but the nesting itself is strict. Once we transition from one + * stack to another, it's never valid to unwind back to that first + * stack. + */ + if (info->type == state->prev_type) { + if (fp <= state->prev_fp) + return -EINVAL; + } else { + __set_bit(state->prev_type, state->stacks_done); + } + + /* + * Record this frame record's values and location. The prev_fp and + * prev_type are only meaningful to the next unwind_next() invocation. + */ + state->fp = READ_ONCE(*(unsigned long *)(kern_fp)); + state->pc = READ_ONCE(*(unsigned long *)(kern_fp + 8)); + state->prev_fp = fp; + state->prev_type = info->type; + + return 0; +} + +#endif /* __ASM_STACKTRACE_COMMON_H */ diff --git a/arch/arm64/include/asm/stacktrace/nvhe.h b/arch/arm64/include/asm/stacktrace/nvhe.h new file mode 100644 index 000000000000..d5527b600390 --- /dev/null +++ b/arch/arm64/include/asm/stacktrace/nvhe.h @@ -0,0 +1,55 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * KVM nVHE hypervisor stack tracing support. + * + * The unwinder implementation depends on the nVHE mode: + * + * 1) Non-protected nVHE mode - the host can directly access the + * HYP stack pages and unwind the HYP stack in EL1. This saves having + * to allocate shared buffers for the host to read the unwinded + * stacktrace. + * + * 2) pKVM (protected nVHE) mode - the host cannot directly access + * the HYP memory. The stack is unwinded in EL2 and dumped to a shared + * buffer where the host can read and print the stacktrace. + * + * Copyright (C) 2022 Google LLC + */ +#ifndef __ASM_STACKTRACE_NVHE_H +#define __ASM_STACKTRACE_NVHE_H + +#include + +/* + * kvm_nvhe_unwind_init - Start an unwind from the given nVHE HYP fp and pc + * + * @state : unwind_state to initialize + * @fp : frame pointer at which to start the unwinding. + * @pc : program counter at which to start the unwinding. + */ +static inline void kvm_nvhe_unwind_init(struct unwind_state *state, + unsigned long fp, + unsigned long pc) +{ + unwind_init_common(state, NULL); + + state->fp = fp; + state->pc = pc; +} + +#ifndef __KVM_NVHE_HYPERVISOR__ +/* + * Conventional (non-protected) nVHE HYP stack unwinder + * + * In non-protected mode, the unwinding is done from kernel proper context + * (by the host in EL1). + */ + +DECLARE_KVM_NVHE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack); +DECLARE_KVM_NVHE_PER_CPU(struct kvm_nvhe_stacktrace_info, kvm_stacktrace_info); +DECLARE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); + +void kvm_nvhe_dump_backtrace(unsigned long hyp_offset); + +#endif /* __KVM_NVHE_HYPERVISOR__ */ +#endif /* __ASM_STACKTRACE_NVHE_H */ diff --git a/arch/arm64/kernel/stacktrace.c b/arch/arm64/kernel/stacktrace.c index 852819759d0a..8705c6473869 100644 --- a/arch/arm64/kernel/stacktrace.c +++ b/arch/arm64/kernel/stacktrace.c @@ -7,74 +7,15 @@ #include #include #include -#include #include #include #include #include #include -#include #include #include -/* - * A snapshot of a frame record or fp/lr register values, along with some - * accounting information necessary for robust unwinding. - * - * @fp: The fp value in the frame record (or the real fp) - * @pc: The lr value in the frame record (or the real lr) - * - * @stacks_done: Stacks which have been entirely unwound, for which it is no - * longer valid to unwind to. - * - * @prev_fp: The fp that pointed to this frame record, or a synthetic value - * of 0. This is used to ensure that within a stack, each - * subsequent frame record is at an increasing address. - * @prev_type: The type of stack this frame record was on, or a synthetic - * value of STACK_TYPE_UNKNOWN. This is used to detect a - * transition from one stack to another. - * - * @kr_cur: When KRETPROBES is selected, holds the kretprobe instance - * associated with the most recently encountered replacement lr - * value. - * - * @task: The task being unwound. - */ -struct unwind_state { - unsigned long fp; - unsigned long pc; - DECLARE_BITMAP(stacks_done, __NR_STACK_TYPES); - unsigned long prev_fp; - enum stack_type prev_type; -#ifdef CONFIG_KRETPROBES - struct llist_node *kr_cur; -#endif - struct task_struct *task; -}; - -static void unwind_init_common(struct unwind_state *state, - struct task_struct *task) -{ - state->task = task; -#ifdef CONFIG_KRETPROBES - state->kr_cur = NULL; -#endif - - /* - * Prime the first unwind. - * - * In unwind_next() we'll check that the FP points to a valid stack, - * which can't be STACK_TYPE_UNKNOWN, and the first unwind will be - * treated as a transition to whichever stack that happens to be. The - * prev_fp value won't be used, but we set it to 0 such that it is - * definitely not an accessible stack address. - */ - bitmap_zero(state->stacks_done, __NR_STACK_TYPES); - state->prev_fp = 0; - state->prev_type = STACK_TYPE_UNKNOWN; -} - /* * Start an unwind from a pt_regs. * @@ -126,6 +67,31 @@ static inline void unwind_init_from_task(struct unwind_state *state, state->pc = thread_saved_pc(task); } +/* + * We can only safely access per-cpu stacks from current in a non-preemptible + * context. + */ +static bool on_accessible_stack(const struct task_struct *tsk, + unsigned long sp, unsigned long size, + struct stack_info *info) +{ + if (info) + info->type = STACK_TYPE_UNKNOWN; + + if (on_task_stack(tsk, sp, size, info)) + return true; + if (tsk != current || preemptible()) + return false; + if (on_irq_stack(sp, size, info)) + return true; + if (on_overflow_stack(sp, size, info)) + return true; + if (on_sdei_stack(sp, size, info)) + return true; + + return false; +} + /* * Unwind from one frame record (A) to the next frame record (B). * @@ -138,48 +104,15 @@ static int notrace unwind_next(struct unwind_state *state) struct task_struct *tsk = state->task; unsigned long fp = state->fp; struct stack_info info; + int err; /* Final frame; nothing to unwind */ if (fp == (unsigned long)task_pt_regs(tsk)->stackframe) return -ENOENT; - if (fp & 0x7) - return -EINVAL; - - if (!on_accessible_stack(tsk, fp, 16, &info)) - return -EINVAL; - - if (test_bit(info.type, state->stacks_done)) - return -EINVAL; - - /* - * As stacks grow downward, any valid record on the same stack must be - * at a strictly higher address than the prior record. - * - * Stacks can nest in several valid orders, e.g. - * - * TASK -> IRQ -> OVERFLOW -> SDEI_NORMAL - * TASK -> SDEI_NORMAL -> SDEI_CRITICAL -> OVERFLOW - * - * ... but the nesting itself is strict. Once we transition from one - * stack to another, it's never valid to unwind back to that first - * stack. - */ - if (info.type == state->prev_type) { - if (fp <= state->prev_fp) - return -EINVAL; - } else { - __set_bit(state->prev_type, state->stacks_done); - } - - /* - * Record this frame record's values and location. The prev_fp and - * prev_type are only meaningful to the next unwind_next() invocation. - */ - state->fp = READ_ONCE(*(unsigned long *)(fp)); - state->pc = READ_ONCE(*(unsigned long *)(fp + 8)); - state->prev_fp = fp; - state->prev_type = info.type; + err = unwind_next_common(state, &info, on_accessible_stack, NULL); + if (err) + return err; state->pc = ptrauth_strip_insn_pac(state->pc); diff --git a/arch/arm64/kernel/topology.c b/arch/arm64/kernel/topology.c index 9ab78ad826e2..869ffc4d4484 100644 --- a/arch/arm64/kernel/topology.c +++ b/arch/arm64/kernel/topology.c @@ -89,8 +89,6 @@ int __init parse_acpi_topology(void) return 0; for_each_possible_cpu(cpu) { - int i, cache_id; - topology_id = find_acpi_cpu_topology(cpu, 0); if (topology_id < 0) return topology_id; @@ -107,18 +105,6 @@ int __init parse_acpi_topology(void) cpu_topology[cpu].cluster_id = topology_id; topology_id = find_acpi_cpu_topology_package(cpu); cpu_topology[cpu].package_id = topology_id; - - i = acpi_find_last_cache_level(cpu); - - if (i > 0) { - /* - * this is the only part of cpu_topology that has - * a direct relationship with the cache topology - */ - cache_id = find_acpi_cpu_cache_topology(cpu, i); - if (cache_id > 0) - cpu_topology[cpu].llc_id = cache_id; - } } return 0; diff --git a/arch/arm64/kvm/Kconfig b/arch/arm64/kvm/Kconfig index 8a5fbbf084df..815cc118c675 100644 --- a/arch/arm64/kvm/Kconfig +++ b/arch/arm64/kvm/Kconfig @@ -56,4 +56,17 @@ config NVHE_EL2_DEBUG If unsure, say N. +config PROTECTED_NVHE_STACKTRACE + bool "Protected KVM hypervisor stacktraces" + depends on NVHE_EL2_DEBUG + default n + help + Say Y here to enable pKVM hypervisor stacktraces on hyp_panic() + + If using protected nVHE mode, but cannot afford the associated + memory cost (less than 0.75 page per CPU) of pKVM stacktraces, + say N. + + If unsure, or not using protected nVHE (pKVM), say N. + endif # VIRTUALIZATION diff --git a/arch/arm64/kvm/Makefile b/arch/arm64/kvm/Makefile index aa127ae9f675..5e33c2d4645a 100644 --- a/arch/arm64/kvm/Makefile +++ b/arch/arm64/kvm/Makefile @@ -12,7 +12,7 @@ obj-$(CONFIG_KVM) += hyp/ kvm-y += arm.o mmu.o mmio.o psci.o hypercalls.o pvtime.o \ inject_fault.o va_layout.o handle_exit.o \ - guest.o debug.o reset.o sys_regs.o \ + guest.o debug.o reset.o sys_regs.o stacktrace.o \ vgic-sys-reg-v3.o fpsimd.o pkvm.o \ arch_timer.o trng.o vmid.o \ vgic/vgic.o vgic/vgic-init.o \ diff --git a/arch/arm64/kvm/arch_timer.c b/arch/arm64/kvm/arch_timer.c index 3b8d062e30ea..bb24a76b4224 100644 --- a/arch/arm64/kvm/arch_timer.c +++ b/arch/arm64/kvm/arch_timer.c @@ -242,7 +242,7 @@ static bool kvm_timer_irq_can_fire(struct arch_timer_context *timer_ctx) static bool vcpu_has_wfit_active(struct kvm_vcpu *vcpu) { return (cpus_have_final_cap(ARM64_HAS_WFXT) && - (vcpu->arch.flags & KVM_ARM64_WFIT)); + vcpu_get_flag(vcpu, IN_WFIT)); } static u64 wfit_delay_ns(struct kvm_vcpu *vcpu) diff --git a/arch/arm64/kvm/arm.c b/arch/arm64/kvm/arm.c index 83a7f61354d3..986cee6fbc7f 100644 --- a/arch/arm64/kvm/arm.c +++ b/arch/arm64/kvm/arm.c @@ -49,7 +49,7 @@ DEFINE_STATIC_KEY_FALSE(kvm_protected_mode_initialized); DECLARE_KVM_HYP_PER_CPU(unsigned long, kvm_hyp_vector); -static DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); +DEFINE_PER_CPU(unsigned long, kvm_arm_hyp_stack_page); unsigned long kvm_arm_hyp_percpu_base[NR_CPUS]; DECLARE_KVM_NVHE_PER_CPU(struct kvm_nvhe_init_params, kvm_init_params); @@ -330,6 +330,12 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO; + /* + * Default value for the FP state, will be overloaded at load + * time if we support FP (pretty likely) + */ + vcpu->arch.fp_state = FP_STATE_FREE; + /* Set up the timer */ kvm_timer_vcpu_init(vcpu); @@ -659,7 +665,7 @@ void kvm_vcpu_wfi(struct kvm_vcpu *vcpu) preempt_enable(); kvm_vcpu_halt(vcpu); - vcpu->arch.flags &= ~KVM_ARM64_WFIT; + vcpu_clear_flag(vcpu, IN_WFIT); kvm_clear_request(KVM_REQ_UNHALT, vcpu); preempt_disable(); @@ -1015,8 +1021,8 @@ out: * the vcpu state. Note that this relies on __kvm_adjust_pc() * being preempt-safe on VHE. */ - if (unlikely(vcpu->arch.flags & (KVM_ARM64_PENDING_EXCEPTION | - KVM_ARM64_INCREMENT_PC))) + if (unlikely(vcpu_get_flag(vcpu, PENDING_EXCEPTION) || + vcpu_get_flag(vcpu, INCREMENT_PC))) kvm_call_hyp(__kvm_adjust_pc, vcpu); vcpu_put(vcpu); @@ -1414,18 +1420,11 @@ void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, static int kvm_vm_ioctl_set_device_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr) { - unsigned long dev_id, type; - - dev_id = (dev_addr->id & KVM_ARM_DEVICE_ID_MASK) >> - KVM_ARM_DEVICE_ID_SHIFT; - type = (dev_addr->id & KVM_ARM_DEVICE_TYPE_MASK) >> - KVM_ARM_DEVICE_TYPE_SHIFT; - - switch (dev_id) { + switch (FIELD_GET(KVM_ARM_DEVICE_ID_MASK, dev_addr->id)) { case KVM_ARM_DEVICE_VGIC_V2: if (!vgic_present) return -ENXIO; - return kvm_vgic_addr(kvm, type, &dev_addr->addr, true); + return kvm_set_legacy_vgic_v2_addr(kvm, dev_addr); default: return -ENODEV; } diff --git a/arch/arm64/kvm/debug.c b/arch/arm64/kvm/debug.c index 4fd5c216c4bb..0b28d7db7c76 100644 --- a/arch/arm64/kvm/debug.c +++ b/arch/arm64/kvm/debug.c @@ -104,11 +104,11 @@ static void kvm_arm_setup_mdcr_el2(struct kvm_vcpu *vcpu) * Trap debug register access when one of the following is true: * - Userspace is using the hardware to debug the guest * (KVM_GUESTDBG_USE_HW is set). - * - The guest is not using debug (KVM_ARM64_DEBUG_DIRTY is clear). + * - The guest is not using debug (DEBUG_DIRTY clear). * - The guest has enabled the OS Lock (debug exceptions are blocked). */ if ((vcpu->guest_debug & KVM_GUESTDBG_USE_HW) || - !(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY) || + !vcpu_get_flag(vcpu, DEBUG_DIRTY) || kvm_vcpu_os_lock_enabled(vcpu)) vcpu->arch.mdcr_el2 |= MDCR_EL2_TDA; @@ -147,8 +147,8 @@ void kvm_arm_reset_debug_ptr(struct kvm_vcpu *vcpu) * debug related registers. * * Additionally, KVM only traps guest accesses to the debug registers if - * the guest is not actively using them (see the KVM_ARM64_DEBUG_DIRTY - * flag on vcpu->arch.flags). Since the guest must not interfere + * the guest is not actively using them (see the DEBUG_DIRTY + * flag on vcpu->arch.iflags). Since the guest must not interfere * with the hardware state when debugging the guest, we must ensure that * trapping is enabled whenever we are debugging the guest using the * debug registers. @@ -205,9 +205,8 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) * * We simply switch the debug_ptr to point to our new * external_debug_state which has been populated by the - * debug ioctl. The existing KVM_ARM64_DEBUG_DIRTY - * mechanism ensures the registers are updated on the - * world switch. + * debug ioctl. The existing DEBUG_DIRTY mechanism ensures + * the registers are updated on the world switch. */ if (vcpu->guest_debug & KVM_GUESTDBG_USE_HW) { /* Enable breakpoints/watchpoints */ @@ -216,7 +215,7 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) vcpu_write_sys_reg(vcpu, mdscr, MDSCR_EL1); vcpu->arch.debug_ptr = &vcpu->arch.external_debug_state; - vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY; + vcpu_set_flag(vcpu, DEBUG_DIRTY); trace_kvm_arm_set_regset("BKPTS", get_num_brps(), &vcpu->arch.debug_ptr->dbg_bcr[0], @@ -246,7 +245,7 @@ void kvm_arm_setup_debug(struct kvm_vcpu *vcpu) /* If KDE or MDE are set, perform a full save/restore cycle. */ if (vcpu_read_sys_reg(vcpu, MDSCR_EL1) & (DBG_MDSCR_KDE | DBG_MDSCR_MDE)) - vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY; + vcpu_set_flag(vcpu, DEBUG_DIRTY); /* Write mdcr_el2 changes since vcpu_load on VHE systems */ if (has_vhe() && orig_mdcr_el2 != vcpu->arch.mdcr_el2) @@ -298,16 +297,16 @@ void kvm_arch_vcpu_load_debug_state_flags(struct kvm_vcpu *vcpu) */ if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_PMSVER_SHIFT) && !(read_sysreg_s(SYS_PMBIDR_EL1) & BIT(SYS_PMBIDR_EL1_P_SHIFT))) - vcpu->arch.flags |= KVM_ARM64_DEBUG_STATE_SAVE_SPE; + vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_SPE); /* Check if we have TRBE implemented and available at the host */ if (cpuid_feature_extract_unsigned_field(dfr0, ID_AA64DFR0_TRBE_SHIFT) && !(read_sysreg_s(SYS_TRBIDR_EL1) & TRBIDR_PROG)) - vcpu->arch.flags |= KVM_ARM64_DEBUG_STATE_SAVE_TRBE; + vcpu_set_flag(vcpu, DEBUG_STATE_SAVE_TRBE); } void kvm_arch_vcpu_put_debug_state_flags(struct kvm_vcpu *vcpu) { - vcpu->arch.flags &= ~(KVM_ARM64_DEBUG_STATE_SAVE_SPE | - KVM_ARM64_DEBUG_STATE_SAVE_TRBE); + vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_SPE); + vcpu_clear_flag(vcpu, DEBUG_STATE_SAVE_TRBE); } diff --git a/arch/arm64/kvm/fpsimd.c b/arch/arm64/kvm/fpsimd.c index 6012b08ecb14..ec8e4494873d 100644 --- a/arch/arm64/kvm/fpsimd.c +++ b/arch/arm64/kvm/fpsimd.c @@ -77,12 +77,14 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) BUG_ON(!current->mm); BUG_ON(test_thread_flag(TIF_SVE)); - vcpu->arch.flags &= ~KVM_ARM64_FP_ENABLED; - vcpu->arch.flags |= KVM_ARM64_FP_HOST; + if (!system_supports_fpsimd()) + return; - vcpu->arch.flags &= ~KVM_ARM64_HOST_SVE_ENABLED; + vcpu->arch.fp_state = FP_STATE_HOST_OWNED; + + vcpu_clear_flag(vcpu, HOST_SVE_ENABLED); if (read_sysreg(cpacr_el1) & CPACR_EL1_ZEN_EL0EN) - vcpu->arch.flags |= KVM_ARM64_HOST_SVE_ENABLED; + vcpu_set_flag(vcpu, HOST_SVE_ENABLED); /* * We don't currently support SME guests but if we leave @@ -94,29 +96,28 @@ void kvm_arch_vcpu_load_fp(struct kvm_vcpu *vcpu) * operations. Do this for ZA as well for now for simplicity. */ if (system_supports_sme()) { - vcpu->arch.flags &= ~KVM_ARM64_HOST_SME_ENABLED; + vcpu_clear_flag(vcpu, HOST_SME_ENABLED); if (read_sysreg(cpacr_el1) & CPACR_EL1_SMEN_EL0EN) - vcpu->arch.flags |= KVM_ARM64_HOST_SME_ENABLED; + vcpu_set_flag(vcpu, HOST_SME_ENABLED); - if (read_sysreg_s(SYS_SVCR) & - (SVCR_SM_MASK | SVCR_ZA_MASK)) { - vcpu->arch.flags &= ~KVM_ARM64_FP_HOST; + if (read_sysreg_s(SYS_SVCR) & (SVCR_SM_MASK | SVCR_ZA_MASK)) { + vcpu->arch.fp_state = FP_STATE_FREE; fpsimd_save_and_flush_cpu_state(); } } } /* - * Called just before entering the guest once we are no longer - * preemptable. Syncs the host's TIF_FOREIGN_FPSTATE with the KVM - * mirror of the flag used by the hypervisor. + * Called just before entering the guest once we are no longer preemptable + * and interrupts are disabled. If we have managed to run anything using + * FP while we were preemptible (such as off the back of an interrupt), + * then neither the host nor the guest own the FP hardware (and it was the + * responsibility of the code that used FP to save the existing state). */ void kvm_arch_vcpu_ctxflush_fp(struct kvm_vcpu *vcpu) { if (test_thread_flag(TIF_FOREIGN_FPSTATE)) - vcpu->arch.flags |= KVM_ARM64_FP_FOREIGN_FPSTATE; - else - vcpu->arch.flags &= ~KVM_ARM64_FP_FOREIGN_FPSTATE; + vcpu->arch.fp_state = FP_STATE_FREE; } /* @@ -130,7 +131,7 @@ void kvm_arch_vcpu_ctxsync_fp(struct kvm_vcpu *vcpu) { WARN_ON_ONCE(!irqs_disabled()); - if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) { + if (vcpu->arch.fp_state == FP_STATE_GUEST_OWNED) { /* * Currently we do not support SME guests so SVCR is * always 0 and we just need a variable to point to. @@ -163,7 +164,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) */ if (has_vhe() && system_supports_sme()) { /* Also restore EL0 state seen on entry */ - if (vcpu->arch.flags & KVM_ARM64_HOST_SME_ENABLED) + if (vcpu_get_flag(vcpu, HOST_SME_ENABLED)) sysreg_clear_set(CPACR_EL1, 0, CPACR_EL1_SMEN_EL0EN | CPACR_EL1_SMEN_EL1EN); @@ -173,7 +174,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) CPACR_EL1_SMEN_EL1EN); } - if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) { + if (vcpu->arch.fp_state == FP_STATE_GUEST_OWNED) { if (vcpu_has_sve(vcpu)) { __vcpu_sys_reg(vcpu, ZCR_EL1) = read_sysreg_el1(SYS_ZCR); @@ -192,7 +193,7 @@ void kvm_arch_vcpu_put_fp(struct kvm_vcpu *vcpu) * for EL0. To avoid spurious traps, restore the trap state * seen by kvm_arch_vcpu_load_fp(): */ - if (vcpu->arch.flags & KVM_ARM64_HOST_SVE_ENABLED) + if (vcpu_get_flag(vcpu, HOST_SVE_ENABLED)) sysreg_clear_set(CPACR_EL1, 0, CPACR_EL1_ZEN_EL0EN); else sysreg_clear_set(CPACR_EL1, CPACR_EL1_ZEN_EL0EN, 0); diff --git a/arch/arm64/kvm/handle_exit.c b/arch/arm64/kvm/handle_exit.c index f66c0142b335..bbe5b393d689 100644 --- a/arch/arm64/kvm/handle_exit.c +++ b/arch/arm64/kvm/handle_exit.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -120,7 +121,7 @@ static int kvm_handle_wfx(struct kvm_vcpu *vcpu) kvm_vcpu_on_spin(vcpu, vcpu_mode_priv(vcpu)); } else { if (esr & ESR_ELx_WFx_ISS_WFxT) - vcpu->arch.flags |= KVM_ARM64_WFIT; + vcpu_set_flag(vcpu, IN_WFIT); kvm_vcpu_wfi(vcpu); } @@ -347,12 +348,15 @@ void __noreturn __cold nvhe_hyp_panic_handler(u64 esr, u64 spsr, kvm_err("nVHE hyp BUG at: %s:%u!\n", file, line); else kvm_err("nVHE hyp BUG at: [<%016llx>] %pB!\n", panic_addr, - (void *)panic_addr); + (void *)(panic_addr + kaslr_offset())); } else { kvm_err("nVHE hyp panic at: [<%016llx>] %pB!\n", panic_addr, - (void *)panic_addr); + (void *)(panic_addr + kaslr_offset())); } + /* Dump the nVHE hypervisor backtrace */ + kvm_nvhe_dump_backtrace(hyp_offset); + /* * Hyp has panicked and we're going to handle that by panicking the * kernel. The kernel offset will be revealed in the panic so we're diff --git a/arch/arm64/kvm/hyp/exception.c b/arch/arm64/kvm/hyp/exception.c index c5d009715402..b7557b25ed56 100644 --- a/arch/arm64/kvm/hyp/exception.c +++ b/arch/arm64/kvm/hyp/exception.c @@ -303,14 +303,14 @@ static void enter_exception32(struct kvm_vcpu *vcpu, u32 mode, u32 vect_offset) static void kvm_inject_exception(struct kvm_vcpu *vcpu) { if (vcpu_el1_is_32bit(vcpu)) { - switch (vcpu->arch.flags & KVM_ARM64_EXCEPT_MASK) { - case KVM_ARM64_EXCEPT_AA32_UND: + switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) { + case unpack_vcpu_flag(EXCEPT_AA32_UND): enter_exception32(vcpu, PSR_AA32_MODE_UND, 4); break; - case KVM_ARM64_EXCEPT_AA32_IABT: + case unpack_vcpu_flag(EXCEPT_AA32_IABT): enter_exception32(vcpu, PSR_AA32_MODE_ABT, 12); break; - case KVM_ARM64_EXCEPT_AA32_DABT: + case unpack_vcpu_flag(EXCEPT_AA32_DABT): enter_exception32(vcpu, PSR_AA32_MODE_ABT, 16); break; default: @@ -318,9 +318,8 @@ static void kvm_inject_exception(struct kvm_vcpu *vcpu) break; } } else { - switch (vcpu->arch.flags & KVM_ARM64_EXCEPT_MASK) { - case (KVM_ARM64_EXCEPT_AA64_ELx_SYNC | - KVM_ARM64_EXCEPT_AA64_EL1): + switch (vcpu_get_flag(vcpu, EXCEPT_MASK)) { + case unpack_vcpu_flag(EXCEPT_AA64_EL1_SYNC): enter_exception64(vcpu, PSR_MODE_EL1h, except_type_sync); break; default: @@ -340,12 +339,12 @@ static void kvm_inject_exception(struct kvm_vcpu *vcpu) */ void __kvm_adjust_pc(struct kvm_vcpu *vcpu) { - if (vcpu->arch.flags & KVM_ARM64_PENDING_EXCEPTION) { + if (vcpu_get_flag(vcpu, PENDING_EXCEPTION)) { kvm_inject_exception(vcpu); - vcpu->arch.flags &= ~(KVM_ARM64_PENDING_EXCEPTION | - KVM_ARM64_EXCEPT_MASK); - } else if (vcpu->arch.flags & KVM_ARM64_INCREMENT_PC) { + vcpu_clear_flag(vcpu, PENDING_EXCEPTION); + vcpu_clear_flag(vcpu, EXCEPT_MASK); + } else if (vcpu_get_flag(vcpu, INCREMENT_PC)) { kvm_skip_instr(vcpu); - vcpu->arch.flags &= ~KVM_ARM64_INCREMENT_PC; + vcpu_clear_flag(vcpu, INCREMENT_PC); } } diff --git a/arch/arm64/kvm/hyp/include/hyp/debug-sr.h b/arch/arm64/kvm/hyp/include/hyp/debug-sr.h index 4ebe9f558f3a..961bbef104a6 100644 --- a/arch/arm64/kvm/hyp/include/hyp/debug-sr.h +++ b/arch/arm64/kvm/hyp/include/hyp/debug-sr.h @@ -132,7 +132,7 @@ static inline void __debug_switch_to_guest_common(struct kvm_vcpu *vcpu) struct kvm_guest_debug_arch *host_dbg; struct kvm_guest_debug_arch *guest_dbg; - if (!(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY)) + if (!vcpu_get_flag(vcpu, DEBUG_DIRTY)) return; host_ctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; @@ -151,7 +151,7 @@ static inline void __debug_switch_to_host_common(struct kvm_vcpu *vcpu) struct kvm_guest_debug_arch *host_dbg; struct kvm_guest_debug_arch *guest_dbg; - if (!(vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY)) + if (!vcpu_get_flag(vcpu, DEBUG_DIRTY)) return; host_ctxt = &this_cpu_ptr(&kvm_host_data)->host_ctxt; @@ -162,7 +162,7 @@ static inline void __debug_switch_to_host_common(struct kvm_vcpu *vcpu) __debug_save_state(guest_dbg, guest_ctxt); __debug_restore_state(host_dbg, host_ctxt); - vcpu->arch.flags &= ~KVM_ARM64_DEBUG_DIRTY; + vcpu_clear_flag(vcpu, DEBUG_DIRTY); } #endif /* __ARM64_KVM_HYP_DEBUG_SR_H__ */ diff --git a/arch/arm64/kvm/hyp/include/hyp/switch.h b/arch/arm64/kvm/hyp/include/hyp/switch.h index 37d9f211c200..6cbbb6c02f66 100644 --- a/arch/arm64/kvm/hyp/include/hyp/switch.h +++ b/arch/arm64/kvm/hyp/include/hyp/switch.h @@ -37,22 +37,10 @@ struct kvm_exception_table_entry { extern struct kvm_exception_table_entry __start___kvm_ex_table; extern struct kvm_exception_table_entry __stop___kvm_ex_table; -/* Check whether the FP regs were dirtied while in the host-side run loop: */ -static inline bool update_fp_enabled(struct kvm_vcpu *vcpu) +/* Check whether the FP regs are owned by the guest */ +static inline bool guest_owns_fp_regs(struct kvm_vcpu *vcpu) { - /* - * When the system doesn't support FP/SIMD, we cannot rely on - * the _TIF_FOREIGN_FPSTATE flag. However, we always inject an - * abort on the very first access to FP and thus we should never - * see KVM_ARM64_FP_ENABLED. For added safety, make sure we always - * trap the accesses. - */ - if (!system_supports_fpsimd() || - vcpu->arch.flags & KVM_ARM64_FP_FOREIGN_FPSTATE) - vcpu->arch.flags &= ~(KVM_ARM64_FP_ENABLED | - KVM_ARM64_FP_HOST); - - return !!(vcpu->arch.flags & KVM_ARM64_FP_ENABLED); + return vcpu->arch.fp_state == FP_STATE_GUEST_OWNED; } /* Save the 32-bit only FPSIMD system register state */ @@ -191,10 +179,8 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) isb(); /* Write out the host state if it's in the registers */ - if (vcpu->arch.flags & KVM_ARM64_FP_HOST) { + if (vcpu->arch.fp_state == FP_STATE_HOST_OWNED) __fpsimd_save_state(vcpu->arch.host_fpsimd_state); - vcpu->arch.flags &= ~KVM_ARM64_FP_HOST; - } /* Restore the guest state */ if (sve_guest) @@ -206,7 +192,7 @@ static bool kvm_hyp_handle_fpsimd(struct kvm_vcpu *vcpu, u64 *exit_code) if (!(read_sysreg(hcr_el2) & HCR_RW)) write_sysreg(__vcpu_sys_reg(vcpu, FPEXC32_EL2), fpexc32_el2); - vcpu->arch.flags |= KVM_ARM64_FP_ENABLED; + vcpu->arch.fp_state = FP_STATE_GUEST_OWNED; return true; } diff --git a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h index 7ecca8b07851..baa5b9b3dde5 100644 --- a/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h +++ b/arch/arm64/kvm/hyp/include/hyp/sysreg-sr.h @@ -195,7 +195,7 @@ static inline void __sysreg32_save_state(struct kvm_vcpu *vcpu) __vcpu_sys_reg(vcpu, DACR32_EL2) = read_sysreg(dacr32_el2); __vcpu_sys_reg(vcpu, IFSR32_EL2) = read_sysreg(ifsr32_el2); - if (has_vhe() || vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY) + if (has_vhe() || vcpu_get_flag(vcpu, DEBUG_DIRTY)) __vcpu_sys_reg(vcpu, DBGVCR32_EL2) = read_sysreg(dbgvcr32_el2); } @@ -212,7 +212,7 @@ static inline void __sysreg32_restore_state(struct kvm_vcpu *vcpu) write_sysreg(__vcpu_sys_reg(vcpu, DACR32_EL2), dacr32_el2); write_sysreg(__vcpu_sys_reg(vcpu, IFSR32_EL2), ifsr32_el2); - if (has_vhe() || vcpu->arch.flags & KVM_ARM64_DEBUG_DIRTY) + if (has_vhe() || vcpu_get_flag(vcpu, DEBUG_DIRTY)) write_sysreg(__vcpu_sys_reg(vcpu, DBGVCR32_EL2), dbgvcr32_el2); } diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile index f9fe4dc21b1f..ed5d222e2826 100644 --- a/arch/arm64/kvm/hyp/nvhe/Makefile +++ b/arch/arm64/kvm/hyp/nvhe/Makefile @@ -12,13 +12,13 @@ HOST_EXTRACFLAGS += -I$(objtree)/include lib-objs := clear_page.o copy_page.o memcpy.o memset.o lib-objs := $(addprefix ../../../lib/, $(lib-objs)) -obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \ +hyp-obj-y := timer-sr.o sysreg-sr.o debug-sr.o switch.o tlb.o hyp-init.o host.o \ hyp-main.o hyp-smp.o psci-relay.o early_alloc.o page_alloc.o \ - cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o -obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ + cache.o setup.o mm.o mem_protect.o sys_regs.o pkvm.o stacktrace.o +hyp-obj-y += ../vgic-v3-sr.o ../aarch32.o ../vgic-v2-cpuif-proxy.o ../entry.o \ ../fpsimd.o ../hyp-entry.o ../exception.o ../pgtable.o -obj-$(CONFIG_DEBUG_LIST) += list_debug.o -obj-y += $(lib-objs) +hyp-obj-$(CONFIG_DEBUG_LIST) += list_debug.o +hyp-obj-y += $(lib-objs) ## ## Build rules for compiling nVHE hyp code @@ -26,9 +26,9 @@ obj-y += $(lib-objs) ## file containing all nVHE hyp code and data. ## -hyp-obj := $(patsubst %.o,%.nvhe.o,$(obj-y)) +hyp-obj := $(patsubst %.o,%.nvhe.o,$(hyp-obj-y)) obj-y := kvm_nvhe.o -extra-y := $(hyp-obj) kvm_nvhe.tmp.o kvm_nvhe.rel.o hyp.lds hyp-reloc.S hyp-reloc.o +targets += $(hyp-obj) kvm_nvhe.tmp.o kvm_nvhe.rel.o hyp.lds hyp-reloc.S hyp-reloc.o # 1) Compile all source files to `.nvhe.o` object files. The file extension # avoids file name clashes for files shared with VHE. diff --git a/arch/arm64/kvm/hyp/nvhe/debug-sr.c b/arch/arm64/kvm/hyp/nvhe/debug-sr.c index df361d839902..e17455773b98 100644 --- a/arch/arm64/kvm/hyp/nvhe/debug-sr.c +++ b/arch/arm64/kvm/hyp/nvhe/debug-sr.c @@ -84,10 +84,10 @@ static void __debug_restore_trace(u64 trfcr_el1) void __debug_save_host_buffers_nvhe(struct kvm_vcpu *vcpu) { /* Disable and flush SPE data generation */ - if (vcpu->arch.flags & KVM_ARM64_DEBUG_STATE_SAVE_SPE) + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE)) __debug_save_spe(&vcpu->arch.host_debug_state.pmscr_el1); /* Disable and flush Self-Hosted Trace generation */ - if (vcpu->arch.flags & KVM_ARM64_DEBUG_STATE_SAVE_TRBE) + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE)) __debug_save_trace(&vcpu->arch.host_debug_state.trfcr_el1); } @@ -98,9 +98,9 @@ void __debug_switch_to_guest(struct kvm_vcpu *vcpu) void __debug_restore_host_buffers_nvhe(struct kvm_vcpu *vcpu) { - if (vcpu->arch.flags & KVM_ARM64_DEBUG_STATE_SAVE_SPE) + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_SPE)) __debug_restore_spe(vcpu->arch.host_debug_state.pmscr_el1); - if (vcpu->arch.flags & KVM_ARM64_DEBUG_STATE_SAVE_TRBE) + if (vcpu_get_flag(vcpu, DEBUG_STATE_SAVE_TRBE)) __debug_restore_trace(vcpu->arch.host_debug_state.trfcr_el1); } diff --git a/arch/arm64/kvm/hyp/nvhe/host.S b/arch/arm64/kvm/hyp/nvhe/host.S index ea6a397b64a6..b6c0188c4b35 100644 --- a/arch/arm64/kvm/hyp/nvhe/host.S +++ b/arch/arm64/kvm/hyp/nvhe/host.S @@ -177,13 +177,8 @@ SYM_FUNC_END(__host_hvc) b hyp_panic .L__hyp_sp_overflow\@: - /* - * Reset SP to the top of the stack, to allow handling the hyp_panic. - * This corrupts the stack but is ok, since we won't be attempting - * any unwinding here. - */ - ldr_this_cpu x0, kvm_init_params + NVHE_INIT_STACK_HYP_VA, x1 - mov sp, x0 + /* Switch to the overflow stack */ + adr_this_cpu sp, overflow_stack + OVERFLOW_STACK_SIZE, x0 b hyp_panic_bad_stack ASM_BUG() diff --git a/arch/arm64/kvm/hyp/nvhe/stacktrace.c b/arch/arm64/kvm/hyp/nvhe/stacktrace.c new file mode 100644 index 000000000000..58f645ad66bc --- /dev/null +++ b/arch/arm64/kvm/hyp/nvhe/stacktrace.c @@ -0,0 +1,160 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * KVM nVHE hypervisor stack tracing support. + * + * Copyright (C) 2022 Google LLC + */ +#include +#include +#include +#include + +DEFINE_PER_CPU(unsigned long [OVERFLOW_STACK_SIZE/sizeof(long)], overflow_stack) + __aligned(16); + +DEFINE_PER_CPU(struct kvm_nvhe_stacktrace_info, kvm_stacktrace_info); + +/* + * hyp_prepare_backtrace - Prepare non-protected nVHE backtrace. + * + * @fp : frame pointer at which to start the unwinding. + * @pc : program counter at which to start the unwinding. + * + * Save the information needed by the host to unwind the non-protected + * nVHE hypervisor stack in EL1. + */ +static void hyp_prepare_backtrace(unsigned long fp, unsigned long pc) +{ + struct kvm_nvhe_stacktrace_info *stacktrace_info = this_cpu_ptr(&kvm_stacktrace_info); + struct kvm_nvhe_init_params *params = this_cpu_ptr(&kvm_init_params); + + stacktrace_info->stack_base = (unsigned long)(params->stack_hyp_va - PAGE_SIZE); + stacktrace_info->overflow_stack_base = (unsigned long)this_cpu_ptr(overflow_stack); + stacktrace_info->fp = fp; + stacktrace_info->pc = pc; +} + +#ifdef CONFIG_PROTECTED_NVHE_STACKTRACE +#include + +DEFINE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], pkvm_stacktrace); + +static bool on_overflow_stack(unsigned long sp, unsigned long size, + struct stack_info *info) +{ + unsigned long low = (unsigned long)this_cpu_ptr(overflow_stack); + unsigned long high = low + OVERFLOW_STACK_SIZE; + + return on_stack(sp, size, low, high, STACK_TYPE_OVERFLOW, info); +} + +static bool on_hyp_stack(unsigned long sp, unsigned long size, + struct stack_info *info) +{ + struct kvm_nvhe_init_params *params = this_cpu_ptr(&kvm_init_params); + unsigned long high = params->stack_hyp_va; + unsigned long low = high - PAGE_SIZE; + + return on_stack(sp, size, low, high, STACK_TYPE_HYP, info); +} + +static bool on_accessible_stack(const struct task_struct *tsk, + unsigned long sp, unsigned long size, + struct stack_info *info) +{ + if (info) + info->type = STACK_TYPE_UNKNOWN; + + return (on_overflow_stack(sp, size, info) || + on_hyp_stack(sp, size, info)); +} + +static int unwind_next(struct unwind_state *state) +{ + struct stack_info info; + + return unwind_next_common(state, &info, on_accessible_stack, NULL); +} + +static void notrace unwind(struct unwind_state *state, + stack_trace_consume_fn consume_entry, + void *cookie) +{ + while (1) { + int ret; + + if (!consume_entry(cookie, state->pc)) + break; + ret = unwind_next(state); + if (ret < 0) + break; + } +} + +/* + * pkvm_save_backtrace_entry - Saves a protected nVHE HYP stacktrace entry + * + * @arg : index of the entry in the stacktrace buffer + * @where : the program counter corresponding to the stack frame + * + * Save the return address of a stack frame to the shared stacktrace buffer. + * The host can access this shared buffer from EL1 to dump the backtrace. + */ +static bool pkvm_save_backtrace_entry(void *arg, unsigned long where) +{ + unsigned long *stacktrace = this_cpu_ptr(pkvm_stacktrace); + int *idx = (int *)arg; + + /* + * Need 2 free slots: 1 for current entry and 1 for the + * delimiter. + */ + if (*idx > ARRAY_SIZE(pkvm_stacktrace) - 2) + return false; + + stacktrace[*idx] = where; + stacktrace[++*idx] = 0UL; + + return true; +} + +/* + * pkvm_save_backtrace - Saves the protected nVHE HYP stacktrace + * + * @fp : frame pointer at which to start the unwinding. + * @pc : program counter at which to start the unwinding. + * + * Save the unwinded stack addresses to the shared stacktrace buffer. + * The host can access this shared buffer from EL1 to dump the backtrace. + */ +static void pkvm_save_backtrace(unsigned long fp, unsigned long pc) +{ + struct unwind_state state; + int idx = 0; + + kvm_nvhe_unwind_init(&state, fp, pc); + + unwind(&state, pkvm_save_backtrace_entry, &idx); +} +#else /* !CONFIG_PROTECTED_NVHE_STACKTRACE */ +static void pkvm_save_backtrace(unsigned long fp, unsigned long pc) +{ +} +#endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */ + +/* + * kvm_nvhe_prepare_backtrace - prepare to dump the nVHE backtrace + * + * @fp : frame pointer at which to start the unwinding. + * @pc : program counter at which to start the unwinding. + * + * Saves the information needed by the host to dump the nVHE hypervisor + * backtrace. + */ +void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc) +{ + if (is_protected_kvm_enabled()) + pkvm_save_backtrace(fp, pc); + else + hyp_prepare_backtrace(fp, pc); +} diff --git a/arch/arm64/kvm/hyp/nvhe/switch.c b/arch/arm64/kvm/hyp/nvhe/switch.c index 6db801db8f27..9f6385702061 100644 --- a/arch/arm64/kvm/hyp/nvhe/switch.c +++ b/arch/arm64/kvm/hyp/nvhe/switch.c @@ -34,6 +34,8 @@ DEFINE_PER_CPU(struct kvm_host_data, kvm_host_data); DEFINE_PER_CPU(struct kvm_cpu_context, kvm_hyp_ctxt); DEFINE_PER_CPU(unsigned long, kvm_hyp_vector); +extern void kvm_nvhe_prepare_backtrace(unsigned long fp, unsigned long pc); + static void __activate_traps(struct kvm_vcpu *vcpu) { u64 val; @@ -43,7 +45,7 @@ static void __activate_traps(struct kvm_vcpu *vcpu) val = vcpu->arch.cptr_el2; val |= CPTR_EL2_TTA | CPTR_EL2_TAM; - if (!update_fp_enabled(vcpu)) { + if (!guest_owns_fp_regs(vcpu)) { val |= CPTR_EL2_TFP | CPTR_EL2_TZ; __activate_traps_fpsimd32(vcpu); } @@ -123,7 +125,7 @@ static void __deactivate_traps(struct kvm_vcpu *vcpu) } cptr = CPTR_EL2_DEFAULT; - if (vcpu_has_sve(vcpu) && (vcpu->arch.flags & KVM_ARM64_FP_ENABLED)) + if (vcpu_has_sve(vcpu) && (vcpu->arch.fp_state == FP_STATE_GUEST_OWNED)) cptr |= CPTR_EL2_TZ; if (cpus_have_final_cap(ARM64_SME)) cptr &= ~CPTR_EL2_TSM; @@ -335,7 +337,7 @@ int __kvm_vcpu_run(struct kvm_vcpu *vcpu) __sysreg_restore_state_nvhe(host_ctxt); - if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) + if (vcpu->arch.fp_state == FP_STATE_GUEST_OWNED) __fpsimd_save_fpexc32(vcpu); __debug_switch_to_host(vcpu); @@ -375,6 +377,10 @@ asmlinkage void __noreturn hyp_panic(void) __sysreg_restore_state_nvhe(host_ctxt); } + /* Prepare to dump kvm nvhe hyp stacktrace */ + kvm_nvhe_prepare_backtrace((unsigned long)__builtin_frame_address(0), + _THIS_IP_); + __hyp_do_panic(host_ctxt, spsr, elr, par); unreachable(); } @@ -386,5 +392,5 @@ asmlinkage void __noreturn hyp_panic_bad_stack(void) asmlinkage void kvm_unexpected_el2_exception(void) { - return __kvm_unexpected_el2_exception(); + __kvm_unexpected_el2_exception(); } diff --git a/arch/arm64/kvm/hyp/nvhe/sys_regs.c b/arch/arm64/kvm/hyp/nvhe/sys_regs.c index 6b94c3e6ff26..e20fa4475dac 100644 --- a/arch/arm64/kvm/hyp/nvhe/sys_regs.c +++ b/arch/arm64/kvm/hyp/nvhe/sys_regs.c @@ -38,9 +38,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu) *vcpu_pc(vcpu) = read_sysreg_el2(SYS_ELR); *vcpu_cpsr(vcpu) = read_sysreg_el2(SYS_SPSR); - vcpu->arch.flags |= (KVM_ARM64_EXCEPT_AA64_EL1 | - KVM_ARM64_EXCEPT_AA64_ELx_SYNC | - KVM_ARM64_PENDING_EXCEPTION); + kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); __kvm_adjust_pc(vcpu); diff --git a/arch/arm64/kvm/hyp/vhe/switch.c b/arch/arm64/kvm/hyp/vhe/switch.c index 969f20daf97a..7acb87eaa092 100644 --- a/arch/arm64/kvm/hyp/vhe/switch.c +++ b/arch/arm64/kvm/hyp/vhe/switch.c @@ -55,7 +55,7 @@ static void __activate_traps(struct kvm_vcpu *vcpu) val |= CPTR_EL2_TAM; - if (update_fp_enabled(vcpu)) { + if (guest_owns_fp_regs(vcpu)) { if (vcpu_has_sve(vcpu)) val |= CPACR_EL1_ZEN_EL0EN | CPACR_EL1_ZEN_EL1EN; } else { @@ -175,7 +175,7 @@ static int __kvm_vcpu_run_vhe(struct kvm_vcpu *vcpu) sysreg_restore_host_state_vhe(host_ctxt); - if (vcpu->arch.flags & KVM_ARM64_FP_ENABLED) + if (vcpu->arch.fp_state == FP_STATE_GUEST_OWNED) __fpsimd_save_fpexc32(vcpu); __debug_switch_to_host(vcpu); @@ -249,5 +249,5 @@ void __noreturn hyp_panic(void) asmlinkage void kvm_unexpected_el2_exception(void) { - return __kvm_unexpected_el2_exception(); + __kvm_unexpected_el2_exception(); } diff --git a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c index 007a12dd4351..7b44f6b3b547 100644 --- a/arch/arm64/kvm/hyp/vhe/sysreg-sr.c +++ b/arch/arm64/kvm/hyp/vhe/sysreg-sr.c @@ -79,7 +79,7 @@ void kvm_vcpu_load_sysregs_vhe(struct kvm_vcpu *vcpu) __sysreg_restore_user_state(guest_ctxt); __sysreg_restore_el1_state(guest_ctxt); - vcpu->arch.sysregs_loaded_on_cpu = true; + vcpu_set_flag(vcpu, SYSREGS_ON_CPU); activate_traps_vhe_load(vcpu); } @@ -110,5 +110,5 @@ void kvm_vcpu_put_sysregs_vhe(struct kvm_vcpu *vcpu) /* Restore host user state */ __sysreg_restore_user_state(host_ctxt); - vcpu->arch.sysregs_loaded_on_cpu = false; + vcpu_clear_flag(vcpu, SYSREGS_ON_CPU); } diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index 55a5dbe957e0..f32f4a2a347f 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -20,9 +20,7 @@ static void inject_abt64(struct kvm_vcpu *vcpu, bool is_iabt, unsigned long addr bool is_aarch32 = vcpu_mode_is_32bit(vcpu); u64 esr = 0; - vcpu->arch.flags |= (KVM_ARM64_EXCEPT_AA64_EL1 | - KVM_ARM64_EXCEPT_AA64_ELx_SYNC | - KVM_ARM64_PENDING_EXCEPTION); + kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); vcpu_write_sys_reg(vcpu, addr, FAR_EL1); @@ -52,9 +50,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu) { u64 esr = (ESR_ELx_EC_UNKNOWN << ESR_ELx_EC_SHIFT); - vcpu->arch.flags |= (KVM_ARM64_EXCEPT_AA64_EL1 | - KVM_ARM64_EXCEPT_AA64_ELx_SYNC | - KVM_ARM64_PENDING_EXCEPTION); + kvm_pend_exception(vcpu, EXCEPT_AA64_EL1_SYNC); /* * Build an unknown exception, depending on the instruction @@ -73,8 +69,7 @@ static void inject_undef64(struct kvm_vcpu *vcpu) static void inject_undef32(struct kvm_vcpu *vcpu) { - vcpu->arch.flags |= (KVM_ARM64_EXCEPT_AA32_UND | - KVM_ARM64_PENDING_EXCEPTION); + kvm_pend_exception(vcpu, EXCEPT_AA32_UND); } /* @@ -97,14 +92,12 @@ static void inject_abt32(struct kvm_vcpu *vcpu, bool is_pabt, u32 addr) far = vcpu_read_sys_reg(vcpu, FAR_EL1); if (is_pabt) { - vcpu->arch.flags |= (KVM_ARM64_EXCEPT_AA32_IABT | - KVM_ARM64_PENDING_EXCEPTION); + kvm_pend_exception(vcpu, EXCEPT_AA32_IABT); far &= GENMASK(31, 0); far |= (u64)addr << 32; vcpu_write_sys_reg(vcpu, fsr, IFSR32_EL2); } else { /* !iabt */ - vcpu->arch.flags |= (KVM_ARM64_EXCEPT_AA32_DABT | - KVM_ARM64_PENDING_EXCEPTION); + kvm_pend_exception(vcpu, EXCEPT_AA32_DABT); far &= GENMASK(63, 32); far |= addr; vcpu_write_sys_reg(vcpu, fsr, ESR_EL1); diff --git a/arch/arm64/kvm/mmu.c b/arch/arm64/kvm/mmu.c index f5651a05b6a8..87f1cd0df36e 100644 --- a/arch/arm64/kvm/mmu.c +++ b/arch/arm64/kvm/mmu.c @@ -786,7 +786,7 @@ int kvm_phys_addr_ioremap(struct kvm *kvm, phys_addr_t guest_ipa, { phys_addr_t addr; int ret = 0; - struct kvm_mmu_memory_cache cache = { 0, __GFP_ZERO, NULL, }; + struct kvm_mmu_memory_cache cache = { .gfp_zero = __GFP_ZERO }; struct kvm_pgtable *pgt = kvm->arch.mmu.pgt; enum kvm_pgtable_prot prot = KVM_PGTABLE_PROT_DEVICE | KVM_PGTABLE_PROT_R | diff --git a/arch/arm64/kvm/reset.c b/arch/arm64/kvm/reset.c index 6c70c6f61c70..0e08fbe68715 100644 --- a/arch/arm64/kvm/reset.c +++ b/arch/arm64/kvm/reset.c @@ -81,7 +81,7 @@ static int kvm_vcpu_enable_sve(struct kvm_vcpu *vcpu) * KVM_REG_ARM64_SVE_VLS. Allocation is deferred until * kvm_arm_vcpu_finalize(), which freezes the configuration. */ - vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_SVE; + vcpu_set_flag(vcpu, GUEST_HAS_SVE); return 0; } @@ -120,7 +120,7 @@ static int kvm_vcpu_finalize_sve(struct kvm_vcpu *vcpu) } vcpu->arch.sve_state = buf; - vcpu->arch.flags |= KVM_ARM64_VCPU_SVE_FINALIZED; + vcpu_set_flag(vcpu, VCPU_SVE_FINALIZED); return 0; } @@ -177,7 +177,7 @@ static int kvm_vcpu_enable_ptrauth(struct kvm_vcpu *vcpu) !system_has_full_ptr_auth()) return -EINVAL; - vcpu->arch.flags |= KVM_ARM64_GUEST_HAS_PTRAUTH; + vcpu_set_flag(vcpu, GUEST_HAS_PTRAUTH); return 0; } diff --git a/arch/arm64/kvm/stacktrace.c b/arch/arm64/kvm/stacktrace.c new file mode 100644 index 000000000000..949d19d603fb --- /dev/null +++ b/arch/arm64/kvm/stacktrace.c @@ -0,0 +1,218 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * KVM nVHE hypervisor stack tracing support. + * + * The unwinder implementation depends on the nVHE mode: + * + * 1) Non-protected nVHE mode - the host can directly access the + * HYP stack pages and unwind the HYP stack in EL1. This saves having + * to allocate shared buffers for the host to read the unwinded + * stacktrace. + * + * 2) pKVM (protected nVHE) mode - the host cannot directly access + * the HYP memory. The stack is unwinded in EL2 and dumped to a shared + * buffer where the host can read and print the stacktrace. + * + * Copyright (C) 2022 Google LLC + */ + +#include +#include + +#include + +/* + * kvm_nvhe_stack_kern_va - Convert KVM nVHE HYP stack addresses to a kernel VAs + * + * The nVHE hypervisor stack is mapped in the flexible 'private' VA range, to + * allow for guard pages below the stack. Consequently, the fixed offset address + * translation macros won't work here. + * + * The kernel VA is calculated as an offset from the kernel VA of the hypervisor + * stack base. + * + * Returns true on success and updates @addr to its corresponding kernel VA; + * otherwise returns false. + */ +static bool kvm_nvhe_stack_kern_va(unsigned long *addr, + enum stack_type type) +{ + struct kvm_nvhe_stacktrace_info *stacktrace_info; + unsigned long hyp_base, kern_base, hyp_offset; + + stacktrace_info = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); + + switch (type) { + case STACK_TYPE_HYP: + kern_base = (unsigned long)*this_cpu_ptr(&kvm_arm_hyp_stack_page); + hyp_base = (unsigned long)stacktrace_info->stack_base; + break; + case STACK_TYPE_OVERFLOW: + kern_base = (unsigned long)this_cpu_ptr_nvhe_sym(overflow_stack); + hyp_base = (unsigned long)stacktrace_info->overflow_stack_base; + break; + default: + return false; + } + + hyp_offset = *addr - hyp_base; + + *addr = kern_base + hyp_offset; + + return true; +} + +static bool on_overflow_stack(unsigned long sp, unsigned long size, + struct stack_info *info) +{ + struct kvm_nvhe_stacktrace_info *stacktrace_info + = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); + unsigned long low = (unsigned long)stacktrace_info->overflow_stack_base; + unsigned long high = low + OVERFLOW_STACK_SIZE; + + return on_stack(sp, size, low, high, STACK_TYPE_OVERFLOW, info); +} + +static bool on_hyp_stack(unsigned long sp, unsigned long size, + struct stack_info *info) +{ + struct kvm_nvhe_stacktrace_info *stacktrace_info + = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); + unsigned long low = (unsigned long)stacktrace_info->stack_base; + unsigned long high = low + PAGE_SIZE; + + return on_stack(sp, size, low, high, STACK_TYPE_HYP, info); +} + +static bool on_accessible_stack(const struct task_struct *tsk, + unsigned long sp, unsigned long size, + struct stack_info *info) +{ + if (info) + info->type = STACK_TYPE_UNKNOWN; + + return (on_overflow_stack(sp, size, info) || + on_hyp_stack(sp, size, info)); +} + +static int unwind_next(struct unwind_state *state) +{ + struct stack_info info; + + return unwind_next_common(state, &info, on_accessible_stack, + kvm_nvhe_stack_kern_va); +} + +static void unwind(struct unwind_state *state, + stack_trace_consume_fn consume_entry, void *cookie) +{ + while (1) { + int ret; + + if (!consume_entry(cookie, state->pc)) + break; + ret = unwind_next(state); + if (ret < 0) + break; + } +} + +/* + * kvm_nvhe_dump_backtrace_entry - Symbolize and print an nVHE backtrace entry + * + * @arg : the hypervisor offset, used for address translation + * @where : the program counter corresponding to the stack frame + */ +static bool kvm_nvhe_dump_backtrace_entry(void *arg, unsigned long where) +{ + unsigned long va_mask = GENMASK_ULL(vabits_actual - 1, 0); + unsigned long hyp_offset = (unsigned long)arg; + + /* Mask tags and convert to kern addr */ + where = (where & va_mask) + hyp_offset; + kvm_err(" [<%016lx>] %pB\n", where, (void *)(where + kaslr_offset())); + + return true; +} + +static void kvm_nvhe_dump_backtrace_start(void) +{ + kvm_err("nVHE call trace:\n"); +} + +static void kvm_nvhe_dump_backtrace_end(void) +{ + kvm_err("---[ end nVHE call trace ]---\n"); +} + +/* + * hyp_dump_backtrace - Dump the non-protected nVHE backtrace. + * + * @hyp_offset: hypervisor offset, used for address translation. + * + * The host can directly access HYP stack pages in non-protected + * mode, so the unwinding is done directly from EL1. This removes + * the need for shared buffers between host and hypervisor for + * the stacktrace. + */ +static void hyp_dump_backtrace(unsigned long hyp_offset) +{ + struct kvm_nvhe_stacktrace_info *stacktrace_info; + struct unwind_state state; + + stacktrace_info = this_cpu_ptr_nvhe_sym(kvm_stacktrace_info); + + kvm_nvhe_unwind_init(&state, stacktrace_info->fp, stacktrace_info->pc); + + kvm_nvhe_dump_backtrace_start(); + unwind(&state, kvm_nvhe_dump_backtrace_entry, (void *)hyp_offset); + kvm_nvhe_dump_backtrace_end(); +} + +#ifdef CONFIG_PROTECTED_NVHE_STACKTRACE +DECLARE_KVM_NVHE_PER_CPU(unsigned long [NVHE_STACKTRACE_SIZE/sizeof(long)], + pkvm_stacktrace); + +/* + * pkvm_dump_backtrace - Dump the protected nVHE HYP backtrace. + * + * @hyp_offset: hypervisor offset, used for address translation. + * + * Dumping of the pKVM HYP backtrace is done by reading the + * stack addresses from the shared stacktrace buffer, since the + * host cannot directly access hypervisor memory in protected + * mode. + */ +static void pkvm_dump_backtrace(unsigned long hyp_offset) +{ + unsigned long *stacktrace + = (unsigned long *) this_cpu_ptr_nvhe_sym(pkvm_stacktrace); + int i; + + kvm_nvhe_dump_backtrace_start(); + /* The saved stacktrace is terminated by a null entry */ + for (i = 0; + i < ARRAY_SIZE(kvm_nvhe_sym(pkvm_stacktrace)) && stacktrace[i]; + i++) + kvm_nvhe_dump_backtrace_entry((void *)hyp_offset, stacktrace[i]); + kvm_nvhe_dump_backtrace_end(); +} +#else /* !CONFIG_PROTECTED_NVHE_STACKTRACE */ +static void pkvm_dump_backtrace(unsigned long hyp_offset) +{ + kvm_err("Cannot dump pKVM nVHE stacktrace: !CONFIG_PROTECTED_NVHE_STACKTRACE\n"); +} +#endif /* CONFIG_PROTECTED_NVHE_STACKTRACE */ + +/* + * kvm_nvhe_dump_backtrace - Dump KVM nVHE hypervisor backtrace. + * + * @hyp_offset: hypervisor offset, used for address translation. + */ +void kvm_nvhe_dump_backtrace(unsigned long hyp_offset) +{ + if (is_protected_kvm_enabled()) + pkvm_dump_backtrace(hyp_offset); + else + hyp_dump_backtrace(hyp_offset); +} diff --git a/arch/arm64/kvm/sys_regs.c b/arch/arm64/kvm/sys_regs.c index c4fb3874b5e2..c059b259aea6 100644 --- a/arch/arm64/kvm/sys_regs.c +++ b/arch/arm64/kvm/sys_regs.c @@ -34,18 +34,11 @@ #include "trace.h" /* - * All of this file is extremely similar to the ARM coproc.c, but the - * types are different. My gut feeling is that it should be pretty - * easy to merge, but that would be an ABI breakage -- again. VFP - * would also need to be abstracted. - * * For AArch32, we only take care of what is being trapped. Anything * that has to do with init and userspace access has to go via the * 64bit interface. */ -static int reg_from_user(u64 *val, const void __user *uaddr, u64 id); -static int reg_to_user(void __user *uaddr, const u64 *val, u64 id); static u64 sys_reg_to_index(const struct sys_reg_desc *reg); static bool read_from_write_only(struct kvm_vcpu *vcpu, @@ -72,7 +65,7 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) { u64 val = 0x8badf00d8badf00d; - if (vcpu->arch.sysregs_loaded_on_cpu && + if (vcpu_get_flag(vcpu, SYSREGS_ON_CPU) && __vcpu_read_sys_reg_from_cpu(reg, &val)) return val; @@ -81,7 +74,7 @@ u64 vcpu_read_sys_reg(const struct kvm_vcpu *vcpu, int reg) void vcpu_write_sys_reg(struct kvm_vcpu *vcpu, u64 val, int reg) { - if (vcpu->arch.sysregs_loaded_on_cpu && + if (vcpu_get_flag(vcpu, SYSREGS_ON_CPU) && __vcpu_write_sys_reg_to_cpu(val, reg)) return; @@ -321,16 +314,8 @@ static bool trap_oslsr_el1(struct kvm_vcpu *vcpu, } static int set_oslsr_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - u64 id = sys_reg_to_index(rd); - u64 val; - int err; - - err = reg_from_user(&val, uaddr, id); - if (err) - return err; - /* * The only modifiable bit is the OSLK bit. Refuse the write if * userspace attempts to change any other bit in the register. @@ -387,7 +372,7 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu, { if (p->is_write) { vcpu_write_sys_reg(vcpu, p->regval, r->reg); - vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY; + vcpu_set_flag(vcpu, DEBUG_DIRTY); } else { p->regval = vcpu_read_sys_reg(vcpu, r->reg); } @@ -403,8 +388,8 @@ static bool trap_debug_regs(struct kvm_vcpu *vcpu, * A 32 bit write to a debug register leave top bits alone * A 32 bit read from a debug register only returns the bottom bits * - * All writes will set the KVM_ARM64_DEBUG_DIRTY flag to ensure the - * hyp.S code switches between host and guest values in future. + * All writes will set the DEBUG_DIRTY flag to ensure the hyp code + * switches between host and guest values in future. */ static void reg_to_dbg(struct kvm_vcpu *vcpu, struct sys_reg_params *p, @@ -420,7 +405,7 @@ static void reg_to_dbg(struct kvm_vcpu *vcpu, val |= (p->regval & (mask >> shift)) << shift; *dbg_reg = val; - vcpu->arch.flags |= KVM_ARM64_DEBUG_DIRTY; + vcpu_set_flag(vcpu, DEBUG_DIRTY); } static void dbg_to_reg(struct kvm_vcpu *vcpu, @@ -451,22 +436,16 @@ static bool trap_bvr(struct kvm_vcpu *vcpu, } static int set_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm]; - - if (copy_from_user(r, uaddr, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm] = val; return 0; } static int get_bvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 *val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm]; - - if (copy_to_user(uaddr, r, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + *val = vcpu->arch.vcpu_debug_state.dbg_bvr[rd->CRm]; return 0; } @@ -493,23 +472,16 @@ static bool trap_bcr(struct kvm_vcpu *vcpu, } static int set_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm]; - - if (copy_from_user(r, uaddr, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; - + vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm] = val; return 0; } static int get_bcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 *val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm]; - - if (copy_to_user(uaddr, r, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + *val = vcpu->arch.vcpu_debug_state.dbg_bcr[rd->CRm]; return 0; } @@ -537,22 +509,16 @@ static bool trap_wvr(struct kvm_vcpu *vcpu, } static int set_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm]; - - if (copy_from_user(r, uaddr, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm] = val; return 0; } static int get_wvr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 *val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm]; - - if (copy_to_user(uaddr, r, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + *val = vcpu->arch.vcpu_debug_state.dbg_wvr[rd->CRm]; return 0; } @@ -579,22 +545,16 @@ static bool trap_wcr(struct kvm_vcpu *vcpu, } static int set_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm]; - - if (copy_from_user(r, uaddr, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm] = val; return 0; } static int get_wcr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 *val) { - __u64 *r = &vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm]; - - if (copy_to_user(uaddr, r, KVM_REG_SIZE(reg->id)) != 0) - return -EFAULT; + *val = vcpu->arch.vcpu_debug_state.dbg_wcr[rd->CRm]; return 0; } @@ -1227,16 +1187,9 @@ static unsigned int sve_visibility(const struct kvm_vcpu *vcpu, static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - const u64 id = sys_reg_to_index(rd); u8 csv2, csv3; - int err; - u64 val; - - err = reg_from_user(&val, uaddr, id); - if (err) - return err; /* * Allow AA64PFR0_EL1.CSV2 to be set from userspace as long as @@ -1262,7 +1215,7 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, return -EINVAL; vcpu->kvm->arch.pfr0_csv2 = csv2; - vcpu->kvm->arch.pfr0_csv3 = csv3 ; + vcpu->kvm->arch.pfr0_csv3 = csv3; return 0; } @@ -1275,27 +1228,17 @@ static int set_id_aa64pfr0_el1(struct kvm_vcpu *vcpu, * to be changed. */ static int __get_id_reg(const struct kvm_vcpu *vcpu, - const struct sys_reg_desc *rd, void __user *uaddr, + const struct sys_reg_desc *rd, u64 *val, bool raz) { - const u64 id = sys_reg_to_index(rd); - const u64 val = read_id_reg(vcpu, rd, raz); - - return reg_to_user(uaddr, &val, id); + *val = read_id_reg(vcpu, rd, raz); + return 0; } static int __set_id_reg(const struct kvm_vcpu *vcpu, - const struct sys_reg_desc *rd, void __user *uaddr, + const struct sys_reg_desc *rd, u64 val, bool raz) { - const u64 id = sys_reg_to_index(rd); - int err; - u64 val; - - err = reg_from_user(&val, uaddr, id); - if (err) - return err; - /* This is what we mean by invariant: you can't change it. */ if (val != read_id_reg(vcpu, rd, raz)) return -EINVAL; @@ -1304,47 +1247,37 @@ static int __set_id_reg(const struct kvm_vcpu *vcpu, } static int get_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 *val) { bool raz = sysreg_visible_as_raz(vcpu, rd); - return __get_id_reg(vcpu, rd, uaddr, raz); + return __get_id_reg(vcpu, rd, val, raz); } static int set_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { bool raz = sysreg_visible_as_raz(vcpu, rd); - return __set_id_reg(vcpu, rd, uaddr, raz); + return __set_id_reg(vcpu, rd, val, raz); } static int set_raz_id_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - return __set_id_reg(vcpu, rd, uaddr, true); + return __set_id_reg(vcpu, rd, val, true); } static int get_raz_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 *val) { - const u64 id = sys_reg_to_index(rd); - const u64 val = 0; - - return reg_to_user(uaddr, &val, id); + *val = 0; + return 0; } static int set_wi_reg(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr) + u64 val) { - int err; - u64 val; - - /* Perform the access even if we are going to ignore the value */ - err = reg_from_user(&val, uaddr, sys_reg_to_index(rd)); - if (err) - return err; - return 0; } @@ -2639,35 +2572,34 @@ static bool index_to_params(u64 id, struct sys_reg_params *params) } } -const struct sys_reg_desc *find_reg_by_id(u64 id, - struct sys_reg_params *params, - const struct sys_reg_desc table[], - unsigned int num) +const struct sys_reg_desc *get_reg_by_id(u64 id, + const struct sys_reg_desc table[], + unsigned int num) { - if (!index_to_params(id, params)) + struct sys_reg_params params; + + if (!index_to_params(id, ¶ms)) return NULL; - return find_reg(params, table, num); + return find_reg(¶ms, table, num); } /* Decode an index value, and find the sys_reg_desc entry. */ -static const struct sys_reg_desc *index_to_sys_reg_desc(struct kvm_vcpu *vcpu, - u64 id) +static const struct sys_reg_desc * +id_to_sys_reg_desc(struct kvm_vcpu *vcpu, u64 id, + const struct sys_reg_desc table[], unsigned int num) + { const struct sys_reg_desc *r; - struct sys_reg_params params; /* We only do sys_reg for now. */ if ((id & KVM_REG_ARM_COPROC_MASK) != KVM_REG_ARM64_SYSREG) return NULL; - if (!index_to_params(id, ¶ms)) - return NULL; - - r = find_reg(¶ms, sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); + r = get_reg_by_id(id, table, num); /* Not saved in the sys_reg array and not otherwise accessible? */ - if (r && !(r->reg || r->get_user)) + if (r && (!(r->reg || r->get_user) || sysreg_hidden(vcpu, r))) r = NULL; return r; @@ -2707,48 +2639,30 @@ static struct sys_reg_desc invariant_sys_regs[] = { { SYS_DESC(SYS_CTR_EL0), NULL, get_ctr_el0 }, }; -static int reg_from_user(u64 *val, const void __user *uaddr, u64 id) +static int get_invariant_sys_reg(u64 id, u64 __user *uaddr) { - if (copy_from_user(val, uaddr, KVM_REG_SIZE(id)) != 0) - return -EFAULT; - return 0; -} - -static int reg_to_user(void __user *uaddr, const u64 *val, u64 id) -{ - if (copy_to_user(uaddr, val, KVM_REG_SIZE(id)) != 0) - return -EFAULT; - return 0; -} - -static int get_invariant_sys_reg(u64 id, void __user *uaddr) -{ - struct sys_reg_params params; const struct sys_reg_desc *r; - r = find_reg_by_id(id, ¶ms, invariant_sys_regs, - ARRAY_SIZE(invariant_sys_regs)); + r = get_reg_by_id(id, invariant_sys_regs, + ARRAY_SIZE(invariant_sys_regs)); if (!r) return -ENOENT; - return reg_to_user(uaddr, &r->val, id); + return put_user(r->val, uaddr); } -static int set_invariant_sys_reg(u64 id, void __user *uaddr) +static int set_invariant_sys_reg(u64 id, u64 __user *uaddr) { - struct sys_reg_params params; const struct sys_reg_desc *r; - int err; - u64 val = 0; /* Make sure high bits are 0 for 32-bit regs */ + u64 val; - r = find_reg_by_id(id, ¶ms, invariant_sys_regs, - ARRAY_SIZE(invariant_sys_regs)); + r = get_reg_by_id(id, invariant_sys_regs, + ARRAY_SIZE(invariant_sys_regs)); if (!r) return -ENOENT; - err = reg_from_user(&val, uaddr, id); - if (err) - return err; + if (get_user(val, uaddr)) + return -EFAULT; /* This is what we mean by invariant: you can't change it. */ if (r->val != val) @@ -2839,54 +2753,86 @@ static int demux_c15_set(u64 id, void __user *uaddr) } } +int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, + const struct sys_reg_desc table[], unsigned int num) +{ + u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; + const struct sys_reg_desc *r; + u64 val; + int ret; + + r = id_to_sys_reg_desc(vcpu, reg->id, table, num); + if (!r) + return -ENOENT; + + if (r->get_user) { + ret = (r->get_user)(vcpu, r, &val); + } else { + val = __vcpu_sys_reg(vcpu, r->reg); + ret = 0; + } + + if (!ret) + ret = put_user(val, uaddr); + + return ret; +} + int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - const struct sys_reg_desc *r; void __user *uaddr = (void __user *)(unsigned long)reg->addr; + int err; if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) return demux_c15_get(reg->id, uaddr); - if (KVM_REG_SIZE(reg->id) != sizeof(__u64)) - return -ENOENT; + err = get_invariant_sys_reg(reg->id, uaddr); + if (err != -ENOENT) + return err; - r = index_to_sys_reg_desc(vcpu, reg->id); + return kvm_sys_reg_get_user(vcpu, reg, + sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); +} + +int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, + const struct sys_reg_desc table[], unsigned int num) +{ + u64 __user *uaddr = (u64 __user *)(unsigned long)reg->addr; + const struct sys_reg_desc *r; + u64 val; + int ret; + + if (get_user(val, uaddr)) + return -EFAULT; + + r = id_to_sys_reg_desc(vcpu, reg->id, table, num); if (!r) - return get_invariant_sys_reg(reg->id, uaddr); - - /* Check for regs disabled by runtime config */ - if (sysreg_hidden(vcpu, r)) return -ENOENT; - if (r->get_user) - return (r->get_user)(vcpu, r, reg, uaddr); + if (r->set_user) { + ret = (r->set_user)(vcpu, r, val); + } else { + __vcpu_sys_reg(vcpu, r->reg) = val; + ret = 0; + } - return reg_to_user(uaddr, &__vcpu_sys_reg(vcpu, r->reg), reg->id); + return ret; } int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { - const struct sys_reg_desc *r; void __user *uaddr = (void __user *)(unsigned long)reg->addr; + int err; if ((reg->id & KVM_REG_ARM_COPROC_MASK) == KVM_REG_ARM_DEMUX) return demux_c15_set(reg->id, uaddr); - if (KVM_REG_SIZE(reg->id) != sizeof(__u64)) - return -ENOENT; + err = set_invariant_sys_reg(reg->id, uaddr); + if (err != -ENOENT) + return err; - r = index_to_sys_reg_desc(vcpu, reg->id); - if (!r) - return set_invariant_sys_reg(reg->id, uaddr); - - /* Check for regs disabled by runtime config */ - if (sysreg_hidden(vcpu, r)) - return -ENOENT; - - if (r->set_user) - return (r->set_user)(vcpu, r, reg, uaddr); - - return reg_from_user(&__vcpu_sys_reg(vcpu, r->reg), uaddr, reg->id); + return kvm_sys_reg_set_user(vcpu, reg, + sys_reg_descs, ARRAY_SIZE(sys_reg_descs)); } static unsigned int num_demux_regs(void) diff --git a/arch/arm64/kvm/sys_regs.h b/arch/arm64/kvm/sys_regs.h index aee8ea054f0d..a8c4cc32eb9a 100644 --- a/arch/arm64/kvm/sys_regs.h +++ b/arch/arm64/kvm/sys_regs.h @@ -75,9 +75,9 @@ struct sys_reg_desc { /* Custom get/set_user functions, fallback to generic if NULL */ int (*get_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr); + u64 *val); int (*set_user)(struct kvm_vcpu *vcpu, const struct sys_reg_desc *rd, - const struct kvm_one_reg *reg, void __user *uaddr); + u64 val); /* Return mask of REG_* runtime visibility overrides */ unsigned int (*visibility)(const struct kvm_vcpu *vcpu, @@ -190,10 +190,16 @@ find_reg(const struct sys_reg_params *params, const struct sys_reg_desc table[], return __inline_bsearch((void *)pval, table, num, sizeof(table[0]), match_sys_reg); } -const struct sys_reg_desc *find_reg_by_id(u64 id, - struct sys_reg_params *params, - const struct sys_reg_desc table[], - unsigned int num); +const struct sys_reg_desc *get_reg_by_id(u64 id, + const struct sys_reg_desc table[], + unsigned int num); + +int kvm_arm_sys_reg_get_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); +int kvm_arm_sys_reg_set_reg(struct kvm_vcpu *vcpu, const struct kvm_one_reg *); +int kvm_sys_reg_get_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, + const struct sys_reg_desc table[], unsigned int num); +int kvm_sys_reg_set_user(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg, + const struct sys_reg_desc table[], unsigned int num); #define AA32(_x) .aarch32_map = AA32_##_x #define Op0(_x) .Op0 = _x diff --git a/arch/arm64/kvm/vgic-sys-reg-v3.c b/arch/arm64/kvm/vgic-sys-reg-v3.c index 07d5271e9f05..9e7c486b48c2 100644 --- a/arch/arm64/kvm/vgic-sys-reg-v3.c +++ b/arch/arm64/kvm/vgic-sys-reg-v3.c @@ -10,293 +10,357 @@ #include "vgic/vgic.h" #include "sys_regs.h" -static bool access_gic_ctlr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int set_gic_ctlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) { u32 host_pri_bits, host_id_bits, host_seis, host_a3v, seis, a3v; struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu; struct vgic_vmcr vmcr; + + vgic_get_vmcr(vcpu, &vmcr); + + /* + * Disallow restoring VM state if not supported by this + * hardware. + */ + host_pri_bits = FIELD_GET(ICC_CTLR_EL1_PRI_BITS_MASK, val) + 1; + if (host_pri_bits > vgic_v3_cpu->num_pri_bits) + return -EINVAL; + + vgic_v3_cpu->num_pri_bits = host_pri_bits; + + host_id_bits = FIELD_GET(ICC_CTLR_EL1_ID_BITS_MASK, val); + if (host_id_bits > vgic_v3_cpu->num_id_bits) + return -EINVAL; + + vgic_v3_cpu->num_id_bits = host_id_bits; + + host_seis = FIELD_GET(ICH_VTR_SEIS_MASK, kvm_vgic_global_state.ich_vtr_el2); + seis = FIELD_GET(ICC_CTLR_EL1_SEIS_MASK, val); + if (host_seis != seis) + return -EINVAL; + + host_a3v = FIELD_GET(ICH_VTR_A3V_MASK, kvm_vgic_global_state.ich_vtr_el2); + a3v = FIELD_GET(ICC_CTLR_EL1_A3V_MASK, val); + if (host_a3v != a3v) + return -EINVAL; + + /* + * Here set VMCR.CTLR in ICC_CTLR_EL1 layout. + * The vgic_set_vmcr() will convert to ICH_VMCR layout. + */ + vmcr.cbpr = FIELD_GET(ICC_CTLR_EL1_CBPR_MASK, val); + vmcr.eoim = FIELD_GET(ICC_CTLR_EL1_EOImode_MASK, val); + vgic_set_vmcr(vcpu, &vmcr); + + return 0; +} + +static int get_gic_ctlr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *valp) +{ + struct vgic_cpu *vgic_v3_cpu = &vcpu->arch.vgic_cpu; + struct vgic_vmcr vmcr; u64 val; vgic_get_vmcr(vcpu, &vmcr); - if (p->is_write) { - val = p->regval; + val = 0; + val |= FIELD_PREP(ICC_CTLR_EL1_PRI_BITS_MASK, vgic_v3_cpu->num_pri_bits - 1); + val |= FIELD_PREP(ICC_CTLR_EL1_ID_BITS_MASK, vgic_v3_cpu->num_id_bits); + val |= FIELD_PREP(ICC_CTLR_EL1_SEIS_MASK, + FIELD_GET(ICH_VTR_SEIS_MASK, + kvm_vgic_global_state.ich_vtr_el2)); + val |= FIELD_PREP(ICC_CTLR_EL1_A3V_MASK, + FIELD_GET(ICH_VTR_A3V_MASK, kvm_vgic_global_state.ich_vtr_el2)); + /* + * The VMCR.CTLR value is in ICC_CTLR_EL1 layout. + * Extract it directly using ICC_CTLR_EL1 reg definitions. + */ + val |= FIELD_PREP(ICC_CTLR_EL1_CBPR_MASK, vmcr.cbpr); + val |= FIELD_PREP(ICC_CTLR_EL1_EOImode_MASK, vmcr.eoim); - /* - * Disallow restoring VM state if not supported by this - * hardware. - */ - host_pri_bits = ((val & ICC_CTLR_EL1_PRI_BITS_MASK) >> - ICC_CTLR_EL1_PRI_BITS_SHIFT) + 1; - if (host_pri_bits > vgic_v3_cpu->num_pri_bits) - return false; + *valp = val; - vgic_v3_cpu->num_pri_bits = host_pri_bits; - - host_id_bits = (val & ICC_CTLR_EL1_ID_BITS_MASK) >> - ICC_CTLR_EL1_ID_BITS_SHIFT; - if (host_id_bits > vgic_v3_cpu->num_id_bits) - return false; - - vgic_v3_cpu->num_id_bits = host_id_bits; - - host_seis = ((kvm_vgic_global_state.ich_vtr_el2 & - ICH_VTR_SEIS_MASK) >> ICH_VTR_SEIS_SHIFT); - seis = (val & ICC_CTLR_EL1_SEIS_MASK) >> - ICC_CTLR_EL1_SEIS_SHIFT; - if (host_seis != seis) - return false; - - host_a3v = ((kvm_vgic_global_state.ich_vtr_el2 & - ICH_VTR_A3V_MASK) >> ICH_VTR_A3V_SHIFT); - a3v = (val & ICC_CTLR_EL1_A3V_MASK) >> ICC_CTLR_EL1_A3V_SHIFT; - if (host_a3v != a3v) - return false; - - /* - * Here set VMCR.CTLR in ICC_CTLR_EL1 layout. - * The vgic_set_vmcr() will convert to ICH_VMCR layout. - */ - vmcr.cbpr = (val & ICC_CTLR_EL1_CBPR_MASK) >> ICC_CTLR_EL1_CBPR_SHIFT; - vmcr.eoim = (val & ICC_CTLR_EL1_EOImode_MASK) >> ICC_CTLR_EL1_EOImode_SHIFT; - vgic_set_vmcr(vcpu, &vmcr); - } else { - val = 0; - val |= (vgic_v3_cpu->num_pri_bits - 1) << - ICC_CTLR_EL1_PRI_BITS_SHIFT; - val |= vgic_v3_cpu->num_id_bits << ICC_CTLR_EL1_ID_BITS_SHIFT; - val |= ((kvm_vgic_global_state.ich_vtr_el2 & - ICH_VTR_SEIS_MASK) >> ICH_VTR_SEIS_SHIFT) << - ICC_CTLR_EL1_SEIS_SHIFT; - val |= ((kvm_vgic_global_state.ich_vtr_el2 & - ICH_VTR_A3V_MASK) >> ICH_VTR_A3V_SHIFT) << - ICC_CTLR_EL1_A3V_SHIFT; - /* - * The VMCR.CTLR value is in ICC_CTLR_EL1 layout. - * Extract it directly using ICC_CTLR_EL1 reg definitions. - */ - val |= (vmcr.cbpr << ICC_CTLR_EL1_CBPR_SHIFT) & ICC_CTLR_EL1_CBPR_MASK; - val |= (vmcr.eoim << ICC_CTLR_EL1_EOImode_SHIFT) & ICC_CTLR_EL1_EOImode_MASK; - - p->regval = val; - } - - return true; + return 0; } -static bool access_gic_pmr(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int set_gic_pmr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) { struct vgic_vmcr vmcr; vgic_get_vmcr(vcpu, &vmcr); - if (p->is_write) { - vmcr.pmr = (p->regval & ICC_PMR_EL1_MASK) >> ICC_PMR_EL1_SHIFT; - vgic_set_vmcr(vcpu, &vmcr); - } else { - p->regval = (vmcr.pmr << ICC_PMR_EL1_SHIFT) & ICC_PMR_EL1_MASK; - } + vmcr.pmr = FIELD_GET(ICC_PMR_EL1_MASK, val); + vgic_set_vmcr(vcpu, &vmcr); - return true; + return 0; } -static bool access_gic_bpr0(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int get_gic_pmr(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) { struct vgic_vmcr vmcr; vgic_get_vmcr(vcpu, &vmcr); - if (p->is_write) { - vmcr.bpr = (p->regval & ICC_BPR0_EL1_MASK) >> - ICC_BPR0_EL1_SHIFT; - vgic_set_vmcr(vcpu, &vmcr); - } else { - p->regval = (vmcr.bpr << ICC_BPR0_EL1_SHIFT) & - ICC_BPR0_EL1_MASK; - } + *val = FIELD_PREP(ICC_PMR_EL1_MASK, vmcr.pmr); - return true; + return 0; } -static bool access_gic_bpr1(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int set_gic_bpr0(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) { struct vgic_vmcr vmcr; - if (!p->is_write) - p->regval = 0; + vgic_get_vmcr(vcpu, &vmcr); + vmcr.bpr = FIELD_GET(ICC_BPR0_EL1_MASK, val); + vgic_set_vmcr(vcpu, &vmcr); + + return 0; +} + +static int get_gic_bpr0(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + struct vgic_vmcr vmcr; + + vgic_get_vmcr(vcpu, &vmcr); + *val = FIELD_PREP(ICC_BPR0_EL1_MASK, vmcr.bpr); + + return 0; +} + +static int set_gic_bpr1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + struct vgic_vmcr vmcr; vgic_get_vmcr(vcpu, &vmcr); if (!vmcr.cbpr) { - if (p->is_write) { - vmcr.abpr = (p->regval & ICC_BPR1_EL1_MASK) >> - ICC_BPR1_EL1_SHIFT; - vgic_set_vmcr(vcpu, &vmcr); - } else { - p->regval = (vmcr.abpr << ICC_BPR1_EL1_SHIFT) & - ICC_BPR1_EL1_MASK; - } - } else { - if (!p->is_write) - p->regval = min((vmcr.bpr + 1), 7U); + vmcr.abpr = FIELD_GET(ICC_BPR1_EL1_MASK, val); + vgic_set_vmcr(vcpu, &vmcr); } - return true; + return 0; } -static bool access_gic_grpen0(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int get_gic_bpr1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) { struct vgic_vmcr vmcr; vgic_get_vmcr(vcpu, &vmcr); - if (p->is_write) { - vmcr.grpen0 = (p->regval & ICC_IGRPEN0_EL1_MASK) >> - ICC_IGRPEN0_EL1_SHIFT; - vgic_set_vmcr(vcpu, &vmcr); - } else { - p->regval = (vmcr.grpen0 << ICC_IGRPEN0_EL1_SHIFT) & - ICC_IGRPEN0_EL1_MASK; - } + if (!vmcr.cbpr) + *val = FIELD_PREP(ICC_BPR1_EL1_MASK, vmcr.abpr); + else + *val = min((vmcr.bpr + 1), 7U); - return true; + + return 0; } -static bool access_gic_grpen1(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int set_gic_grpen0(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) { struct vgic_vmcr vmcr; vgic_get_vmcr(vcpu, &vmcr); - if (p->is_write) { - vmcr.grpen1 = (p->regval & ICC_IGRPEN1_EL1_MASK) >> - ICC_IGRPEN1_EL1_SHIFT; - vgic_set_vmcr(vcpu, &vmcr); - } else { - p->regval = (vmcr.grpen1 << ICC_IGRPEN1_EL1_SHIFT) & - ICC_IGRPEN1_EL1_MASK; - } + vmcr.grpen0 = FIELD_GET(ICC_IGRPEN0_EL1_MASK, val); + vgic_set_vmcr(vcpu, &vmcr); - return true; + return 0; } -static void vgic_v3_access_apr_reg(struct kvm_vcpu *vcpu, - struct sys_reg_params *p, u8 apr, u8 idx) +static int get_gic_grpen0(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + struct vgic_vmcr vmcr; + + vgic_get_vmcr(vcpu, &vmcr); + *val = FIELD_PREP(ICC_IGRPEN0_EL1_MASK, vmcr.grpen0); + + return 0; +} + +static int set_gic_grpen1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + struct vgic_vmcr vmcr; + + vgic_get_vmcr(vcpu, &vmcr); + vmcr.grpen1 = FIELD_GET(ICC_IGRPEN1_EL1_MASK, val); + vgic_set_vmcr(vcpu, &vmcr); + + return 0; +} + +static int get_gic_grpen1(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + struct vgic_vmcr vmcr; + + vgic_get_vmcr(vcpu, &vmcr); + *val = FIELD_GET(ICC_IGRPEN1_EL1_MASK, vmcr.grpen1); + + return 0; +} + +static void set_apr_reg(struct kvm_vcpu *vcpu, u64 val, u8 apr, u8 idx) { struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3; - uint32_t *ap_reg; if (apr) - ap_reg = &vgicv3->vgic_ap1r[idx]; + vgicv3->vgic_ap1r[idx] = val; else - ap_reg = &vgicv3->vgic_ap0r[idx]; - - if (p->is_write) - *ap_reg = p->regval; - else - p->regval = *ap_reg; + vgicv3->vgic_ap0r[idx] = val; } -static bool access_gic_aprn(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r, u8 apr) +static u64 get_apr_reg(struct kvm_vcpu *vcpu, u8 apr, u8 idx) +{ + struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3; + + if (apr) + return vgicv3->vgic_ap1r[idx]; + else + return vgicv3->vgic_ap0r[idx]; +} + +static int set_gic_ap0r(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) + { u8 idx = r->Op2 & 3; if (idx > vgic_v3_max_apr_idx(vcpu)) - goto err; + return -EINVAL; - vgic_v3_access_apr_reg(vcpu, p, apr, idx); - return true; -err: - if (!p->is_write) - p->regval = 0; - - return false; + set_apr_reg(vcpu, val, 0, idx); + return 0; } -static bool access_gic_ap0r(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int get_gic_ap0r(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) +{ + u8 idx = r->Op2 & 3; + + if (idx > vgic_v3_max_apr_idx(vcpu)) + return -EINVAL; + + *val = get_apr_reg(vcpu, 0, idx); + + return 0; +} + +static int set_gic_ap1r(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) { - return access_gic_aprn(vcpu, p, r, 0); + u8 idx = r->Op2 & 3; + + if (idx > vgic_v3_max_apr_idx(vcpu)) + return -EINVAL; + + set_apr_reg(vcpu, val, 1, idx); + return 0; } -static bool access_gic_ap1r(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int get_gic_ap1r(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) { - return access_gic_aprn(vcpu, p, r, 1); + u8 idx = r->Op2 & 3; + + if (idx > vgic_v3_max_apr_idx(vcpu)) + return -EINVAL; + + *val = get_apr_reg(vcpu, 1, idx); + + return 0; } -static bool access_gic_sre(struct kvm_vcpu *vcpu, struct sys_reg_params *p, - const struct sys_reg_desc *r) +static int set_gic_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 val) +{ + /* Validate SRE bit */ + if (!(val & ICC_SRE_EL1_SRE)) + return -EINVAL; + + return 0; +} + +static int get_gic_sre(struct kvm_vcpu *vcpu, const struct sys_reg_desc *r, + u64 *val) { struct vgic_v3_cpu_if *vgicv3 = &vcpu->arch.vgic_cpu.vgic_v3; - /* Validate SRE bit */ - if (p->is_write) { - if (!(p->regval & ICC_SRE_EL1_SRE)) - return false; - } else { - p->regval = vgicv3->vgic_sre; - } + *val = vgicv3->vgic_sre; - return true; + return 0; } + static const struct sys_reg_desc gic_v3_icc_reg_descs[] = { - { SYS_DESC(SYS_ICC_PMR_EL1), access_gic_pmr }, - { SYS_DESC(SYS_ICC_BPR0_EL1), access_gic_bpr0 }, - { SYS_DESC(SYS_ICC_AP0R0_EL1), access_gic_ap0r }, - { SYS_DESC(SYS_ICC_AP0R1_EL1), access_gic_ap0r }, - { SYS_DESC(SYS_ICC_AP0R2_EL1), access_gic_ap0r }, - { SYS_DESC(SYS_ICC_AP0R3_EL1), access_gic_ap0r }, - { SYS_DESC(SYS_ICC_AP1R0_EL1), access_gic_ap1r }, - { SYS_DESC(SYS_ICC_AP1R1_EL1), access_gic_ap1r }, - { SYS_DESC(SYS_ICC_AP1R2_EL1), access_gic_ap1r }, - { SYS_DESC(SYS_ICC_AP1R3_EL1), access_gic_ap1r }, - { SYS_DESC(SYS_ICC_BPR1_EL1), access_gic_bpr1 }, - { SYS_DESC(SYS_ICC_CTLR_EL1), access_gic_ctlr }, - { SYS_DESC(SYS_ICC_SRE_EL1), access_gic_sre }, - { SYS_DESC(SYS_ICC_IGRPEN0_EL1), access_gic_grpen0 }, - { SYS_DESC(SYS_ICC_IGRPEN1_EL1), access_gic_grpen1 }, + { SYS_DESC(SYS_ICC_PMR_EL1), + .set_user = set_gic_pmr, .get_user = get_gic_pmr, }, + { SYS_DESC(SYS_ICC_BPR0_EL1), + .set_user = set_gic_bpr0, .get_user = get_gic_bpr0, }, + { SYS_DESC(SYS_ICC_AP0R0_EL1), + .set_user = set_gic_ap0r, .get_user = get_gic_ap0r, }, + { SYS_DESC(SYS_ICC_AP0R1_EL1), + .set_user = set_gic_ap0r, .get_user = get_gic_ap0r, }, + { SYS_DESC(SYS_ICC_AP0R2_EL1), + .set_user = set_gic_ap0r, .get_user = get_gic_ap0r, }, + { SYS_DESC(SYS_ICC_AP0R3_EL1), + .set_user = set_gic_ap0r, .get_user = get_gic_ap0r, }, + { SYS_DESC(SYS_ICC_AP1R0_EL1), + .set_user = set_gic_ap1r, .get_user = get_gic_ap1r, }, + { SYS_DESC(SYS_ICC_AP1R1_EL1), + .set_user = set_gic_ap1r, .get_user = get_gic_ap1r, }, + { SYS_DESC(SYS_ICC_AP1R2_EL1), + .set_user = set_gic_ap1r, .get_user = get_gic_ap1r, }, + { SYS_DESC(SYS_ICC_AP1R3_EL1), + .set_user = set_gic_ap1r, .get_user = get_gic_ap1r, }, + { SYS_DESC(SYS_ICC_BPR1_EL1), + .set_user = set_gic_bpr1, .get_user = get_gic_bpr1, }, + { SYS_DESC(SYS_ICC_CTLR_EL1), + .set_user = set_gic_ctlr, .get_user = get_gic_ctlr, }, + { SYS_DESC(SYS_ICC_SRE_EL1), + .set_user = set_gic_sre, .get_user = get_gic_sre, }, + { SYS_DESC(SYS_ICC_IGRPEN0_EL1), + .set_user = set_gic_grpen0, .get_user = get_gic_grpen0, }, + { SYS_DESC(SYS_ICC_IGRPEN1_EL1), + .set_user = set_gic_grpen1, .get_user = get_gic_grpen1, }, }; -int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id, - u64 *reg) +static u64 attr_to_id(u64 attr) { - struct sys_reg_params params; - u64 sysreg = (id & KVM_DEV_ARM_VGIC_SYSREG_MASK) | KVM_REG_SIZE_U64; + return ARM64_SYS_REG(FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_OP0_MASK, attr), + FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_OP1_MASK, attr), + FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_CRN_MASK, attr), + FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_CRM_MASK, attr), + FIELD_GET(KVM_REG_ARM_VGIC_SYSREG_OP2_MASK, attr)); +} - params.regval = *reg; - params.is_write = is_write; - - if (find_reg_by_id(sysreg, ¶ms, gic_v3_icc_reg_descs, - ARRAY_SIZE(gic_v3_icc_reg_descs))) +int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr) +{ + if (get_reg_by_id(attr_to_id(attr->attr), gic_v3_icc_reg_descs, + ARRAY_SIZE(gic_v3_icc_reg_descs))) return 0; return -ENXIO; } -int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, u64 id, - u64 *reg) +int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr, + bool is_write) { - struct sys_reg_params params; - const struct sys_reg_desc *r; - u64 sysreg = (id & KVM_DEV_ARM_VGIC_SYSREG_MASK) | KVM_REG_SIZE_U64; + struct kvm_one_reg reg = { + .id = attr_to_id(attr->attr), + .addr = attr->addr, + }; if (is_write) - params.regval = *reg; - params.is_write = is_write; - - r = find_reg_by_id(sysreg, ¶ms, gic_v3_icc_reg_descs, - ARRAY_SIZE(gic_v3_icc_reg_descs)); - if (!r) - return -ENXIO; - - if (!r->access(vcpu, ¶ms, r)) - return -EINVAL; - - if (!is_write) - *reg = params.regval; - - return 0; + return kvm_sys_reg_set_user(vcpu, ®, gic_v3_icc_reg_descs, + ARRAY_SIZE(gic_v3_icc_reg_descs)); + else + return kvm_sys_reg_get_user(vcpu, ®, gic_v3_icc_reg_descs, + ARRAY_SIZE(gic_v3_icc_reg_descs)); } diff --git a/arch/arm64/kvm/vgic/vgic-kvm-device.c b/arch/arm64/kvm/vgic/vgic-kvm-device.c index c6d52a1fd9c8..edeac2380591 100644 --- a/arch/arm64/kvm/vgic/vgic-kvm-device.c +++ b/arch/arm64/kvm/vgic/vgic-kvm-device.c @@ -41,11 +41,42 @@ static int vgic_check_type(struct kvm *kvm, int type_needed) return 0; } +int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr) +{ + struct vgic_dist *vgic = &kvm->arch.vgic; + int r; + + mutex_lock(&kvm->lock); + switch (FIELD_GET(KVM_ARM_DEVICE_TYPE_MASK, dev_addr->id)) { + case KVM_VGIC_V2_ADDR_TYPE_DIST: + r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); + if (!r) + r = vgic_check_iorange(kvm, vgic->vgic_dist_base, dev_addr->addr, + SZ_4K, KVM_VGIC_V2_DIST_SIZE); + if (!r) + vgic->vgic_dist_base = dev_addr->addr; + break; + case KVM_VGIC_V2_ADDR_TYPE_CPU: + r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); + if (!r) + r = vgic_check_iorange(kvm, vgic->vgic_cpu_base, dev_addr->addr, + SZ_4K, KVM_VGIC_V2_CPU_SIZE); + if (!r) + vgic->vgic_cpu_base = dev_addr->addr; + break; + default: + r = -ENODEV; + } + + mutex_unlock(&kvm->lock); + + return r; +} + /** * kvm_vgic_addr - set or get vgic VM base addresses * @kvm: pointer to the vm struct - * @type: the VGIC addr type, one of KVM_VGIC_V[23]_ADDR_TYPE_XXX - * @addr: pointer to address value + * @attr: pointer to the attribute being retrieved/updated * @write: if true set the address in the VM address space, if false read the * address * @@ -57,15 +88,22 @@ static int vgic_check_type(struct kvm *kvm, int type_needed) * overlapping regions in case of a virtual GICv3 here, since we don't know * the number of VCPUs yet, so we defer this check to map_resources(). */ -int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) +static int kvm_vgic_addr(struct kvm *kvm, struct kvm_device_attr *attr, bool write) { - int r = 0; + u64 __user *uaddr = (u64 __user *)attr->addr; struct vgic_dist *vgic = &kvm->arch.vgic; phys_addr_t *addr_ptr, alignment, size; u64 undef_value = VGIC_ADDR_UNDEF; + u64 addr; + int r; + + /* Reading a redistributor region addr implies getting the index */ + if (write || attr->attr == KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION) + if (get_user(addr, uaddr)) + return -EFAULT; mutex_lock(&kvm->lock); - switch (type) { + switch (attr->attr) { case KVM_VGIC_V2_ADDR_TYPE_DIST: r = vgic_check_type(kvm, KVM_DEV_TYPE_ARM_VGIC_V2); addr_ptr = &vgic->vgic_dist_base; @@ -91,7 +129,7 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) if (r) break; if (write) { - r = vgic_v3_set_redist_base(kvm, 0, *addr, 0); + r = vgic_v3_set_redist_base(kvm, 0, addr, 0); goto out; } rdreg = list_first_entry_or_null(&vgic->rd_regions, @@ -111,14 +149,12 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) if (r) break; - index = *addr & KVM_VGIC_V3_RDIST_INDEX_MASK; + index = addr & KVM_VGIC_V3_RDIST_INDEX_MASK; if (write) { - gpa_t base = *addr & KVM_VGIC_V3_RDIST_BASE_MASK; - u32 count = (*addr & KVM_VGIC_V3_RDIST_COUNT_MASK) - >> KVM_VGIC_V3_RDIST_COUNT_SHIFT; - u8 flags = (*addr & KVM_VGIC_V3_RDIST_FLAGS_MASK) - >> KVM_VGIC_V3_RDIST_FLAGS_SHIFT; + gpa_t base = addr & KVM_VGIC_V3_RDIST_BASE_MASK; + u32 count = FIELD_GET(KVM_VGIC_V3_RDIST_COUNT_MASK, addr); + u8 flags = FIELD_GET(KVM_VGIC_V3_RDIST_FLAGS_MASK, addr); if (!count || flags) r = -EINVAL; @@ -134,9 +170,9 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) goto out; } - *addr = index; - *addr |= rdreg->base; - *addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT; + addr = index; + addr |= rdreg->base; + addr |= (u64)rdreg->count << KVM_VGIC_V3_RDIST_COUNT_SHIFT; goto out; } default: @@ -147,15 +183,19 @@ int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write) goto out; if (write) { - r = vgic_check_iorange(kvm, *addr_ptr, *addr, alignment, size); + r = vgic_check_iorange(kvm, *addr_ptr, addr, alignment, size); if (!r) - *addr_ptr = *addr; + *addr_ptr = addr; } else { - *addr = *addr_ptr; + addr = *addr_ptr; } out: mutex_unlock(&kvm->lock); + + if (!r && !write) + r = put_user(addr, uaddr); + return r; } @@ -165,17 +205,9 @@ static int vgic_set_common_attr(struct kvm_device *dev, int r; switch (attr->group) { - case KVM_DEV_ARM_VGIC_GRP_ADDR: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; - u64 addr; - unsigned long type = (unsigned long)attr->attr; - - if (copy_from_user(&addr, uaddr, sizeof(addr))) - return -EFAULT; - - r = kvm_vgic_addr(dev->kvm, type, &addr, true); + case KVM_DEV_ARM_VGIC_GRP_ADDR: + r = kvm_vgic_addr(dev->kvm, attr, true); return (r == -ENODEV) ? -ENXIO : r; - } case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { u32 __user *uaddr = (u32 __user *)(long)attr->addr; u32 val; @@ -214,6 +246,24 @@ static int vgic_set_common_attr(struct kvm_device *dev, r = vgic_init(dev->kvm); mutex_unlock(&dev->kvm->lock); return r; + case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: + /* + * OK, this one isn't common at all, but we + * want to handle all control group attributes + * in a single place. + */ + if (vgic_check_type(dev->kvm, KVM_DEV_TYPE_ARM_VGIC_V3)) + return -ENXIO; + mutex_lock(&dev->kvm->lock); + + if (!lock_all_vcpus(dev->kvm)) { + mutex_unlock(&dev->kvm->lock); + return -EBUSY; + } + r = vgic_v3_save_pending_tables(dev->kvm); + unlock_all_vcpus(dev->kvm); + mutex_unlock(&dev->kvm->lock); + return r; } break; } @@ -228,22 +278,9 @@ static int vgic_get_common_attr(struct kvm_device *dev, int r = -ENXIO; switch (attr->group) { - case KVM_DEV_ARM_VGIC_GRP_ADDR: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; - u64 addr; - unsigned long type = (unsigned long)attr->attr; - - if (copy_from_user(&addr, uaddr, sizeof(addr))) - return -EFAULT; - - r = kvm_vgic_addr(dev->kvm, type, &addr, false); - if (r) - return (r == -ENODEV) ? -ENXIO : r; - - if (copy_to_user(uaddr, &addr, sizeof(addr))) - return -EFAULT; - break; - } + case KVM_DEV_ARM_VGIC_GRP_ADDR: + r = kvm_vgic_addr(dev->kvm, attr, false); + return (r == -ENODEV) ? -ENXIO : r; case KVM_DEV_ARM_VGIC_GRP_NR_IRQS: { u32 __user *uaddr = (u32 __user *)(long)attr->addr; @@ -348,17 +385,18 @@ bool lock_all_vcpus(struct kvm *kvm) * * @dev: kvm device handle * @attr: kvm device attribute - * @reg: address the value is read or written * @is_write: true if userspace is writing a register */ static int vgic_v2_attr_regs_access(struct kvm_device *dev, struct kvm_device_attr *attr, - u32 *reg, bool is_write) + bool is_write) { + u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; struct vgic_reg_attr reg_attr; gpa_t addr; struct kvm_vcpu *vcpu; int ret; + u32 val; ret = vgic_v2_parse_attr(dev, attr, ®_attr); if (ret) @@ -367,6 +405,10 @@ static int vgic_v2_attr_regs_access(struct kvm_device *dev, vcpu = reg_attr.vcpu; addr = reg_attr.addr; + if (is_write) + if (get_user(val, uaddr)) + return -EFAULT; + mutex_lock(&dev->kvm->lock); ret = vgic_init(dev->kvm); @@ -380,10 +422,10 @@ static int vgic_v2_attr_regs_access(struct kvm_device *dev, switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: - ret = vgic_v2_cpuif_uaccess(vcpu, is_write, addr, reg); + ret = vgic_v2_cpuif_uaccess(vcpu, is_write, addr, &val); break; case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: - ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, reg); + ret = vgic_v2_dist_uaccess(vcpu, is_write, addr, &val); break; default: ret = -EINVAL; @@ -393,57 +435,35 @@ static int vgic_v2_attr_regs_access(struct kvm_device *dev, unlock_all_vcpus(dev->kvm); out: mutex_unlock(&dev->kvm->lock); + + if (!ret && !is_write) + ret = put_user(val, uaddr); + return ret; } static int vgic_v2_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { - int ret; - - ret = vgic_set_common_attr(dev, attr); - if (ret != -ENXIO) - return ret; - switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: - case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - u32 reg; - - if (get_user(reg, uaddr)) - return -EFAULT; - - return vgic_v2_attr_regs_access(dev, attr, ®, true); + case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: + return vgic_v2_attr_regs_access(dev, attr, true); + default: + return vgic_set_common_attr(dev, attr); } - } - - return -ENXIO; } static int vgic_v2_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { - int ret; - - ret = vgic_get_common_attr(dev, attr); - if (ret != -ENXIO) - return ret; - switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: - case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - u32 reg = 0; - - ret = vgic_v2_attr_regs_access(dev, attr, ®, false); - if (ret) - return ret; - return put_user(reg, uaddr); + case KVM_DEV_ARM_VGIC_GRP_CPU_REGS: + return vgic_v2_attr_regs_access(dev, attr, false); + default: + return vgic_get_common_attr(dev, attr); } - } - - return -ENXIO; } static int vgic_v2_has_attr(struct kvm_device *dev, @@ -512,18 +532,18 @@ int vgic_v3_parse_attr(struct kvm_device *dev, struct kvm_device_attr *attr, * * @dev: kvm device handle * @attr: kvm device attribute - * @reg: address the value is read or written * @is_write: true if userspace is writing a register */ static int vgic_v3_attr_regs_access(struct kvm_device *dev, struct kvm_device_attr *attr, - u64 *reg, bool is_write) + bool is_write) { struct vgic_reg_attr reg_attr; gpa_t addr; struct kvm_vcpu *vcpu; + bool uaccess; + u32 val; int ret; - u32 tmp32; ret = vgic_v3_parse_attr(dev, attr, ®_attr); if (ret) @@ -532,6 +552,21 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, vcpu = reg_attr.vcpu; addr = reg_attr.addr; + switch (attr->group) { + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + /* Sysregs uaccess is performed by the sysreg handling code */ + uaccess = false; + break; + default: + uaccess = true; + } + + if (uaccess && is_write) { + u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; + if (get_user(val, uaddr)) + return -EFAULT; + } + mutex_lock(&dev->kvm->lock); if (unlikely(!vgic_initialized(dev->kvm))) { @@ -546,29 +581,14 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: - if (is_write) - tmp32 = *reg; - - ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &tmp32); - if (!is_write) - *reg = tmp32; + ret = vgic_v3_dist_uaccess(vcpu, is_write, addr, &val); break; case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: - if (is_write) - tmp32 = *reg; - - ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &tmp32); - if (!is_write) - *reg = tmp32; + ret = vgic_v3_redist_uaccess(vcpu, is_write, addr, &val); break; - case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { - u64 regid; - - regid = (attr->attr & KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK); - ret = vgic_v3_cpu_sysregs_uaccess(vcpu, is_write, - regid, reg); + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + ret = vgic_v3_cpu_sysregs_uaccess(vcpu, attr, is_write); break; - } case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { unsigned int info, intid; @@ -578,7 +598,7 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, intid = attr->attr & KVM_DEV_ARM_VGIC_LINE_LEVEL_INTID_MASK; ret = vgic_v3_line_level_info_uaccess(vcpu, is_write, - intid, reg); + intid, &val); } else { ret = -EINVAL; } @@ -592,117 +612,41 @@ static int vgic_v3_attr_regs_access(struct kvm_device *dev, unlock_all_vcpus(dev->kvm); out: mutex_unlock(&dev->kvm->lock); + + if (!ret && uaccess && !is_write) { + u32 __user *uaddr = (u32 __user *)(unsigned long)attr->addr; + ret = put_user(val, uaddr); + } + return ret; } static int vgic_v3_set_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { - int ret; - - ret = vgic_set_common_attr(dev, attr); - if (ret != -ENXIO) - return ret; - switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: - case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - u32 tmp32; - u64 reg; - - if (get_user(tmp32, uaddr)) - return -EFAULT; - - reg = tmp32; - return vgic_v3_attr_regs_access(dev, attr, ®, true); + case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: + return vgic_v3_attr_regs_access(dev, attr, true); + default: + return vgic_set_common_attr(dev, attr); } - case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; - u64 reg; - - if (get_user(reg, uaddr)) - return -EFAULT; - - return vgic_v3_attr_regs_access(dev, attr, ®, true); - } - case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - u64 reg; - u32 tmp32; - - if (get_user(tmp32, uaddr)) - return -EFAULT; - - reg = tmp32; - return vgic_v3_attr_regs_access(dev, attr, ®, true); - } - case KVM_DEV_ARM_VGIC_GRP_CTRL: { - int ret; - - switch (attr->attr) { - case KVM_DEV_ARM_VGIC_SAVE_PENDING_TABLES: - mutex_lock(&dev->kvm->lock); - - if (!lock_all_vcpus(dev->kvm)) { - mutex_unlock(&dev->kvm->lock); - return -EBUSY; - } - ret = vgic_v3_save_pending_tables(dev->kvm); - unlock_all_vcpus(dev->kvm); - mutex_unlock(&dev->kvm->lock); - return ret; - } - break; - } - } - return -ENXIO; } static int vgic_v3_get_attr(struct kvm_device *dev, struct kvm_device_attr *attr) { - int ret; - - ret = vgic_get_common_attr(dev, attr); - if (ret != -ENXIO) - return ret; - switch (attr->group) { case KVM_DEV_ARM_VGIC_GRP_DIST_REGS: - case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - u64 reg; - u32 tmp32; - - ret = vgic_v3_attr_regs_access(dev, attr, ®, false); - if (ret) - return ret; - tmp32 = reg; - return put_user(tmp32, uaddr); + case KVM_DEV_ARM_VGIC_GRP_REDIST_REGS: + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: + return vgic_v3_attr_regs_access(dev, attr, false); + default: + return vgic_get_common_attr(dev, attr); } - case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { - u64 __user *uaddr = (u64 __user *)(long)attr->addr; - u64 reg; - - ret = vgic_v3_attr_regs_access(dev, attr, ®, false); - if (ret) - return ret; - return put_user(reg, uaddr); - } - case KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO: { - u32 __user *uaddr = (u32 __user *)(long)attr->addr; - u64 reg; - u32 tmp32; - - ret = vgic_v3_attr_regs_access(dev, attr, ®, false); - if (ret) - return ret; - tmp32 = reg; - return put_user(tmp32, uaddr); - } - } - return -ENXIO; } static int vgic_v3_has_attr(struct kvm_device *dev, diff --git a/arch/arm64/kvm/vgic/vgic-mmio-v3.c b/arch/arm64/kvm/vgic/vgic-mmio-v3.c index f15e29cc63ce..91201f743033 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio-v3.c +++ b/arch/arm64/kvm/vgic/vgic-mmio-v3.c @@ -986,12 +986,8 @@ int vgic_v3_has_attr_regs(struct kvm_device *dev, struct kvm_device_attr *attr) iodev.base_addr = 0; break; } - case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: { - u64 reg, id; - - id = (attr->attr & KVM_DEV_ARM_VGIC_SYSREG_INSTR_MASK); - return vgic_v3_has_cpu_sysregs_attr(vcpu, 0, id, ®); - } + case KVM_DEV_ARM_VGIC_GRP_CPU_SYSREGS: + return vgic_v3_has_cpu_sysregs_attr(vcpu, attr); default: return -ENXIO; } @@ -1158,7 +1154,7 @@ int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write, } int vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write, - u32 intid, u64 *val) + u32 intid, u32 *val) { if (intid % 32) return -EINVAL; diff --git a/arch/arm64/kvm/vgic/vgic-mmio.c b/arch/arm64/kvm/vgic/vgic-mmio.c index 997d0fce2088..b32d434c1d4a 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.c +++ b/arch/arm64/kvm/vgic/vgic-mmio.c @@ -775,10 +775,10 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu, } } -u64 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid) +u32 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid) { int i; - u64 val = 0; + u32 val = 0; int nr_irqs = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS; for (i = 0; i < 32; i++) { @@ -798,7 +798,7 @@ u64 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid) } void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid, - const u64 val) + const u32 val) { int i; int nr_irqs = vcpu->kvm->arch.vgic.nr_spis + VGIC_NR_PRIVATE_IRQS; diff --git a/arch/arm64/kvm/vgic/vgic-mmio.h b/arch/arm64/kvm/vgic/vgic-mmio.h index 6082d4b66d39..5b490a4dfa5e 100644 --- a/arch/arm64/kvm/vgic/vgic-mmio.h +++ b/arch/arm64/kvm/vgic/vgic-mmio.h @@ -207,10 +207,10 @@ void vgic_mmio_write_config(struct kvm_vcpu *vcpu, int vgic_uaccess(struct kvm_vcpu *vcpu, struct vgic_io_device *dev, bool is_write, int offset, u32 *val); -u64 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid); +u32 vgic_read_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid); void vgic_write_irq_line_level_info(struct kvm_vcpu *vcpu, u32 intid, - const u64 val); + const u32 val); unsigned int vgic_v2_init_dist_iodev(struct vgic_io_device *dev); diff --git a/arch/arm64/kvm/vgic/vgic.h b/arch/arm64/kvm/vgic/vgic.h index 4c6bdd321faa..0c8da72953f0 100644 --- a/arch/arm64/kvm/vgic/vgic.h +++ b/arch/arm64/kvm/vgic/vgic.h @@ -245,12 +245,11 @@ int vgic_v3_dist_uaccess(struct kvm_vcpu *vcpu, bool is_write, int offset, u32 *val); int vgic_v3_redist_uaccess(struct kvm_vcpu *vcpu, bool is_write, int offset, u32 *val); -int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, bool is_write, - u64 id, u64 *val); -int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, bool is_write, u64 id, - u64 *reg); +int vgic_v3_cpu_sysregs_uaccess(struct kvm_vcpu *vcpu, + struct kvm_device_attr *attr, bool is_write); +int vgic_v3_has_cpu_sysregs_attr(struct kvm_vcpu *vcpu, struct kvm_device_attr *attr); int vgic_v3_line_level_info_uaccess(struct kvm_vcpu *vcpu, bool is_write, - u32 intid, u64 *val); + u32 intid, u32 *val); int kvm_register_vgic_device(unsigned long type); void vgic_set_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); void vgic_get_vmcr(struct kvm_vcpu *vcpu, struct vgic_vmcr *vmcr); diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig index f55ba1745f7b..3cbc2dc62baf 100644 --- a/arch/csky/Kconfig +++ b/arch/csky/Kconfig @@ -8,6 +8,33 @@ config CSKY select ARCH_HAS_SYNC_DMA_FOR_DEVICE select ARCH_USE_BUILTIN_BSWAP select ARCH_USE_QUEUED_RWLOCKS + select ARCH_USE_QUEUED_SPINLOCKS + select ARCH_INLINE_READ_LOCK if !PREEMPTION + select ARCH_INLINE_READ_LOCK_BH if !PREEMPTION + select ARCH_INLINE_READ_LOCK_IRQ if !PREEMPTION + select ARCH_INLINE_READ_LOCK_IRQSAVE if !PREEMPTION + select ARCH_INLINE_READ_UNLOCK if !PREEMPTION + select ARCH_INLINE_READ_UNLOCK_BH if !PREEMPTION + select ARCH_INLINE_READ_UNLOCK_IRQ if !PREEMPTION + select ARCH_INLINE_READ_UNLOCK_IRQRESTORE if !PREEMPTION + select ARCH_INLINE_WRITE_LOCK if !PREEMPTION + select ARCH_INLINE_WRITE_LOCK_BH if !PREEMPTION + select ARCH_INLINE_WRITE_LOCK_IRQ if !PREEMPTION + select ARCH_INLINE_WRITE_LOCK_IRQSAVE if !PREEMPTION + select ARCH_INLINE_WRITE_UNLOCK if !PREEMPTION + select ARCH_INLINE_WRITE_UNLOCK_BH if !PREEMPTION + select ARCH_INLINE_WRITE_UNLOCK_IRQ if !PREEMPTION + select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE if !PREEMPTION + select ARCH_INLINE_SPIN_TRYLOCK if !PREEMPTION + select ARCH_INLINE_SPIN_TRYLOCK_BH if !PREEMPTION + select ARCH_INLINE_SPIN_LOCK if !PREEMPTION + select ARCH_INLINE_SPIN_LOCK_BH if !PREEMPTION + select ARCH_INLINE_SPIN_LOCK_IRQ if !PREEMPTION + select ARCH_INLINE_SPIN_LOCK_IRQSAVE if !PREEMPTION + select ARCH_INLINE_SPIN_UNLOCK if !PREEMPTION + select ARCH_INLINE_SPIN_UNLOCK_BH if !PREEMPTION + select ARCH_INLINE_SPIN_UNLOCK_IRQ if !PREEMPTION + select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION select ARCH_WANT_FRAME_POINTERS if !CPU_CK610 && $(cc-option,-mbacktrace) select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT select COMMON_CLK @@ -40,6 +67,8 @@ config CSKY select GX6605S_TIMER if CPU_CK610 select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_AUDITSYSCALL + select HAVE_ARCH_JUMP_LABEL if !CPU_CK610 + select HAVE_ARCH_JUMP_LABEL_RELATIVE select HAVE_ARCH_MMAP_RND_BITS select HAVE_ARCH_SECCOMP_FILTER select HAVE_CONTEXT_TRACKING_USER diff --git a/arch/csky/abiv1/inc/abi/string.h b/arch/csky/abiv1/inc/abi/string.h index 9d95594b0feb..de50117b904d 100644 --- a/arch/csky/abiv1/inc/abi/string.h +++ b/arch/csky/abiv1/inc/abi/string.h @@ -6,4 +6,10 @@ #define __HAVE_ARCH_MEMCPY extern void *memcpy(void *, const void *, __kernel_size_t); +#define __HAVE_ARCH_MEMMOVE +extern void *memmove(void *, const void *, __kernel_size_t); + +#define __HAVE_ARCH_MEMSET +extern void *memset(void *, int, __kernel_size_t); + #endif /* __ABI_CSKY_STRING_H */ diff --git a/arch/csky/include/asm/Kbuild b/arch/csky/include/asm/Kbuild index 103207a58f97..1117c28cb7e8 100644 --- a/arch/csky/include/asm/Kbuild +++ b/arch/csky/include/asm/Kbuild @@ -3,10 +3,10 @@ generic-y += asm-offsets.h generic-y += extable.h generic-y += gpio.h generic-y += kvm_para.h -generic-y += spinlock.h -generic-y += spinlock_types.h +generic-y += mcs_spinlock.h generic-y += qrwlock.h generic-y += qrwlock_types.h +generic-y += qspinlock.h generic-y += parport.h generic-y += user.h generic-y += vmlinux.lds.h diff --git a/arch/csky/include/asm/cmpxchg.h b/arch/csky/include/asm/cmpxchg.h index 5b8faccd65e4..916043b845f1 100644 --- a/arch/csky/include/asm/cmpxchg.h +++ b/arch/csky/include/asm/cmpxchg.h @@ -4,10 +4,9 @@ #define __ASM_CSKY_CMPXCHG_H #ifdef CONFIG_SMP +#include #include -extern void __bad_xchg(void); - #define __xchg_relaxed(new, ptr, size) \ ({ \ __typeof__(ptr) __ptr = (ptr); \ @@ -15,6 +14,26 @@ extern void __bad_xchg(void); __typeof__(*(ptr)) __ret; \ unsigned long tmp; \ switch (size) { \ + case 2: { \ + u32 ret; \ + u32 shif = ((ulong)__ptr & 2) ? 16 : 0; \ + u32 mask = 0xffff << shif; \ + __ptr = (__typeof__(ptr))((ulong)__ptr & ~2); \ + __asm__ __volatile__ ( \ + "1: ldex.w %0, (%4)\n" \ + " and %1, %0, %2\n" \ + " or %1, %1, %3\n" \ + " stex.w %1, (%4)\n" \ + " bez %1, 1b\n" \ + : "=&r" (ret), "=&r" (tmp) \ + : "r" (~mask), \ + "r" ((u32)__new << shif), \ + "r" (__ptr) \ + : "memory"); \ + __ret = (__typeof__(*(ptr))) \ + ((ret & mask) >> shif); \ + break; \ + } \ case 4: \ asm volatile ( \ "1: ldex.w %0, (%3) \n" \ @@ -26,7 +45,7 @@ extern void __bad_xchg(void); :); \ break; \ default: \ - __bad_xchg(); \ + BUILD_BUG(); \ } \ __ret; \ }) @@ -56,7 +75,7 @@ extern void __bad_xchg(void); :); \ break; \ default: \ - __bad_xchg(); \ + BUILD_BUG(); \ } \ __ret; \ }) @@ -87,7 +106,7 @@ extern void __bad_xchg(void); :); \ break; \ default: \ - __bad_xchg(); \ + BUILD_BUG(); \ } \ __ret; \ }) @@ -119,7 +138,7 @@ extern void __bad_xchg(void); :); \ break; \ default: \ - __bad_xchg(); \ + BUILD_BUG(); \ } \ __ret; \ }) diff --git a/arch/csky/include/asm/jump_label.h b/arch/csky/include/asm/jump_label.h new file mode 100644 index 000000000000..d488ba6084bc --- /dev/null +++ b/arch/csky/include/asm/jump_label.h @@ -0,0 +1,47 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ASM_CSKY_JUMP_LABEL_H +#define __ASM_CSKY_JUMP_LABEL_H + +#ifndef __ASSEMBLY__ + +#include + +#define JUMP_LABEL_NOP_SIZE 4 + +static __always_inline bool arch_static_branch(struct static_key *key, + bool branch) +{ + asm_volatile_goto( + "1: nop32 \n" + " .pushsection __jump_table, \"aw\" \n" + " .align 2 \n" + " .long 1b - ., %l[label] - . \n" + " .long %0 - . \n" + " .popsection \n" + : : "i"(&((char *)key)[branch]) : : label); + + return false; +label: + return true; +} + +static __always_inline bool arch_static_branch_jump(struct static_key *key, + bool branch) +{ + asm_volatile_goto( + "1: bsr32 %l[label] \n" + " .pushsection __jump_table, \"aw\" \n" + " .align 2 \n" + " .long 1b - ., %l[label] - . \n" + " .long %0 - . \n" + " .popsection \n" + : : "i"(&((char *)key)[branch]) : : label); + + return false; +label: + return true; +} + +#endif /* __ASSEMBLY__ */ +#endif /* __ASM_CSKY_JUMP_LABEL_H */ diff --git a/arch/csky/include/asm/sections.h b/arch/csky/include/asm/sections.h new file mode 100644 index 000000000000..4192cba8445d --- /dev/null +++ b/arch/csky/include/asm/sections.h @@ -0,0 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_SECTIONS_H +#define __ASM_SECTIONS_H + +#include + +extern char _start[]; + +#endif /* __ASM_SECTIONS_H */ diff --git a/arch/csky/include/asm/spinlock.h b/arch/csky/include/asm/spinlock.h new file mode 100644 index 000000000000..83a2005341f5 --- /dev/null +++ b/arch/csky/include/asm/spinlock.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_CSKY_SPINLOCK_H +#define __ASM_CSKY_SPINLOCK_H + +#include +#include + +/* See include/linux/spinlock.h */ +#define smp_mb__after_spinlock() smp_mb() + +#endif /* __ASM_CSKY_SPINLOCK_H */ diff --git a/arch/csky/include/asm/spinlock_types.h b/arch/csky/include/asm/spinlock_types.h new file mode 100644 index 000000000000..75bdf3af80ba --- /dev/null +++ b/arch/csky/include/asm/spinlock_types.h @@ -0,0 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __ASM_CSKY_SPINLOCK_TYPES_H +#define __ASM_CSKY_SPINLOCK_TYPES_H + +#include +#include + +#endif /* __ASM_CSKY_SPINLOCK_TYPES_H */ diff --git a/arch/csky/kernel/Makefile b/arch/csky/kernel/Makefile index 4eb41421ca5b..6f14c924b20d 100644 --- a/arch/csky/kernel/Makefile +++ b/arch/csky/kernel/Makefile @@ -13,6 +13,7 @@ obj-$(CONFIG_STACKTRACE) += stacktrace.o obj-$(CONFIG_CSKY_PMU_V1) += perf_event.o obj-$(CONFIG_PERF_EVENTS) += perf_callchain.o obj-$(CONFIG_HAVE_PERF_REGS) += perf_regs.o +obj-$(CONFIG_JUMP_LABEL) += jump_label.o ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE) diff --git a/arch/csky/kernel/jump_label.c b/arch/csky/kernel/jump_label.c new file mode 100644 index 000000000000..d0e8b21447e1 --- /dev/null +++ b/arch/csky/kernel/jump_label.c @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include + +#define NOP32_HI 0xc400 +#define NOP32_LO 0x4820 +#define BSR_LINK 0xe000 + +void arch_jump_label_transform(struct jump_entry *entry, + enum jump_label_type type) +{ + unsigned long addr = jump_entry_code(entry); + u16 insn[2]; + int ret = 0; + + if (type == JUMP_LABEL_JMP) { + long offset = jump_entry_target(entry) - jump_entry_code(entry); + + if (WARN_ON(offset & 1 || offset < -67108864 || offset >= 67108864)) + return; + + offset = offset >> 1; + + insn[0] = BSR_LINK | + ((uint16_t)((unsigned long) offset >> 16) & 0x3ff); + insn[1] = (uint16_t)((unsigned long) offset & 0xffff); + } else { + insn[0] = NOP32_HI; + insn[1] = NOP32_LO; + } + + ret = copy_to_kernel_nofault((void *)addr, insn, 4); + WARN_ON(ret); + + flush_icache_range(addr, addr + 4); +} + +void arch_jump_label_transform_static(struct jump_entry *entry, + enum jump_label_type type) +{ + /* + * We use the same instructions in the arch_static_branch and + * arch_static_branch_jump inline functions, so there's no + * need to patch them up here. + * The core will call arch_jump_label_transform when those + * instructions need to be replaced. + */ + arch_jump_label_transform(entry, type); +} diff --git a/arch/csky/kernel/probes/kprobes.c b/arch/csky/kernel/probes/kprobes.c index 34ba684d5962..3c6e5c725d81 100644 --- a/arch/csky/kernel/probes/kprobes.c +++ b/arch/csky/kernel/probes/kprobes.c @@ -124,6 +124,10 @@ void __kprobes arch_disarm_kprobe(struct kprobe *p) void __kprobes arch_remove_kprobe(struct kprobe *p) { + if (p->ainsn.api.insn) { + free_insn_slot(p->ainsn.api.insn, 0); + p->ainsn.api.insn = NULL; + } } static void __kprobes save_previous_kprobe(struct kprobe_ctlblk *kcb) diff --git a/arch/csky/kernel/setup.c b/arch/csky/kernel/setup.c index c64e7be2045b..106fbf0b6f3b 100644 --- a/arch/csky/kernel/setup.c +++ b/arch/csky/kernel/setup.c @@ -31,7 +31,7 @@ static void __init csky_memblock_init(void) unsigned long max_zone_pfn[MAX_NR_ZONES] = { 0 }; signed long size; - memblock_reserve(__pa(_stext), _end - _stext); + memblock_reserve(__pa(_start), _end - _start); early_init_fdt_reserve_self(); early_init_fdt_scan_reserved_mem(); @@ -78,7 +78,7 @@ void __init setup_arch(char **cmdline_p) pr_info("Phys. mem: %ldMB\n", (unsigned long) memblock_phys_mem_size()/1024/1024); - setup_initial_init_mm(_stext, _etext, _edata, _end); + setup_initial_init_mm(_start, _etext, _edata, _end); parse_early_param(); diff --git a/arch/csky/kernel/vmlinux.lds.S b/arch/csky/kernel/vmlinux.lds.S index e8b1a4a49798..68c980d08482 100644 --- a/arch/csky/kernel/vmlinux.lds.S +++ b/arch/csky/kernel/vmlinux.lds.S @@ -22,17 +22,13 @@ SECTIONS { . = PAGE_OFFSET + PHYS_OFFSET_OFFSET; - _stext = .; - __init_begin = .; + _start = .; HEAD_TEXT_SECTION - INIT_TEXT_SECTION(PAGE_SIZE) - INIT_DATA_SECTION(PAGE_SIZE) - PERCPU_SECTION(L1_CACHE_BYTES) . = ALIGN(PAGE_SIZE); - __init_end = .; .text : AT(ADDR(.text) - LOAD_OFFSET) { _text = .; + _stext = .; VBR_BASE IRQENTRY_TEXT SOFTIRQENTRY_TEXT @@ -48,7 +44,12 @@ SECTIONS /* __init_begin __init_end must be page aligned for free_initmem */ . = ALIGN(PAGE_SIZE); - + __init_begin = .; + INIT_TEXT_SECTION(PAGE_SIZE) + INIT_DATA_SECTION(PAGE_SIZE) + PERCPU_SECTION(L1_CACHE_BYTES) + . = ALIGN(PAGE_SIZE); + __init_end = .; _sdata = .; RO_DATA(PAGE_SIZE) diff --git a/arch/csky/mm/asid.c b/arch/csky/mm/asid.c index b2e914745c1d..7fb6c417bbac 100644 --- a/arch/csky/mm/asid.c +++ b/arch/csky/mm/asid.c @@ -27,7 +27,7 @@ static void flush_context(struct asid_info *info) u64 asid; /* Update the list of reserved ASIDs and the ASID bitmap. */ - bitmap_clear(info->map, 0, NUM_CTXT_ASIDS(info)); + bitmap_zero(info->map, NUM_CTXT_ASIDS(info)); for_each_possible_cpu(i) { asid = atomic64_xchg_relaxed(&active_asid(info, i), 0); @@ -178,8 +178,7 @@ int asid_allocator_init(struct asid_info *info, */ WARN_ON(NUM_CTXT_ASIDS(info) - 1 <= num_possible_cpus()); atomic64_set(&info->generation, ASID_FIRST_VERSION(info)); - info->map = kcalloc(BITS_TO_LONGS(NUM_CTXT_ASIDS(info)), - sizeof(*info->map), GFP_KERNEL); + info->map = bitmap_zalloc(NUM_CTXT_ASIDS(info), GFP_KERNEL); if (!info->map) return -ENOMEM; diff --git a/arch/mips/configs/cavium_octeon_defconfig b/arch/mips/configs/cavium_octeon_defconfig index b6695367aa33..97ceaf080c0c 100644 --- a/arch/mips/configs/cavium_octeon_defconfig +++ b/arch/mips/configs/cavium_octeon_defconfig @@ -134,7 +134,7 @@ CONFIG_RTC_CLASS=y CONFIG_RTC_DRV_DS1307=y CONFIG_STAGING=y CONFIG_OCTEON_ETHERNET=y -CONFIG_OCTEON_USB=y +CONFIG_USB_OCTEON_HCD=y # CONFIG_IOMMU_SUPPORT is not set CONFIG_RAS=y CONFIG_EXT4_FS=y diff --git a/arch/powerpc/kernel/kgdb.c b/arch/powerpc/kernel/kgdb.c index 9f8d0fa7b718..a20deebf233f 100644 --- a/arch/powerpc/kernel/kgdb.c +++ b/arch/powerpc/kernel/kgdb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * PowerPC backend to the KGDB stub. * @@ -8,10 +9,6 @@ * PPC32 support restored by Vitaly Wool and * Sergei Shtylyov * Copyright (C) 2007-2008 Wind River Systems, Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program as licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/arch/riscv/include/asm/csr.h b/arch/riscv/include/asm/csr.h index 6d85655e7edf..17516afc389a 100644 --- a/arch/riscv/include/asm/csr.h +++ b/arch/riscv/include/asm/csr.h @@ -156,6 +156,18 @@ (_AC(1, UL) << IRQ_S_TIMER) | \ (_AC(1, UL) << IRQ_S_EXT)) +/* xENVCFG flags */ +#define ENVCFG_STCE (_AC(1, ULL) << 63) +#define ENVCFG_PBMTE (_AC(1, ULL) << 62) +#define ENVCFG_CBZE (_AC(1, UL) << 7) +#define ENVCFG_CBCFE (_AC(1, UL) << 6) +#define ENVCFG_CBIE_SHIFT 4 +#define ENVCFG_CBIE (_AC(0x3, UL) << ENVCFG_CBIE_SHIFT) +#define ENVCFG_CBIE_ILL _AC(0x0, UL) +#define ENVCFG_CBIE_FLUSH _AC(0x1, UL) +#define ENVCFG_CBIE_INV _AC(0x3, UL) +#define ENVCFG_FIOM _AC(0x1, UL) + /* symbolic CSR names: */ #define CSR_CYCLE 0xc00 #define CSR_TIME 0xc01 @@ -252,7 +264,9 @@ #define CSR_HTIMEDELTA 0x605 #define CSR_HCOUNTEREN 0x606 #define CSR_HGEIE 0x607 +#define CSR_HENVCFG 0x60a #define CSR_HTIMEDELTAH 0x615 +#define CSR_HENVCFGH 0x61a #define CSR_HTVAL 0x643 #define CSR_HIP 0x644 #define CSR_HVIP 0x645 @@ -264,6 +278,8 @@ #define CSR_MISA 0x301 #define CSR_MIE 0x304 #define CSR_MTVEC 0x305 +#define CSR_MENVCFG 0x30a +#define CSR_MENVCFGH 0x31a #define CSR_MSCRATCH 0x340 #define CSR_MEPC 0x341 #define CSR_MCAUSE 0x342 diff --git a/arch/riscv/include/asm/kvm_host.h b/arch/riscv/include/asm/kvm_host.h index 319c8aeb42af..60c517e4d576 100644 --- a/arch/riscv/include/asm/kvm_host.h +++ b/arch/riscv/include/asm/kvm_host.h @@ -14,7 +14,9 @@ #include #include #include +#include #include +#include #include #define KVM_MAX_VCPUS 1024 @@ -63,6 +65,8 @@ struct kvm_vcpu_stat { u64 wfi_exit_stat; u64 mmio_exit_user; u64 mmio_exit_kernel; + u64 csr_exit_user; + u64 csr_exit_kernel; u64 exits; }; @@ -90,14 +94,6 @@ struct kvm_arch { struct kvm_guest_timer timer; }; -struct kvm_mmio_decode { - unsigned long insn; - int insn_len; - int len; - int shift; - int return_handled; -}; - struct kvm_sbi_context { int return_handled; }; @@ -170,7 +166,7 @@ struct kvm_vcpu_arch { int last_exit_cpu; /* ISA feature bits (similar to MISA) */ - unsigned long isa; + DECLARE_BITMAP(isa, RISCV_ISA_EXT_MAX); /* SSCRATCH, STVEC, and SCOUNTEREN of Host */ unsigned long host_sscratch; @@ -216,6 +212,9 @@ struct kvm_vcpu_arch { /* MMIO instruction details */ struct kvm_mmio_decode mmio_decode; + /* CSR instruction details */ + struct kvm_csr_decode csr_decode; + /* SBI context */ struct kvm_sbi_context sbi_context; @@ -285,6 +284,11 @@ void kvm_riscv_hfence_vvma_gva(struct kvm *kvm, void kvm_riscv_hfence_vvma_all(struct kvm *kvm, unsigned long hbase, unsigned long hmask); +int kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, + phys_addr_t hpa, unsigned long size, + bool writable, bool in_atomic); +void kvm_riscv_gstage_iounmap(struct kvm *kvm, gpa_t gpa, + unsigned long size); int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, struct kvm_memory_slot *memslot, gpa_t gpa, unsigned long hva, bool is_write); @@ -303,14 +307,12 @@ void kvm_riscv_gstage_vmid_update(struct kvm_vcpu *vcpu); void __kvm_riscv_unpriv_trap(void); -void kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu); unsigned long kvm_riscv_vcpu_unpriv_read(struct kvm_vcpu *vcpu, bool read_insn, unsigned long guest_addr, struct kvm_cpu_trap *trap); void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu, struct kvm_cpu_trap *trap); -int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run); int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_cpu_trap *trap); diff --git a/arch/riscv/include/asm/kvm_vcpu_fp.h b/arch/riscv/include/asm/kvm_vcpu_fp.h index 4da9b8e0f050..b5540147409f 100644 --- a/arch/riscv/include/asm/kvm_vcpu_fp.h +++ b/arch/riscv/include/asm/kvm_vcpu_fp.h @@ -22,9 +22,9 @@ void __kvm_riscv_fp_d_restore(struct kvm_cpu_context *context); void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu); void kvm_riscv_vcpu_guest_fp_save(struct kvm_cpu_context *cntx, - unsigned long isa); + const unsigned long *isa); void kvm_riscv_vcpu_guest_fp_restore(struct kvm_cpu_context *cntx, - unsigned long isa); + const unsigned long *isa); void kvm_riscv_vcpu_host_fp_save(struct kvm_cpu_context *cntx); void kvm_riscv_vcpu_host_fp_restore(struct kvm_cpu_context *cntx); #else @@ -32,12 +32,12 @@ static inline void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu) { } static inline void kvm_riscv_vcpu_guest_fp_save(struct kvm_cpu_context *cntx, - unsigned long isa) + const unsigned long *isa) { } static inline void kvm_riscv_vcpu_guest_fp_restore( struct kvm_cpu_context *cntx, - unsigned long isa) + const unsigned long *isa) { } static inline void kvm_riscv_vcpu_host_fp_save(struct kvm_cpu_context *cntx) diff --git a/arch/riscv/include/asm/kvm_vcpu_insn.h b/arch/riscv/include/asm/kvm_vcpu_insn.h new file mode 100644 index 000000000000..350011c83581 --- /dev/null +++ b/arch/riscv/include/asm/kvm_vcpu_insn.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#ifndef __KVM_VCPU_RISCV_INSN_H +#define __KVM_VCPU_RISCV_INSN_H + +struct kvm_vcpu; +struct kvm_run; +struct kvm_cpu_trap; + +struct kvm_mmio_decode { + unsigned long insn; + int insn_len; + int len; + int shift; + int return_handled; +}; + +struct kvm_csr_decode { + unsigned long insn; + int return_handled; +}; + +/* Return values used by function emulating a particular instruction */ +enum kvm_insn_return { + KVM_INSN_EXIT_TO_USER_SPACE = 0, + KVM_INSN_CONTINUE_NEXT_SEPC, + KVM_INSN_CONTINUE_SAME_SEPC, + KVM_INSN_ILLEGAL_TRAP, + KVM_INSN_VIRTUAL_TRAP +}; + +void kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu); +int kvm_riscv_vcpu_csr_return(struct kvm_vcpu *vcpu, struct kvm_run *run); +int kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, + struct kvm_cpu_trap *trap); + +int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long fault_addr, + unsigned long htinst); +int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long fault_addr, + unsigned long htinst); +int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run); + +#endif diff --git a/arch/riscv/include/asm/kvm_vcpu_timer.h b/arch/riscv/include/asm/kvm_vcpu_timer.h index 375281eb49e0..50138e2eb91b 100644 --- a/arch/riscv/include/asm/kvm_vcpu_timer.h +++ b/arch/riscv/include/asm/kvm_vcpu_timer.h @@ -39,6 +39,6 @@ int kvm_riscv_vcpu_timer_init(struct kvm_vcpu *vcpu); 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); -int kvm_riscv_guest_timer_init(struct kvm *kvm); +void kvm_riscv_guest_timer_init(struct kvm *kvm); #endif diff --git a/arch/riscv/include/uapi/asm/kvm.h b/arch/riscv/include/uapi/asm/kvm.h index 6119368ba6d5..24b2a6e27698 100644 --- a/arch/riscv/include/uapi/asm/kvm.h +++ b/arch/riscv/include/uapi/asm/kvm.h @@ -96,6 +96,7 @@ enum KVM_RISCV_ISA_EXT_ID { KVM_RISCV_ISA_EXT_H, KVM_RISCV_ISA_EXT_I, KVM_RISCV_ISA_EXT_M, + KVM_RISCV_ISA_EXT_SVPBMT, KVM_RISCV_ISA_EXT_MAX, }; diff --git a/arch/riscv/kvm/Makefile b/arch/riscv/kvm/Makefile index e5c56182f48f..019df9208bdd 100644 --- a/arch/riscv/kvm/Makefile +++ b/arch/riscv/kvm/Makefile @@ -17,6 +17,7 @@ kvm-y += mmu.o kvm-y += vcpu.o kvm-y += vcpu_exit.o kvm-y += vcpu_fp.o +kvm-y += vcpu_insn.o kvm-y += vcpu_switch.o kvm-y += vcpu_sbi.o kvm-$(CONFIG_RISCV_SBI_V01) += vcpu_sbi_v01.o diff --git a/arch/riscv/kvm/mmu.c b/arch/riscv/kvm/mmu.c index 9826073fbc67..3a35b2d95697 100644 --- a/arch/riscv/kvm/mmu.c +++ b/arch/riscv/kvm/mmu.c @@ -343,23 +343,24 @@ static void gstage_wp_memory_region(struct kvm *kvm, int slot) kvm_flush_remote_tlbs(kvm); } -static int gstage_ioremap(struct kvm *kvm, gpa_t gpa, phys_addr_t hpa, - unsigned long size, bool writable) +int kvm_riscv_gstage_ioremap(struct kvm *kvm, gpa_t gpa, + phys_addr_t hpa, unsigned long size, + bool writable, bool in_atomic) { pte_t pte; int ret = 0; unsigned long pfn; phys_addr_t addr, end; - struct kvm_mmu_memory_cache pcache; - - memset(&pcache, 0, sizeof(pcache)); - pcache.gfp_zero = __GFP_ZERO; + struct kvm_mmu_memory_cache pcache = { + .gfp_custom = (in_atomic) ? GFP_ATOMIC | __GFP_ACCOUNT : 0, + .gfp_zero = __GFP_ZERO, + }; end = (gpa + size + PAGE_SIZE - 1) & PAGE_MASK; pfn = __phys_to_pfn(hpa); for (addr = gpa; addr < end; addr += PAGE_SIZE) { - pte = pfn_pte(pfn, PAGE_KERNEL); + pte = pfn_pte(pfn, PAGE_KERNEL_IO); if (!writable) pte = pte_wrprotect(pte); @@ -382,6 +383,13 @@ out: return ret; } +void kvm_riscv_gstage_iounmap(struct kvm *kvm, gpa_t gpa, unsigned long size) +{ + spin_lock(&kvm->mmu_lock); + gstage_unmap_range(kvm, gpa, size, false); + spin_unlock(&kvm->mmu_lock); +} + void kvm_arch_mmu_enable_log_dirty_pt_masked(struct kvm *kvm, struct kvm_memory_slot *slot, gfn_t gfn_offset, @@ -517,8 +525,9 @@ int kvm_arch_prepare_memory_region(struct kvm *kvm, goto out; } - ret = gstage_ioremap(kvm, gpa, pa, - vm_end - vm_start, writable); + ret = kvm_riscv_gstage_ioremap(kvm, gpa, pa, + vm_end - vm_start, + writable, false); if (ret) break; } @@ -611,7 +620,7 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, { int ret; kvm_pfn_t hfn; - bool writeable; + bool writable; short vma_pageshift; gfn_t gfn = gpa >> PAGE_SHIFT; struct vm_area_struct *vma; @@ -659,7 +668,7 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, mmu_seq = kvm->mmu_notifier_seq; - hfn = gfn_to_pfn_prot(kvm, gfn, is_write, &writeable); + hfn = gfn_to_pfn_prot(kvm, gfn, is_write, &writable); if (hfn == KVM_PFN_ERR_HWPOISON) { send_sig_mceerr(BUS_MCEERR_AR, (void __user *)hva, vma_pageshift, current); @@ -673,14 +682,14 @@ int kvm_riscv_gstage_map(struct kvm_vcpu *vcpu, * for write faults. */ if (logging && !is_write) - writeable = false; + writable = false; spin_lock(&kvm->mmu_lock); if (mmu_notifier_retry(kvm, mmu_seq)) goto out_unlock; - if (writeable) { + if (writable) { kvm_set_pfn_dirty(hfn); mark_page_dirty(kvm, gfn); ret = gstage_map_page(kvm, pcache, gpa, hfn << PAGE_SHIFT, diff --git a/arch/riscv/kvm/vcpu.c b/arch/riscv/kvm/vcpu.c index f3455dc013fa..5d271b597613 100644 --- a/arch/riscv/kvm/vcpu.c +++ b/arch/riscv/kvm/vcpu.c @@ -26,6 +26,8 @@ const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = { STATS_DESC_COUNTER(VCPU, wfi_exit_stat), STATS_DESC_COUNTER(VCPU, mmio_exit_user), STATS_DESC_COUNTER(VCPU, mmio_exit_kernel), + STATS_DESC_COUNTER(VCPU, csr_exit_user), + STATS_DESC_COUNTER(VCPU, csr_exit_kernel), STATS_DESC_COUNTER(VCPU, exits) }; @@ -38,16 +40,58 @@ const struct kvm_stats_header kvm_vcpu_stats_header = { sizeof(kvm_vcpu_stats_desc), }; -#define KVM_RISCV_ISA_DISABLE_ALLOWED (riscv_isa_extension_mask(d) | \ - riscv_isa_extension_mask(f)) +#define KVM_RISCV_BASE_ISA_MASK GENMASK(25, 0) -#define KVM_RISCV_ISA_DISABLE_NOT_ALLOWED (riscv_isa_extension_mask(a) | \ - riscv_isa_extension_mask(c) | \ - riscv_isa_extension_mask(i) | \ - riscv_isa_extension_mask(m)) +/* Mapping between KVM ISA Extension ID & Host ISA extension ID */ +static const unsigned long kvm_isa_ext_arr[] = { + RISCV_ISA_EXT_a, + RISCV_ISA_EXT_c, + RISCV_ISA_EXT_d, + RISCV_ISA_EXT_f, + RISCV_ISA_EXT_h, + RISCV_ISA_EXT_i, + RISCV_ISA_EXT_m, + RISCV_ISA_EXT_SVPBMT, +}; -#define KVM_RISCV_ISA_ALLOWED (KVM_RISCV_ISA_DISABLE_ALLOWED | \ - KVM_RISCV_ISA_DISABLE_NOT_ALLOWED) +static unsigned long kvm_riscv_vcpu_base2isa_ext(unsigned long base_ext) +{ + unsigned long i; + + for (i = 0; i < KVM_RISCV_ISA_EXT_MAX; i++) { + if (kvm_isa_ext_arr[i] == base_ext) + return i; + } + + return KVM_RISCV_ISA_EXT_MAX; +} + +static bool kvm_riscv_vcpu_isa_enable_allowed(unsigned long ext) +{ + switch (ext) { + case KVM_RISCV_ISA_EXT_H: + return false; + default: + break; + } + + return true; +} + +static bool kvm_riscv_vcpu_isa_disable_allowed(unsigned long ext) +{ + switch (ext) { + case KVM_RISCV_ISA_EXT_A: + case KVM_RISCV_ISA_EXT_C: + case KVM_RISCV_ISA_EXT_I: + case KVM_RISCV_ISA_EXT_M: + return false; + default: + break; + } + + return true; +} static void kvm_riscv_reset_vcpu(struct kvm_vcpu *vcpu) { @@ -99,13 +143,20 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) { struct kvm_cpu_context *cntx; struct kvm_vcpu_csr *reset_csr = &vcpu->arch.guest_reset_csr; + unsigned long host_isa, i; /* Mark this VCPU never ran */ vcpu->arch.ran_atleast_once = false; vcpu->arch.mmu_page_cache.gfp_zero = __GFP_ZERO; + bitmap_zero(vcpu->arch.isa, RISCV_ISA_EXT_MAX); /* Setup ISA features available to VCPU */ - vcpu->arch.isa = riscv_isa_extension_base(NULL) & KVM_RISCV_ISA_ALLOWED; + for (i = 0; i < ARRAY_SIZE(kvm_isa_ext_arr); i++) { + host_isa = kvm_isa_ext_arr[i]; + if (__riscv_isa_extension_available(NULL, host_isa) && + kvm_riscv_vcpu_isa_enable_allowed(i)) + set_bit(host_isa, vcpu->arch.isa); + } /* Setup VCPU hfence queue */ spin_lock_init(&vcpu->arch.hfence_lock); @@ -199,7 +250,7 @@ static int kvm_riscv_vcpu_get_reg_config(struct kvm_vcpu *vcpu, switch (reg_num) { case KVM_REG_RISCV_CONFIG_REG(isa): - reg_val = vcpu->arch.isa; + reg_val = vcpu->arch.isa[0] & KVM_RISCV_BASE_ISA_MASK; break; default: return -EINVAL; @@ -219,7 +270,7 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | KVM_REG_SIZE_MASK | KVM_REG_RISCV_CONFIG); - unsigned long reg_val; + unsigned long i, isa_ext, reg_val; if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) return -EINVAL; @@ -227,13 +278,32 @@ static int kvm_riscv_vcpu_set_reg_config(struct kvm_vcpu *vcpu, if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) return -EFAULT; + /* This ONE REG interface is only defined for single letter extensions */ + if (fls(reg_val) >= RISCV_ISA_EXT_BASE) + return -EINVAL; + switch (reg_num) { case KVM_REG_RISCV_CONFIG_REG(isa): if (!vcpu->arch.ran_atleast_once) { - /* Ignore the disable request for these extensions */ - vcpu->arch.isa = reg_val | KVM_RISCV_ISA_DISABLE_NOT_ALLOWED; - vcpu->arch.isa &= riscv_isa_extension_base(NULL); - vcpu->arch.isa &= KVM_RISCV_ISA_ALLOWED; + /* Ignore the enable/disable request for certain extensions */ + for (i = 0; i < RISCV_ISA_EXT_BASE; i++) { + isa_ext = kvm_riscv_vcpu_base2isa_ext(i); + if (isa_ext >= KVM_RISCV_ISA_EXT_MAX) { + reg_val &= ~BIT(i); + continue; + } + if (!kvm_riscv_vcpu_isa_enable_allowed(isa_ext)) + if (reg_val & BIT(i)) + reg_val &= ~BIT(i); + if (!kvm_riscv_vcpu_isa_disable_allowed(isa_ext)) + if (!(reg_val & BIT(i))) + reg_val |= BIT(i); + } + reg_val &= riscv_isa_extension_base(NULL); + /* Do not modify anything beyond single letter extensions */ + reg_val = (vcpu->arch.isa[0] & ~KVM_RISCV_BASE_ISA_MASK) | + (reg_val & KVM_RISCV_BASE_ISA_MASK); + vcpu->arch.isa[0] = reg_val; kvm_riscv_vcpu_fp_reset(vcpu); } else { return -EOPNOTSUPP; @@ -374,17 +444,6 @@ static int kvm_riscv_vcpu_set_reg_csr(struct kvm_vcpu *vcpu, return 0; } -/* Mapping between KVM ISA Extension ID & Host ISA extension ID */ -static unsigned long kvm_isa_ext_arr[] = { - RISCV_ISA_EXT_a, - RISCV_ISA_EXT_c, - RISCV_ISA_EXT_d, - RISCV_ISA_EXT_f, - RISCV_ISA_EXT_h, - RISCV_ISA_EXT_i, - RISCV_ISA_EXT_m, -}; - static int kvm_riscv_vcpu_get_reg_isa_ext(struct kvm_vcpu *vcpu, const struct kvm_one_reg *reg) { @@ -399,11 +458,12 @@ static int kvm_riscv_vcpu_get_reg_isa_ext(struct kvm_vcpu *vcpu, if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) return -EINVAL; - if (reg_num >= KVM_RISCV_ISA_EXT_MAX || reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) + if (reg_num >= KVM_RISCV_ISA_EXT_MAX || + reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) return -EINVAL; host_isa_ext = kvm_isa_ext_arr[reg_num]; - if (__riscv_isa_extension_available(&vcpu->arch.isa, host_isa_ext)) + if (__riscv_isa_extension_available(vcpu->arch.isa, host_isa_ext)) reg_val = 1; /* Mark the given extension as available */ if (copy_to_user(uaddr, ®_val, KVM_REG_SIZE(reg->id))) @@ -422,12 +482,12 @@ static int kvm_riscv_vcpu_set_reg_isa_ext(struct kvm_vcpu *vcpu, KVM_REG_RISCV_ISA_EXT); unsigned long reg_val; unsigned long host_isa_ext; - unsigned long host_isa_ext_mask; if (KVM_REG_SIZE(reg->id) != sizeof(unsigned long)) return -EINVAL; - if (reg_num >= KVM_RISCV_ISA_EXT_MAX || reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) + if (reg_num >= KVM_RISCV_ISA_EXT_MAX || + reg_num >= ARRAY_SIZE(kvm_isa_ext_arr)) return -EINVAL; if (copy_from_user(®_val, uaddr, KVM_REG_SIZE(reg->id))) @@ -437,30 +497,19 @@ static int kvm_riscv_vcpu_set_reg_isa_ext(struct kvm_vcpu *vcpu, if (!__riscv_isa_extension_available(NULL, host_isa_ext)) return -EOPNOTSUPP; - if (host_isa_ext >= RISCV_ISA_EXT_BASE && - host_isa_ext < RISCV_ISA_EXT_MAX) { - /* - * Multi-letter ISA extension. Currently there is no provision - * to enable/disable the multi-letter ISA extensions for guests. - * Return success if the request is to enable any ISA extension - * that is available in the hardware. - * Return -EOPNOTSUPP otherwise. - */ - if (!reg_val) - return -EOPNOTSUPP; - else - return 0; - } - - /* Single letter base ISA extension */ if (!vcpu->arch.ran_atleast_once) { - host_isa_ext_mask = BIT_MASK(host_isa_ext); - if (!reg_val && (host_isa_ext_mask & KVM_RISCV_ISA_DISABLE_ALLOWED)) - vcpu->arch.isa &= ~host_isa_ext_mask; + /* + * All multi-letter extension and a few single letter + * extension can be disabled + */ + if (reg_val == 1 && + kvm_riscv_vcpu_isa_enable_allowed(reg_num)) + set_bit(host_isa_ext, vcpu->arch.isa); + else if (!reg_val && + kvm_riscv_vcpu_isa_disable_allowed(reg_num)) + clear_bit(host_isa_ext, vcpu->arch.isa); else - vcpu->arch.isa |= host_isa_ext_mask; - vcpu->arch.isa &= riscv_isa_extension_base(NULL); - vcpu->arch.isa &= KVM_RISCV_ISA_ALLOWED; + return -EINVAL; kvm_riscv_vcpu_fp_reset(vcpu); } else { return -EOPNOTSUPP; @@ -729,6 +778,19 @@ int kvm_arch_vcpu_ioctl_set_guest_debug(struct kvm_vcpu *vcpu, return -EINVAL; } +static void kvm_riscv_vcpu_update_config(const unsigned long *isa) +{ + u64 henvcfg = 0; + + if (__riscv_isa_extension_available(isa, RISCV_ISA_EXT_SVPBMT)) + henvcfg |= ENVCFG_PBMTE; + + csr_write(CSR_HENVCFG, henvcfg); +#ifdef CONFIG_32BIT + csr_write(CSR_HENVCFGH, henvcfg >> 32); +#endif +} + void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) { struct kvm_vcpu_csr *csr = &vcpu->arch.guest_csr; @@ -743,6 +805,8 @@ void kvm_arch_vcpu_load(struct kvm_vcpu *vcpu, int cpu) csr_write(CSR_HVIP, csr->hvip); csr_write(CSR_VSATP, csr->vsatp); + kvm_riscv_vcpu_update_config(vcpu->arch.isa); + kvm_riscv_gstage_update_hgatp(vcpu); kvm_riscv_vcpu_timer_restore(vcpu); @@ -853,22 +917,26 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_vcpu_srcu_read_lock(vcpu); - /* Process MMIO value returned from user-space */ - if (run->exit_reason == KVM_EXIT_MMIO) { + switch (run->exit_reason) { + case KVM_EXIT_MMIO: + /* Process MMIO value returned from user-space */ ret = kvm_riscv_vcpu_mmio_return(vcpu, vcpu->run); - if (ret) { - kvm_vcpu_srcu_read_unlock(vcpu); - return ret; - } - } - - /* Process SBI value returned from user-space */ - if (run->exit_reason == KVM_EXIT_RISCV_SBI) { + break; + case KVM_EXIT_RISCV_SBI: + /* Process SBI value returned from user-space */ ret = kvm_riscv_vcpu_sbi_return(vcpu, vcpu->run); - if (ret) { - kvm_vcpu_srcu_read_unlock(vcpu); - return ret; - } + break; + case KVM_EXIT_RISCV_CSR: + /* Process CSR value returned from user-space */ + ret = kvm_riscv_vcpu_csr_return(vcpu, vcpu->run); + break; + default: + ret = 0; + break; + } + if (ret) { + kvm_vcpu_srcu_read_unlock(vcpu); + return ret; } if (run->immediate_exit) { @@ -890,8 +958,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_riscv_check_vcpu_requests(vcpu); - preempt_disable(); - local_irq_disable(); /* @@ -928,7 +994,6 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) kvm_request_pending(vcpu)) { vcpu->mode = OUTSIDE_GUEST_MODE; local_irq_enable(); - preempt_enable(); kvm_vcpu_srcu_read_lock(vcpu); continue; } @@ -962,6 +1027,8 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) /* Syncup interrupts state with HW */ kvm_riscv_vcpu_sync_interrupts(vcpu); + preempt_disable(); + /* * We must ensure that any pending interrupts are taken before * we exit guest timing so that timer ticks are accounted as diff --git a/arch/riscv/kvm/vcpu_exit.c b/arch/riscv/kvm/vcpu_exit.c index dbb09afd7546..d5c36386878a 100644 --- a/arch/riscv/kvm/vcpu_exit.c +++ b/arch/riscv/kvm/vcpu_exit.c @@ -6,435 +6,34 @@ * Anup Patel */ -#include -#include -#include #include #include -#define INSN_OPCODE_MASK 0x007c -#define INSN_OPCODE_SHIFT 2 -#define INSN_OPCODE_SYSTEM 28 - -#define INSN_MASK_WFI 0xffffffff -#define INSN_MATCH_WFI 0x10500073 - -#define INSN_MATCH_LB 0x3 -#define INSN_MASK_LB 0x707f -#define INSN_MATCH_LH 0x1003 -#define INSN_MASK_LH 0x707f -#define INSN_MATCH_LW 0x2003 -#define INSN_MASK_LW 0x707f -#define INSN_MATCH_LD 0x3003 -#define INSN_MASK_LD 0x707f -#define INSN_MATCH_LBU 0x4003 -#define INSN_MASK_LBU 0x707f -#define INSN_MATCH_LHU 0x5003 -#define INSN_MASK_LHU 0x707f -#define INSN_MATCH_LWU 0x6003 -#define INSN_MASK_LWU 0x707f -#define INSN_MATCH_SB 0x23 -#define INSN_MASK_SB 0x707f -#define INSN_MATCH_SH 0x1023 -#define INSN_MASK_SH 0x707f -#define INSN_MATCH_SW 0x2023 -#define INSN_MASK_SW 0x707f -#define INSN_MATCH_SD 0x3023 -#define INSN_MASK_SD 0x707f - -#define INSN_MATCH_C_LD 0x6000 -#define INSN_MASK_C_LD 0xe003 -#define INSN_MATCH_C_SD 0xe000 -#define INSN_MASK_C_SD 0xe003 -#define INSN_MATCH_C_LW 0x4000 -#define INSN_MASK_C_LW 0xe003 -#define INSN_MATCH_C_SW 0xc000 -#define INSN_MASK_C_SW 0xe003 -#define INSN_MATCH_C_LDSP 0x6002 -#define INSN_MASK_C_LDSP 0xe003 -#define INSN_MATCH_C_SDSP 0xe002 -#define INSN_MASK_C_SDSP 0xe003 -#define INSN_MATCH_C_LWSP 0x4002 -#define INSN_MASK_C_LWSP 0xe003 -#define INSN_MATCH_C_SWSP 0xc002 -#define INSN_MASK_C_SWSP 0xe003 - -#define INSN_16BIT_MASK 0x3 - -#define INSN_IS_16BIT(insn) (((insn) & INSN_16BIT_MASK) != INSN_16BIT_MASK) - -#define INSN_LEN(insn) (INSN_IS_16BIT(insn) ? 2 : 4) - -#ifdef CONFIG_64BIT -#define LOG_REGBYTES 3 -#else -#define LOG_REGBYTES 2 -#endif -#define REGBYTES (1 << LOG_REGBYTES) - -#define SH_RD 7 -#define SH_RS1 15 -#define SH_RS2 20 -#define SH_RS2C 2 - -#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1)) -#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \ - (RV_X(x, 10, 3) << 3) | \ - (RV_X(x, 5, 1) << 6)) -#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \ - (RV_X(x, 5, 2) << 6)) -#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \ - (RV_X(x, 12, 1) << 5) | \ - (RV_X(x, 2, 2) << 6)) -#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \ - (RV_X(x, 12, 1) << 5) | \ - (RV_X(x, 2, 3) << 6)) -#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \ - (RV_X(x, 7, 2) << 6)) -#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \ - (RV_X(x, 7, 3) << 6)) -#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3)) -#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3)) -#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5) - -#define SHIFT_RIGHT(x, y) \ - ((y) < 0 ? ((x) << -(y)) : ((x) >> (y))) - -#define REG_MASK \ - ((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES)) - -#define REG_OFFSET(insn, pos) \ - (SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK) - -#define REG_PTR(insn, pos, regs) \ - ((ulong *)((ulong)(regs) + REG_OFFSET(insn, pos))) - -#define GET_RM(insn) (((insn) >> 12) & 7) - -#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs)) -#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs)) -#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs)) -#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs)) -#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs)) -#define GET_SP(regs) (*REG_PTR(2, 0, regs)) -#define SET_RD(insn, regs, val) (*REG_PTR(insn, SH_RD, regs) = (val)) -#define IMM_I(insn) ((s32)(insn) >> 20) -#define IMM_S(insn) (((s32)(insn) >> 25 << 5) | \ - (s32)(((insn) >> 7) & 0x1f)) -#define MASK_FUNCT3 0x7000 - -static int truly_illegal_insn(struct kvm_vcpu *vcpu, - struct kvm_run *run, - ulong insn) -{ - struct kvm_cpu_trap utrap = { 0 }; - - /* Redirect trap to Guest VCPU */ - utrap.sepc = vcpu->arch.guest_context.sepc; - utrap.scause = EXC_INST_ILLEGAL; - utrap.stval = insn; - kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); - - return 1; -} - -static int system_opcode_insn(struct kvm_vcpu *vcpu, - struct kvm_run *run, - ulong insn) -{ - if ((insn & INSN_MASK_WFI) == INSN_MATCH_WFI) { - vcpu->stat.wfi_exit_stat++; - kvm_riscv_vcpu_wfi(vcpu); - vcpu->arch.guest_context.sepc += INSN_LEN(insn); - return 1; - } - - return truly_illegal_insn(vcpu, run, insn); -} - -static int virtual_inst_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, - struct kvm_cpu_trap *trap) -{ - unsigned long insn = trap->stval; - struct kvm_cpu_trap utrap = { 0 }; - struct kvm_cpu_context *ct; - - if (unlikely(INSN_IS_16BIT(insn))) { - if (insn == 0) { - ct = &vcpu->arch.guest_context; - insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, - ct->sepc, - &utrap); - if (utrap.scause) { - utrap.sepc = ct->sepc; - kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); - return 1; - } - } - if (INSN_IS_16BIT(insn)) - return truly_illegal_insn(vcpu, run, insn); - } - - switch ((insn & INSN_OPCODE_MASK) >> INSN_OPCODE_SHIFT) { - case INSN_OPCODE_SYSTEM: - return system_opcode_insn(vcpu, run, insn); - default: - return truly_illegal_insn(vcpu, run, insn); - } -} - -static int emulate_load(struct kvm_vcpu *vcpu, struct kvm_run *run, - unsigned long fault_addr, unsigned long htinst) -{ - u8 data_buf[8]; - unsigned long insn; - int shift = 0, len = 0, insn_len = 0; - struct kvm_cpu_trap utrap = { 0 }; - struct kvm_cpu_context *ct = &vcpu->arch.guest_context; - - /* Determine trapped instruction */ - if (htinst & 0x1) { - /* - * Bit[0] == 1 implies trapped instruction value is - * transformed instruction or custom instruction. - */ - insn = htinst | INSN_16BIT_MASK; - insn_len = (htinst & BIT(1)) ? INSN_LEN(insn) : 2; - } else { - /* - * Bit[0] == 0 implies trapped instruction value is - * zero or special value. - */ - insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, ct->sepc, - &utrap); - if (utrap.scause) { - /* Redirect trap if we failed to read instruction */ - utrap.sepc = ct->sepc; - kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); - return 1; - } - insn_len = INSN_LEN(insn); - } - - /* Decode length of MMIO and shift */ - if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) { - len = 4; - shift = 8 * (sizeof(ulong) - len); - } else if ((insn & INSN_MASK_LB) == INSN_MATCH_LB) { - len = 1; - shift = 8 * (sizeof(ulong) - len); - } else if ((insn & INSN_MASK_LBU) == INSN_MATCH_LBU) { - len = 1; - shift = 8 * (sizeof(ulong) - len); -#ifdef CONFIG_64BIT - } else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) { - len = 8; - shift = 8 * (sizeof(ulong) - len); - } else if ((insn & INSN_MASK_LWU) == INSN_MATCH_LWU) { - len = 4; -#endif - } else if ((insn & INSN_MASK_LH) == INSN_MATCH_LH) { - len = 2; - shift = 8 * (sizeof(ulong) - len); - } else if ((insn & INSN_MASK_LHU) == INSN_MATCH_LHU) { - len = 2; -#ifdef CONFIG_64BIT - } else if ((insn & INSN_MASK_C_LD) == INSN_MATCH_C_LD) { - len = 8; - shift = 8 * (sizeof(ulong) - len); - insn = RVC_RS2S(insn) << SH_RD; - } else if ((insn & INSN_MASK_C_LDSP) == INSN_MATCH_C_LDSP && - ((insn >> SH_RD) & 0x1f)) { - len = 8; - shift = 8 * (sizeof(ulong) - len); -#endif - } else if ((insn & INSN_MASK_C_LW) == INSN_MATCH_C_LW) { - len = 4; - shift = 8 * (sizeof(ulong) - len); - insn = RVC_RS2S(insn) << SH_RD; - } else if ((insn & INSN_MASK_C_LWSP) == INSN_MATCH_C_LWSP && - ((insn >> SH_RD) & 0x1f)) { - len = 4; - shift = 8 * (sizeof(ulong) - len); - } else { - return -EOPNOTSUPP; - } - - /* Fault address should be aligned to length of MMIO */ - if (fault_addr & (len - 1)) - return -EIO; - - /* Save instruction decode info */ - vcpu->arch.mmio_decode.insn = insn; - vcpu->arch.mmio_decode.insn_len = insn_len; - vcpu->arch.mmio_decode.shift = shift; - vcpu->arch.mmio_decode.len = len; - vcpu->arch.mmio_decode.return_handled = 0; - - /* Update MMIO details in kvm_run struct */ - run->mmio.is_write = false; - run->mmio.phys_addr = fault_addr; - run->mmio.len = len; - - /* Try to handle MMIO access in the kernel */ - if (!kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_addr, len, data_buf)) { - /* Successfully handled MMIO access in the kernel so resume */ - memcpy(run->mmio.data, data_buf, len); - vcpu->stat.mmio_exit_kernel++; - kvm_riscv_vcpu_mmio_return(vcpu, run); - return 1; - } - - /* Exit to userspace for MMIO emulation */ - vcpu->stat.mmio_exit_user++; - run->exit_reason = KVM_EXIT_MMIO; - - return 0; -} - -static int emulate_store(struct kvm_vcpu *vcpu, struct kvm_run *run, - unsigned long fault_addr, unsigned long htinst) -{ - u8 data8; - u16 data16; - u32 data32; - u64 data64; - ulong data; - unsigned long insn; - int len = 0, insn_len = 0; - struct kvm_cpu_trap utrap = { 0 }; - struct kvm_cpu_context *ct = &vcpu->arch.guest_context; - - /* Determine trapped instruction */ - if (htinst & 0x1) { - /* - * Bit[0] == 1 implies trapped instruction value is - * transformed instruction or custom instruction. - */ - insn = htinst | INSN_16BIT_MASK; - insn_len = (htinst & BIT(1)) ? INSN_LEN(insn) : 2; - } else { - /* - * Bit[0] == 0 implies trapped instruction value is - * zero or special value. - */ - insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, ct->sepc, - &utrap); - if (utrap.scause) { - /* Redirect trap if we failed to read instruction */ - utrap.sepc = ct->sepc; - kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); - return 1; - } - insn_len = INSN_LEN(insn); - } - - data = GET_RS2(insn, &vcpu->arch.guest_context); - data8 = data16 = data32 = data64 = data; - - if ((insn & INSN_MASK_SW) == INSN_MATCH_SW) { - len = 4; - } else if ((insn & INSN_MASK_SB) == INSN_MATCH_SB) { - len = 1; -#ifdef CONFIG_64BIT - } else if ((insn & INSN_MASK_SD) == INSN_MATCH_SD) { - len = 8; -#endif - } else if ((insn & INSN_MASK_SH) == INSN_MATCH_SH) { - len = 2; -#ifdef CONFIG_64BIT - } else if ((insn & INSN_MASK_C_SD) == INSN_MATCH_C_SD) { - len = 8; - data64 = GET_RS2S(insn, &vcpu->arch.guest_context); - } else if ((insn & INSN_MASK_C_SDSP) == INSN_MATCH_C_SDSP && - ((insn >> SH_RD) & 0x1f)) { - len = 8; - data64 = GET_RS2C(insn, &vcpu->arch.guest_context); -#endif - } else if ((insn & INSN_MASK_C_SW) == INSN_MATCH_C_SW) { - len = 4; - data32 = GET_RS2S(insn, &vcpu->arch.guest_context); - } else if ((insn & INSN_MASK_C_SWSP) == INSN_MATCH_C_SWSP && - ((insn >> SH_RD) & 0x1f)) { - len = 4; - data32 = GET_RS2C(insn, &vcpu->arch.guest_context); - } else { - return -EOPNOTSUPP; - } - - /* Fault address should be aligned to length of MMIO */ - if (fault_addr & (len - 1)) - return -EIO; - - /* Save instruction decode info */ - vcpu->arch.mmio_decode.insn = insn; - vcpu->arch.mmio_decode.insn_len = insn_len; - vcpu->arch.mmio_decode.shift = 0; - vcpu->arch.mmio_decode.len = len; - vcpu->arch.mmio_decode.return_handled = 0; - - /* Copy data to kvm_run instance */ - switch (len) { - case 1: - *((u8 *)run->mmio.data) = data8; - break; - case 2: - *((u16 *)run->mmio.data) = data16; - break; - case 4: - *((u32 *)run->mmio.data) = data32; - break; - case 8: - *((u64 *)run->mmio.data) = data64; - break; - default: - return -EOPNOTSUPP; - } - - /* Update MMIO details in kvm_run struct */ - run->mmio.is_write = true; - run->mmio.phys_addr = fault_addr; - run->mmio.len = len; - - /* Try to handle MMIO access in the kernel */ - if (!kvm_io_bus_write(vcpu, KVM_MMIO_BUS, - fault_addr, len, run->mmio.data)) { - /* Successfully handled MMIO access in the kernel so resume */ - vcpu->stat.mmio_exit_kernel++; - kvm_riscv_vcpu_mmio_return(vcpu, run); - return 1; - } - - /* Exit to userspace for MMIO emulation */ - vcpu->stat.mmio_exit_user++; - run->exit_reason = KVM_EXIT_MMIO; - - return 0; -} - static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, struct kvm_cpu_trap *trap) { struct kvm_memory_slot *memslot; unsigned long hva, fault_addr; - bool writeable; + bool writable; gfn_t gfn; int ret; fault_addr = (trap->htval << 2) | (trap->stval & 0x3); gfn = fault_addr >> PAGE_SHIFT; memslot = gfn_to_memslot(vcpu->kvm, gfn); - hva = gfn_to_hva_memslot_prot(memslot, gfn, &writeable); + hva = gfn_to_hva_memslot_prot(memslot, gfn, &writable); if (kvm_is_error_hva(hva) || - (trap->scause == EXC_STORE_GUEST_PAGE_FAULT && !writeable)) { + (trap->scause == EXC_STORE_GUEST_PAGE_FAULT && !writable)) { switch (trap->scause) { case EXC_LOAD_GUEST_PAGE_FAULT: - return emulate_load(vcpu, run, fault_addr, - trap->htinst); + return kvm_riscv_vcpu_mmio_load(vcpu, run, + fault_addr, + trap->htinst); case EXC_STORE_GUEST_PAGE_FAULT: - return emulate_store(vcpu, run, fault_addr, - trap->htinst); + return kvm_riscv_vcpu_mmio_store(vcpu, run, + fault_addr, + trap->htinst); default: return -EOPNOTSUPP; }; @@ -448,21 +47,6 @@ static int gstage_page_fault(struct kvm_vcpu *vcpu, struct kvm_run *run, return 1; } -/** - * kvm_riscv_vcpu_wfi -- Emulate wait for interrupt (WFI) behaviour - * - * @vcpu: The VCPU pointer - */ -void kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu) -{ - if (!kvm_arch_vcpu_runnable(vcpu)) { - kvm_vcpu_srcu_read_unlock(vcpu); - kvm_vcpu_halt(vcpu); - kvm_vcpu_srcu_read_lock(vcpu); - kvm_clear_request(KVM_REQ_UNHALT, vcpu); - } -} - /** * kvm_riscv_vcpu_unpriv_read -- Read machine word from Guest memory * @@ -601,66 +185,6 @@ void kvm_riscv_vcpu_trap_redirect(struct kvm_vcpu *vcpu, vcpu->arch.guest_context.sepc = csr_read(CSR_VSTVEC); } -/** - * kvm_riscv_vcpu_mmio_return -- Handle MMIO loads after user space emulation - * or in-kernel IO emulation - * - * @vcpu: The VCPU pointer - * @run: The VCPU run struct containing the mmio data - */ -int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) -{ - u8 data8; - u16 data16; - u32 data32; - u64 data64; - ulong insn; - int len, shift; - - if (vcpu->arch.mmio_decode.return_handled) - return 0; - - vcpu->arch.mmio_decode.return_handled = 1; - insn = vcpu->arch.mmio_decode.insn; - - if (run->mmio.is_write) - goto done; - - len = vcpu->arch.mmio_decode.len; - shift = vcpu->arch.mmio_decode.shift; - - switch (len) { - case 1: - data8 = *((u8 *)run->mmio.data); - SET_RD(insn, &vcpu->arch.guest_context, - (ulong)data8 << shift >> shift); - break; - case 2: - data16 = *((u16 *)run->mmio.data); - SET_RD(insn, &vcpu->arch.guest_context, - (ulong)data16 << shift >> shift); - break; - case 4: - data32 = *((u32 *)run->mmio.data); - SET_RD(insn, &vcpu->arch.guest_context, - (ulong)data32 << shift >> shift); - break; - case 8: - data64 = *((u64 *)run->mmio.data); - SET_RD(insn, &vcpu->arch.guest_context, - (ulong)data64 << shift >> shift); - break; - default: - return -EOPNOTSUPP; - } - -done: - /* Move to next instruction */ - vcpu->arch.guest_context.sepc += vcpu->arch.mmio_decode.insn_len; - - return 0; -} - /* * Return > 0 to return to guest, < 0 on error, 0 (and set exit_reason) on * proper exit to userspace. @@ -680,7 +204,7 @@ int kvm_riscv_vcpu_exit(struct kvm_vcpu *vcpu, struct kvm_run *run, switch (trap->scause) { case EXC_VIRTUAL_INST_FAULT: if (vcpu->arch.guest_context.hstatus & HSTATUS_SPV) - ret = virtual_inst_fault(vcpu, run, trap); + ret = kvm_riscv_vcpu_virtual_insn(vcpu, run, trap); break; case EXC_INST_GUEST_PAGE_FAULT: case EXC_LOAD_GUEST_PAGE_FAULT: diff --git a/arch/riscv/kvm/vcpu_fp.c b/arch/riscv/kvm/vcpu_fp.c index d4308c512007..9d8cbc42057a 100644 --- a/arch/riscv/kvm/vcpu_fp.c +++ b/arch/riscv/kvm/vcpu_fp.c @@ -16,12 +16,11 @@ #ifdef CONFIG_FPU void kvm_riscv_vcpu_fp_reset(struct kvm_vcpu *vcpu) { - unsigned long isa = vcpu->arch.isa; struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; cntx->sstatus &= ~SR_FS; - if (riscv_isa_extension_available(&isa, f) || - riscv_isa_extension_available(&isa, d)) + if (riscv_isa_extension_available(vcpu->arch.isa, f) || + riscv_isa_extension_available(vcpu->arch.isa, d)) cntx->sstatus |= SR_FS_INITIAL; else cntx->sstatus |= SR_FS_OFF; @@ -34,24 +33,24 @@ static void kvm_riscv_vcpu_fp_clean(struct kvm_cpu_context *cntx) } void kvm_riscv_vcpu_guest_fp_save(struct kvm_cpu_context *cntx, - unsigned long isa) + const unsigned long *isa) { if ((cntx->sstatus & SR_FS) == SR_FS_DIRTY) { - if (riscv_isa_extension_available(&isa, d)) + if (riscv_isa_extension_available(isa, d)) __kvm_riscv_fp_d_save(cntx); - else if (riscv_isa_extension_available(&isa, f)) + else if (riscv_isa_extension_available(isa, f)) __kvm_riscv_fp_f_save(cntx); kvm_riscv_vcpu_fp_clean(cntx); } } void kvm_riscv_vcpu_guest_fp_restore(struct kvm_cpu_context *cntx, - unsigned long isa) + const unsigned long *isa) { if ((cntx->sstatus & SR_FS) != SR_FS_OFF) { - if (riscv_isa_extension_available(&isa, d)) + if (riscv_isa_extension_available(isa, d)) __kvm_riscv_fp_d_restore(cntx); - else if (riscv_isa_extension_available(&isa, f)) + else if (riscv_isa_extension_available(isa, f)) __kvm_riscv_fp_f_restore(cntx); kvm_riscv_vcpu_fp_clean(cntx); } @@ -80,7 +79,6 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu, unsigned long rtype) { struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; - unsigned long isa = vcpu->arch.isa; unsigned long __user *uaddr = (unsigned long __user *)(unsigned long)reg->addr; unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | @@ -89,7 +87,7 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu, void *reg_val; if ((rtype == KVM_REG_RISCV_FP_F) && - riscv_isa_extension_available(&isa, f)) { + riscv_isa_extension_available(vcpu->arch.isa, f)) { if (KVM_REG_SIZE(reg->id) != sizeof(u32)) return -EINVAL; if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr)) @@ -100,7 +98,7 @@ int kvm_riscv_vcpu_get_reg_fp(struct kvm_vcpu *vcpu, else return -EINVAL; } else if ((rtype == KVM_REG_RISCV_FP_D) && - riscv_isa_extension_available(&isa, d)) { + riscv_isa_extension_available(vcpu->arch.isa, d)) { if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) { if (KVM_REG_SIZE(reg->id) != sizeof(u32)) return -EINVAL; @@ -126,7 +124,6 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu, unsigned long rtype) { struct kvm_cpu_context *cntx = &vcpu->arch.guest_context; - unsigned long isa = vcpu->arch.isa; unsigned long __user *uaddr = (unsigned long __user *)(unsigned long)reg->addr; unsigned long reg_num = reg->id & ~(KVM_REG_ARCH_MASK | @@ -135,7 +132,7 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu, void *reg_val; if ((rtype == KVM_REG_RISCV_FP_F) && - riscv_isa_extension_available(&isa, f)) { + riscv_isa_extension_available(vcpu->arch.isa, f)) { if (KVM_REG_SIZE(reg->id) != sizeof(u32)) return -EINVAL; if (reg_num == KVM_REG_RISCV_FP_F_REG(fcsr)) @@ -146,7 +143,7 @@ int kvm_riscv_vcpu_set_reg_fp(struct kvm_vcpu *vcpu, else return -EINVAL; } else if ((rtype == KVM_REG_RISCV_FP_D) && - riscv_isa_extension_available(&isa, d)) { + riscv_isa_extension_available(vcpu->arch.isa, d)) { if (reg_num == KVM_REG_RISCV_FP_D_REG(fcsr)) { if (KVM_REG_SIZE(reg->id) != sizeof(u32)) return -EINVAL; diff --git a/arch/riscv/kvm/vcpu_insn.c b/arch/riscv/kvm/vcpu_insn.c new file mode 100644 index 000000000000..7eb90a47b571 --- /dev/null +++ b/arch/riscv/kvm/vcpu_insn.c @@ -0,0 +1,752 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2019 Western Digital Corporation or its affiliates. + * Copyright (c) 2022 Ventana Micro Systems Inc. + */ + +#include +#include + +#define INSN_OPCODE_MASK 0x007c +#define INSN_OPCODE_SHIFT 2 +#define INSN_OPCODE_SYSTEM 28 + +#define INSN_MASK_WFI 0xffffffff +#define INSN_MATCH_WFI 0x10500073 + +#define INSN_MATCH_CSRRW 0x1073 +#define INSN_MASK_CSRRW 0x707f +#define INSN_MATCH_CSRRS 0x2073 +#define INSN_MASK_CSRRS 0x707f +#define INSN_MATCH_CSRRC 0x3073 +#define INSN_MASK_CSRRC 0x707f +#define INSN_MATCH_CSRRWI 0x5073 +#define INSN_MASK_CSRRWI 0x707f +#define INSN_MATCH_CSRRSI 0x6073 +#define INSN_MASK_CSRRSI 0x707f +#define INSN_MATCH_CSRRCI 0x7073 +#define INSN_MASK_CSRRCI 0x707f + +#define INSN_MATCH_LB 0x3 +#define INSN_MASK_LB 0x707f +#define INSN_MATCH_LH 0x1003 +#define INSN_MASK_LH 0x707f +#define INSN_MATCH_LW 0x2003 +#define INSN_MASK_LW 0x707f +#define INSN_MATCH_LD 0x3003 +#define INSN_MASK_LD 0x707f +#define INSN_MATCH_LBU 0x4003 +#define INSN_MASK_LBU 0x707f +#define INSN_MATCH_LHU 0x5003 +#define INSN_MASK_LHU 0x707f +#define INSN_MATCH_LWU 0x6003 +#define INSN_MASK_LWU 0x707f +#define INSN_MATCH_SB 0x23 +#define INSN_MASK_SB 0x707f +#define INSN_MATCH_SH 0x1023 +#define INSN_MASK_SH 0x707f +#define INSN_MATCH_SW 0x2023 +#define INSN_MASK_SW 0x707f +#define INSN_MATCH_SD 0x3023 +#define INSN_MASK_SD 0x707f + +#define INSN_MATCH_C_LD 0x6000 +#define INSN_MASK_C_LD 0xe003 +#define INSN_MATCH_C_SD 0xe000 +#define INSN_MASK_C_SD 0xe003 +#define INSN_MATCH_C_LW 0x4000 +#define INSN_MASK_C_LW 0xe003 +#define INSN_MATCH_C_SW 0xc000 +#define INSN_MASK_C_SW 0xe003 +#define INSN_MATCH_C_LDSP 0x6002 +#define INSN_MASK_C_LDSP 0xe003 +#define INSN_MATCH_C_SDSP 0xe002 +#define INSN_MASK_C_SDSP 0xe003 +#define INSN_MATCH_C_LWSP 0x4002 +#define INSN_MASK_C_LWSP 0xe003 +#define INSN_MATCH_C_SWSP 0xc002 +#define INSN_MASK_C_SWSP 0xe003 + +#define INSN_16BIT_MASK 0x3 + +#define INSN_IS_16BIT(insn) (((insn) & INSN_16BIT_MASK) != INSN_16BIT_MASK) + +#define INSN_LEN(insn) (INSN_IS_16BIT(insn) ? 2 : 4) + +#ifdef CONFIG_64BIT +#define LOG_REGBYTES 3 +#else +#define LOG_REGBYTES 2 +#endif +#define REGBYTES (1 << LOG_REGBYTES) + +#define SH_RD 7 +#define SH_RS1 15 +#define SH_RS2 20 +#define SH_RS2C 2 +#define MASK_RX 0x1f + +#define RV_X(x, s, n) (((x) >> (s)) & ((1 << (n)) - 1)) +#define RVC_LW_IMM(x) ((RV_X(x, 6, 1) << 2) | \ + (RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 5, 1) << 6)) +#define RVC_LD_IMM(x) ((RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 5, 2) << 6)) +#define RVC_LWSP_IMM(x) ((RV_X(x, 4, 3) << 2) | \ + (RV_X(x, 12, 1) << 5) | \ + (RV_X(x, 2, 2) << 6)) +#define RVC_LDSP_IMM(x) ((RV_X(x, 5, 2) << 3) | \ + (RV_X(x, 12, 1) << 5) | \ + (RV_X(x, 2, 3) << 6)) +#define RVC_SWSP_IMM(x) ((RV_X(x, 9, 4) << 2) | \ + (RV_X(x, 7, 2) << 6)) +#define RVC_SDSP_IMM(x) ((RV_X(x, 10, 3) << 3) | \ + (RV_X(x, 7, 3) << 6)) +#define RVC_RS1S(insn) (8 + RV_X(insn, SH_RD, 3)) +#define RVC_RS2S(insn) (8 + RV_X(insn, SH_RS2C, 3)) +#define RVC_RS2(insn) RV_X(insn, SH_RS2C, 5) + +#define SHIFT_RIGHT(x, y) \ + ((y) < 0 ? ((x) << -(y)) : ((x) >> (y))) + +#define REG_MASK \ + ((1 << (5 + LOG_REGBYTES)) - (1 << LOG_REGBYTES)) + +#define REG_OFFSET(insn, pos) \ + (SHIFT_RIGHT((insn), (pos) - LOG_REGBYTES) & REG_MASK) + +#define REG_PTR(insn, pos, regs) \ + ((ulong *)((ulong)(regs) + REG_OFFSET(insn, pos))) + +#define GET_FUNCT3(insn) (((insn) >> 12) & 7) + +#define GET_RS1(insn, regs) (*REG_PTR(insn, SH_RS1, regs)) +#define GET_RS2(insn, regs) (*REG_PTR(insn, SH_RS2, regs)) +#define GET_RS1S(insn, regs) (*REG_PTR(RVC_RS1S(insn), 0, regs)) +#define GET_RS2S(insn, regs) (*REG_PTR(RVC_RS2S(insn), 0, regs)) +#define GET_RS2C(insn, regs) (*REG_PTR(insn, SH_RS2C, regs)) +#define GET_SP(regs) (*REG_PTR(2, 0, regs)) +#define SET_RD(insn, regs, val) (*REG_PTR(insn, SH_RD, regs) = (val)) +#define IMM_I(insn) ((s32)(insn) >> 20) +#define IMM_S(insn) (((s32)(insn) >> 25 << 5) | \ + (s32)(((insn) >> 7) & 0x1f)) + +struct insn_func { + unsigned long mask; + unsigned long match; + /* + * Possible return values are as follows: + * 1) Returns < 0 for error case + * 2) Returns 0 for exit to user-space + * 3) Returns 1 to continue with next sepc + * 4) Returns 2 to continue with same sepc + * 5) Returns 3 to inject illegal instruction trap and continue + * 6) Returns 4 to inject virtual instruction trap and continue + * + * Use enum kvm_insn_return for return values + */ + int (*func)(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn); +}; + +static int truly_illegal_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, + ulong insn) +{ + struct kvm_cpu_trap utrap = { 0 }; + + /* Redirect trap to Guest VCPU */ + utrap.sepc = vcpu->arch.guest_context.sepc; + utrap.scause = EXC_INST_ILLEGAL; + utrap.stval = insn; + utrap.htval = 0; + utrap.htinst = 0; + kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); + + return 1; +} + +static int truly_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, + ulong insn) +{ + struct kvm_cpu_trap utrap = { 0 }; + + /* Redirect trap to Guest VCPU */ + utrap.sepc = vcpu->arch.guest_context.sepc; + utrap.scause = EXC_VIRTUAL_INST_FAULT; + utrap.stval = insn; + utrap.htval = 0; + utrap.htinst = 0; + kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); + + return 1; +} + +/** + * kvm_riscv_vcpu_wfi -- Emulate wait for interrupt (WFI) behaviour + * + * @vcpu: The VCPU pointer + */ +void kvm_riscv_vcpu_wfi(struct kvm_vcpu *vcpu) +{ + if (!kvm_arch_vcpu_runnable(vcpu)) { + kvm_vcpu_srcu_read_unlock(vcpu); + kvm_vcpu_halt(vcpu); + kvm_vcpu_srcu_read_lock(vcpu); + kvm_clear_request(KVM_REQ_UNHALT, vcpu); + } +} + +static int wfi_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn) +{ + vcpu->stat.wfi_exit_stat++; + kvm_riscv_vcpu_wfi(vcpu); + return KVM_INSN_CONTINUE_NEXT_SEPC; +} + +struct csr_func { + unsigned int base; + unsigned int count; + /* + * Possible return values are as same as "func" callback in + * "struct insn_func". + */ + int (*func)(struct kvm_vcpu *vcpu, unsigned int csr_num, + unsigned long *val, unsigned long new_val, + unsigned long wr_mask); +}; + +static const struct csr_func csr_funcs[] = { }; + +/** + * kvm_riscv_vcpu_csr_return -- Handle CSR read/write after user space + * emulation or in-kernel emulation + * + * @vcpu: The VCPU pointer + * @run: The VCPU run struct containing the CSR data + * + * Returns > 0 upon failure and 0 upon success + */ +int kvm_riscv_vcpu_csr_return(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + ulong insn; + + if (vcpu->arch.csr_decode.return_handled) + return 0; + vcpu->arch.csr_decode.return_handled = 1; + + /* Update destination register for CSR reads */ + insn = vcpu->arch.csr_decode.insn; + if ((insn >> SH_RD) & MASK_RX) + SET_RD(insn, &vcpu->arch.guest_context, + run->riscv_csr.ret_value); + + /* Move to next instruction */ + vcpu->arch.guest_context.sepc += INSN_LEN(insn); + + return 0; +} + +static int csr_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, ulong insn) +{ + int i, rc = KVM_INSN_ILLEGAL_TRAP; + unsigned int csr_num = insn >> SH_RS2; + unsigned int rs1_num = (insn >> SH_RS1) & MASK_RX; + ulong rs1_val = GET_RS1(insn, &vcpu->arch.guest_context); + const struct csr_func *tcfn, *cfn = NULL; + ulong val = 0, wr_mask = 0, new_val = 0; + + /* Decode the CSR instruction */ + switch (GET_FUNCT3(insn)) { + case GET_FUNCT3(INSN_MATCH_CSRRW): + wr_mask = -1UL; + new_val = rs1_val; + break; + case GET_FUNCT3(INSN_MATCH_CSRRS): + wr_mask = rs1_val; + new_val = -1UL; + break; + case GET_FUNCT3(INSN_MATCH_CSRRC): + wr_mask = rs1_val; + new_val = 0; + break; + case GET_FUNCT3(INSN_MATCH_CSRRWI): + wr_mask = -1UL; + new_val = rs1_num; + break; + case GET_FUNCT3(INSN_MATCH_CSRRSI): + wr_mask = rs1_num; + new_val = -1UL; + break; + case GET_FUNCT3(INSN_MATCH_CSRRCI): + wr_mask = rs1_num; + new_val = 0; + break; + default: + return rc; + } + + /* Save instruction decode info */ + vcpu->arch.csr_decode.insn = insn; + vcpu->arch.csr_decode.return_handled = 0; + + /* Update CSR details in kvm_run struct */ + run->riscv_csr.csr_num = csr_num; + run->riscv_csr.new_value = new_val; + run->riscv_csr.write_mask = wr_mask; + run->riscv_csr.ret_value = 0; + + /* Find in-kernel CSR function */ + for (i = 0; i < ARRAY_SIZE(csr_funcs); i++) { + tcfn = &csr_funcs[i]; + if ((tcfn->base <= csr_num) && + (csr_num < (tcfn->base + tcfn->count))) { + cfn = tcfn; + break; + } + } + + /* First try in-kernel CSR emulation */ + if (cfn && cfn->func) { + rc = cfn->func(vcpu, csr_num, &val, new_val, wr_mask); + if (rc > KVM_INSN_EXIT_TO_USER_SPACE) { + if (rc == KVM_INSN_CONTINUE_NEXT_SEPC) { + run->riscv_csr.ret_value = val; + vcpu->stat.csr_exit_kernel++; + kvm_riscv_vcpu_csr_return(vcpu, run); + rc = KVM_INSN_CONTINUE_SAME_SEPC; + } + return rc; + } + } + + /* Exit to user-space for CSR emulation */ + if (rc <= KVM_INSN_EXIT_TO_USER_SPACE) { + vcpu->stat.csr_exit_user++; + run->exit_reason = KVM_EXIT_RISCV_CSR; + } + + return rc; +} + +static const struct insn_func system_opcode_funcs[] = { + { + .mask = INSN_MASK_CSRRW, + .match = INSN_MATCH_CSRRW, + .func = csr_insn, + }, + { + .mask = INSN_MASK_CSRRS, + .match = INSN_MATCH_CSRRS, + .func = csr_insn, + }, + { + .mask = INSN_MASK_CSRRC, + .match = INSN_MATCH_CSRRC, + .func = csr_insn, + }, + { + .mask = INSN_MASK_CSRRWI, + .match = INSN_MATCH_CSRRWI, + .func = csr_insn, + }, + { + .mask = INSN_MASK_CSRRSI, + .match = INSN_MATCH_CSRRSI, + .func = csr_insn, + }, + { + .mask = INSN_MASK_CSRRCI, + .match = INSN_MATCH_CSRRCI, + .func = csr_insn, + }, + { + .mask = INSN_MASK_WFI, + .match = INSN_MATCH_WFI, + .func = wfi_insn, + }, +}; + +static int system_opcode_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, + ulong insn) +{ + int i, rc = KVM_INSN_ILLEGAL_TRAP; + const struct insn_func *ifn; + + for (i = 0; i < ARRAY_SIZE(system_opcode_funcs); i++) { + ifn = &system_opcode_funcs[i]; + if ((insn & ifn->mask) == ifn->match) { + rc = ifn->func(vcpu, run, insn); + break; + } + } + + switch (rc) { + case KVM_INSN_ILLEGAL_TRAP: + return truly_illegal_insn(vcpu, run, insn); + case KVM_INSN_VIRTUAL_TRAP: + return truly_virtual_insn(vcpu, run, insn); + case KVM_INSN_CONTINUE_NEXT_SEPC: + vcpu->arch.guest_context.sepc += INSN_LEN(insn); + break; + default: + break; + } + + return (rc <= 0) ? rc : 1; +} + +/** + * kvm_riscv_vcpu_virtual_insn -- Handle virtual instruction trap + * + * @vcpu: The VCPU pointer + * @run: The VCPU run struct containing the mmio data + * @trap: Trap details + * + * Returns > 0 to continue run-loop + * Returns 0 to exit run-loop and handle in user-space. + * Returns < 0 to report failure and exit run-loop + */ +int kvm_riscv_vcpu_virtual_insn(struct kvm_vcpu *vcpu, struct kvm_run *run, + struct kvm_cpu_trap *trap) +{ + unsigned long insn = trap->stval; + struct kvm_cpu_trap utrap = { 0 }; + struct kvm_cpu_context *ct; + + if (unlikely(INSN_IS_16BIT(insn))) { + if (insn == 0) { + ct = &vcpu->arch.guest_context; + insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, + ct->sepc, + &utrap); + if (utrap.scause) { + utrap.sepc = ct->sepc; + kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); + return 1; + } + } + if (INSN_IS_16BIT(insn)) + return truly_illegal_insn(vcpu, run, insn); + } + + switch ((insn & INSN_OPCODE_MASK) >> INSN_OPCODE_SHIFT) { + case INSN_OPCODE_SYSTEM: + return system_opcode_insn(vcpu, run, insn); + default: + return truly_illegal_insn(vcpu, run, insn); + } +} + +/** + * kvm_riscv_vcpu_mmio_load -- Emulate MMIO load instruction + * + * @vcpu: The VCPU pointer + * @run: The VCPU run struct containing the mmio data + * @fault_addr: Guest physical address to load + * @htinst: Transformed encoding of the load instruction + * + * Returns > 0 to continue run-loop + * Returns 0 to exit run-loop and handle in user-space. + * Returns < 0 to report failure and exit run-loop + */ +int kvm_riscv_vcpu_mmio_load(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long fault_addr, + unsigned long htinst) +{ + u8 data_buf[8]; + unsigned long insn; + int shift = 0, len = 0, insn_len = 0; + struct kvm_cpu_trap utrap = { 0 }; + struct kvm_cpu_context *ct = &vcpu->arch.guest_context; + + /* Determine trapped instruction */ + if (htinst & 0x1) { + /* + * Bit[0] == 1 implies trapped instruction value is + * transformed instruction or custom instruction. + */ + insn = htinst | INSN_16BIT_MASK; + insn_len = (htinst & BIT(1)) ? INSN_LEN(insn) : 2; + } else { + /* + * Bit[0] == 0 implies trapped instruction value is + * zero or special value. + */ + insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, ct->sepc, + &utrap); + if (utrap.scause) { + /* Redirect trap if we failed to read instruction */ + utrap.sepc = ct->sepc; + kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); + return 1; + } + insn_len = INSN_LEN(insn); + } + + /* Decode length of MMIO and shift */ + if ((insn & INSN_MASK_LW) == INSN_MATCH_LW) { + len = 4; + shift = 8 * (sizeof(ulong) - len); + } else if ((insn & INSN_MASK_LB) == INSN_MATCH_LB) { + len = 1; + shift = 8 * (sizeof(ulong) - len); + } else if ((insn & INSN_MASK_LBU) == INSN_MATCH_LBU) { + len = 1; + shift = 8 * (sizeof(ulong) - len); +#ifdef CONFIG_64BIT + } else if ((insn & INSN_MASK_LD) == INSN_MATCH_LD) { + len = 8; + shift = 8 * (sizeof(ulong) - len); + } else if ((insn & INSN_MASK_LWU) == INSN_MATCH_LWU) { + len = 4; +#endif + } else if ((insn & INSN_MASK_LH) == INSN_MATCH_LH) { + len = 2; + shift = 8 * (sizeof(ulong) - len); + } else if ((insn & INSN_MASK_LHU) == INSN_MATCH_LHU) { + len = 2; +#ifdef CONFIG_64BIT + } else if ((insn & INSN_MASK_C_LD) == INSN_MATCH_C_LD) { + len = 8; + shift = 8 * (sizeof(ulong) - len); + insn = RVC_RS2S(insn) << SH_RD; + } else if ((insn & INSN_MASK_C_LDSP) == INSN_MATCH_C_LDSP && + ((insn >> SH_RD) & 0x1f)) { + len = 8; + shift = 8 * (sizeof(ulong) - len); +#endif + } else if ((insn & INSN_MASK_C_LW) == INSN_MATCH_C_LW) { + len = 4; + shift = 8 * (sizeof(ulong) - len); + insn = RVC_RS2S(insn) << SH_RD; + } else if ((insn & INSN_MASK_C_LWSP) == INSN_MATCH_C_LWSP && + ((insn >> SH_RD) & 0x1f)) { + len = 4; + shift = 8 * (sizeof(ulong) - len); + } else { + return -EOPNOTSUPP; + } + + /* Fault address should be aligned to length of MMIO */ + if (fault_addr & (len - 1)) + return -EIO; + + /* Save instruction decode info */ + vcpu->arch.mmio_decode.insn = insn; + vcpu->arch.mmio_decode.insn_len = insn_len; + vcpu->arch.mmio_decode.shift = shift; + vcpu->arch.mmio_decode.len = len; + vcpu->arch.mmio_decode.return_handled = 0; + + /* Update MMIO details in kvm_run struct */ + run->mmio.is_write = false; + run->mmio.phys_addr = fault_addr; + run->mmio.len = len; + + /* Try to handle MMIO access in the kernel */ + if (!kvm_io_bus_read(vcpu, KVM_MMIO_BUS, fault_addr, len, data_buf)) { + /* Successfully handled MMIO access in the kernel so resume */ + memcpy(run->mmio.data, data_buf, len); + vcpu->stat.mmio_exit_kernel++; + kvm_riscv_vcpu_mmio_return(vcpu, run); + return 1; + } + + /* Exit to userspace for MMIO emulation */ + vcpu->stat.mmio_exit_user++; + run->exit_reason = KVM_EXIT_MMIO; + + return 0; +} + +/** + * kvm_riscv_vcpu_mmio_store -- Emulate MMIO store instruction + * + * @vcpu: The VCPU pointer + * @run: The VCPU run struct containing the mmio data + * @fault_addr: Guest physical address to store + * @htinst: Transformed encoding of the store instruction + * + * Returns > 0 to continue run-loop + * Returns 0 to exit run-loop and handle in user-space. + * Returns < 0 to report failure and exit run-loop + */ +int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run, + unsigned long fault_addr, + unsigned long htinst) +{ + u8 data8; + u16 data16; + u32 data32; + u64 data64; + ulong data; + unsigned long insn; + int len = 0, insn_len = 0; + struct kvm_cpu_trap utrap = { 0 }; + struct kvm_cpu_context *ct = &vcpu->arch.guest_context; + + /* Determine trapped instruction */ + if (htinst & 0x1) { + /* + * Bit[0] == 1 implies trapped instruction value is + * transformed instruction or custom instruction. + */ + insn = htinst | INSN_16BIT_MASK; + insn_len = (htinst & BIT(1)) ? INSN_LEN(insn) : 2; + } else { + /* + * Bit[0] == 0 implies trapped instruction value is + * zero or special value. + */ + insn = kvm_riscv_vcpu_unpriv_read(vcpu, true, ct->sepc, + &utrap); + if (utrap.scause) { + /* Redirect trap if we failed to read instruction */ + utrap.sepc = ct->sepc; + kvm_riscv_vcpu_trap_redirect(vcpu, &utrap); + return 1; + } + insn_len = INSN_LEN(insn); + } + + data = GET_RS2(insn, &vcpu->arch.guest_context); + data8 = data16 = data32 = data64 = data; + + if ((insn & INSN_MASK_SW) == INSN_MATCH_SW) { + len = 4; + } else if ((insn & INSN_MASK_SB) == INSN_MATCH_SB) { + len = 1; +#ifdef CONFIG_64BIT + } else if ((insn & INSN_MASK_SD) == INSN_MATCH_SD) { + len = 8; +#endif + } else if ((insn & INSN_MASK_SH) == INSN_MATCH_SH) { + len = 2; +#ifdef CONFIG_64BIT + } else if ((insn & INSN_MASK_C_SD) == INSN_MATCH_C_SD) { + len = 8; + data64 = GET_RS2S(insn, &vcpu->arch.guest_context); + } else if ((insn & INSN_MASK_C_SDSP) == INSN_MATCH_C_SDSP && + ((insn >> SH_RD) & 0x1f)) { + len = 8; + data64 = GET_RS2C(insn, &vcpu->arch.guest_context); +#endif + } else if ((insn & INSN_MASK_C_SW) == INSN_MATCH_C_SW) { + len = 4; + data32 = GET_RS2S(insn, &vcpu->arch.guest_context); + } else if ((insn & INSN_MASK_C_SWSP) == INSN_MATCH_C_SWSP && + ((insn >> SH_RD) & 0x1f)) { + len = 4; + data32 = GET_RS2C(insn, &vcpu->arch.guest_context); + } else { + return -EOPNOTSUPP; + } + + /* Fault address should be aligned to length of MMIO */ + if (fault_addr & (len - 1)) + return -EIO; + + /* Save instruction decode info */ + vcpu->arch.mmio_decode.insn = insn; + vcpu->arch.mmio_decode.insn_len = insn_len; + vcpu->arch.mmio_decode.shift = 0; + vcpu->arch.mmio_decode.len = len; + vcpu->arch.mmio_decode.return_handled = 0; + + /* Copy data to kvm_run instance */ + switch (len) { + case 1: + *((u8 *)run->mmio.data) = data8; + break; + case 2: + *((u16 *)run->mmio.data) = data16; + break; + case 4: + *((u32 *)run->mmio.data) = data32; + break; + case 8: + *((u64 *)run->mmio.data) = data64; + break; + default: + return -EOPNOTSUPP; + } + + /* Update MMIO details in kvm_run struct */ + run->mmio.is_write = true; + run->mmio.phys_addr = fault_addr; + run->mmio.len = len; + + /* Try to handle MMIO access in the kernel */ + if (!kvm_io_bus_write(vcpu, KVM_MMIO_BUS, + fault_addr, len, run->mmio.data)) { + /* Successfully handled MMIO access in the kernel so resume */ + vcpu->stat.mmio_exit_kernel++; + kvm_riscv_vcpu_mmio_return(vcpu, run); + return 1; + } + + /* Exit to userspace for MMIO emulation */ + vcpu->stat.mmio_exit_user++; + run->exit_reason = KVM_EXIT_MMIO; + + return 0; +} + +/** + * kvm_riscv_vcpu_mmio_return -- Handle MMIO loads after user space emulation + * or in-kernel IO emulation + * + * @vcpu: The VCPU pointer + * @run: The VCPU run struct containing the mmio data + */ +int kvm_riscv_vcpu_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run) +{ + u8 data8; + u16 data16; + u32 data32; + u64 data64; + ulong insn; + int len, shift; + + if (vcpu->arch.mmio_decode.return_handled) + return 0; + + vcpu->arch.mmio_decode.return_handled = 1; + insn = vcpu->arch.mmio_decode.insn; + + if (run->mmio.is_write) + goto done; + + len = vcpu->arch.mmio_decode.len; + shift = vcpu->arch.mmio_decode.shift; + + switch (len) { + case 1: + data8 = *((u8 *)run->mmio.data); + SET_RD(insn, &vcpu->arch.guest_context, + (ulong)data8 << shift >> shift); + break; + case 2: + data16 = *((u16 *)run->mmio.data); + SET_RD(insn, &vcpu->arch.guest_context, + (ulong)data16 << shift >> shift); + break; + case 4: + data32 = *((u32 *)run->mmio.data); + SET_RD(insn, &vcpu->arch.guest_context, + (ulong)data32 << shift >> shift); + break; + case 8: + data64 = *((u64 *)run->mmio.data); + SET_RD(insn, &vcpu->arch.guest_context, + (ulong)data64 << shift >> shift); + break; + default: + return -EOPNOTSUPP; + } + +done: + /* Move to next instruction */ + vcpu->arch.guest_context.sepc += vcpu->arch.mmio_decode.insn_len; + + return 0; +} diff --git a/arch/riscv/kvm/vcpu_timer.c b/arch/riscv/kvm/vcpu_timer.c index 5c4c37ff2d48..595043857049 100644 --- a/arch/riscv/kvm/vcpu_timer.c +++ b/arch/riscv/kvm/vcpu_timer.c @@ -214,12 +214,10 @@ void kvm_riscv_vcpu_timer_restore(struct kvm_vcpu *vcpu) #endif } -int kvm_riscv_guest_timer_init(struct kvm *kvm) +void kvm_riscv_guest_timer_init(struct kvm *kvm) { struct kvm_guest_timer *gt = &kvm->arch.timer; riscv_cs_get_mult_shift(>->nsec_mult, >->nsec_shift); gt->time_delta = -get_cycles64(); - - return 0; } diff --git a/arch/riscv/kvm/vm.c b/arch/riscv/kvm/vm.c index 945a2bf5e3f6..65a964d7e70d 100644 --- a/arch/riscv/kvm/vm.c +++ b/arch/riscv/kvm/vm.c @@ -41,7 +41,9 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) return r; } - return kvm_riscv_guest_timer_init(kvm); + kvm_riscv_guest_timer_init(kvm); + + return 0; } void kvm_arch_destroy_vm(struct kvm *kvm) diff --git a/arch/s390/boot/uv.c b/arch/s390/boot/uv.c index e6be155ab2e5..a5fa667160b2 100644 --- a/arch/s390/boot/uv.c +++ b/arch/s390/boot/uv.c @@ -41,6 +41,12 @@ void uv_query_info(void) uv_info.max_num_sec_conf = uvcb.max_num_sec_conf; uv_info.max_guest_cpu_id = uvcb.max_guest_cpu_id; uv_info.uv_feature_indications = uvcb.uv_feature_indications; + uv_info.supp_se_hdr_ver = uvcb.supp_se_hdr_versions; + uv_info.supp_se_hdr_pcf = uvcb.supp_se_hdr_pcf; + uv_info.conf_dump_storage_state_len = uvcb.conf_dump_storage_state_len; + uv_info.conf_dump_finalize_len = uvcb.conf_dump_finalize_len; + uv_info.supp_att_req_hdr_ver = uvcb.supp_att_req_hdr_ver; + uv_info.supp_att_pflags = uvcb.supp_att_pflags; } #ifdef CONFIG_PROTECTED_VIRTUALIZATION_GUEST diff --git a/arch/s390/include/asm/airq.h b/arch/s390/include/asm/airq.h index 01936fdfaddb..e82e5626e139 100644 --- a/arch/s390/include/asm/airq.h +++ b/arch/s390/include/asm/airq.h @@ -12,10 +12,11 @@ #include #include +#include struct airq_struct { struct hlist_node list; /* Handler queueing. */ - void (*handler)(struct airq_struct *airq, bool floating); + void (*handler)(struct airq_struct *airq, struct tpi_info *tpi_info); u8 *lsi_ptr; /* Local-Summary-Indicator pointer */ u8 lsi_mask; /* Local-Summary-Indicator mask */ u8 isc; /* Interrupt-subclass */ @@ -46,8 +47,10 @@ struct airq_iv { #define AIRQ_IV_PTR 4 /* Allocate the ptr array */ #define AIRQ_IV_DATA 8 /* Allocate the data array */ #define AIRQ_IV_CACHELINE 16 /* Cacheline alignment for the vector */ +#define AIRQ_IV_GUESTVEC 32 /* Vector is a pinned guest page */ -struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags); +struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags, + unsigned long *vec); void airq_iv_release(struct airq_iv *iv); unsigned long airq_iv_alloc(struct airq_iv *iv, unsigned long num); void airq_iv_free(struct airq_iv *iv, unsigned long bit, unsigned long num); diff --git a/arch/s390/include/asm/gmap.h b/arch/s390/include/asm/gmap.h index 40264f60b0da..5cc46e0dde62 100644 --- a/arch/s390/include/asm/gmap.h +++ b/arch/s390/include/asm/gmap.h @@ -147,5 +147,42 @@ int gmap_mprotect_notify(struct gmap *, unsigned long start, void gmap_sync_dirty_log_pmd(struct gmap *gmap, unsigned long dirty_bitmap[4], unsigned long gaddr, unsigned long vmaddr); int gmap_mark_unmergeable(void); -void s390_reset_acc(struct mm_struct *mm); +void s390_unlist_old_asce(struct gmap *gmap); +int s390_replace_asce(struct gmap *gmap); +void s390_uv_destroy_pfns(unsigned long count, unsigned long *pfns); +int __s390_uv_destroy_range(struct mm_struct *mm, unsigned long start, + unsigned long end, bool interruptible); + +/** + * s390_uv_destroy_range - Destroy a range of pages in the given mm. + * @mm: the mm on which to operate on + * @start: the start of the range + * @end: the end of the range + * + * This function will call cond_sched, so it should not generate stalls, but + * it will otherwise only return when it completed. + */ +static inline void s390_uv_destroy_range(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + (void)__s390_uv_destroy_range(mm, start, end, false); +} + +/** + * s390_uv_destroy_range_interruptible - Destroy a range of pages in the + * given mm, but stop when a fatal signal is received. + * @mm: the mm on which to operate on + * @start: the start of the range + * @end: the end of the range + * + * This function will call cond_sched, so it should not generate stalls. If + * a fatal signal is received, it will return with -EINTR immediately, + * without finishing destroying the whole range. Upon successful + * completion, 0 is returned. + */ +static inline int s390_uv_destroy_range_interruptible(struct mm_struct *mm, unsigned long start, + unsigned long end) +{ + return __s390_uv_destroy_range(mm, start, end, true); +} #endif /* _ASM_S390_GMAP_H */ diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 766028d54a3e..f39092e0ceaa 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -19,6 +19,8 @@ #include #include #include +#include +#include #include #include #include @@ -93,19 +95,30 @@ union ipte_control { }; }; +union sca_utility { + __u16 val; + struct { + __u16 mtcr : 1; + __u16 reserved : 15; + }; +}; + struct bsca_block { union ipte_control ipte_control; __u64 reserved[5]; __u64 mcn; - __u64 reserved2; + union sca_utility utility; + __u8 reserved2[6]; struct bsca_entry cpu[KVM_S390_BSCA_CPU_SLOTS]; }; struct esca_block { union ipte_control ipte_control; - __u64 reserved1[7]; + __u64 reserved1[6]; + union sca_utility utility; + __u8 reserved2[6]; __u64 mcn[4]; - __u64 reserved2[20]; + __u64 reserved3[20]; struct esca_entry cpu[KVM_S390_ESCA_CPU_SLOTS]; }; @@ -249,12 +262,16 @@ struct kvm_s390_sie_block { #define ECB_SPECI 0x08 #define ECB_SRSI 0x04 #define ECB_HOSTPROTINT 0x02 +#define ECB_PTF 0x01 __u8 ecb; /* 0x0061 */ #define ECB2_CMMA 0x80 #define ECB2_IEP 0x20 #define ECB2_PFMFI 0x08 #define ECB2_ESCA 0x04 +#define ECB2_ZPCI_LSI 0x02 __u8 ecb2; /* 0x0062 */ +#define ECB3_AISI 0x20 +#define ECB3_AISII 0x10 #define ECB3_DEA 0x08 #define ECB3_AES 0x04 #define ECB3_RI 0x01 @@ -759,6 +776,7 @@ struct kvm_vm_stat { u64 inject_pfault_done; u64 inject_service_signal; u64 inject_virtio; + u64 aen_forward; }; struct kvm_arch_memory_slot { @@ -923,6 +941,8 @@ struct kvm_s390_pv { u64 guest_len; unsigned long stor_base; void *stor_var; + bool dumping; + struct mmu_notifier mmu_notifier; }; struct kvm_arch{ @@ -939,6 +959,7 @@ struct kvm_arch{ int use_cmma; int use_pfmfi; int use_skf; + int use_zpci_interp; int user_cpu_state_ctrl; int user_sigp; int user_stsi; @@ -962,6 +983,8 @@ struct kvm_arch{ DECLARE_BITMAP(idle_mask, KVM_MAX_VCPUS); struct kvm_s390_gisa_interrupt gisa_int; struct kvm_s390_pv pv; + struct list_head kzdev_list; + spinlock_t kzdev_list_lock; }; #define KVM_HVA_ERR_BAD (-1UL) @@ -1012,4 +1035,19 @@ static inline void kvm_arch_flush_shadow_memslot(struct kvm *kvm, static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {} static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {} +#define __KVM_HAVE_ARCH_VM_FREE +void kvm_arch_free_vm(struct kvm *kvm); + +#ifdef CONFIG_VFIO_PCI_ZDEV_KVM +int kvm_s390_pci_register_kvm(struct zpci_dev *zdev, struct kvm *kvm); +void kvm_s390_pci_unregister_kvm(struct zpci_dev *zdev); +#else +static inline int kvm_s390_pci_register_kvm(struct zpci_dev *dev, + struct kvm *kvm) +{ + return -EPERM; +} +static inline void kvm_s390_pci_unregister_kvm(struct zpci_dev *dev) {} +#endif + #endif diff --git a/arch/s390/include/asm/mmu.h b/arch/s390/include/asm/mmu.h index 82aae78e1315..1572b3634cdd 100644 --- a/arch/s390/include/asm/mmu.h +++ b/arch/s390/include/asm/mmu.h @@ -18,7 +18,7 @@ typedef struct { unsigned long asce_limit; unsigned long vdso_base; /* The mmu context belongs to a secure guest. */ - atomic_t is_protected; + atomic_t protected_count; /* * The following bitfields need a down_write on the mm * semaphore when they are written to. As they are only diff --git a/arch/s390/include/asm/mmu_context.h b/arch/s390/include/asm/mmu_context.h index c7937f369e62..2a38af5a00c2 100644 --- a/arch/s390/include/asm/mmu_context.h +++ b/arch/s390/include/asm/mmu_context.h @@ -26,7 +26,7 @@ static inline int init_new_context(struct task_struct *tsk, INIT_LIST_HEAD(&mm->context.gmap_list); cpumask_clear(&mm->context.cpu_attach_mask); atomic_set(&mm->context.flush_count, 0); - atomic_set(&mm->context.is_protected, 0); + atomic_set(&mm->context.protected_count, 0); mm->context.gmap_asce = 0; mm->context.flush_mm = 0; #ifdef CONFIG_PGSTE diff --git a/arch/s390/include/asm/pci.h b/arch/s390/include/asm/pci.h index 5889ddcbc374..7b4cdadbc023 100644 --- a/arch/s390/include/asm/pci.h +++ b/arch/s390/include/asm/pci.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #define PCIBIOS_MIN_IO 0x1000 @@ -96,6 +97,7 @@ struct zpci_bar_struct { }; struct s390_domain; +struct kvm_zdev; #define ZPCI_FUNCTIONS_PER_BUS 256 struct zpci_bus { @@ -122,11 +124,14 @@ struct zpci_dev { enum zpci_state state; u32 fid; /* function ID, used by sclp */ u32 fh; /* function handle, used by insn's */ + u32 gisa; /* GISA designation for passthrough */ u16 vfn; /* virtual function number */ u16 pchid; /* physical channel ID */ + u16 maxstbl; /* Maximum store block size */ u8 pfgid; /* function group ID */ u8 pft; /* pci function type */ u8 port; + u8 dtsm; /* Supported DT mask */ u8 rid_available : 1; u8 has_hp_slot : 1; u8 has_resources : 1; @@ -185,7 +190,10 @@ struct zpci_dev { struct dentry *debugfs_dev; + /* IOMMU and passthrough */ struct s390_domain *s390_domain; /* s390 IOMMU domain data */ + struct kvm_zdev *kzdev; + struct mutex kzdev_lock; }; static inline bool zdev_enabled(struct zpci_dev *zdev) @@ -197,6 +205,9 @@ extern const struct attribute_group *zpci_attr_groups[]; extern unsigned int s390_pci_force_floating __initdata; extern unsigned int s390_pci_no_rid; +extern union zpci_sic_iib *zpci_aipb; +extern struct airq_iv *zpci_aif_sbv; + /* ----------------------------------------------------------------------------- Prototypes ----------------------------------------------------------------------------- */ diff --git a/arch/s390/include/asm/pci_clp.h b/arch/s390/include/asm/pci_clp.h index 1f4b666e85ee..d6189ed14f84 100644 --- a/arch/s390/include/asm/pci_clp.h +++ b/arch/s390/include/asm/pci_clp.h @@ -153,9 +153,11 @@ struct clp_rsp_query_pci_grp { u8 : 6; u8 frame : 1; u8 refresh : 1; /* TLB refresh mode */ - u16 reserved2; + u16 : 3; + u16 maxstbl : 13; /* Maximum store block size */ u16 mui; - u16 : 16; + u8 dtsm; /* Supported DT mask */ + u8 reserved3; u16 maxfaal; u16 : 4; u16 dnoi : 12; @@ -173,7 +175,8 @@ struct clp_req_set_pci { u16 reserved2; u8 oc; /* operation controls */ u8 ndas; /* number of dma spaces */ - u64 reserved3; + u32 reserved3; + u32 gisa; /* GISA designation */ } __packed; /* Set PCI function response */ diff --git a/arch/s390/include/asm/pci_insn.h b/arch/s390/include/asm/pci_insn.h index 61cf9531f68f..e5f57cfe1d45 100644 --- a/arch/s390/include/asm/pci_insn.h +++ b/arch/s390/include/asm/pci_insn.h @@ -98,6 +98,15 @@ struct zpci_fib { u32 gd; } __packed __aligned(8); +/* Set Interruption Controls Operation Controls */ +#define SIC_IRQ_MODE_ALL 0 +#define SIC_IRQ_MODE_SINGLE 1 +#define SIC_SET_AENI_CONTROLS 2 +#define SIC_IRQ_MODE_DIRECT 4 +#define SIC_IRQ_MODE_D_ALL 16 +#define SIC_IRQ_MODE_D_SINGLE 17 +#define SIC_IRQ_MODE_SET_CPU 18 + /* directed interruption information block */ struct zpci_diib { u32 : 1; @@ -119,9 +128,20 @@ struct zpci_cdiib { u64 : 64; } __packed __aligned(8); +/* adapter interruption parameters block */ +struct zpci_aipb { + u64 faisb; + u64 gait; + u16 : 13; + u16 afi : 3; + u32 : 32; + u16 faal; +} __packed __aligned(8); + union zpci_sic_iib { struct zpci_diib diib; struct zpci_cdiib cdiib; + struct zpci_aipb aipb; }; DECLARE_STATIC_KEY_FALSE(have_mio); @@ -134,13 +154,6 @@ int __zpci_store(u64 data, u64 req, u64 offset); int zpci_store(const volatile void __iomem *addr, u64 data, unsigned long len); int __zpci_store_block(const u64 *data, u64 req, u64 offset); void zpci_barrier(void); -int __zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib); - -static inline int zpci_set_irq_ctrl(u16 ctl, u8 isc) -{ - union zpci_sic_iib iib = {{0}}; - - return __zpci_set_irq_ctrl(ctl, isc, &iib); -} +int zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib); #endif diff --git a/arch/s390/include/asm/pgtable.h b/arch/s390/include/asm/pgtable.h index a397b072a580..cf81acf3879c 100644 --- a/arch/s390/include/asm/pgtable.h +++ b/arch/s390/include/asm/pgtable.h @@ -525,7 +525,7 @@ static inline int mm_has_pgste(struct mm_struct *mm) static inline int mm_is_protected(struct mm_struct *mm) { #ifdef CONFIG_PGSTE - if (unlikely(atomic_read(&mm->context.is_protected))) + if (unlikely(atomic_read(&mm->context.protected_count))) return 1; #endif return 0; @@ -1182,9 +1182,22 @@ static inline pte_t ptep_get_and_clear_full(struct mm_struct *mm, } else { res = ptep_xchg_lazy(mm, addr, ptep, __pte(_PAGE_INVALID)); } - /* At this point the reference through the mapping is still present */ - if (mm_is_protected(mm) && pte_present(res)) - uv_convert_owned_from_secure(pte_val(res) & PAGE_MASK); + /* Nothing to do */ + if (!mm_is_protected(mm) || !pte_present(res)) + return res; + /* + * At this point the reference through the mapping is still present. + * The notifier should have destroyed all protected vCPUs at this + * point, so the destroy should be successful. + */ + if (full && !uv_destroy_owned_page(pte_val(res) & PAGE_MASK)) + return res; + /* + * If something went wrong and the page could not be destroyed, or + * if this is not a mm teardown, the slower export is used as + * fallback instead. + */ + uv_convert_owned_from_secure(pte_val(res) & PAGE_MASK); return res; } diff --git a/arch/s390/include/asm/sclp.h b/arch/s390/include/asm/sclp.h index 236b34b75ddb..addefe8ccdba 100644 --- a/arch/s390/include/asm/sclp.h +++ b/arch/s390/include/asm/sclp.h @@ -88,6 +88,10 @@ struct sclp_info { unsigned char has_sipl : 1; unsigned char has_dirq : 1; unsigned char has_iplcc : 1; + unsigned char has_zpci_lsi : 1; + unsigned char has_aisii : 1; + unsigned char has_aeni : 1; + unsigned char has_aisi : 1; unsigned int ibc; unsigned int mtid; unsigned int mtid_cp; diff --git a/arch/s390/include/asm/tpi.h b/arch/s390/include/asm/tpi.h index 1ac538b8cbf5..f76e5fdff23a 100644 --- a/arch/s390/include/asm/tpi.h +++ b/arch/s390/include/asm/tpi.h @@ -19,6 +19,19 @@ struct tpi_info { u32 :12; } __packed __aligned(4); +/* I/O-Interruption Code as stored by TPI for an Adapter I/O */ +struct tpi_adapter_info { + u32 aism:8; + u32 :22; + u32 error:1; + u32 forward:1; + u32 reserved; + u32 adapter_IO:1; + u32 directed_irq:1; + u32 isc:3; + u32 :27; +} __packed __aligned(4); + #endif /* __ASSEMBLY__ */ #endif /* _ASM_S390_TPI_H */ diff --git a/arch/s390/include/asm/uv.h b/arch/s390/include/asm/uv.h index cfea7b77a5b8..be3ef9dd6972 100644 --- a/arch/s390/include/asm/uv.h +++ b/arch/s390/include/asm/uv.h @@ -50,6 +50,10 @@ #define UVC_CMD_SET_UNSHARE_ALL 0x0340 #define UVC_CMD_PIN_PAGE_SHARED 0x0341 #define UVC_CMD_UNPIN_PAGE_SHARED 0x0342 +#define UVC_CMD_DUMP_INIT 0x0400 +#define UVC_CMD_DUMP_CONF_STOR_STATE 0x0401 +#define UVC_CMD_DUMP_CPU 0x0402 +#define UVC_CMD_DUMP_COMPLETE 0x0403 #define UVC_CMD_SET_SHARED_ACCESS 0x1000 #define UVC_CMD_REMOVE_SHARED_ACCESS 0x1001 #define UVC_CMD_RETR_ATTEST 0x1020 @@ -77,6 +81,10 @@ enum uv_cmds_inst { BIT_UVC_CMD_UNSHARE_ALL = 20, BIT_UVC_CMD_PIN_PAGE_SHARED = 21, BIT_UVC_CMD_UNPIN_PAGE_SHARED = 22, + BIT_UVC_CMD_DUMP_INIT = 24, + BIT_UVC_CMD_DUMP_CONFIG_STOR_STATE = 25, + BIT_UVC_CMD_DUMP_CPU = 26, + BIT_UVC_CMD_DUMP_COMPLETE = 27, BIT_UVC_CMD_RETR_ATTEST = 28, }; @@ -110,7 +118,16 @@ struct uv_cb_qui { u8 reserved88[158 - 136]; /* 0x0088 */ u16 max_guest_cpu_id; /* 0x009e */ u64 uv_feature_indications; /* 0x00a0 */ - u8 reserveda8[200 - 168]; /* 0x00a8 */ + u64 reserveda8; /* 0x00a8 */ + u64 supp_se_hdr_versions; /* 0x00b0 */ + u64 supp_se_hdr_pcf; /* 0x00b8 */ + u64 reservedc0; /* 0x00c0 */ + u64 conf_dump_storage_state_len; /* 0x00c8 */ + u64 conf_dump_finalize_len; /* 0x00d0 */ + u64 reservedd8; /* 0x00d8 */ + u64 supp_att_req_hdr_ver; /* 0x00e0 */ + u64 supp_att_pflags; /* 0x00e8 */ + u8 reservedf0[256 - 240]; /* 0x00f0 */ } __packed __aligned(8); /* Initialize Ultravisor */ @@ -240,6 +257,31 @@ struct uv_cb_attest { u64 reserved168[4]; /* 0x0168 */ } __packed __aligned(8); +struct uv_cb_dump_cpu { + struct uv_cb_header header; + u64 reserved08[2]; + u64 cpu_handle; + u64 dump_area_origin; + u64 reserved28[5]; +} __packed __aligned(8); + +struct uv_cb_dump_stor_state { + struct uv_cb_header header; + u64 reserved08[2]; + u64 config_handle; + u64 dump_area_origin; + u64 gaddr; + u64 reserved28[4]; +} __packed __aligned(8); + +struct uv_cb_dump_complete { + struct uv_cb_header header; + u64 reserved08[2]; + u64 config_handle; + u64 dump_area_origin; + u64 reserved30[5]; +} __packed __aligned(8); + static inline int __uv_call(unsigned long r1, unsigned long r2) { int cc; @@ -307,6 +349,12 @@ struct uv_info { unsigned int max_num_sec_conf; unsigned short max_guest_cpu_id; unsigned long uv_feature_indications; + unsigned long supp_se_hdr_ver; + unsigned long supp_se_hdr_pcf; + unsigned long conf_dump_storage_state_len; + unsigned long conf_dump_finalize_len; + unsigned long supp_att_req_hdr_ver; + unsigned long supp_att_pflags; }; extern struct uv_info uv_info; @@ -378,6 +426,7 @@ static inline int is_prot_virt_host(void) } int gmap_make_secure(struct gmap *gmap, unsigned long gaddr, void *uvcb); +int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr); int uv_destroy_owned_page(unsigned long paddr); int uv_convert_from_secure(unsigned long paddr); int uv_convert_owned_from_secure(unsigned long paddr); diff --git a/arch/s390/include/uapi/asm/kvm.h b/arch/s390/include/uapi/asm/kvm.h index 7a6b14874d65..a73cf01a1606 100644 --- a/arch/s390/include/uapi/asm/kvm.h +++ b/arch/s390/include/uapi/asm/kvm.h @@ -74,6 +74,7 @@ struct kvm_s390_io_adapter_req { #define KVM_S390_VM_CRYPTO 2 #define KVM_S390_VM_CPU_MODEL 3 #define KVM_S390_VM_MIGRATION 4 +#define KVM_S390_VM_CPU_TOPOLOGY 5 /* kvm attributes for mem_ctrl */ #define KVM_S390_VM_MEM_ENABLE_CMMA 0 diff --git a/arch/s390/kernel/uv.c b/arch/s390/kernel/uv.c index a5425075dd25..f9810d2a267c 100644 --- a/arch/s390/kernel/uv.c +++ b/arch/s390/kernel/uv.c @@ -234,6 +234,32 @@ static int make_secure_pte(pte_t *ptep, unsigned long addr, return uvcb->rc == 0x10a ? -ENXIO : -EINVAL; } +/** + * should_export_before_import - Determine whether an export is needed + * before an import-like operation + * @uvcb: the Ultravisor control block of the UVC to be performed + * @mm: the mm of the process + * + * Returns whether an export is needed before every import-like operation. + * This is needed for shared pages, which don't trigger a secure storage + * exception when accessed from a different guest. + * + * Although considered as one, the Unpin Page UVC is not an actual import, + * so it is not affected. + * + * No export is needed also when there is only one protected VM, because the + * page cannot belong to the wrong VM in that case (there is no "other VM" + * it can belong to). + * + * Return: true if an export is needed before every import, otherwise false. + */ +static bool should_export_before_import(struct uv_cb_header *uvcb, struct mm_struct *mm) +{ + if (uvcb->cmd == UVC_CMD_UNPIN_PAGE_SHARED) + return false; + return atomic_read(&mm->context.protected_count) > 1; +} + /* * Requests the Ultravisor to make a page accessible to a guest. * If it's brought in the first time, it will be cleared. If @@ -277,6 +303,8 @@ again: lock_page(page); ptep = get_locked_pte(gmap->mm, uaddr, &ptelock); + if (should_export_before_import(uvcb, gmap->mm)) + uv_convert_from_secure(page_to_phys(page)); rc = make_secure_pte(ptep, uaddr, page, uvcb); pte_unmap_unlock(ptep, ptelock); unlock_page(page); @@ -334,6 +362,61 @@ int gmap_convert_to_secure(struct gmap *gmap, unsigned long gaddr) } EXPORT_SYMBOL_GPL(gmap_convert_to_secure); +/** + * gmap_destroy_page - Destroy a guest page. + * @gmap: the gmap of the guest + * @gaddr: the guest address to destroy + * + * An attempt will be made to destroy the given guest page. If the attempt + * fails, an attempt is made to export the page. If both attempts fail, an + * appropriate error is returned. + */ +int gmap_destroy_page(struct gmap *gmap, unsigned long gaddr) +{ + struct vm_area_struct *vma; + unsigned long uaddr; + struct page *page; + int rc; + + rc = -EFAULT; + mmap_read_lock(gmap->mm); + + uaddr = __gmap_translate(gmap, gaddr); + if (IS_ERR_VALUE(uaddr)) + goto out; + vma = vma_lookup(gmap->mm, uaddr); + if (!vma) + goto out; + /* + * Huge pages should not be able to become secure + */ + if (is_vm_hugetlb_page(vma)) + goto out; + + rc = 0; + /* we take an extra reference here */ + page = follow_page(vma, uaddr, FOLL_WRITE | FOLL_GET); + if (IS_ERR_OR_NULL(page)) + goto out; + rc = uv_destroy_owned_page(page_to_phys(page)); + /* + * Fault handlers can race; it is possible that two CPUs will fault + * on the same secure page. One CPU can destroy the page, reboot, + * re-enter secure mode and import it, while the second CPU was + * stuck at the beginning of the handler. At some point the second + * CPU will be able to progress, and it will not be able to destroy + * the page. In that case we do not want to terminate the process, + * we instead try to export the page. + */ + if (rc) + rc = uv_convert_owned_from_secure(page_to_phys(page)); + put_page(page); +out: + mmap_read_unlock(gmap->mm); + return rc; +} +EXPORT_SYMBOL_GPL(gmap_destroy_page); + /* * To be called with the page locked or with an extra reference! This will * prevent gmap_make_secure from touching the page concurrently. Having 2 @@ -392,6 +475,54 @@ static ssize_t uv_query_facilities(struct kobject *kobj, static struct kobj_attribute uv_query_facilities_attr = __ATTR(facilities, 0444, uv_query_facilities, NULL); +static ssize_t uv_query_supp_se_hdr_ver(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%lx\n", uv_info.supp_se_hdr_ver); +} + +static struct kobj_attribute uv_query_supp_se_hdr_ver_attr = + __ATTR(supp_se_hdr_ver, 0444, uv_query_supp_se_hdr_ver, NULL); + +static ssize_t uv_query_supp_se_hdr_pcf(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%lx\n", uv_info.supp_se_hdr_pcf); +} + +static struct kobj_attribute uv_query_supp_se_hdr_pcf_attr = + __ATTR(supp_se_hdr_pcf, 0444, uv_query_supp_se_hdr_pcf, NULL); + +static ssize_t uv_query_dump_cpu_len(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return scnprintf(page, PAGE_SIZE, "%lx\n", + uv_info.guest_cpu_stor_len); +} + +static struct kobj_attribute uv_query_dump_cpu_len_attr = + __ATTR(uv_query_dump_cpu_len, 0444, uv_query_dump_cpu_len, NULL); + +static ssize_t uv_query_dump_storage_state_len(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return scnprintf(page, PAGE_SIZE, "%lx\n", + uv_info.conf_dump_storage_state_len); +} + +static struct kobj_attribute uv_query_dump_storage_state_len_attr = + __ATTR(dump_storage_state_len, 0444, uv_query_dump_storage_state_len, NULL); + +static ssize_t uv_query_dump_finalize_len(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return scnprintf(page, PAGE_SIZE, "%lx\n", + uv_info.conf_dump_finalize_len); +} + +static struct kobj_attribute uv_query_dump_finalize_len_attr = + __ATTR(dump_finalize_len, 0444, uv_query_dump_finalize_len, NULL); + static ssize_t uv_query_feature_indications(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { @@ -431,12 +562,37 @@ static ssize_t uv_query_max_guest_addr(struct kobject *kobj, static struct kobj_attribute uv_query_max_guest_addr_attr = __ATTR(max_address, 0444, uv_query_max_guest_addr, NULL); +static ssize_t uv_query_supp_att_req_hdr_ver(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_att_req_hdr_ver); +} + +static struct kobj_attribute uv_query_supp_att_req_hdr_ver_attr = + __ATTR(supp_att_req_hdr_ver, 0444, uv_query_supp_att_req_hdr_ver, NULL); + +static ssize_t uv_query_supp_att_pflags(struct kobject *kobj, + struct kobj_attribute *attr, char *page) +{ + return scnprintf(page, PAGE_SIZE, "%lx\n", uv_info.supp_att_pflags); +} + +static struct kobj_attribute uv_query_supp_att_pflags_attr = + __ATTR(supp_att_pflags, 0444, uv_query_supp_att_pflags, NULL); + static struct attribute *uv_query_attrs[] = { &uv_query_facilities_attr.attr, &uv_query_feature_indications_attr.attr, &uv_query_max_guest_cpus_attr.attr, &uv_query_max_guest_vms_attr.attr, &uv_query_max_guest_addr_attr.attr, + &uv_query_supp_se_hdr_ver_attr.attr, + &uv_query_supp_se_hdr_pcf_attr.attr, + &uv_query_dump_storage_state_len_attr.attr, + &uv_query_dump_finalize_len_attr.attr, + &uv_query_dump_cpu_len_attr.attr, + &uv_query_supp_att_req_hdr_ver_attr.attr, + &uv_query_supp_att_pflags_attr.attr, NULL, }; diff --git a/arch/s390/kvm/Kconfig b/arch/s390/kvm/Kconfig index 2e84d3922f7c..33f4ff909476 100644 --- a/arch/s390/kvm/Kconfig +++ b/arch/s390/kvm/Kconfig @@ -34,6 +34,7 @@ config KVM select SRCU select KVM_VFIO select INTERVAL_TREE + select MMU_NOTIFIER help Support hosting paravirtualized guest machines using the SIE virtualization capability on the mainframe. This should work diff --git a/arch/s390/kvm/Makefile b/arch/s390/kvm/Makefile index 26f4a74e5ce4..02217fb4ae10 100644 --- a/arch/s390/kvm/Makefile +++ b/arch/s390/kvm/Makefile @@ -10,4 +10,5 @@ ccflags-y := -Ivirt/kvm -Iarch/s390/kvm kvm-y += kvm-s390.o intercept.o interrupt.o priv.o sigp.o kvm-y += diag.o gaccess.o guestdbg.o vsie.o pv.o +kvm-$(CONFIG_VFIO_PCI_ZDEV_KVM) += pci.o obj-$(CONFIG_KVM) += kvm.o diff --git a/arch/s390/kvm/gaccess.c b/arch/s390/kvm/gaccess.c index 227ed0009354..082ec5f2c3a5 100644 --- a/arch/s390/kvm/gaccess.c +++ b/arch/s390/kvm/gaccess.c @@ -262,77 +262,77 @@ struct aste { /* .. more fields there */ }; -int ipte_lock_held(struct kvm_vcpu *vcpu) +int ipte_lock_held(struct kvm *kvm) { - if (vcpu->arch.sie_block->eca & ECA_SII) { + if (sclp.has_siif) { int rc; - read_lock(&vcpu->kvm->arch.sca_lock); - rc = kvm_s390_get_ipte_control(vcpu->kvm)->kh != 0; - read_unlock(&vcpu->kvm->arch.sca_lock); + read_lock(&kvm->arch.sca_lock); + rc = kvm_s390_get_ipte_control(kvm)->kh != 0; + read_unlock(&kvm->arch.sca_lock); return rc; } - return vcpu->kvm->arch.ipte_lock_count != 0; + return kvm->arch.ipte_lock_count != 0; } -static void ipte_lock_simple(struct kvm_vcpu *vcpu) +static void ipte_lock_simple(struct kvm *kvm) { union ipte_control old, new, *ic; - mutex_lock(&vcpu->kvm->arch.ipte_mutex); - vcpu->kvm->arch.ipte_lock_count++; - if (vcpu->kvm->arch.ipte_lock_count > 1) + mutex_lock(&kvm->arch.ipte_mutex); + kvm->arch.ipte_lock_count++; + if (kvm->arch.ipte_lock_count > 1) goto out; retry: - read_lock(&vcpu->kvm->arch.sca_lock); - ic = kvm_s390_get_ipte_control(vcpu->kvm); + read_lock(&kvm->arch.sca_lock); + ic = kvm_s390_get_ipte_control(kvm); do { old = READ_ONCE(*ic); if (old.k) { - read_unlock(&vcpu->kvm->arch.sca_lock); + read_unlock(&kvm->arch.sca_lock); cond_resched(); goto retry; } new = old; new.k = 1; } while (cmpxchg(&ic->val, old.val, new.val) != old.val); - read_unlock(&vcpu->kvm->arch.sca_lock); + read_unlock(&kvm->arch.sca_lock); out: - mutex_unlock(&vcpu->kvm->arch.ipte_mutex); + mutex_unlock(&kvm->arch.ipte_mutex); } -static void ipte_unlock_simple(struct kvm_vcpu *vcpu) +static void ipte_unlock_simple(struct kvm *kvm) { union ipte_control old, new, *ic; - mutex_lock(&vcpu->kvm->arch.ipte_mutex); - vcpu->kvm->arch.ipte_lock_count--; - if (vcpu->kvm->arch.ipte_lock_count) + mutex_lock(&kvm->arch.ipte_mutex); + kvm->arch.ipte_lock_count--; + if (kvm->arch.ipte_lock_count) goto out; - read_lock(&vcpu->kvm->arch.sca_lock); - ic = kvm_s390_get_ipte_control(vcpu->kvm); + read_lock(&kvm->arch.sca_lock); + ic = kvm_s390_get_ipte_control(kvm); do { old = READ_ONCE(*ic); new = old; new.k = 0; } while (cmpxchg(&ic->val, old.val, new.val) != old.val); - read_unlock(&vcpu->kvm->arch.sca_lock); - wake_up(&vcpu->kvm->arch.ipte_wq); + read_unlock(&kvm->arch.sca_lock); + wake_up(&kvm->arch.ipte_wq); out: - mutex_unlock(&vcpu->kvm->arch.ipte_mutex); + mutex_unlock(&kvm->arch.ipte_mutex); } -static void ipte_lock_siif(struct kvm_vcpu *vcpu) +static void ipte_lock_siif(struct kvm *kvm) { union ipte_control old, new, *ic; retry: - read_lock(&vcpu->kvm->arch.sca_lock); - ic = kvm_s390_get_ipte_control(vcpu->kvm); + read_lock(&kvm->arch.sca_lock); + ic = kvm_s390_get_ipte_control(kvm); do { old = READ_ONCE(*ic); if (old.kg) { - read_unlock(&vcpu->kvm->arch.sca_lock); + read_unlock(&kvm->arch.sca_lock); cond_resched(); goto retry; } @@ -340,15 +340,15 @@ retry: new.k = 1; new.kh++; } while (cmpxchg(&ic->val, old.val, new.val) != old.val); - read_unlock(&vcpu->kvm->arch.sca_lock); + read_unlock(&kvm->arch.sca_lock); } -static void ipte_unlock_siif(struct kvm_vcpu *vcpu) +static void ipte_unlock_siif(struct kvm *kvm) { union ipte_control old, new, *ic; - read_lock(&vcpu->kvm->arch.sca_lock); - ic = kvm_s390_get_ipte_control(vcpu->kvm); + read_lock(&kvm->arch.sca_lock); + ic = kvm_s390_get_ipte_control(kvm); do { old = READ_ONCE(*ic); new = old; @@ -356,25 +356,25 @@ static void ipte_unlock_siif(struct kvm_vcpu *vcpu) if (!new.kh) new.k = 0; } while (cmpxchg(&ic->val, old.val, new.val) != old.val); - read_unlock(&vcpu->kvm->arch.sca_lock); + read_unlock(&kvm->arch.sca_lock); if (!new.kh) - wake_up(&vcpu->kvm->arch.ipte_wq); + wake_up(&kvm->arch.ipte_wq); } -void ipte_lock(struct kvm_vcpu *vcpu) +void ipte_lock(struct kvm *kvm) { - if (vcpu->arch.sie_block->eca & ECA_SII) - ipte_lock_siif(vcpu); + if (sclp.has_siif) + ipte_lock_siif(kvm); else - ipte_lock_simple(vcpu); + ipte_lock_simple(kvm); } -void ipte_unlock(struct kvm_vcpu *vcpu) +void ipte_unlock(struct kvm *kvm) { - if (vcpu->arch.sie_block->eca & ECA_SII) - ipte_unlock_siif(vcpu); + if (sclp.has_siif) + ipte_unlock_siif(kvm); else - ipte_unlock_simple(vcpu); + ipte_unlock_simple(kvm); } static int ar_translation(struct kvm_vcpu *vcpu, union asce *asce, u8 ar, @@ -1086,7 +1086,7 @@ int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, try_storage_prot_override = storage_prot_override_applicable(vcpu); need_ipte_lock = psw_bits(*psw).dat && !asce.r; if (need_ipte_lock) - ipte_lock(vcpu); + ipte_lock(vcpu->kvm); /* * Since we do the access further down ultimately via a move instruction * that does key checking and returns an error in case of a protection @@ -1127,7 +1127,7 @@ int access_guest_with_key(struct kvm_vcpu *vcpu, unsigned long ga, u8 ar, } out_unlock: if (need_ipte_lock) - ipte_unlock(vcpu); + ipte_unlock(vcpu->kvm); if (nr_pages > ARRAY_SIZE(gpa_array)) vfree(gpas); return rc; @@ -1199,10 +1199,10 @@ int check_gva_range(struct kvm_vcpu *vcpu, unsigned long gva, u8 ar, rc = get_vcpu_asce(vcpu, &asce, gva, ar, mode); if (rc) return rc; - ipte_lock(vcpu); + ipte_lock(vcpu->kvm); rc = guest_range_to_gpas(vcpu, gva, ar, NULL, length, asce, mode, access_key); - ipte_unlock(vcpu); + ipte_unlock(vcpu->kvm); return rc; } @@ -1465,7 +1465,7 @@ int kvm_s390_shadow_fault(struct kvm_vcpu *vcpu, struct gmap *sg, * tables/pointers we read stay valid - unshadowing is however * always possible - only guest_table_lock protects us. */ - ipte_lock(vcpu); + ipte_lock(vcpu->kvm); rc = gmap_shadow_pgt_lookup(sg, saddr, &pgt, &dat_protection, &fake); if (rc) @@ -1499,7 +1499,7 @@ shadow_page: pte.p |= dat_protection; if (!rc) rc = gmap_shadow_page(sg, saddr, __pte(pte.val)); - ipte_unlock(vcpu); + ipte_unlock(vcpu->kvm); mmap_read_unlock(sg->mm); return rc; } diff --git a/arch/s390/kvm/gaccess.h b/arch/s390/kvm/gaccess.h index 1124ff282012..9408d6cc8e2c 100644 --- a/arch/s390/kvm/gaccess.h +++ b/arch/s390/kvm/gaccess.h @@ -440,9 +440,9 @@ int read_guest_real(struct kvm_vcpu *vcpu, unsigned long gra, void *data, return access_guest_real(vcpu, gra, data, len, 0); } -void ipte_lock(struct kvm_vcpu *vcpu); -void ipte_unlock(struct kvm_vcpu *vcpu); -int ipte_lock_held(struct kvm_vcpu *vcpu); +void ipte_lock(struct kvm *kvm); +void ipte_unlock(struct kvm *kvm); +int ipte_lock_held(struct kvm *kvm); int kvm_s390_check_low_addr_prot_real(struct kvm_vcpu *vcpu, unsigned long gra); /* MVPG PEI indication bits */ diff --git a/arch/s390/kvm/intercept.c b/arch/s390/kvm/intercept.c index 8bd42a20d924..88112065d941 100644 --- a/arch/s390/kvm/intercept.c +++ b/arch/s390/kvm/intercept.c @@ -528,12 +528,27 @@ static int handle_pv_uvc(struct kvm_vcpu *vcpu) static int handle_pv_notification(struct kvm_vcpu *vcpu) { + int ret; + if (vcpu->arch.sie_block->ipa == 0xb210) return handle_pv_spx(vcpu); if (vcpu->arch.sie_block->ipa == 0xb220) return handle_pv_sclp(vcpu); if (vcpu->arch.sie_block->ipa == 0xb9a4) return handle_pv_uvc(vcpu); + if (vcpu->arch.sie_block->ipa >> 8 == 0xae) { + /* + * Besides external call, other SIGP orders also cause a + * 108 (pv notify) intercept. In contrast to external call, + * these orders need to be emulated and hence the appropriate + * place to handle them is in handle_instruction(). + * So first try kvm_s390_handle_sigp_pei() and if that isn't + * successful, go on with handle_instruction(). + */ + ret = kvm_s390_handle_sigp_pei(vcpu); + if (!ret) + return ret; + } return handle_instruction(vcpu); } diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index af96dc0549a4..b9c944b262c7 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -28,9 +28,11 @@ #include #include #include +#include #include "kvm-s390.h" #include "gaccess.h" #include "trace-s390.h" +#include "pci.h" #define PFAULT_INIT 0x0600 #define PFAULT_DONE 0x0680 @@ -702,7 +704,7 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu) /* * We indicate floating repressible conditions along with * other pending conditions. Channel Report Pending and Channel - * Subsystem damage are the only two and and are indicated by + * Subsystem damage are the only two and are indicated by * bits in mcic and masked in cr14. */ if (test_and_clear_bit(IRQ_PEND_MCHK_REP, &fi->pending_irqs)) { @@ -3311,10 +3313,87 @@ out: } EXPORT_SYMBOL_GPL(kvm_s390_gisc_unregister); -static void gib_alert_irq_handler(struct airq_struct *airq, bool floating) +static void aen_host_forward(unsigned long si) { + struct kvm_s390_gisa_interrupt *gi; + struct zpci_gaite *gaite; + struct kvm *kvm; + + gaite = (struct zpci_gaite *)aift->gait + + (si * sizeof(struct zpci_gaite)); + if (gaite->count == 0) + return; + if (gaite->aisb != 0) + set_bit_inv(gaite->aisbo, (unsigned long *)gaite->aisb); + + kvm = kvm_s390_pci_si_to_kvm(aift, si); + if (!kvm) + return; + gi = &kvm->arch.gisa_int; + + if (!(gi->origin->g1.simm & AIS_MODE_MASK(gaite->gisc)) || + !(gi->origin->g1.nimm & AIS_MODE_MASK(gaite->gisc))) { + gisa_set_ipm_gisc(gi->origin, gaite->gisc); + if (hrtimer_active(&gi->timer)) + hrtimer_cancel(&gi->timer); + hrtimer_start(&gi->timer, 0, HRTIMER_MODE_REL); + kvm->stat.aen_forward++; + } +} + +static void aen_process_gait(u8 isc) +{ + bool found = false, first = true; + union zpci_sic_iib iib = {{0}}; + unsigned long si, flags; + + spin_lock_irqsave(&aift->gait_lock, flags); + + if (!aift->gait) { + spin_unlock_irqrestore(&aift->gait_lock, flags); + return; + } + + for (si = 0;;) { + /* Scan adapter summary indicator bit vector */ + si = airq_iv_scan(aift->sbv, si, airq_iv_end(aift->sbv)); + if (si == -1UL) { + if (first || found) { + /* Re-enable interrupts. */ + zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, isc, + &iib); + first = found = false; + } else { + /* Interrupts on and all bits processed */ + break; + } + found = false; + si = 0; + /* Scan again after re-enabling interrupts */ + continue; + } + found = true; + aen_host_forward(si); + } + + spin_unlock_irqrestore(&aift->gait_lock, flags); +} + +static void gib_alert_irq_handler(struct airq_struct *airq, + struct tpi_info *tpi_info) +{ + struct tpi_adapter_info *info = (struct tpi_adapter_info *)tpi_info; + inc_irq_stat(IRQIO_GAL); - process_gib_alert_list(); + + if ((info->forward || info->error) && + IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) { + aen_process_gait(info->isc); + if (info->aism != 0) + process_gib_alert_list(); + } else { + process_gib_alert_list(); + } } static struct airq_struct gib_alert_irq = { @@ -3326,6 +3405,11 @@ void kvm_s390_gib_destroy(void) { if (!gib) return; + if (kvm_s390_pci_interp_allowed() && aift) { + mutex_lock(&aift->aift_lock); + kvm_s390_pci_aen_exit(); + mutex_unlock(&aift->aift_lock); + } chsc_sgib(0); unregister_adapter_interrupt(&gib_alert_irq); free_page((unsigned long)gib); @@ -3363,6 +3447,14 @@ int kvm_s390_gib_init(u8 nisc) goto out_unreg_gal; } + if (kvm_s390_pci_interp_allowed()) { + if (kvm_s390_pci_aen_init(nisc)) { + pr_err("Initializing AEN for PCI failed\n"); + rc = -EIO; + goto out_unreg_gal; + } + } + KVM_EVENT(3, "gib 0x%pK (nisc=%d) initialized", gib, gib->nisc); goto out; diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index 8fcb56141689..edfd4bbd0cba 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -47,6 +48,7 @@ #include #include "kvm-s390.h" #include "gaccess.h" +#include "pci.h" #define CREATE_TRACE_POINTS #include "trace.h" @@ -63,7 +65,8 @@ const struct _kvm_stats_desc kvm_vm_stats_desc[] = { STATS_DESC_COUNTER(VM, inject_float_mchk), STATS_DESC_COUNTER(VM, inject_pfault_done), STATS_DESC_COUNTER(VM, inject_service_signal), - STATS_DESC_COUNTER(VM, inject_virtio) + STATS_DESC_COUNTER(VM, inject_virtio), + STATS_DESC_COUNTER(VM, aen_forward) }; const struct kvm_stats_header kvm_vm_stats_header = { @@ -502,6 +505,14 @@ int kvm_arch_init(void *opaque) goto out; } + if (kvm_s390_pci_interp_allowed()) { + rc = kvm_s390_pci_init(); + if (rc) { + pr_err("Unable to allocate AIFT for PCI\n"); + goto out; + } + } + rc = kvm_s390_gib_init(GAL_ISC); if (rc) goto out; @@ -516,6 +527,8 @@ out: void kvm_arch_exit(void) { kvm_s390_gib_destroy(); + if (kvm_s390_pci_interp_allowed()) + kvm_s390_pci_exit(); debug_unregister(kvm_s390_dbf); debug_unregister(kvm_s390_dbf_uv); } @@ -606,6 +619,32 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_S390_PROTECTED: r = is_prot_virt_host(); break; + case KVM_CAP_S390_PROTECTED_DUMP: { + u64 pv_cmds_dump[] = { + BIT_UVC_CMD_DUMP_INIT, + BIT_UVC_CMD_DUMP_CONFIG_STOR_STATE, + BIT_UVC_CMD_DUMP_CPU, + BIT_UVC_CMD_DUMP_COMPLETE, + }; + int i; + + r = is_prot_virt_host(); + + for (i = 0; i < ARRAY_SIZE(pv_cmds_dump); i++) { + if (!test_bit_inv(pv_cmds_dump[i], + (unsigned long *)&uv_info.inst_calls_list)) { + r = 0; + break; + } + } + break; + } + case KVM_CAP_S390_ZPCI_OP: + r = kvm_s390_pci_interp_allowed(); + break; + case KVM_CAP_S390_CPU_TOPOLOGY: + r = test_facility(11); + break; default: r = 0; } @@ -817,6 +856,20 @@ int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) icpt_operexc_on_all_vcpus(kvm); r = 0; break; + case KVM_CAP_S390_CPU_TOPOLOGY: + r = -EINVAL; + mutex_lock(&kvm->lock); + if (kvm->created_vcpus) { + r = -EBUSY; + } else if (test_facility(11)) { + set_kvm_facility(kvm->arch.model.fac_mask, 11); + set_kvm_facility(kvm->arch.model.fac_list, 11); + r = 0; + } + mutex_unlock(&kvm->lock); + VM_EVENT(kvm, 3, "ENABLE: CAP_S390_CPU_TOPOLOGY %s", + r ? "(not available)" : "(success)"); + break; default: r = -EINVAL; break; @@ -1019,6 +1072,42 @@ static int kvm_s390_vm_set_crypto(struct kvm *kvm, struct kvm_device_attr *attr) return 0; } +static void kvm_s390_vcpu_pci_setup(struct kvm_vcpu *vcpu) +{ + /* Only set the ECB bits after guest requests zPCI interpretation */ + if (!vcpu->kvm->arch.use_zpci_interp) + return; + + vcpu->arch.sie_block->ecb2 |= ECB2_ZPCI_LSI; + vcpu->arch.sie_block->ecb3 |= ECB3_AISII + ECB3_AISI; +} + +void kvm_s390_vcpu_pci_enable_interp(struct kvm *kvm) +{ + struct kvm_vcpu *vcpu; + unsigned long i; + + lockdep_assert_held(&kvm->lock); + + if (!kvm_s390_pci_interp_allowed()) + return; + + /* + * If host is configured for PCI and the necessary facilities are + * available, turn on interpretation for the life of this guest + */ + kvm->arch.use_zpci_interp = 1; + + kvm_s390_vcpu_block_all(kvm); + + kvm_for_each_vcpu(i, vcpu, kvm) { + kvm_s390_vcpu_pci_setup(vcpu); + kvm_s390_sync_request(KVM_REQ_VSIE_RESTART, vcpu); + } + + kvm_s390_vcpu_unblock_all(kvm); +} + static void kvm_s390_sync_request_broadcast(struct kvm *kvm, int req) { unsigned long cx; @@ -1691,6 +1780,57 @@ static int kvm_s390_get_cpu_model(struct kvm *kvm, struct kvm_device_attr *attr) return ret; } +/** + * kvm_s390_update_topology_change_report - update CPU topology change report + * @kvm: guest KVM description + * @val: set or clear the MTCR bit + * + * Updates the Multiprocessor Topology-Change-Report bit to signal + * the guest with a topology change. + * This is only relevant if the topology facility is present. + * + * The SCA version, bsca or esca, doesn't matter as offset is the same. + */ +static void kvm_s390_update_topology_change_report(struct kvm *kvm, bool val) +{ + union sca_utility new, old; + struct bsca_block *sca; + + read_lock(&kvm->arch.sca_lock); + sca = kvm->arch.sca; + do { + old = READ_ONCE(sca->utility); + new = old; + new.mtcr = val; + } while (cmpxchg(&sca->utility.val, old.val, new.val) != old.val); + read_unlock(&kvm->arch.sca_lock); +} + +static int kvm_s390_set_topo_change_indication(struct kvm *kvm, + struct kvm_device_attr *attr) +{ + if (!test_kvm_facility(kvm, 11)) + return -ENXIO; + + kvm_s390_update_topology_change_report(kvm, !!attr->attr); + return 0; +} + +static int kvm_s390_get_topo_change_indication(struct kvm *kvm, + struct kvm_device_attr *attr) +{ + u8 topo; + + if (!test_kvm_facility(kvm, 11)) + return -ENXIO; + + read_lock(&kvm->arch.sca_lock); + topo = ((struct bsca_block *)kvm->arch.sca)->utility.mtcr; + read_unlock(&kvm->arch.sca_lock); + + return put_user(topo, (u8 __user *)attr->addr); +} + static int kvm_s390_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr) { int ret; @@ -1711,6 +1851,9 @@ static int kvm_s390_vm_set_attr(struct kvm *kvm, struct kvm_device_attr *attr) case KVM_S390_VM_MIGRATION: ret = kvm_s390_vm_set_migration(kvm, attr); break; + case KVM_S390_VM_CPU_TOPOLOGY: + ret = kvm_s390_set_topo_change_indication(kvm, attr); + break; default: ret = -ENXIO; break; @@ -1736,6 +1879,9 @@ static int kvm_s390_vm_get_attr(struct kvm *kvm, struct kvm_device_attr *attr) case KVM_S390_VM_MIGRATION: ret = kvm_s390_vm_get_migration(kvm, attr); break; + case KVM_S390_VM_CPU_TOPOLOGY: + ret = kvm_s390_get_topo_change_indication(kvm, attr); + break; default: ret = -ENXIO; break; @@ -1809,6 +1955,9 @@ static int kvm_s390_vm_has_attr(struct kvm *kvm, struct kvm_device_attr *attr) case KVM_S390_VM_MIGRATION: ret = 0; break; + case KVM_S390_VM_CPU_TOPOLOGY: + ret = test_kvm_facility(kvm, 11) ? 0 : -ENXIO; + break; default: ret = -ENXIO; break; @@ -2166,12 +2315,25 @@ out: return r; } -static int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rcp, u16 *rrcp) +/** + * kvm_s390_cpus_from_pv - Convert all protected vCPUs in a protected VM to + * non protected. + * @kvm: the VM whose protected vCPUs are to be converted + * @rc: return value for the RC field of the UVC (in case of error) + * @rrc: return value for the RRC field of the UVC (in case of error) + * + * Does not stop in case of error, tries to convert as many + * CPUs as possible. In case of error, the RC and RRC of the last error are + * returned. + * + * Return: 0 in case of success, otherwise -EIO + */ +int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rc, u16 *rrc) { struct kvm_vcpu *vcpu; - u16 rc, rrc; - int ret = 0; unsigned long i; + u16 _rc, _rrc; + int ret = 0; /* * We ignore failures and try to destroy as many CPUs as possible. @@ -2183,9 +2345,9 @@ static int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rcp, u16 *rrcp) */ kvm_for_each_vcpu(i, vcpu, kvm) { mutex_lock(&vcpu->mutex); - if (kvm_s390_pv_destroy_cpu(vcpu, &rc, &rrc) && !ret) { - *rcp = rc; - *rrcp = rrc; + if (kvm_s390_pv_destroy_cpu(vcpu, &_rc, &_rrc) && !ret) { + *rc = _rc; + *rrc = _rrc; ret = -EIO; } mutex_unlock(&vcpu->mutex); @@ -2196,6 +2358,17 @@ static int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rcp, u16 *rrcp) return ret; } +/** + * kvm_s390_cpus_to_pv - Convert all non-protected vCPUs in a protected VM + * to protected. + * @kvm: the VM whose protected vCPUs are to be converted + * @rc: return value for the RC field of the UVC (in case of error) + * @rrc: return value for the RRC field of the UVC (in case of error) + * + * Tries to undo the conversion in case of error. + * + * Return: 0 in case of success, otherwise -EIO + */ static int kvm_s390_cpus_to_pv(struct kvm *kvm, u16 *rc, u16 *rrc) { unsigned long i; @@ -2220,6 +2393,115 @@ static int kvm_s390_cpus_to_pv(struct kvm *kvm, u16 *rc, u16 *rrc) return r; } +/* + * Here we provide user space with a direct interface to query UV + * related data like UV maxima and available features as well as + * feature specific data. + * + * To facilitate future extension of the data structures we'll try to + * write data up to the maximum requested length. + */ +static ssize_t kvm_s390_handle_pv_info(struct kvm_s390_pv_info *info) +{ + ssize_t len_min; + + switch (info->header.id) { + case KVM_PV_INFO_VM: { + len_min = sizeof(info->header) + sizeof(info->vm); + + if (info->header.len_max < len_min) + return -EINVAL; + + memcpy(info->vm.inst_calls_list, + uv_info.inst_calls_list, + sizeof(uv_info.inst_calls_list)); + + /* It's max cpuid not max cpus, so it's off by one */ + info->vm.max_cpus = uv_info.max_guest_cpu_id + 1; + info->vm.max_guests = uv_info.max_num_sec_conf; + info->vm.max_guest_addr = uv_info.max_sec_stor_addr; + info->vm.feature_indication = uv_info.uv_feature_indications; + + return len_min; + } + case KVM_PV_INFO_DUMP: { + len_min = sizeof(info->header) + sizeof(info->dump); + + if (info->header.len_max < len_min) + return -EINVAL; + + info->dump.dump_cpu_buffer_len = uv_info.guest_cpu_stor_len; + info->dump.dump_config_mem_buffer_per_1m = uv_info.conf_dump_storage_state_len; + info->dump.dump_config_finalize_len = uv_info.conf_dump_finalize_len; + return len_min; + } + default: + return -EINVAL; + } +} + +static int kvm_s390_pv_dmp(struct kvm *kvm, struct kvm_pv_cmd *cmd, + struct kvm_s390_pv_dmp dmp) +{ + int r = -EINVAL; + void __user *result_buff = (void __user *)dmp.buff_addr; + + switch (dmp.subcmd) { + case KVM_PV_DUMP_INIT: { + if (kvm->arch.pv.dumping) + break; + + /* + * Block SIE entry as concurrent dump UVCs could lead + * to validities. + */ + kvm_s390_vcpu_block_all(kvm); + + r = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm), + UVC_CMD_DUMP_INIT, &cmd->rc, &cmd->rrc); + KVM_UV_EVENT(kvm, 3, "PROTVIRT DUMP INIT: rc %x rrc %x", + cmd->rc, cmd->rrc); + if (!r) { + kvm->arch.pv.dumping = true; + } else { + kvm_s390_vcpu_unblock_all(kvm); + r = -EINVAL; + } + break; + } + case KVM_PV_DUMP_CONFIG_STOR_STATE: { + if (!kvm->arch.pv.dumping) + break; + + /* + * gaddr is an output parameter since we might stop + * early. As dmp will be copied back in our caller, we + * don't need to do it ourselves. + */ + r = kvm_s390_pv_dump_stor_state(kvm, result_buff, &dmp.gaddr, dmp.buff_len, + &cmd->rc, &cmd->rrc); + break; + } + case KVM_PV_DUMP_COMPLETE: { + if (!kvm->arch.pv.dumping) + break; + + r = -EINVAL; + if (dmp.buff_len < uv_info.conf_dump_finalize_len) + break; + + r = kvm_s390_pv_dump_complete(kvm, result_buff, + &cmd->rc, &cmd->rrc); + break; + } + default: + r = -ENOTTY; + break; + } + + return r; +} + static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd) { int r = 0; @@ -2356,6 +2638,68 @@ static int kvm_s390_handle_pv(struct kvm *kvm, struct kvm_pv_cmd *cmd) cmd->rc, cmd->rrc); break; } + case KVM_PV_INFO: { + struct kvm_s390_pv_info info = {}; + ssize_t data_len; + + /* + * No need to check the VM protection here. + * + * Maybe user space wants to query some of the data + * when the VM is still unprotected. If we see the + * need to fence a new data command we can still + * return an error in the info handler. + */ + + r = -EFAULT; + if (copy_from_user(&info, argp, sizeof(info.header))) + break; + + r = -EINVAL; + if (info.header.len_max < sizeof(info.header)) + break; + + data_len = kvm_s390_handle_pv_info(&info); + if (data_len < 0) { + r = data_len; + break; + } + /* + * If a data command struct is extended (multiple + * times) this can be used to determine how much of it + * is valid. + */ + info.header.len_written = data_len; + + r = -EFAULT; + if (copy_to_user(argp, &info, data_len)) + break; + + r = 0; + break; + } + case KVM_PV_DUMP: { + struct kvm_s390_pv_dmp dmp; + + r = -EINVAL; + if (!kvm_s390_pv_is_protected(kvm)) + break; + + r = -EFAULT; + if (copy_from_user(&dmp, argp, sizeof(dmp))) + break; + + r = kvm_s390_pv_dmp(kvm, cmd, dmp); + if (r) + break; + + if (copy_to_user(argp, &dmp, sizeof(dmp))) { + r = -EFAULT; + break; + } + + break; + } default: r = -ENOTTY; } @@ -2581,6 +2925,19 @@ long kvm_arch_vm_ioctl(struct file *filp, r = -EFAULT; break; } + case KVM_S390_ZPCI_OP: { + struct kvm_s390_zpci_op args; + + r = -EINVAL; + if (!IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) + break; + if (copy_from_user(&args, argp, sizeof(args))) { + r = -EFAULT; + break; + } + r = kvm_s390_pci_zpci_op(kvm, &args); + break; + } default: r = -ENOTTY; } @@ -2742,6 +3099,14 @@ static void sca_dispose(struct kvm *kvm) kvm->arch.sca = NULL; } +void kvm_arch_free_vm(struct kvm *kvm) +{ + if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) + kvm_s390_pci_clear_list(kvm); + + __kvm_arch_free_vm(kvm); +} + int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) { gfp_t alloc_flags = GFP_KERNEL_ACCOUNT; @@ -2824,6 +3189,13 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm_s390_crypto_init(kvm); + if (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM)) { + mutex_lock(&kvm->lock); + kvm_s390_pci_init_list(kvm); + kvm_s390_vcpu_pci_enable_interp(kvm); + mutex_unlock(&kvm->lock); + } + mutex_init(&kvm->arch.float_int.ais_lock); spin_lock_init(&kvm->arch.float_int.lock); for (i = 0; i < FIRQ_LIST_COUNT; i++) @@ -2877,6 +3249,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) kvm_clear_async_pf_completion_queue(vcpu); if (!kvm_is_ucontrol(vcpu->kvm)) sca_del_vcpu(vcpu); + kvm_s390_update_topology_change_report(vcpu->kvm, 1); if (kvm_is_ucontrol(vcpu->kvm)) gmap_remove(vcpu->arch.gmap); @@ -2904,6 +3277,15 @@ void kvm_arch_destroy_vm(struct kvm *kvm) */ if (kvm_s390_pv_get_handle(kvm)) kvm_s390_pv_deinit_vm(kvm, &rc, &rrc); + /* + * Remove the mmu notifier only when the whole KVM VM is torn down, + * and only if one was registered to begin with. If the VM is + * currently not protected, but has been previously been protected, + * then it's possible that the notifier is still registered. + */ + if (kvm->arch.pv.mmu_notifier.ops) + mmu_notifier_unregister(&kvm->arch.pv.mmu_notifier, kvm->mm); + debug_unregister(kvm->arch.dbf); free_page((unsigned long)kvm->arch.sie_page2); if (!kvm_is_ucontrol(kvm)) @@ -3047,9 +3429,7 @@ static int sca_can_add_vcpu(struct kvm *kvm, unsigned int id) if (!sclp.has_esca || !sclp.has_64bscao) return false; - mutex_lock(&kvm->lock); rc = kvm->arch.use_esca ? 0 : sca_switch_to_extended(kvm); - mutex_unlock(&kvm->lock); return rc == 0 && id < KVM_S390_ESCA_CPU_SLOTS; } @@ -3272,6 +3652,8 @@ static int kvm_s390_vcpu_setup(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->ecb |= ECB_HOSTPROTINT; if (test_kvm_facility(vcpu->kvm, 9)) vcpu->arch.sie_block->ecb |= ECB_SRSI; + if (test_kvm_facility(vcpu->kvm, 11)) + vcpu->arch.sie_block->ecb |= ECB_PTF; if (test_kvm_facility(vcpu->kvm, 73)) vcpu->arch.sie_block->ecb |= ECB_TE; if (!kvm_is_ucontrol(vcpu->kvm)) @@ -3324,6 +3706,8 @@ static int kvm_s390_vcpu_setup(struct kvm_vcpu *vcpu) kvm_s390_vcpu_crypto_setup(vcpu); + kvm_s390_vcpu_pci_setup(vcpu); + mutex_lock(&vcpu->kvm->lock); if (kvm_s390_pv_is_protected(vcpu->kvm)) { rc = kvm_s390_pv_create_cpu(vcpu, &uvrc, &uvrrc); @@ -3403,6 +3787,8 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) rc = kvm_s390_vcpu_setup(vcpu); if (rc) goto out_ucontrol_uninit; + + kvm_s390_update_topology_change_report(vcpu->kvm, 1); return 0; out_ucontrol_uninit: @@ -4473,6 +4859,15 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) struct kvm_run *kvm_run = vcpu->run; int rc; + /* + * Running a VM while dumping always has the potential to + * produce inconsistent dump data. But for PV vcpus a SIE + * entry while dumping could also lead to a fatal validity + * intercept which we absolutely want to avoid. + */ + if (vcpu->kvm->arch.pv.dumping) + return -EINVAL; + if (kvm_run->immediate_exit) return -EINTR; @@ -4912,6 +5307,48 @@ long kvm_arch_vcpu_async_ioctl(struct file *filp, return -ENOIOCTLCMD; } +static int kvm_s390_handle_pv_vcpu_dump(struct kvm_vcpu *vcpu, + struct kvm_pv_cmd *cmd) +{ + struct kvm_s390_pv_dmp dmp; + void *data; + int ret; + + /* Dump initialization is a prerequisite */ + if (!vcpu->kvm->arch.pv.dumping) + return -EINVAL; + + if (copy_from_user(&dmp, (__u8 __user *)cmd->data, sizeof(dmp))) + return -EFAULT; + + /* We only handle this subcmd right now */ + if (dmp.subcmd != KVM_PV_DUMP_CPU) + return -EINVAL; + + /* CPU dump length is the same as create cpu storage donation. */ + if (dmp.buff_len != uv_info.guest_cpu_stor_len) + return -EINVAL; + + data = kvzalloc(uv_info.guest_cpu_stor_len, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = kvm_s390_pv_dump_cpu(vcpu, data, &cmd->rc, &cmd->rrc); + + VCPU_EVENT(vcpu, 3, "PROTVIRT DUMP CPU %d rc %x rrc %x", + vcpu->vcpu_id, cmd->rc, cmd->rrc); + + if (ret) + ret = -EINVAL; + + /* On success copy over the dump data */ + if (!ret && copy_to_user((__u8 __user *)dmp.buff_addr, data, uv_info.guest_cpu_stor_len)) + ret = -EFAULT; + + kvfree(data); + return ret; +} + long kvm_arch_vcpu_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) { @@ -5076,6 +5513,33 @@ long kvm_arch_vcpu_ioctl(struct file *filp, irq_state.len); break; } + case KVM_S390_PV_CPU_COMMAND: { + struct kvm_pv_cmd cmd; + + r = -EINVAL; + if (!is_prot_virt_host()) + break; + + r = -EFAULT; + if (copy_from_user(&cmd, argp, sizeof(cmd))) + break; + + r = -EINVAL; + if (cmd.flags) + break; + + /* We only handle this cmd right now */ + if (cmd.cmd != KVM_PV_DUMP) + break; + + r = kvm_s390_handle_pv_vcpu_dump(vcpu, &cmd); + + /* Always copy over UV rc / rrc data */ + if (copy_to_user((__u8 __user *)argp, &cmd.rc, + sizeof(cmd.rc) + sizeof(cmd.rrc))) + r = -EFAULT; + break; + } default: r = -ENOTTY; } diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index 497d52a83c78..f6fd668f887e 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -250,6 +250,11 @@ int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length, u16 *rc, int kvm_s390_pv_unpack(struct kvm *kvm, unsigned long addr, unsigned long size, unsigned long tweak, u16 *rc, u16 *rrc); int kvm_s390_pv_set_cpu_state(struct kvm_vcpu *vcpu, u8 state); +int kvm_s390_pv_dump_cpu(struct kvm_vcpu *vcpu, void *buff, u16 *rc, u16 *rrc); +int kvm_s390_pv_dump_stor_state(struct kvm *kvm, void __user *buff_user, + u64 *gaddr, u64 buff_user_len, u16 *rc, u16 *rrc); +int kvm_s390_pv_dump_complete(struct kvm *kvm, void __user *buff_user, + u16 *rc, u16 *rrc); static inline u64 kvm_s390_pv_get_handle(struct kvm *kvm) { @@ -374,6 +379,7 @@ int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu); void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu); void kvm_s390_set_cpu_timer(struct kvm_vcpu *vcpu, __u64 cputm); __u64 kvm_s390_get_cpu_timer(struct kvm_vcpu *vcpu); +int kvm_s390_cpus_from_pv(struct kvm *kvm, u16 *rc, u16 *rrc); /* implemented in diag.c */ int kvm_s390_handle_diag(struct kvm_vcpu *vcpu); @@ -507,6 +513,16 @@ void kvm_s390_reinject_machine_check(struct kvm_vcpu *vcpu, */ void kvm_s390_vcpu_crypto_reset_all(struct kvm *kvm); +/** + * kvm_s390_vcpu_pci_enable_interp + * + * Set the associated PCI attributes for each vcpu to allow for zPCI Load/Store + * interpretation as well as adapter interruption forwarding. + * + * @kvm: the KVM guest + */ +void kvm_s390_vcpu_pci_enable_interp(struct kvm *kvm); + /** * diag9c_forwarding_hz * diff --git a/arch/s390/kvm/pci.c b/arch/s390/kvm/pci.c new file mode 100644 index 000000000000..4946fb7757d6 --- /dev/null +++ b/arch/s390/kvm/pci.c @@ -0,0 +1,690 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * s390 kvm PCI passthrough support + * + * Copyright IBM Corp. 2022 + * + * Author(s): Matthew Rosato + */ + +#include +#include +#include +#include +#include +#include +#include "pci.h" +#include "kvm-s390.h" + +struct zpci_aift *aift; + +static inline int __set_irq_noiib(u16 ctl, u8 isc) +{ + union zpci_sic_iib iib = {{0}}; + + return zpci_set_irq_ctrl(ctl, isc, &iib); +} + +void kvm_s390_pci_aen_exit(void) +{ + unsigned long flags; + struct kvm_zdev **gait_kzdev; + + lockdep_assert_held(&aift->aift_lock); + + /* + * Contents of the aipb remain registered for the life of the host + * kernel, the information preserved in zpci_aipb and zpci_aif_sbv + * in case we insert the KVM module again later. Clear the AIFT + * information and free anything not registered with underlying + * firmware. + */ + spin_lock_irqsave(&aift->gait_lock, flags); + gait_kzdev = aift->kzdev; + aift->gait = NULL; + aift->sbv = NULL; + aift->kzdev = NULL; + spin_unlock_irqrestore(&aift->gait_lock, flags); + + kfree(gait_kzdev); +} + +static int zpci_setup_aipb(u8 nisc) +{ + struct page *page; + int size, rc; + + zpci_aipb = kzalloc(sizeof(union zpci_sic_iib), GFP_KERNEL); + if (!zpci_aipb) + return -ENOMEM; + + aift->sbv = airq_iv_create(ZPCI_NR_DEVICES, AIRQ_IV_ALLOC, 0); + if (!aift->sbv) { + rc = -ENOMEM; + goto free_aipb; + } + zpci_aif_sbv = aift->sbv; + size = get_order(PAGE_ALIGN(ZPCI_NR_DEVICES * + sizeof(struct zpci_gaite))); + page = alloc_pages(GFP_KERNEL | __GFP_ZERO, size); + if (!page) { + rc = -ENOMEM; + goto free_sbv; + } + aift->gait = (struct zpci_gaite *)page_to_phys(page); + + zpci_aipb->aipb.faisb = virt_to_phys(aift->sbv->vector); + zpci_aipb->aipb.gait = virt_to_phys(aift->gait); + zpci_aipb->aipb.afi = nisc; + zpci_aipb->aipb.faal = ZPCI_NR_DEVICES; + + /* Setup Adapter Event Notification Interpretation */ + if (zpci_set_irq_ctrl(SIC_SET_AENI_CONTROLS, 0, zpci_aipb)) { + rc = -EIO; + goto free_gait; + } + + return 0; + +free_gait: + free_pages((unsigned long)aift->gait, size); +free_sbv: + airq_iv_release(aift->sbv); + zpci_aif_sbv = NULL; +free_aipb: + kfree(zpci_aipb); + zpci_aipb = NULL; + + return rc; +} + +static int zpci_reset_aipb(u8 nisc) +{ + /* + * AEN registration can only happen once per system boot. If + * an aipb already exists then AEN was already registered and + * we can re-use the aipb contents. This can only happen if + * the KVM module was removed and re-inserted. However, we must + * ensure that the same forwarding ISC is used as this is assigned + * during KVM module load. + */ + if (zpci_aipb->aipb.afi != nisc) + return -EINVAL; + + aift->sbv = zpci_aif_sbv; + aift->gait = (struct zpci_gaite *)zpci_aipb->aipb.gait; + + return 0; +} + +int kvm_s390_pci_aen_init(u8 nisc) +{ + int rc = 0; + + /* If already enabled for AEN, bail out now */ + if (aift->gait || aift->sbv) + return -EPERM; + + mutex_lock(&aift->aift_lock); + aift->kzdev = kcalloc(ZPCI_NR_DEVICES, sizeof(struct kvm_zdev), + GFP_KERNEL); + if (!aift->kzdev) { + rc = -ENOMEM; + goto unlock; + } + + if (!zpci_aipb) + rc = zpci_setup_aipb(nisc); + else + rc = zpci_reset_aipb(nisc); + if (rc) + goto free_zdev; + + /* Enable floating IRQs */ + if (__set_irq_noiib(SIC_IRQ_MODE_SINGLE, nisc)) { + rc = -EIO; + kvm_s390_pci_aen_exit(); + } + + goto unlock; + +free_zdev: + kfree(aift->kzdev); +unlock: + mutex_unlock(&aift->aift_lock); + return rc; +} + +/* Modify PCI: Register floating adapter interruption forwarding */ +static int kvm_zpci_set_airq(struct zpci_dev *zdev) +{ + u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, ZPCI_MOD_FC_REG_INT); + struct zpci_fib fib = {}; + u8 status; + + fib.fmt0.isc = zdev->kzdev->fib.fmt0.isc; + fib.fmt0.sum = 1; /* enable summary notifications */ + fib.fmt0.noi = airq_iv_end(zdev->aibv); + fib.fmt0.aibv = virt_to_phys(zdev->aibv->vector); + fib.fmt0.aibvo = 0; + fib.fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8); + fib.fmt0.aisbo = zdev->aisb & 63; + fib.gd = zdev->gisa; + + return zpci_mod_fc(req, &fib, &status) ? -EIO : 0; +} + +/* Modify PCI: Unregister floating adapter interruption forwarding */ +static int kvm_zpci_clear_airq(struct zpci_dev *zdev) +{ + u64 req = ZPCI_CREATE_REQ(zdev->fh, 0, ZPCI_MOD_FC_DEREG_INT); + struct zpci_fib fib = {}; + u8 cc, status; + + fib.gd = zdev->gisa; + + cc = zpci_mod_fc(req, &fib, &status); + if (cc == 3 || (cc == 1 && status == 24)) + /* Function already gone or IRQs already deregistered. */ + cc = 0; + + return cc ? -EIO : 0; +} + +static inline void unaccount_mem(unsigned long nr_pages) +{ + struct user_struct *user = get_uid(current_user()); + + if (user) + atomic_long_sub(nr_pages, &user->locked_vm); + if (current->mm) + atomic64_sub(nr_pages, ¤t->mm->pinned_vm); +} + +static inline int account_mem(unsigned long nr_pages) +{ + struct user_struct *user = get_uid(current_user()); + unsigned long page_limit, cur_pages, new_pages; + + page_limit = rlimit(RLIMIT_MEMLOCK) >> PAGE_SHIFT; + + do { + cur_pages = atomic_long_read(&user->locked_vm); + new_pages = cur_pages + nr_pages; + if (new_pages > page_limit) + return -ENOMEM; + } while (atomic_long_cmpxchg(&user->locked_vm, cur_pages, + new_pages) != cur_pages); + + atomic64_add(nr_pages, ¤t->mm->pinned_vm); + + return 0; +} + +static int kvm_s390_pci_aif_enable(struct zpci_dev *zdev, struct zpci_fib *fib, + bool assist) +{ + struct page *pages[1], *aibv_page, *aisb_page = NULL; + unsigned int msi_vecs, idx; + struct zpci_gaite *gaite; + unsigned long hva, bit; + struct kvm *kvm; + phys_addr_t gaddr; + int rc = 0, gisc, npages, pcount = 0; + + /* + * Interrupt forwarding is only applicable if the device is already + * enabled for interpretation + */ + if (zdev->gisa == 0) + return -EINVAL; + + kvm = zdev->kzdev->kvm; + msi_vecs = min_t(unsigned int, fib->fmt0.noi, zdev->max_msi); + + /* Get the associated forwarding ISC - if invalid, return the error */ + gisc = kvm_s390_gisc_register(kvm, fib->fmt0.isc); + if (gisc < 0) + return gisc; + + /* Replace AIBV address */ + idx = srcu_read_lock(&kvm->srcu); + hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aibv)); + npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, pages); + srcu_read_unlock(&kvm->srcu, idx); + if (npages < 1) { + rc = -EIO; + goto out; + } + aibv_page = pages[0]; + pcount++; + gaddr = page_to_phys(aibv_page) + (fib->fmt0.aibv & ~PAGE_MASK); + fib->fmt0.aibv = gaddr; + + /* Pin the guest AISB if one was specified */ + if (fib->fmt0.sum == 1) { + idx = srcu_read_lock(&kvm->srcu); + hva = gfn_to_hva(kvm, gpa_to_gfn((gpa_t)fib->fmt0.aisb)); + npages = pin_user_pages_fast(hva, 1, FOLL_WRITE | FOLL_LONGTERM, + pages); + srcu_read_unlock(&kvm->srcu, idx); + if (npages < 1) { + rc = -EIO; + goto unpin1; + } + aisb_page = pages[0]; + pcount++; + } + + /* Account for pinned pages, roll back on failure */ + if (account_mem(pcount)) + goto unpin2; + + /* AISB must be allocated before we can fill in GAITE */ + mutex_lock(&aift->aift_lock); + bit = airq_iv_alloc_bit(aift->sbv); + if (bit == -1UL) + goto unlock; + zdev->aisb = bit; /* store the summary bit number */ + zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | + AIRQ_IV_BITLOCK | + AIRQ_IV_GUESTVEC, + phys_to_virt(fib->fmt0.aibv)); + + spin_lock_irq(&aift->gait_lock); + gaite = (struct zpci_gaite *)aift->gait + (zdev->aisb * + sizeof(struct zpci_gaite)); + + /* If assist not requested, host will get all alerts */ + if (assist) + gaite->gisa = (u32)virt_to_phys(&kvm->arch.sie_page2->gisa); + else + gaite->gisa = 0; + + gaite->gisc = fib->fmt0.isc; + gaite->count++; + gaite->aisbo = fib->fmt0.aisbo; + gaite->aisb = virt_to_phys(page_address(aisb_page) + (fib->fmt0.aisb & + ~PAGE_MASK)); + aift->kzdev[zdev->aisb] = zdev->kzdev; + spin_unlock_irq(&aift->gait_lock); + + /* Update guest FIB for re-issue */ + fib->fmt0.aisbo = zdev->aisb & 63; + fib->fmt0.aisb = virt_to_phys(aift->sbv->vector + (zdev->aisb / 64) * 8); + fib->fmt0.isc = gisc; + + /* Save some guest fib values in the host for later use */ + zdev->kzdev->fib.fmt0.isc = fib->fmt0.isc; + zdev->kzdev->fib.fmt0.aibv = fib->fmt0.aibv; + mutex_unlock(&aift->aift_lock); + + /* Issue the clp to setup the irq now */ + rc = kvm_zpci_set_airq(zdev); + return rc; + +unlock: + mutex_unlock(&aift->aift_lock); +unpin2: + if (fib->fmt0.sum == 1) + unpin_user_page(aisb_page); +unpin1: + unpin_user_page(aibv_page); +out: + return rc; +} + +static int kvm_s390_pci_aif_disable(struct zpci_dev *zdev, bool force) +{ + struct kvm_zdev *kzdev = zdev->kzdev; + struct zpci_gaite *gaite; + struct page *vpage = NULL, *spage = NULL; + int rc, pcount = 0; + u8 isc; + + if (zdev->gisa == 0) + return -EINVAL; + + mutex_lock(&aift->aift_lock); + + /* + * If the clear fails due to an error, leave now unless we know this + * device is about to go away (force) -- In that case clear the GAITE + * regardless. + */ + rc = kvm_zpci_clear_airq(zdev); + if (rc && !force) + goto out; + + if (zdev->kzdev->fib.fmt0.aibv == 0) + goto out; + spin_lock_irq(&aift->gait_lock); + gaite = (struct zpci_gaite *)aift->gait + (zdev->aisb * + sizeof(struct zpci_gaite)); + isc = gaite->gisc; + gaite->count--; + if (gaite->count == 0) { + /* Release guest AIBV and AISB */ + vpage = phys_to_page(kzdev->fib.fmt0.aibv); + if (gaite->aisb != 0) + spage = phys_to_page(gaite->aisb); + /* Clear the GAIT entry */ + gaite->aisb = 0; + gaite->gisc = 0; + gaite->aisbo = 0; + gaite->gisa = 0; + aift->kzdev[zdev->aisb] = 0; + /* Clear zdev info */ + airq_iv_free_bit(aift->sbv, zdev->aisb); + airq_iv_release(zdev->aibv); + zdev->aisb = 0; + zdev->aibv = NULL; + } + spin_unlock_irq(&aift->gait_lock); + kvm_s390_gisc_unregister(kzdev->kvm, isc); + kzdev->fib.fmt0.isc = 0; + kzdev->fib.fmt0.aibv = 0; + + if (vpage) { + unpin_user_page(vpage); + pcount++; + } + if (spage) { + unpin_user_page(spage); + pcount++; + } + if (pcount > 0) + unaccount_mem(pcount); +out: + mutex_unlock(&aift->aift_lock); + + return rc; +} + +static int kvm_s390_pci_dev_open(struct zpci_dev *zdev) +{ + struct kvm_zdev *kzdev; + + kzdev = kzalloc(sizeof(struct kvm_zdev), GFP_KERNEL); + if (!kzdev) + return -ENOMEM; + + kzdev->zdev = zdev; + zdev->kzdev = kzdev; + + return 0; +} + +static void kvm_s390_pci_dev_release(struct zpci_dev *zdev) +{ + struct kvm_zdev *kzdev; + + kzdev = zdev->kzdev; + WARN_ON(kzdev->zdev != zdev); + zdev->kzdev = NULL; + kfree(kzdev); +} + + +/* + * Register device with the specified KVM. If interpetation facilities are + * available, enable them and let userspace indicate whether or not they will + * be used (specify SHM bit to disable). + */ +int kvm_s390_pci_register_kvm(struct zpci_dev *zdev, struct kvm *kvm) +{ + int rc; + + if (!zdev) + return -EINVAL; + + mutex_lock(&zdev->kzdev_lock); + + if (zdev->kzdev || zdev->gisa != 0 || !kvm) { + mutex_unlock(&zdev->kzdev_lock); + return -EINVAL; + } + + kvm_get_kvm(kvm); + + mutex_lock(&kvm->lock); + + rc = kvm_s390_pci_dev_open(zdev); + if (rc) + goto err; + + /* + * If interpretation facilities aren't available, add the device to + * the kzdev list but don't enable for interpretation. + */ + if (!kvm_s390_pci_interp_allowed()) + goto out; + + /* + * If this is the first request to use an interpreted device, make the + * necessary vcpu changes + */ + if (!kvm->arch.use_zpci_interp) + kvm_s390_vcpu_pci_enable_interp(kvm); + + if (zdev_enabled(zdev)) { + rc = zpci_disable_device(zdev); + if (rc) + goto err; + } + + /* + * Store information about the identity of the kvm guest allowed to + * access this device via interpretation to be used by host CLP + */ + zdev->gisa = (u32)virt_to_phys(&kvm->arch.sie_page2->gisa); + + rc = zpci_enable_device(zdev); + if (rc) + goto clear_gisa; + + /* Re-register the IOMMU that was already created */ + rc = zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, + virt_to_phys(zdev->dma_table)); + if (rc) + goto clear_gisa; + +out: + zdev->kzdev->kvm = kvm; + + spin_lock(&kvm->arch.kzdev_list_lock); + list_add_tail(&zdev->kzdev->entry, &kvm->arch.kzdev_list); + spin_unlock(&kvm->arch.kzdev_list_lock); + + mutex_unlock(&kvm->lock); + mutex_unlock(&zdev->kzdev_lock); + return 0; + +clear_gisa: + zdev->gisa = 0; +err: + if (zdev->kzdev) + kvm_s390_pci_dev_release(zdev); + mutex_unlock(&kvm->lock); + mutex_unlock(&zdev->kzdev_lock); + kvm_put_kvm(kvm); + return rc; +} +EXPORT_SYMBOL_GPL(kvm_s390_pci_register_kvm); + +void kvm_s390_pci_unregister_kvm(struct zpci_dev *zdev) +{ + struct kvm *kvm; + + if (!zdev) + return; + + mutex_lock(&zdev->kzdev_lock); + + if (WARN_ON(!zdev->kzdev)) { + mutex_unlock(&zdev->kzdev_lock); + return; + } + + kvm = zdev->kzdev->kvm; + mutex_lock(&kvm->lock); + + /* + * A 0 gisa means interpretation was never enabled, just remove the + * device from the list. + */ + if (zdev->gisa == 0) + goto out; + + /* Forwarding must be turned off before interpretation */ + if (zdev->kzdev->fib.fmt0.aibv != 0) + kvm_s390_pci_aif_disable(zdev, true); + + /* Remove the host CLP guest designation */ + zdev->gisa = 0; + + if (zdev_enabled(zdev)) { + if (zpci_disable_device(zdev)) + goto out; + } + + if (zpci_enable_device(zdev)) + goto out; + + /* Re-register the IOMMU that was already created */ + zpci_register_ioat(zdev, 0, zdev->start_dma, zdev->end_dma, + virt_to_phys(zdev->dma_table)); + +out: + spin_lock(&kvm->arch.kzdev_list_lock); + list_del(&zdev->kzdev->entry); + spin_unlock(&kvm->arch.kzdev_list_lock); + kvm_s390_pci_dev_release(zdev); + + mutex_unlock(&kvm->lock); + mutex_unlock(&zdev->kzdev_lock); + + kvm_put_kvm(kvm); +} +EXPORT_SYMBOL_GPL(kvm_s390_pci_unregister_kvm); + +void kvm_s390_pci_init_list(struct kvm *kvm) +{ + spin_lock_init(&kvm->arch.kzdev_list_lock); + INIT_LIST_HEAD(&kvm->arch.kzdev_list); +} + +void kvm_s390_pci_clear_list(struct kvm *kvm) +{ + /* + * This list should already be empty, either via vfio device closures + * or kvm fd cleanup. + */ + spin_lock(&kvm->arch.kzdev_list_lock); + WARN_ON_ONCE(!list_empty(&kvm->arch.kzdev_list)); + spin_unlock(&kvm->arch.kzdev_list_lock); +} + +static struct zpci_dev *get_zdev_from_kvm_by_fh(struct kvm *kvm, u32 fh) +{ + struct zpci_dev *zdev = NULL; + struct kvm_zdev *kzdev; + + spin_lock(&kvm->arch.kzdev_list_lock); + list_for_each_entry(kzdev, &kvm->arch.kzdev_list, entry) { + if (kzdev->zdev->fh == fh) { + zdev = kzdev->zdev; + break; + } + } + spin_unlock(&kvm->arch.kzdev_list_lock); + + return zdev; +} + +static int kvm_s390_pci_zpci_reg_aen(struct zpci_dev *zdev, + struct kvm_s390_zpci_op *args) +{ + struct zpci_fib fib = {}; + bool hostflag; + + fib.fmt0.aibv = args->u.reg_aen.ibv; + fib.fmt0.isc = args->u.reg_aen.isc; + fib.fmt0.noi = args->u.reg_aen.noi; + if (args->u.reg_aen.sb != 0) { + fib.fmt0.aisb = args->u.reg_aen.sb; + fib.fmt0.aisbo = args->u.reg_aen.sbo; + fib.fmt0.sum = 1; + } else { + fib.fmt0.aisb = 0; + fib.fmt0.aisbo = 0; + fib.fmt0.sum = 0; + } + + hostflag = !(args->u.reg_aen.flags & KVM_S390_ZPCIOP_REGAEN_HOST); + return kvm_s390_pci_aif_enable(zdev, &fib, hostflag); +} + +int kvm_s390_pci_zpci_op(struct kvm *kvm, struct kvm_s390_zpci_op *args) +{ + struct kvm_zdev *kzdev; + struct zpci_dev *zdev; + int r; + + zdev = get_zdev_from_kvm_by_fh(kvm, args->fh); + if (!zdev) + return -ENODEV; + + mutex_lock(&zdev->kzdev_lock); + mutex_lock(&kvm->lock); + + kzdev = zdev->kzdev; + if (!kzdev) { + r = -ENODEV; + goto out; + } + if (kzdev->kvm != kvm) { + r = -EPERM; + goto out; + } + + switch (args->op) { + case KVM_S390_ZPCIOP_REG_AEN: + /* Fail on unknown flags */ + if (args->u.reg_aen.flags & ~KVM_S390_ZPCIOP_REGAEN_HOST) { + r = -EINVAL; + break; + } + r = kvm_s390_pci_zpci_reg_aen(zdev, args); + break; + case KVM_S390_ZPCIOP_DEREG_AEN: + r = kvm_s390_pci_aif_disable(zdev, false); + break; + default: + r = -EINVAL; + } + +out: + mutex_unlock(&kvm->lock); + mutex_unlock(&zdev->kzdev_lock); + return r; +} + +int kvm_s390_pci_init(void) +{ + aift = kzalloc(sizeof(struct zpci_aift), GFP_KERNEL); + if (!aift) + return -ENOMEM; + + spin_lock_init(&aift->gait_lock); + mutex_init(&aift->aift_lock); + + return 0; +} + +void kvm_s390_pci_exit(void) +{ + mutex_destroy(&aift->aift_lock); + + kfree(aift); +} diff --git a/arch/s390/kvm/pci.h b/arch/s390/kvm/pci.h new file mode 100644 index 000000000000..3a3606c3a0fe --- /dev/null +++ b/arch/s390/kvm/pci.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * s390 kvm PCI passthrough support + * + * Copyright IBM Corp. 2022 + * + * Author(s): Matthew Rosato + */ + +#ifndef __KVM_S390_PCI_H +#define __KVM_S390_PCI_H + +#include +#include +#include +#include +#include +#include + +struct kvm_zdev { + struct zpci_dev *zdev; + struct kvm *kvm; + struct zpci_fib fib; + struct list_head entry; +}; + +struct zpci_gaite { + u32 gisa; + u8 gisc; + u8 count; + u8 reserved; + u8 aisbo; + u64 aisb; +}; + +struct zpci_aift { + struct zpci_gaite *gait; + struct airq_iv *sbv; + struct kvm_zdev **kzdev; + spinlock_t gait_lock; /* Protects the gait, used during AEN forward */ + struct mutex aift_lock; /* Protects the other structures in aift */ +}; + +extern struct zpci_aift *aift; + +static inline struct kvm *kvm_s390_pci_si_to_kvm(struct zpci_aift *aift, + unsigned long si) +{ + if (!IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM) || aift->kzdev == 0 || + aift->kzdev[si] == 0) + return 0; + return aift->kzdev[si]->kvm; +}; + +int kvm_s390_pci_aen_init(u8 nisc); +void kvm_s390_pci_aen_exit(void); + +void kvm_s390_pci_init_list(struct kvm *kvm); +void kvm_s390_pci_clear_list(struct kvm *kvm); + +int kvm_s390_pci_zpci_op(struct kvm *kvm, struct kvm_s390_zpci_op *args); + +int kvm_s390_pci_init(void); +void kvm_s390_pci_exit(void); + +static inline bool kvm_s390_pci_interp_allowed(void) +{ + struct cpuid cpu_id; + + get_cpu_id(&cpu_id); + switch (cpu_id.machine) { + case 0x2817: + case 0x2818: + case 0x2827: + case 0x2828: + case 0x2964: + case 0x2965: + /* No SHM on certain machines */ + return false; + default: + return (IS_ENABLED(CONFIG_VFIO_PCI_ZDEV_KVM) && + sclp.has_zpci_lsi && sclp.has_aeni && sclp.has_aisi && + sclp.has_aisii); + } +} + +#endif /* __KVM_S390_PCI_H */ diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index 83bb5cf97282..3335fa09b6f1 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -442,7 +442,7 @@ static int handle_ipte_interlock(struct kvm_vcpu *vcpu) vcpu->stat.instruction_ipte_interlock++; if (psw_bits(vcpu->arch.sie_block->gpsw).pstate) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); - wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu)); + wait_event(vcpu->kvm->arch.ipte_wq, !ipte_lock_held(vcpu->kvm)); kvm_s390_retry_instr(vcpu); VCPU_EVENT(vcpu, 4, "%s", "retrying ipte interlock operation"); return 0; @@ -873,10 +873,18 @@ static int handle_stsi(struct kvm_vcpu *vcpu) if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); - if (fc > 3) { - kvm_s390_set_psw_cc(vcpu, 3); - return 0; - } + /* Bailout forbidden function codes */ + if (fc > 3 && fc != 15) + goto out_no_data; + + /* + * fc 15 is provided only with + * - PTF/CPU topology support through facility 15 + * - KVM_CAP_S390_USER_STSI + */ + if (fc == 15 && (!test_kvm_facility(vcpu->kvm, 11) || + !vcpu->kvm->arch.user_stsi)) + goto out_no_data; if (vcpu->run->s.regs.gprs[0] & 0x0fffff00 || vcpu->run->s.regs.gprs[1] & 0xffff0000) @@ -910,6 +918,10 @@ static int handle_stsi(struct kvm_vcpu *vcpu) goto out_no_data; handle_stsi_3_2_2(vcpu, (void *) mem); break; + case 15: /* fc 15 is fully handled in userspace */ + insert_stsi_usr_data(vcpu, operand2, ar, fc, sel1, sel2); + trace_kvm_s390_handle_stsi(vcpu, fc, sel1, sel2, operand2); + return -EREMOTE; } if (kvm_s390_pv_cpu_is_protected(vcpu)) { memcpy((void *)sida_origin(vcpu->arch.sie_block), (void *)mem, @@ -1471,7 +1483,7 @@ static int handle_tprot(struct kvm_vcpu *vcpu) access_key = (operand2 & 0xf0) >> 4; if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT) - ipte_lock(vcpu); + ipte_lock(vcpu->kvm); ret = guest_translate_address_with_key(vcpu, address, ar, &gpa, GACC_STORE, access_key); @@ -1508,7 +1520,7 @@ static int handle_tprot(struct kvm_vcpu *vcpu) } if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_DAT) - ipte_unlock(vcpu); + ipte_unlock(vcpu->kvm); return ret; } diff --git a/arch/s390/kvm/pv.c b/arch/s390/kvm/pv.c index cc7c9599f43e..7cb7799a0acb 100644 --- a/arch/s390/kvm/pv.c +++ b/arch/s390/kvm/pv.c @@ -7,13 +7,25 @@ */ #include #include +#include #include #include #include #include #include +#include +#include +#include #include "kvm-s390.h" +static void kvm_s390_clear_pv_state(struct kvm *kvm) +{ + kvm->arch.pv.handle = 0; + kvm->arch.pv.guest_len = 0; + kvm->arch.pv.stor_base = 0; + kvm->arch.pv.stor_var = NULL; +} + int kvm_s390_pv_destroy_cpu(struct kvm_vcpu *vcpu, u16 *rc, u16 *rrc) { int cc; @@ -108,7 +120,7 @@ static void kvm_s390_pv_dealloc_vm(struct kvm *kvm) vfree(kvm->arch.pv.stor_var); free_pages(kvm->arch.pv.stor_base, get_order(uv_info.guest_base_stor_len)); - memset(&kvm->arch.pv, 0, sizeof(kvm->arch.pv)); + kvm_s390_clear_pv_state(kvm); } static int kvm_s390_pv_alloc_vm(struct kvm *kvm) @@ -152,21 +164,51 @@ int kvm_s390_pv_deinit_vm(struct kvm *kvm, u16 *rc, u16 *rrc) { int cc; - /* make all pages accessible before destroying the guest */ - s390_reset_acc(kvm->mm); - cc = uv_cmd_nodata(kvm_s390_pv_get_handle(kvm), UVC_CMD_DESTROY_SEC_CONF, rc, rrc); WRITE_ONCE(kvm->arch.gmap->guest_handle, 0); - atomic_set(&kvm->mm->context.is_protected, 0); + /* + * if the mm still has a mapping, make all its pages accessible + * before destroying the guest + */ + if (mmget_not_zero(kvm->mm)) { + s390_uv_destroy_range(kvm->mm, 0, TASK_SIZE); + mmput(kvm->mm); + } + + if (!cc) { + atomic_dec(&kvm->mm->context.protected_count); + kvm_s390_pv_dealloc_vm(kvm); + } else { + /* Intended memory leak on "impossible" error */ + s390_replace_asce(kvm->arch.gmap); + } KVM_UV_EVENT(kvm, 3, "PROTVIRT DESTROY VM: rc %x rrc %x", *rc, *rrc); WARN_ONCE(cc, "protvirt destroy vm failed rc %x rrc %x", *rc, *rrc); - /* Inteded memory leak on "impossible" error */ - if (!cc) - kvm_s390_pv_dealloc_vm(kvm); + return cc ? -EIO : 0; } +static void kvm_s390_pv_mmu_notifier_release(struct mmu_notifier *subscription, + struct mm_struct *mm) +{ + struct kvm *kvm = container_of(subscription, struct kvm, arch.pv.mmu_notifier); + u16 dummy; + + /* + * No locking is needed since this is the last thread of the last user of this + * struct mm. + * When the struct kvm gets deinitialized, this notifier is also + * unregistered. This means that if this notifier runs, then the + * struct kvm is still valid. + */ + kvm_s390_cpus_from_pv(kvm, &dummy, &dummy); +} + +static const struct mmu_notifier_ops kvm_s390_pv_mmu_notifier_ops = { + .release = kvm_s390_pv_mmu_notifier_release, +}; + int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc) { struct uv_cb_cgc uvcb = { @@ -197,14 +239,22 @@ int kvm_s390_pv_init_vm(struct kvm *kvm, u16 *rc, u16 *rrc) /* Outputs */ kvm->arch.pv.handle = uvcb.guest_handle; + atomic_inc(&kvm->mm->context.protected_count); if (cc) { - if (uvcb.header.rc & UVC_RC_NEED_DESTROY) + if (uvcb.header.rc & UVC_RC_NEED_DESTROY) { kvm_s390_pv_deinit_vm(kvm, &dummy, &dummy); - else + } else { + atomic_dec(&kvm->mm->context.protected_count); kvm_s390_pv_dealloc_vm(kvm); + } return -EIO; } kvm->arch.gmap->guest_handle = uvcb.guest_handle; + /* Add the notifier only once. No races because we hold kvm->lock */ + if (kvm->arch.pv.mmu_notifier.ops != &kvm_s390_pv_mmu_notifier_ops) { + kvm->arch.pv.mmu_notifier.ops = &kvm_s390_pv_mmu_notifier_ops; + mmu_notifier_register(&kvm->arch.pv.mmu_notifier, kvm->mm); + } return 0; } @@ -224,8 +274,6 @@ int kvm_s390_pv_set_sec_parms(struct kvm *kvm, void *hdr, u64 length, u16 *rc, *rrc = uvcb.header.rrc; KVM_UV_EVENT(kvm, 3, "PROTVIRT VM SET PARMS: rc %x rrc %x", *rc, *rrc); - if (!cc) - atomic_set(&kvm->mm->context.is_protected, 1); return cc ? -EINVAL : 0; } @@ -298,3 +346,200 @@ int kvm_s390_pv_set_cpu_state(struct kvm_vcpu *vcpu, u8 state) return -EINVAL; return 0; } + +int kvm_s390_pv_dump_cpu(struct kvm_vcpu *vcpu, void *buff, u16 *rc, u16 *rrc) +{ + struct uv_cb_dump_cpu uvcb = { + .header.cmd = UVC_CMD_DUMP_CPU, + .header.len = sizeof(uvcb), + .cpu_handle = vcpu->arch.pv.handle, + .dump_area_origin = (u64)buff, + }; + int cc; + + cc = uv_call_sched(0, (u64)&uvcb); + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + return cc; +} + +/* Size of the cache for the storage state dump data. 1MB for now */ +#define DUMP_BUFF_LEN HPAGE_SIZE + +/** + * kvm_s390_pv_dump_stor_state + * + * @kvm: pointer to the guest's KVM struct + * @buff_user: Userspace pointer where we will write the results to + * @gaddr: Starting absolute guest address for which the storage state + * is requested. + * @buff_user_len: Length of the buff_user buffer + * @rc: Pointer to where the uvcb return code is stored + * @rrc: Pointer to where the uvcb return reason code is stored + * + * Stores buff_len bytes of tweak component values to buff_user + * starting with the 1MB block specified by the absolute guest address + * (gaddr). The gaddr pointer will be updated with the last address + * for which data was written when returning to userspace. buff_user + * might be written to even if an error rc is returned. For instance + * if we encounter a fault after writing the first page of data. + * + * Context: kvm->lock needs to be held + * + * Return: + * 0 on success + * -ENOMEM if allocating the cache fails + * -EINVAL if gaddr is not aligned to 1MB + * -EINVAL if buff_user_len is not aligned to uv_info.conf_dump_storage_state_len + * -EINVAL if the UV call fails, rc and rrc will be set in this case + * -EFAULT if copying the result to buff_user failed + */ +int kvm_s390_pv_dump_stor_state(struct kvm *kvm, void __user *buff_user, + u64 *gaddr, u64 buff_user_len, u16 *rc, u16 *rrc) +{ + struct uv_cb_dump_stor_state uvcb = { + .header.cmd = UVC_CMD_DUMP_CONF_STOR_STATE, + .header.len = sizeof(uvcb), + .config_handle = kvm->arch.pv.handle, + .gaddr = *gaddr, + .dump_area_origin = 0, + }; + const u64 increment_len = uv_info.conf_dump_storage_state_len; + size_t buff_kvm_size; + size_t size_done = 0; + u8 *buff_kvm = NULL; + int cc, ret; + + ret = -EINVAL; + /* UV call processes 1MB guest storage chunks at a time */ + if (!IS_ALIGNED(*gaddr, HPAGE_SIZE)) + goto out; + + /* + * We provide the storage state for 1MB chunks of guest + * storage. The buffer will need to be aligned to + * conf_dump_storage_state_len so we don't end on a partial + * chunk. + */ + if (!buff_user_len || + !IS_ALIGNED(buff_user_len, increment_len)) + goto out; + + /* + * Allocate a buffer from which we will later copy to the user + * process. We don't want userspace to dictate our buffer size + * so we limit it to DUMP_BUFF_LEN. + */ + ret = -ENOMEM; + buff_kvm_size = min_t(u64, buff_user_len, DUMP_BUFF_LEN); + buff_kvm = vzalloc(buff_kvm_size); + if (!buff_kvm) + goto out; + + ret = 0; + uvcb.dump_area_origin = (u64)buff_kvm; + /* We will loop until the user buffer is filled or an error occurs */ + do { + /* Get 1MB worth of guest storage state data */ + cc = uv_call_sched(0, (u64)&uvcb); + + /* All or nothing */ + if (cc) { + ret = -EINVAL; + break; + } + + size_done += increment_len; + uvcb.dump_area_origin += increment_len; + buff_user_len -= increment_len; + uvcb.gaddr += HPAGE_SIZE; + + /* KVM Buffer full, time to copy to the process */ + if (!buff_user_len || size_done == DUMP_BUFF_LEN) { + if (copy_to_user(buff_user, buff_kvm, size_done)) { + ret = -EFAULT; + break; + } + + buff_user += size_done; + size_done = 0; + uvcb.dump_area_origin = (u64)buff_kvm; + } + } while (buff_user_len); + + /* Report back where we ended dumping */ + *gaddr = uvcb.gaddr; + + /* Lets only log errors, we don't want to spam */ +out: + if (ret) + KVM_UV_EVENT(kvm, 3, + "PROTVIRT DUMP STORAGE STATE: addr %llx ret %d, uvcb rc %x rrc %x", + uvcb.gaddr, ret, uvcb.header.rc, uvcb.header.rrc); + *rc = uvcb.header.rc; + *rrc = uvcb.header.rrc; + vfree(buff_kvm); + + return ret; +} + +/** + * kvm_s390_pv_dump_complete + * + * @kvm: pointer to the guest's KVM struct + * @buff_user: Userspace pointer where we will write the results to + * @rc: Pointer to where the uvcb return code is stored + * @rrc: Pointer to where the uvcb return reason code is stored + * + * Completes the dumping operation and writes the completion data to + * user space. + * + * Context: kvm->lock needs to be held + * + * Return: + * 0 on success + * -ENOMEM if allocating the completion buffer fails + * -EINVAL if the UV call fails, rc and rrc will be set in this case + * -EFAULT if copying the result to buff_user failed + */ +int kvm_s390_pv_dump_complete(struct kvm *kvm, void __user *buff_user, + u16 *rc, u16 *rrc) +{ + struct uv_cb_dump_complete complete = { + .header.len = sizeof(complete), + .header.cmd = UVC_CMD_DUMP_COMPLETE, + .config_handle = kvm_s390_pv_get_handle(kvm), + }; + u64 *compl_data; + int ret; + + /* Allocate dump area */ + compl_data = vzalloc(uv_info.conf_dump_finalize_len); + if (!compl_data) + return -ENOMEM; + complete.dump_area_origin = (u64)compl_data; + + ret = uv_call_sched(0, (u64)&complete); + *rc = complete.header.rc; + *rrc = complete.header.rrc; + KVM_UV_EVENT(kvm, 3, "PROTVIRT DUMP COMPLETE: rc %x rrc %x", + complete.header.rc, complete.header.rrc); + + if (!ret) { + /* + * kvm_s390_pv_dealloc_vm() will also (mem)set + * this to false on a reboot or other destroy + * operation for this vm. + */ + kvm->arch.pv.dumping = false; + kvm_s390_vcpu_unblock_all(kvm); + ret = copy_to_user(buff_user, compl_data, uv_info.conf_dump_finalize_len); + if (ret) + ret = -EFAULT; + } + vfree(compl_data); + /* If the UVC returned an error, translate it to -EINVAL */ + if (ret > 0) + ret = -EINVAL; + return ret; +} diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index 8aaee2892ec3..cb747bf6c798 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c @@ -480,9 +480,9 @@ int kvm_s390_handle_sigp_pei(struct kvm_vcpu *vcpu) struct kvm_vcpu *dest_vcpu; u8 order_code = kvm_s390_get_base_disp_rs(vcpu, NULL); - trace_kvm_s390_handle_sigp_pei(vcpu, order_code, cpu_addr); - if (order_code == SIGP_EXTERNAL_CALL) { + trace_kvm_s390_handle_sigp_pei(vcpu, order_code, cpu_addr); + dest_vcpu = kvm_get_vcpu_by_id(vcpu->kvm, cpu_addr); BUG_ON(dest_vcpu == NULL); diff --git a/arch/s390/kvm/vsie.c b/arch/s390/kvm/vsie.c index dada78b92691..94138f8f0c1c 100644 --- a/arch/s390/kvm/vsie.c +++ b/arch/s390/kvm/vsie.c @@ -503,6 +503,14 @@ static int shadow_scb(struct kvm_vcpu *vcpu, struct vsie_page *vsie_page) /* Host-protection-interruption introduced with ESOP */ if (test_kvm_cpu_feat(vcpu->kvm, KVM_S390_VM_CPU_FEAT_ESOP)) scb_s->ecb |= scb_o->ecb & ECB_HOSTPROTINT; + /* + * CPU Topology + * This facility only uses the utility field of the SCA and none of + * the cpu entries that are problematic with the other interpretation + * facilities so we can pass it through + */ + if (test_kvm_facility(vcpu->kvm, 11)) + scb_s->ecb |= scb_o->ecb & ECB_PTF; /* transactional execution */ if (test_kvm_facility(vcpu->kvm, 73) && wants_tx) { /* remap the prefix is tx is toggled on */ diff --git a/arch/s390/mm/fault.c b/arch/s390/mm/fault.c index e173b6187ad5..ee7871f770fb 100644 --- a/arch/s390/mm/fault.c +++ b/arch/s390/mm/fault.c @@ -754,6 +754,7 @@ void do_secure_storage_access(struct pt_regs *regs) struct vm_area_struct *vma; struct mm_struct *mm; struct page *page; + struct gmap *gmap; int rc; /* @@ -783,6 +784,17 @@ void do_secure_storage_access(struct pt_regs *regs) } switch (get_fault_type(regs)) { + case GMAP_FAULT: + mm = current->mm; + gmap = (struct gmap *)S390_lowcore.gmap; + mmap_read_lock(mm); + addr = __gmap_translate(gmap, addr); + mmap_read_unlock(mm); + if (IS_ERR_VALUE(addr)) { + do_fault_error(regs, VM_ACCESS_FLAGS, VM_FAULT_BADMAP); + break; + } + fallthrough; case USER_FAULT: mm = current->mm; mmap_read_lock(mm); @@ -811,7 +823,6 @@ void do_secure_storage_access(struct pt_regs *regs) if (rc) BUG(); break; - case GMAP_FAULT: default: do_fault_error(regs, VM_READ | VM_WRITE, VM_FAULT_BADMAP); WARN_ON_ONCE(1); @@ -837,6 +848,16 @@ NOKPROBE_SYMBOL(do_non_secure_storage_access); void do_secure_storage_violation(struct pt_regs *regs) { + unsigned long gaddr = regs->int_parm_long & __FAIL_ADDR_MASK; + struct gmap *gmap = (struct gmap *)S390_lowcore.gmap; + + /* + * If the VM has been rebooted, its address space might still contain + * secure pages from the previous boot. + * Clear the page so it can be reused. + */ + if (!gmap_destroy_page(gmap, gaddr)) + return; /* * Either KVM messed up the secure guest mapping or the same * page is mapped into multiple secure guests. diff --git a/arch/s390/mm/gmap.c b/arch/s390/mm/gmap.c index b8ae4a4aa2ba..62758cb5872f 100644 --- a/arch/s390/mm/gmap.c +++ b/arch/s390/mm/gmap.c @@ -2697,41 +2697,168 @@ void s390_reset_cmma(struct mm_struct *mm) } EXPORT_SYMBOL_GPL(s390_reset_cmma); -/* - * make inaccessible pages accessible again - */ -static int __s390_reset_acc(pte_t *ptep, unsigned long addr, - unsigned long next, struct mm_walk *walk) +#define GATHER_GET_PAGES 32 + +struct reset_walk_state { + unsigned long next; + unsigned long count; + unsigned long pfns[GATHER_GET_PAGES]; +}; + +static int s390_gather_pages(pte_t *ptep, unsigned long addr, + unsigned long next, struct mm_walk *walk) { + struct reset_walk_state *p = walk->private; pte_t pte = READ_ONCE(*ptep); - /* There is a reference through the mapping */ - if (pte_present(pte)) - WARN_ON_ONCE(uv_destroy_owned_page(pte_val(pte) & PAGE_MASK)); + if (pte_present(pte)) { + /* we have a reference from the mapping, take an extra one */ + get_page(phys_to_page(pte_val(pte))); + p->pfns[p->count] = phys_to_pfn(pte_val(pte)); + p->next = next; + p->count++; + } + return p->count >= GATHER_GET_PAGES; +} + +static const struct mm_walk_ops gather_pages_ops = { + .pte_entry = s390_gather_pages, +}; + +/* + * Call the Destroy secure page UVC on each page in the given array of PFNs. + * Each page needs to have an extra reference, which will be released here. + */ +void s390_uv_destroy_pfns(unsigned long count, unsigned long *pfns) +{ + unsigned long i; + + for (i = 0; i < count; i++) { + /* we always have an extra reference */ + uv_destroy_owned_page(pfn_to_phys(pfns[i])); + /* get rid of the extra reference */ + put_page(pfn_to_page(pfns[i])); + cond_resched(); + } +} +EXPORT_SYMBOL_GPL(s390_uv_destroy_pfns); + +/** + * __s390_uv_destroy_range - Call the destroy secure page UVC on each page + * in the given range of the given address space. + * @mm: the mm to operate on + * @start: the start of the range + * @end: the end of the range + * @interruptible: if not 0, stop when a fatal signal is received + * + * Walk the given range of the given address space and call the destroy + * secure page UVC on each page. Optionally exit early if a fatal signal is + * pending. + * + * Return: 0 on success, -EINTR if the function stopped before completing + */ +int __s390_uv_destroy_range(struct mm_struct *mm, unsigned long start, + unsigned long end, bool interruptible) +{ + struct reset_walk_state state = { .next = start }; + int r = 1; + + while (r > 0) { + state.count = 0; + mmap_read_lock(mm); + r = walk_page_range(mm, state.next, end, &gather_pages_ops, &state); + mmap_read_unlock(mm); + cond_resched(); + s390_uv_destroy_pfns(state.count, state.pfns); + if (interruptible && fatal_signal_pending(current)) + return -EINTR; + } + return 0; +} +EXPORT_SYMBOL_GPL(__s390_uv_destroy_range); + +/** + * s390_unlist_old_asce - Remove the topmost level of page tables from the + * list of page tables of the gmap. + * @gmap: the gmap whose table is to be removed + * + * On s390x, KVM keeps a list of all pages containing the page tables of the + * gmap (the CRST list). This list is used at tear down time to free all + * pages that are now not needed anymore. + * + * This function removes the topmost page of the tree (the one pointed to by + * the ASCE) from the CRST list. + * + * This means that it will not be freed when the VM is torn down, and needs + * to be handled separately by the caller, unless a leak is actually + * intended. Notice that this function will only remove the page from the + * list, the page will still be used as a top level page table (and ASCE). + */ +void s390_unlist_old_asce(struct gmap *gmap) +{ + struct page *old; + + old = virt_to_page(gmap->table); + spin_lock(&gmap->guest_table_lock); + list_del(&old->lru); + /* + * Sometimes the topmost page might need to be "removed" multiple + * times, for example if the VM is rebooted into secure mode several + * times concurrently, or if s390_replace_asce fails after calling + * s390_remove_old_asce and is attempted again later. In that case + * the old asce has been removed from the list, and therefore it + * will not be freed when the VM terminates, but the ASCE is still + * in use and still pointed to. + * A subsequent call to replace_asce will follow the pointer and try + * to remove the same page from the list again. + * Therefore it's necessary that the page of the ASCE has valid + * pointers, so list_del can work (and do nothing) without + * dereferencing stale or invalid pointers. + */ + INIT_LIST_HEAD(&old->lru); + spin_unlock(&gmap->guest_table_lock); +} +EXPORT_SYMBOL_GPL(s390_unlist_old_asce); + +/** + * s390_replace_asce - Try to replace the current ASCE of a gmap with a copy + * @gmap: the gmap whose ASCE needs to be replaced + * + * If the allocation of the new top level page table fails, the ASCE is not + * replaced. + * In any case, the old ASCE is always removed from the gmap CRST list. + * Therefore the caller has to make sure to save a pointer to it + * beforehand, unless a leak is actually intended. + */ +int s390_replace_asce(struct gmap *gmap) +{ + unsigned long asce; + struct page *page; + void *table; + + s390_unlist_old_asce(gmap); + + page = alloc_pages(GFP_KERNEL_ACCOUNT, CRST_ALLOC_ORDER); + if (!page) + return -ENOMEM; + table = page_to_virt(page); + memcpy(table, gmap->table, 1UL << (CRST_ALLOC_ORDER + PAGE_SHIFT)); + + /* + * The caller has to deal with the old ASCE, but here we make sure + * the new one is properly added to the CRST list, so that + * it will be freed when the VM is torn down. + */ + spin_lock(&gmap->guest_table_lock); + list_add(&page->lru, &gmap->crst_list); + spin_unlock(&gmap->guest_table_lock); + + /* Set new table origin while preserving existing ASCE control bits */ + asce = (gmap->asce & ~_ASCE_ORIGIN) | __pa(table); + WRITE_ONCE(gmap->asce, asce); + WRITE_ONCE(gmap->mm->context.gmap_asce, asce); + WRITE_ONCE(gmap->table, table); return 0; } - -static const struct mm_walk_ops reset_acc_walk_ops = { - .pte_entry = __s390_reset_acc, -}; - -#include -void s390_reset_acc(struct mm_struct *mm) -{ - if (!mm_is_protected(mm)) - return; - /* - * we might be called during - * reset: we walk the pages and clear - * close of all kvm file descriptors: we walk the pages and clear - * exit of process on fd closure: vma already gone, do nothing - */ - if (!mmget_not_zero(mm)) - return; - mmap_read_lock(mm); - walk_page_range(mm, 0, TASK_SIZE, &reset_acc_walk_ops, NULL); - mmap_read_unlock(mm); - mmput(mm); -} -EXPORT_SYMBOL_GPL(s390_reset_acc); +EXPORT_SYMBOL_GPL(s390_replace_asce); diff --git a/arch/s390/mm/init.c b/arch/s390/mm/init.c index 6a0ac00d5a42..4a154a084966 100644 --- a/arch/s390/mm/init.c +++ b/arch/s390/mm/init.c @@ -31,7 +31,6 @@ #include #include #include -#include #include #include #include @@ -48,6 +47,7 @@ #include #include #include +#include #include pgd_t swapper_pg_dir[PTRS_PER_PGD] __section(".bss..swapper_pg_dir"); @@ -175,7 +175,7 @@ static void pv_init(void) if (!is_prot_virt_guest()) return; - platform_set(PLATFORM_VIRTIO_RESTRICTED_MEM_ACCESS); + virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); /* make sure bounce buffers are shared */ swiotlb_init(true, SWIOTLB_FORCE | SWIOTLB_VERBOSE); diff --git a/arch/s390/pci/pci.c b/arch/s390/pci/pci.c index bc980fd313d5..73cdc5539384 100644 --- a/arch/s390/pci/pci.c +++ b/arch/s390/pci/pci.c @@ -61,6 +61,12 @@ DEFINE_STATIC_KEY_FALSE(have_mio); static struct kmem_cache *zdev_fmb_cache; +/* AEN structures that must be preserved over KVM module re-insertion */ +union zpci_sic_iib *zpci_aipb; +EXPORT_SYMBOL_GPL(zpci_aipb); +struct airq_iv *zpci_aif_sbv; +EXPORT_SYMBOL_GPL(zpci_aif_sbv); + struct zpci_dev *get_zdev_by_fid(u32 fid) { struct zpci_dev *tmp, *zdev = NULL; @@ -120,11 +126,13 @@ int zpci_register_ioat(struct zpci_dev *zdev, u8 dmaas, fib.pba = base; fib.pal = limit; fib.iota = iota | ZPCI_IOTA_RTTO_FLAG; + fib.gd = zdev->gisa; cc = zpci_mod_fc(req, &fib, &status); if (cc) zpci_dbg(3, "reg ioat fid:%x, cc:%d, status:%d\n", zdev->fid, cc, status); return cc; } +EXPORT_SYMBOL_GPL(zpci_register_ioat); /* Modify PCI: Unregister I/O address translation parameters */ int zpci_unregister_ioat(struct zpci_dev *zdev, u8 dmaas) @@ -133,6 +141,8 @@ int zpci_unregister_ioat(struct zpci_dev *zdev, u8 dmaas) struct zpci_fib fib = {0}; u8 cc, status; + fib.gd = zdev->gisa; + cc = zpci_mod_fc(req, &fib, &status); if (cc) zpci_dbg(3, "unreg ioat fid:%x, cc:%d, status:%d\n", zdev->fid, cc, status); @@ -160,6 +170,7 @@ int zpci_fmb_enable_device(struct zpci_dev *zdev) atomic64_set(&zdev->unmapped_pages, 0); fib.fmb_addr = virt_to_phys(zdev->fmb); + fib.gd = zdev->gisa; cc = zpci_mod_fc(req, &fib, &status); if (cc) { kmem_cache_free(zdev_fmb_cache, zdev->fmb); @@ -178,6 +189,8 @@ int zpci_fmb_disable_device(struct zpci_dev *zdev) if (!zdev->fmb) return -EINVAL; + fib.gd = zdev->gisa; + /* Function measurement is disabled if fmb address is zero */ cc = zpci_mod_fc(req, &fib, &status); if (cc == 3) /* Function already gone. */ @@ -700,6 +713,7 @@ int zpci_enable_device(struct zpci_dev *zdev) zpci_update_fh(zdev, fh); return rc; } +EXPORT_SYMBOL_GPL(zpci_enable_device); int zpci_disable_device(struct zpci_dev *zdev) { @@ -723,6 +737,7 @@ int zpci_disable_device(struct zpci_dev *zdev) } return rc; } +EXPORT_SYMBOL_GPL(zpci_disable_device); /** * zpci_hot_reset_device - perform a reset of the given zPCI function @@ -816,6 +831,7 @@ struct zpci_dev *zpci_create_device(u32 fid, u32 fh, enum zpci_state state) kref_init(&zdev->kref); mutex_init(&zdev->lock); + mutex_init(&zdev->kzdev_lock); rc = zpci_init_iommu(zdev); if (rc) diff --git a/arch/s390/pci/pci_clp.c b/arch/s390/pci/pci_clp.c index 375e0a5120bc..ee367798e388 100644 --- a/arch/s390/pci/pci_clp.c +++ b/arch/s390/pci/pci_clp.c @@ -106,6 +106,8 @@ static void clp_store_query_pci_fngrp(struct zpci_dev *zdev, zdev->max_msi = response->noi; zdev->fmb_update = response->mui; zdev->version = response->version; + zdev->maxstbl = response->maxstbl; + zdev->dtsm = response->dtsm; switch (response->version) { case 1: @@ -229,12 +231,16 @@ static int clp_set_pci_fn(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as, u8 comma { struct clp_req_rsp_set_pci *rrb; int rc, retries = 100; + u32 gisa = 0; *fh = 0; rrb = clp_alloc_block(GFP_KERNEL); if (!rrb) return -ENOMEM; + if (command != CLP_SET_DISABLE_PCI_FN) + gisa = zdev->gisa; + do { memset(rrb, 0, sizeof(*rrb)); rrb->request.hdr.len = sizeof(rrb->request); @@ -243,6 +249,7 @@ static int clp_set_pci_fn(struct zpci_dev *zdev, u32 *fh, u8 nr_dma_as, u8 comma rrb->request.fh = zdev->fh; rrb->request.oc = command; rrb->request.ndas = nr_dma_as; + rrb->request.gisa = gisa; rc = clp_req(rrb, CLP_LPS_PCI); if (rrb->response.hdr.rsp == CLP_RC_SETPCIFN_BUSY) { diff --git a/arch/s390/pci/pci_insn.c b/arch/s390/pci/pci_insn.c index 1a822b7799f8..56480be48244 100644 --- a/arch/s390/pci/pci_insn.c +++ b/arch/s390/pci/pci_insn.c @@ -92,6 +92,7 @@ u8 zpci_mod_fc(u64 req, struct zpci_fib *fib, u8 *status) return cc; } +EXPORT_SYMBOL_GPL(zpci_mod_fc); /* Refresh PCI Translations */ static inline u8 __rpcit(u64 fn, u64 addr, u64 range, u8 *status) @@ -138,7 +139,7 @@ int zpci_refresh_trans(u64 fn, u64 addr, u64 range) } /* Set Interruption Controls */ -int __zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib) +int zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib) { if (!test_facility(72)) return -EIO; @@ -149,6 +150,7 @@ int __zpci_set_irq_ctrl(u16 ctl, u8 isc, union zpci_sic_iib *iib) return 0; } +EXPORT_SYMBOL_GPL(zpci_set_irq_ctrl); /* PCI Load */ static inline int ____pcilg(u64 *data, u64 req, u64 offset, u8 *status) diff --git a/arch/s390/pci/pci_irq.c b/arch/s390/pci/pci_irq.c index 500cd2dbdf53..a2b42a63a53b 100644 --- a/arch/s390/pci/pci_irq.c +++ b/arch/s390/pci/pci_irq.c @@ -11,16 +11,10 @@ #include #include +#include static enum {FLOATING, DIRECTED} irq_delivery; -#define SIC_IRQ_MODE_ALL 0 -#define SIC_IRQ_MODE_SINGLE 1 -#define SIC_IRQ_MODE_DIRECT 4 -#define SIC_IRQ_MODE_D_ALL 16 -#define SIC_IRQ_MODE_D_SINGLE 17 -#define SIC_IRQ_MODE_SET_CPU 18 - /* * summary bit vector * FLOATING - summary bit per function @@ -49,6 +43,7 @@ static int zpci_set_airq(struct zpci_dev *zdev) fib.fmt0.aibvo = 0; /* each zdev has its own interrupt vector */ fib.fmt0.aisb = virt_to_phys(zpci_sbv->vector) + (zdev->aisb / 64) * 8; fib.fmt0.aisbo = zdev->aisb & 63; + fib.gd = zdev->gisa; return zpci_mod_fc(req, &fib, &status) ? -EIO : 0; } @@ -60,6 +55,8 @@ static int zpci_clear_airq(struct zpci_dev *zdev) struct zpci_fib fib = {0}; u8 cc, status; + fib.gd = zdev->gisa; + cc = zpci_mod_fc(req, &fib, &status); if (cc == 3 || (cc == 1 && status == 24)) /* Function already gone or IRQs already deregistered. */ @@ -78,6 +75,7 @@ static int zpci_set_directed_irq(struct zpci_dev *zdev) fib.fmt = 1; fib.fmt1.noi = zdev->msi_nr_irqs; fib.fmt1.dibvo = zdev->msi_first_bit; + fib.gd = zdev->gisa; return zpci_mod_fc(req, &fib, &status) ? -EIO : 0; } @@ -90,6 +88,7 @@ static int zpci_clear_directed_irq(struct zpci_dev *zdev) u8 cc, status; fib.fmt = 1; + fib.gd = zdev->gisa; cc = zpci_mod_fc(req, &fib, &status); if (cc == 3 || (cc == 1 && status == 24)) /* Function already gone or IRQs already deregistered. */ @@ -153,6 +152,7 @@ static struct irq_chip zpci_irq_chip = { static void zpci_handle_cpu_local_irq(bool rescan) { struct airq_iv *dibv = zpci_ibv[smp_processor_id()]; + union zpci_sic_iib iib = {{0}}; unsigned long bit; int irqs_on = 0; @@ -164,7 +164,7 @@ static void zpci_handle_cpu_local_irq(bool rescan) /* End of second scan with interrupts on. */ break; /* First scan complete, reenable interrupts. */ - if (zpci_set_irq_ctrl(SIC_IRQ_MODE_D_SINGLE, PCI_ISC)) + if (zpci_set_irq_ctrl(SIC_IRQ_MODE_D_SINGLE, PCI_ISC, &iib)) break; bit = 0; continue; @@ -192,6 +192,7 @@ static void zpci_handle_remote_irq(void *data) static void zpci_handle_fallback_irq(void) { struct cpu_irq_data *cpu_data; + union zpci_sic_iib iib = {{0}}; unsigned long cpu; int irqs_on = 0; @@ -202,7 +203,7 @@ static void zpci_handle_fallback_irq(void) /* End of second scan with interrupts on. */ break; /* First scan complete, reenable interrupts. */ - if (zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC)) + if (zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib)) break; cpu = 0; continue; @@ -216,8 +217,11 @@ static void zpci_handle_fallback_irq(void) } } -static void zpci_directed_irq_handler(struct airq_struct *airq, bool floating) +static void zpci_directed_irq_handler(struct airq_struct *airq, + struct tpi_info *tpi_info) { + bool floating = !tpi_info->directed_irq; + if (floating) { inc_irq_stat(IRQIO_PCF); zpci_handle_fallback_irq(); @@ -227,8 +231,10 @@ static void zpci_directed_irq_handler(struct airq_struct *airq, bool floating) } } -static void zpci_floating_irq_handler(struct airq_struct *airq, bool floating) +static void zpci_floating_irq_handler(struct airq_struct *airq, + struct tpi_info *tpi_info) { + union zpci_sic_iib iib = {{0}}; unsigned long si, ai; struct airq_iv *aibv; int irqs_on = 0; @@ -242,7 +248,7 @@ static void zpci_floating_irq_handler(struct airq_struct *airq, bool floating) /* End of second scan with interrupts on. */ break; /* First scan complete, reenable interrupts. */ - if (zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC)) + if (zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib)) break; si = 0; continue; @@ -291,7 +297,7 @@ int arch_setup_msi_irqs(struct pci_dev *pdev, int nvec, int type) zdev->aisb = bit; /* Create adapter interrupt vector */ - zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK); + zdev->aibv = airq_iv_create(msi_vecs, AIRQ_IV_DATA | AIRQ_IV_BITLOCK, NULL); if (!zdev->aibv) return -ENOMEM; @@ -402,11 +408,12 @@ static struct airq_struct zpci_airq = { static void __init cpu_enable_directed_irq(void *unused) { union zpci_sic_iib iib = {{0}}; + union zpci_sic_iib ziib = {{0}}; iib.cdiib.dibv_addr = (u64) zpci_ibv[smp_processor_id()]->vector; - __zpci_set_irq_ctrl(SIC_IRQ_MODE_SET_CPU, 0, &iib); - zpci_set_irq_ctrl(SIC_IRQ_MODE_D_SINGLE, PCI_ISC); + zpci_set_irq_ctrl(SIC_IRQ_MODE_SET_CPU, 0, &iib); + zpci_set_irq_ctrl(SIC_IRQ_MODE_D_SINGLE, PCI_ISC, &ziib); } static int __init zpci_directed_irq_init(void) @@ -414,14 +421,14 @@ static int __init zpci_directed_irq_init(void) union zpci_sic_iib iib = {{0}}; unsigned int cpu; - zpci_sbv = airq_iv_create(num_possible_cpus(), 0); + zpci_sbv = airq_iv_create(num_possible_cpus(), 0, NULL); if (!zpci_sbv) return -ENOMEM; iib.diib.isc = PCI_ISC; iib.diib.nr_cpus = num_possible_cpus(); iib.diib.disb_addr = virt_to_phys(zpci_sbv->vector); - __zpci_set_irq_ctrl(SIC_IRQ_MODE_DIRECT, 0, &iib); + zpci_set_irq_ctrl(SIC_IRQ_MODE_DIRECT, 0, &iib); zpci_ibv = kcalloc(num_possible_cpus(), sizeof(*zpci_ibv), GFP_KERNEL); @@ -436,7 +443,7 @@ static int __init zpci_directed_irq_init(void) zpci_ibv[cpu] = airq_iv_create(cache_line_size() * BITS_PER_BYTE, AIRQ_IV_DATA | AIRQ_IV_CACHELINE | - (!cpu ? AIRQ_IV_ALLOC : 0)); + (!cpu ? AIRQ_IV_ALLOC : 0), NULL); if (!zpci_ibv[cpu]) return -ENOMEM; } @@ -453,7 +460,7 @@ static int __init zpci_floating_irq_init(void) if (!zpci_ibv) return -ENOMEM; - zpci_sbv = airq_iv_create(ZPCI_NR_DEVICES, AIRQ_IV_ALLOC); + zpci_sbv = airq_iv_create(ZPCI_NR_DEVICES, AIRQ_IV_ALLOC, NULL); if (!zpci_sbv) goto out_free; @@ -466,6 +473,7 @@ out_free: int __init zpci_irq_init(void) { + union zpci_sic_iib iib = {{0}}; int rc; irq_delivery = sclp.has_dirq ? DIRECTED : FLOATING; @@ -497,7 +505,7 @@ int __init zpci_irq_init(void) * Enable floating IRQs (with suppression after one IRQ). When using * directed IRQs this enables the fallback path. */ - zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC); + zpci_set_irq_ctrl(SIC_IRQ_MODE_SINGLE, PCI_ISC, &iib); return 0; out_airq: diff --git a/arch/s390/tools/gen_facilities.c b/arch/s390/tools/gen_facilities.c index 530dd941d140..cb0aff5c0187 100644 --- a/arch/s390/tools/gen_facilities.c +++ b/arch/s390/tools/gen_facilities.c @@ -111,6 +111,7 @@ static struct facility_def facility_defs[] = { 193, /* bear enhancement facility */ 194, /* rdp enhancement facility */ 196, /* processor activity instrumentation facility */ + 197, /* processor activity instrumentation extension 1 */ -1 /* END */ } }, diff --git a/arch/sparc/vdso/vdso2c.c b/arch/sparc/vdso/vdso2c.c index ab7504176a7f..dc81240aab6f 100644 --- a/arch/sparc/vdso/vdso2c.c +++ b/arch/sparc/vdso/vdso2c.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vdso2c - A vdso image preparation tool * Copyright (c) 2014 Andy Lutomirski and others - * Licensed under the GPL v2 * * vdso2c requires stripped and unstripped input. It would be trivial * to fully strip the input in here, but, for reasons described below, diff --git a/arch/x86/crypto/crc32-pclmul_asm.S b/arch/x86/crypto/crc32-pclmul_asm.S index c392a6edbfff..ca53e96996ac 100644 --- a/arch/x86/crypto/crc32-pclmul_asm.S +++ b/arch/x86/crypto/crc32-pclmul_asm.S @@ -1,26 +1,4 @@ -/* GPL HEADER START - * - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 only, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License version 2 for more details (a copy is included - * in the LICENSE file that accompanied this code). - * - * You should have received a copy of the GNU General Public License - * version 2 along with this program; If not, see http://www.gnu.org/licenses - * - * Please visit http://www.xyratex.com/contact if you need additional - * information or have any questions. - * - * GPL HEADER END - */ - +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2012 Xyratex Technology Limited * diff --git a/arch/x86/entry/vdso/vdso2c.c b/arch/x86/entry/vdso/vdso2c.c index edfe9780f6d1..90d15f2a7205 100644 --- a/arch/x86/entry/vdso/vdso2c.c +++ b/arch/x86/entry/vdso/vdso2c.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * vdso2c - A vdso image preparation tool * Copyright (c) 2014 Andy Lutomirski and others - * Licensed under the GPL v2 * * vdso2c requires stripped and unstripped input. It would be trivial * to fully strip the input in here, but, for reasons described below, diff --git a/arch/x86/events/core.c b/arch/x86/events/core.c index 30788894124f..f969410d0c90 100644 --- a/arch/x86/events/core.c +++ b/arch/x86/events/core.c @@ -693,9 +693,9 @@ void x86_pmu_disable_all(void) } } -struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr) +struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr, void *data) { - return static_call(x86_pmu_guest_get_msrs)(nr); + return static_call(x86_pmu_guest_get_msrs)(nr, data); } EXPORT_SYMBOL_GPL(perf_guest_get_msrs); @@ -2103,14 +2103,15 @@ static int __init init_hw_perf_events(void) } if (err != 0) { pr_cont("no PMU driver, software events only.\n"); - return 0; + err = 0; + goto out_bad_pmu; } pmu_check_apic(); /* sanity check that the hardware exists or is emulated */ if (!check_hw_exists(&pmu, x86_pmu.num_counters, x86_pmu.num_counters_fixed)) - return 0; + goto out_bad_pmu; pr_cont("%s PMU driver.\n", x86_pmu.name); @@ -2219,6 +2220,8 @@ out1: cpuhp_remove_state(CPUHP_AP_PERF_X86_STARTING); out: cpuhp_remove_state(CPUHP_PERF_X86_PREPARE); +out_bad_pmu: + memset(&x86_pmu, 0, sizeof(x86_pmu)); return err; } early_initcall(init_hw_perf_events); @@ -2990,6 +2993,11 @@ unsigned long perf_misc_flags(struct pt_regs *regs) void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) { + if (!x86_pmu_initialized()) { + memset(cap, 0, sizeof(*cap)); + return; + } + cap->version = x86_pmu.version; /* * KVM doesn't support the hybrid PMU yet. @@ -3002,5 +3010,17 @@ void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) cap->bit_width_fixed = x86_pmu.cntval_bits; cap->events_mask = (unsigned int)x86_pmu.events_maskl; cap->events_mask_len = x86_pmu.events_mask_len; + cap->pebs_ept = x86_pmu.pebs_ept; } EXPORT_SYMBOL_GPL(perf_get_x86_pmu_capability); + +u64 perf_get_hw_event_config(int hw_event) +{ + int max = x86_pmu.max_events; + + if (hw_event < max) + return x86_pmu.event_map(array_index_nospec(hw_event, max)); + + return 0; +} +EXPORT_SYMBOL_GPL(perf_get_hw_event_config); diff --git a/arch/x86/events/intel/core.c b/arch/x86/events/intel/core.c index bd8b98857609..2db93498ff71 100644 --- a/arch/x86/events/intel/core.c +++ b/arch/x86/events/intel/core.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -2852,6 +2853,47 @@ static void intel_pmu_reset(void) local_irq_restore(flags); } +/* + * We may be running with guest PEBS events created by KVM, and the + * PEBS records are logged into the guest's DS and invisible to host. + * + * In the case of guest PEBS overflow, we only trigger a fake event + * to emulate the PEBS overflow PMI for guest PEBS counters in KVM. + * The guest will then vm-entry and check the guest DS area to read + * the guest PEBS records. + * + * The contents and other behavior of the guest event do not matter. + */ +static void x86_pmu_handle_guest_pebs(struct pt_regs *regs, + struct perf_sample_data *data) +{ + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); + u64 guest_pebs_idxs = cpuc->pebs_enabled & ~cpuc->intel_ctrl_host_mask; + struct perf_event *event = NULL; + int bit; + + if (!unlikely(perf_guest_state())) + return; + + if (!x86_pmu.pebs_ept || !x86_pmu.pebs_active || + !guest_pebs_idxs) + return; + + for_each_set_bit(bit, (unsigned long *)&guest_pebs_idxs, + INTEL_PMC_IDX_FIXED + x86_pmu.num_counters_fixed) { + event = cpuc->events[bit]; + if (!event->attr.precise_ip) + continue; + + perf_sample_data_init(data, 0, event->hw.last_period); + if (perf_event_overflow(event, data, regs)) + x86_pmu_stop(event, 0); + + /* Inject one fake event is enough. */ + break; + } +} + static int handle_pmi_common(struct pt_regs *regs, u64 status) { struct perf_sample_data data; @@ -2891,10 +2933,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) * counters from the GLOBAL_STATUS mask and we always process PEBS * events via drain_pebs(). */ - if (x86_pmu.flags & PMU_FL_PEBS_ALL) - status &= ~cpuc->pebs_enabled; - else - status &= ~(cpuc->pebs_enabled & PEBS_COUNTER_MASK); + status &= ~(cpuc->pebs_enabled & x86_pmu.pebs_capable); /* * PEBS overflow sets bit 62 in the global status register @@ -2903,6 +2942,7 @@ static int handle_pmi_common(struct pt_regs *regs, u64 status) u64 pebs_enabled = cpuc->pebs_enabled; handled++; + x86_pmu_handle_guest_pebs(regs, &data); x86_pmu.drain_pebs(regs, &data); status &= intel_ctrl | GLOBAL_STATUS_TRACE_TOPAPMI; @@ -3930,40 +3970,98 @@ static int intel_pmu_hw_config(struct perf_event *event) return 0; } -static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr) +/* + * Currently, the only caller of this function is the atomic_switch_perf_msrs(). + * The host perf conext helps to prepare the values of the real hardware for + * a set of msrs that need to be switched atomically in a vmx transaction. + * + * For example, the pseudocode needed to add a new msr should look like: + * + * arr[(*nr)++] = (struct perf_guest_switch_msr){ + * .msr = the hardware msr address, + * .host = the value the hardware has when it doesn't run a guest, + * .guest = the value the hardware has when it runs a guest, + * }; + * + * These values have nothing to do with the emulated values the guest sees + * when it uses {RD,WR}MSR, which should be handled by the KVM context, + * specifically in the intel_pmu_{get,set}_msr(). + */ +static struct perf_guest_switch_msr *intel_guest_get_msrs(int *nr, void *data) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs; + struct kvm_pmu *kvm_pmu = (struct kvm_pmu *)data; u64 intel_ctrl = hybrid(cpuc->pmu, intel_ctrl); + u64 pebs_mask = cpuc->pebs_enabled & x86_pmu.pebs_capable; + int global_ctrl, pebs_enable; - arr[0].msr = MSR_CORE_PERF_GLOBAL_CTRL; - arr[0].host = intel_ctrl & ~cpuc->intel_ctrl_guest_mask; - arr[0].guest = intel_ctrl & ~cpuc->intel_ctrl_host_mask; - if (x86_pmu.flags & PMU_FL_PEBS_ALL) - arr[0].guest &= ~cpuc->pebs_enabled; - else - arr[0].guest &= ~(cpuc->pebs_enabled & PEBS_COUNTER_MASK); - *nr = 1; + *nr = 0; + global_ctrl = (*nr)++; + arr[global_ctrl] = (struct perf_guest_switch_msr){ + .msr = MSR_CORE_PERF_GLOBAL_CTRL, + .host = intel_ctrl & ~cpuc->intel_ctrl_guest_mask, + .guest = intel_ctrl & (~cpuc->intel_ctrl_host_mask | ~pebs_mask), + }; - if (x86_pmu.pebs && x86_pmu.pebs_no_isolation) { - /* - * If PMU counter has PEBS enabled it is not enough to - * disable counter on a guest entry since PEBS memory - * write can overshoot guest entry and corrupt guest - * memory. Disabling PEBS solves the problem. - * - * Don't do this if the CPU already enforces it. - */ - arr[1].msr = MSR_IA32_PEBS_ENABLE; - arr[1].host = cpuc->pebs_enabled; - arr[1].guest = 0; - *nr = 2; + if (!x86_pmu.pebs) + return arr; + + /* + * If PMU counter has PEBS enabled it is not enough to + * disable counter on a guest entry since PEBS memory + * write can overshoot guest entry and corrupt guest + * memory. Disabling PEBS solves the problem. + * + * Don't do this if the CPU already enforces it. + */ + if (x86_pmu.pebs_no_isolation) { + arr[(*nr)++] = (struct perf_guest_switch_msr){ + .msr = MSR_IA32_PEBS_ENABLE, + .host = cpuc->pebs_enabled, + .guest = 0, + }; + return arr; + } + + if (!kvm_pmu || !x86_pmu.pebs_ept) + return arr; + + arr[(*nr)++] = (struct perf_guest_switch_msr){ + .msr = MSR_IA32_DS_AREA, + .host = (unsigned long)cpuc->ds, + .guest = kvm_pmu->ds_area, + }; + + if (x86_pmu.intel_cap.pebs_baseline) { + arr[(*nr)++] = (struct perf_guest_switch_msr){ + .msr = MSR_PEBS_DATA_CFG, + .host = cpuc->pebs_data_cfg, + .guest = kvm_pmu->pebs_data_cfg, + }; + } + + pebs_enable = (*nr)++; + arr[pebs_enable] = (struct perf_guest_switch_msr){ + .msr = MSR_IA32_PEBS_ENABLE, + .host = cpuc->pebs_enabled & ~cpuc->intel_ctrl_guest_mask, + .guest = pebs_mask & ~cpuc->intel_ctrl_host_mask, + }; + + if (arr[pebs_enable].host) { + /* Disable guest PEBS if host PEBS is enabled. */ + arr[pebs_enable].guest = 0; + } else { + /* Disable guest PEBS for cross-mapped PEBS counters. */ + arr[pebs_enable].guest &= ~kvm_pmu->host_cross_mapped_mask; + /* Set hw GLOBAL_CTRL bits for PEBS counter when it runs for guest */ + arr[global_ctrl].guest |= arr[pebs_enable].guest; } return arr; } -static struct perf_guest_switch_msr *core_guest_get_msrs(int *nr) +static struct perf_guest_switch_msr *core_guest_get_msrs(int *nr, void *data) { struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); struct perf_guest_switch_msr *arr = cpuc->guest_switch_msrs; @@ -5650,6 +5748,7 @@ __init int intel_pmu_init(void) x86_pmu.events_mask_len = eax.split.mask_length; x86_pmu.max_pebs_events = min_t(unsigned, MAX_PEBS_EVENTS, x86_pmu.num_counters); + x86_pmu.pebs_capable = PEBS_COUNTER_MASK; /* * Quirk: v2 perfmon does not report fixed-purpose events, so @@ -5834,6 +5933,7 @@ __init int intel_pmu_init(void) x86_pmu.pebs_aliases = NULL; x86_pmu.pebs_prec_dist = true; x86_pmu.lbr_pt_coexist = true; + x86_pmu.pebs_capable = ~0ULL; x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_PEBS_ALL; x86_pmu.get_event_constraints = glp_get_event_constraints; @@ -6138,6 +6238,7 @@ __init int intel_pmu_init(void) case INTEL_FAM6_ICELAKE_X: case INTEL_FAM6_ICELAKE_D: + x86_pmu.pebs_ept = 1; pmem = true; fallthrough; case INTEL_FAM6_ICELAKE_L: @@ -6190,6 +6291,7 @@ __init int intel_pmu_init(void) x86_pmu.pebs_aliases = NULL; x86_pmu.pebs_prec_dist = true; x86_pmu.pebs_block = true; + x86_pmu.pebs_capable = ~0ULL; x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_NO_HT_SHARING; x86_pmu.flags |= PMU_FL_PEBS_ALL; @@ -6235,6 +6337,7 @@ __init int intel_pmu_init(void) x86_pmu.pebs_aliases = NULL; x86_pmu.pebs_prec_dist = true; x86_pmu.pebs_block = true; + x86_pmu.pebs_capable = ~0ULL; x86_pmu.flags |= PMU_FL_HAS_RSP_1; x86_pmu.flags |= PMU_FL_NO_HT_SHARING; x86_pmu.flags |= PMU_FL_PEBS_ALL; @@ -6399,8 +6502,7 @@ __init int intel_pmu_init(void) x86_pmu.intel_ctrl); /* * Access LBR MSR may cause #GP under certain circumstances. - * E.g. KVM doesn't support LBR MSR - * Check all LBT MSR here. + * Check all LBR MSR here. * Disable LBR access if any LBR MSRs can not be accessed. */ if (x86_pmu.lbr_tos && !check_msr(x86_pmu.lbr_tos, 0x3UL)) diff --git a/arch/x86/events/perf_event.h b/arch/x86/events/perf_event.h index ca2f8bfe6ff1..ba3d24a6a4ec 100644 --- a/arch/x86/events/perf_event.h +++ b/arch/x86/events/perf_event.h @@ -828,7 +828,8 @@ struct x86_pmu { pebs_prec_dist :1, pebs_no_tlb :1, pebs_no_isolation :1, - pebs_block :1; + pebs_block :1, + pebs_ept :1; int pebs_record_size; int pebs_buffer_size; int max_pebs_events; @@ -838,6 +839,7 @@ struct x86_pmu { u64 (*pebs_latency_data)(struct perf_event *event, u64 status); unsigned long large_pebs_flags; u64 rtm_abort_event; + u64 pebs_capable; /* * Intel LBR @@ -913,7 +915,7 @@ struct x86_pmu { /* * Intel host/guest support (KVM) */ - struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr); + struct perf_guest_switch_msr *(*guest_get_msrs)(int *nr, void *data); /* * Check period value for PERF_EVENT_IOC_PERIOD ioctl. diff --git a/arch/x86/hyperv/hv_apic.c b/arch/x86/hyperv/hv_apic.c index db2d92fb44da..fb8b2c088681 100644 --- a/arch/x86/hyperv/hv_apic.c +++ b/arch/x86/hyperv/hv_apic.c @@ -46,7 +46,7 @@ static void hv_apic_icr_write(u32 low, u32 id) { u64 reg_val; - reg_val = SET_APIC_DEST_FIELD(id); + reg_val = SET_XAPIC_DEST_FIELD(id); reg_val = reg_val << 32; reg_val |= low; diff --git a/arch/x86/include/asm/apicdef.h b/arch/x86/include/asm/apicdef.h index 92035eb3afee..68d213e83fcc 100644 --- a/arch/x86/include/asm/apicdef.h +++ b/arch/x86/include/asm/apicdef.h @@ -89,8 +89,8 @@ #define APIC_DM_EXTINT 0x00700 #define APIC_VECTOR_MASK 0x000FF #define APIC_ICR2 0x310 -#define GET_APIC_DEST_FIELD(x) (((x) >> 24) & 0xFF) -#define SET_APIC_DEST_FIELD(x) ((x) << 24) +#define GET_XAPIC_DEST_FIELD(x) (((x) >> 24) & 0xFF) +#define SET_XAPIC_DEST_FIELD(x) ((x) << 24) #define APIC_LVTT 0x320 #define APIC_LVTTHMR 0x330 #define APIC_LVTPC 0x340 diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 5fe7f6c8a7a4..14ed039dff55 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -353,6 +353,7 @@ #define X86_FEATURE_AVIC (15*32+13) /* Virtual Interrupt Controller */ #define X86_FEATURE_V_VMSAVE_VMLOAD (15*32+15) /* Virtual VMSAVE VMLOAD */ #define X86_FEATURE_VGIF (15*32+16) /* Virtual GIF */ +#define X86_FEATURE_X2AVIC (15*32+18) /* Virtual x2apic */ #define X86_FEATURE_V_SPEC_CTRL (15*32+20) /* Virtual SPEC_CTRL */ #define X86_FEATURE_SVME_ADDR_CHK (15*32+28) /* "" SVME addr check */ diff --git a/arch/x86/include/asm/kvm-x86-ops.h b/arch/x86/include/asm/kvm-x86-ops.h index da47f60a4650..51f777071584 100644 --- a/arch/x86/include/asm/kvm-x86-ops.h +++ b/arch/x86/include/asm/kvm-x86-ops.h @@ -21,6 +21,7 @@ KVM_X86_OP(has_emulated_msr) KVM_X86_OP(vcpu_after_set_cpuid) KVM_X86_OP(vm_init) KVM_X86_OP_OPTIONAL(vm_destroy) +KVM_X86_OP_OPTIONAL_RET0(vcpu_precreate) KVM_X86_OP(vcpu_create) KVM_X86_OP(vcpu_free) KVM_X86_OP(vcpu_reset) @@ -87,7 +88,7 @@ KVM_X86_OP(deliver_interrupt) KVM_X86_OP_OPTIONAL(sync_pir_to_irr) KVM_X86_OP_OPTIONAL_RET0(set_tss_addr) KVM_X86_OP_OPTIONAL_RET0(set_identity_map_addr) -KVM_X86_OP(get_mt_mask) +KVM_X86_OP_OPTIONAL_RET0(get_mt_mask) KVM_X86_OP(load_mmu_pgd) KVM_X86_OP(has_wbinvd_exit) KVM_X86_OP(get_l2_tsc_offset) diff --git a/arch/x86/include/asm/kvm-x86-pmu-ops.h b/arch/x86/include/asm/kvm-x86-pmu-ops.h index fdfd8e06fee6..c17e3e96fc1d 100644 --- a/arch/x86/include/asm/kvm-x86-pmu-ops.h +++ b/arch/x86/include/asm/kvm-x86-pmu-ops.h @@ -12,7 +12,7 @@ BUILD_BUG_ON(1) * a NULL definition, for example if "static_call_cond()" will be used * at the call sites. */ -KVM_X86_PMU_OP(pmc_perf_hw_id) +KVM_X86_PMU_OP(hw_event_available) KVM_X86_PMU_OP(pmc_is_enabled) KVM_X86_PMU_OP(pmc_idx_to_pmc) KVM_X86_PMU_OP(rdpmc_ecx_to_pmc) diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 9217bd6cf0d1..e8281d64a431 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -65,6 +65,9 @@ #define KVM_BUS_LOCK_DETECTION_VALID_MODE (KVM_BUS_LOCK_DETECTION_OFF | \ KVM_BUS_LOCK_DETECTION_EXIT) +#define KVM_X86_NOTIFY_VMEXIT_VALID_BITS (KVM_X86_NOTIFY_VMEXIT_ENABLED | \ + KVM_X86_NOTIFY_VMEXIT_USER) + /* x86-specific vcpu->requests bit members */ #define KVM_REQ_MIGRATE_TIMER KVM_ARCH_REQ(0) #define KVM_REQ_REPORT_TPR_ACCESS KVM_ARCH_REQ(1) @@ -126,7 +129,6 @@ #define INVALID_PAGE (~(hpa_t)0) #define VALID_PAGE(x) ((x) != INVALID_PAGE) -#define UNMAPPED_GVA (~(gpa_t)0) #define INVALID_GPA (~(gpa_t)0) /* KVM Hugepage definitions for x86 */ @@ -505,6 +507,7 @@ struct kvm_pmu { unsigned nr_arch_fixed_counters; unsigned available_event_types; u64 fixed_ctr_ctrl; + u64 fixed_ctr_ctrl_mask; u64 global_ctrl; u64 global_status; u64 counter_bitmask[2]; @@ -520,6 +523,21 @@ struct kvm_pmu { DECLARE_BITMAP(all_valid_pmc_idx, X86_PMC_IDX_MAX); DECLARE_BITMAP(pmc_in_use, X86_PMC_IDX_MAX); + u64 ds_area; + u64 pebs_enable; + u64 pebs_enable_mask; + u64 pebs_data_cfg; + u64 pebs_data_cfg_mask; + + /* + * If a guest counter is cross-mapped to host counter with different + * index, its PEBS capability will be temporarily disabled. + * + * The user should make sure that this mask is updated + * after disabling interrupts and before perf_guest_get_msrs(); + */ + u64 host_cross_mapped_mask; + /* * The gate to release perf_events not marked in * pmc_in_use only once in a vcpu time slice. @@ -644,7 +662,6 @@ struct kvm_vcpu_arch { u64 efer; u64 apic_base; struct kvm_lapic *apic; /* kernel irqchip context */ - bool apicv_active; bool load_eoi_exitmap_pending; DECLARE_BITMAP(ioapic_handled_vectors, 256); unsigned long apic_attention; @@ -695,7 +712,7 @@ struct kvm_vcpu_arch { struct kvm_mmu_memory_cache mmu_pte_list_desc_cache; struct kvm_mmu_memory_cache mmu_shadow_page_cache; - struct kvm_mmu_memory_cache mmu_gfn_array_cache; + struct kvm_mmu_memory_cache mmu_shadowed_info_cache; struct kvm_mmu_memory_cache mmu_page_header_cache; /* @@ -808,6 +825,7 @@ struct kvm_vcpu_arch { u64 mcg_ctl; u64 mcg_ext_ctl; u64 *mce_banks; + u64 *mci_ctl2_banks; /* Cache MMIO info */ u64 mmio_gva; @@ -1110,11 +1128,6 @@ enum kvm_apicv_inhibit { */ APICV_INHIBIT_REASON_PIT_REINJ, - /* - * AVIC is inhibited because the guest has x2apic in its CPUID. - */ - APICV_INHIBIT_REASON_X2APIC, - /* * AVIC is disabled because SEV doesn't support it. */ @@ -1222,8 +1235,13 @@ struct kvm_arch { bool guest_can_read_msr_platform_info; bool exception_payload_enabled; + bool triple_fault_event; + bool bus_lock_detection_enabled; bool enable_pmu; + + u32 notify_window; + u32 notify_vmexit_flags; /* * If exit_on_emulation_error is set, and the in-kernel instruction * emulator fails to emulate an instruction, allow userspace @@ -1307,6 +1325,36 @@ struct kvm_arch { hpa_t hv_root_tdp; spinlock_t hv_root_tdp_lock; #endif + /* + * VM-scope maximum vCPU ID. Used to determine the size of structures + * that increase along with the maximum vCPU ID, in which case, using + * the global KVM_MAX_VCPU_IDS may lead to significant memory waste. + */ + u32 max_vcpu_ids; + + bool disable_nx_huge_pages; + + /* + * Memory caches used to allocate shadow pages when performing eager + * page splitting. No need for a shadowed_info_cache since eager page + * splitting only allocates direct shadow pages. + * + * Protected by kvm->slots_lock. + */ + struct kvm_mmu_memory_cache split_shadow_page_cache; + struct kvm_mmu_memory_cache split_page_header_cache; + + /* + * Memory cache used to allocate pte_list_desc structs while splitting + * huge pages. In the worst case, to split one huge page, 512 + * pte_list_desc structs are needed to add each lower level leaf sptep + * to the rmap plus 1 to extend the parent_ptes rmap of the lower level + * page table. + * + * Protected by kvm->slots_lock. + */ +#define SPLIT_DESC_CACHE_MIN_NR_OBJECTS (SPTE_ENT_PER_PAGE + 1) + struct kvm_mmu_memory_cache split_desc_cache; }; struct kvm_vm_stat { @@ -1367,6 +1415,7 @@ struct kvm_vcpu_stat { u64 preemption_reported; u64 preemption_other; u64 guest_mode; + u64 notify_window_exits; }; struct x86_instruction_info; @@ -1407,6 +1456,7 @@ struct kvm_x86_ops { void (*vm_destroy)(struct kvm *kvm); /* Create, but do not attach this VCPU */ + int (*vcpu_precreate)(struct kvm *kvm); int (*vcpu_create)(struct kvm_vcpu *vcpu); void (*vcpu_free)(struct kvm_vcpu *vcpu); void (*vcpu_reset)(struct kvm_vcpu *vcpu, bool init_event); @@ -1471,7 +1521,7 @@ struct kvm_x86_ops { u32 (*get_interrupt_shadow)(struct kvm_vcpu *vcpu); void (*patch_hypercall)(struct kvm_vcpu *vcpu, unsigned char *hypercall_addr); - void (*inject_irq)(struct kvm_vcpu *vcpu); + void (*inject_irq)(struct kvm_vcpu *vcpu, bool reinjected); void (*inject_nmi)(struct kvm_vcpu *vcpu); void (*queue_exception)(struct kvm_vcpu *vcpu); void (*cancel_injection)(struct kvm_vcpu *vcpu); @@ -1485,7 +1535,7 @@ struct kvm_x86_ops { bool (*check_apicv_inhibit_reasons)(enum kvm_apicv_inhibit reason); void (*refresh_apicv_exec_ctrl)(struct kvm_vcpu *vcpu); void (*hwapic_irr_update)(struct kvm_vcpu *vcpu, int max_irr); - void (*hwapic_isr_update)(struct kvm_vcpu *vcpu, int isr); + void (*hwapic_isr_update)(int isr); bool (*guest_apic_has_interrupt)(struct kvm_vcpu *vcpu); void (*load_eoi_exitmap)(struct kvm_vcpu *vcpu, u64 *eoi_exit_bitmap); void (*set_virtual_apic_mode)(struct kvm_vcpu *vcpu); @@ -1495,7 +1545,7 @@ struct kvm_x86_ops { int (*sync_pir_to_irr)(struct kvm_vcpu *vcpu); int (*set_tss_addr)(struct kvm *kvm, unsigned int addr); int (*set_identity_map_addr)(struct kvm *kvm, u64 ident_addr); - u64 (*get_mt_mask)(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio); + u8 (*get_mt_mask)(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio); void (*load_mmu_pgd)(struct kvm_vcpu *vcpu, hpa_t root_hpa, int root_level); @@ -1705,21 +1755,6 @@ extern bool tdp_enabled; u64 vcpu_tsc_khz(struct kvm_vcpu *vcpu); -/* control of guest tsc rate supported? */ -extern bool kvm_has_tsc_control; -/* maximum supported tsc_khz for guests */ -extern u32 kvm_max_guest_tsc_khz; -/* number of bits of the fractional part of the TSC scaling ratio */ -extern u8 kvm_tsc_scaling_ratio_frac_bits; -/* maximum allowed value of TSC scaling ratio */ -extern u64 kvm_max_tsc_scaling_ratio; -/* 1ull << kvm_tsc_scaling_ratio_frac_bits */ -extern u64 kvm_default_tsc_scaling_ratio; -/* bus lock detection supported? */ -extern bool kvm_has_bus_lock_exit; - -extern u64 kvm_mce_cap_supported; - /* * EMULTYPE_NO_DECODE - Set when re-emulating an instruction (after completing * userspace I/O) to indicate that the emulation context @@ -2060,6 +2095,7 @@ int memslot_rmap_alloc(struct kvm_memory_slot *slot, unsigned long npages); KVM_X86_QUIRK_LAPIC_MMIO_HOLE | \ KVM_X86_QUIRK_OUT_7E_INC_RIP | \ KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT | \ - KVM_X86_QUIRK_FIX_HYPERCALL_INSN) + KVM_X86_QUIRK_FIX_HYPERCALL_INSN | \ + KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS) #endif /* _ASM_X86_KVM_HOST_H */ diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 1ac0f9bf4b90..182b2a1f71fe 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -231,6 +231,12 @@ #define PERF_CAP_PT_IDX 16 #define MSR_PEBS_LD_LAT_THRESHOLD 0x000003f6 +#define PERF_CAP_PEBS_TRAP BIT_ULL(6) +#define PERF_CAP_ARCH_REG BIT_ULL(7) +#define PERF_CAP_PEBS_FORMAT 0xf00 +#define PERF_CAP_PEBS_BASELINE BIT_ULL(14) +#define PERF_CAP_PEBS_MASK (PERF_CAP_PEBS_TRAP | PERF_CAP_ARCH_REG | \ + PERF_CAP_PEBS_FORMAT | PERF_CAP_PEBS_BASELINE) #define MSR_IA32_RTIT_CTL 0x00000570 #define RTIT_CTL_TRACEEN BIT(0) @@ -1019,6 +1025,7 @@ #define MSR_IA32_VMX_TRUE_EXIT_CTLS 0x0000048f #define MSR_IA32_VMX_TRUE_ENTRY_CTLS 0x00000490 #define MSR_IA32_VMX_VMFUNC 0x00000491 +#define MSR_IA32_VMX_PROCBASED_CTLS3 0x00000492 /* VMX_BASIC bits and bitmasks */ #define VMX_BASIC_VMCS_SIZE_SHIFT 32 diff --git a/arch/x86/include/asm/perf_event.h b/arch/x86/include/asm/perf_event.h index 34348ae41cdb..f6fc8dd51ef4 100644 --- a/arch/x86/include/asm/perf_event.h +++ b/arch/x86/include/asm/perf_event.h @@ -222,6 +222,7 @@ struct x86_pmu_capability { int bit_width_fixed; unsigned int events_mask; int events_mask_len; + unsigned int pebs_ept :1; }; /* @@ -520,6 +521,7 @@ struct x86_pmu_lbr { }; extern void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap); +extern u64 perf_get_hw_event_config(int hw_event); extern void perf_check_microcode(void); extern void perf_clear_dirty_counters(void); extern int x86_perf_rdpmc_index(struct perf_event *event); @@ -529,15 +531,20 @@ static inline void perf_get_x86_pmu_capability(struct x86_pmu_capability *cap) memset(cap, 0, sizeof(*cap)); } +static inline u64 perf_get_hw_event_config(int hw_event) +{ + return 0; +} + static inline void perf_events_lapic_init(void) { } static inline void perf_check_microcode(void) { } #endif #if defined(CONFIG_PERF_EVENTS) && defined(CONFIG_CPU_SUP_INTEL) -extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); +extern struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr, void *data); extern int x86_perf_get_lbr(struct x86_pmu_lbr *lbr); #else -struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr); +struct perf_guest_switch_msr *perf_guest_get_msrs(int *nr, void *data); static inline int x86_perf_get_lbr(struct x86_pmu_lbr *lbr) { return -1; diff --git a/arch/x86/include/asm/svm.h b/arch/x86/include/asm/svm.h index 1b07fba11704..0361626841bc 100644 --- a/arch/x86/include/asm/svm.h +++ b/arch/x86/include/asm/svm.h @@ -195,6 +195,9 @@ struct __attribute__ ((__packed__)) vmcb_control_area { #define AVIC_ENABLE_SHIFT 31 #define AVIC_ENABLE_MASK (1 << AVIC_ENABLE_SHIFT) +#define X2APIC_MODE_SHIFT 30 +#define X2APIC_MODE_MASK (1 << X2APIC_MODE_SHIFT) + #define LBR_CTL_ENABLE_MASK BIT_ULL(0) #define VIRTUAL_VMLOAD_VMSAVE_ENABLE_MASK BIT_ULL(1) @@ -253,12 +256,19 @@ enum avic_ipi_failure_cause { AVIC_IPI_FAILURE_INVALID_BACKING_PAGE, }; +#define AVIC_PHYSICAL_MAX_INDEX_MASK GENMASK_ULL(9, 0) /* - * 0xff is broadcast, so the max index allowed for physical APIC ID - * table is 0xfe. APIC IDs above 0xff are reserved. + * For AVIC, the max index allowed for physical APIC ID + * table is 0xff (255). */ -#define AVIC_MAX_PHYSICAL_ID_COUNT 0xff +#define AVIC_MAX_PHYSICAL_ID 0XFEULL + +/* + * For x2AVIC, the max index allowed for physical APIC ID + * table is 0x1ff (511). + */ +#define X2AVIC_MAX_PHYSICAL_ID 0x1FFUL #define AVIC_HPA_MASK ~((0xFFFULL << 52) | 0xFFF) #define VMCB_AVIC_APIC_BAR_MASK 0xFFFFFFFFFF000ULL diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index 6c343c6a1855..c371ef695fcc 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -31,6 +31,7 @@ #define CPU_BASED_RDTSC_EXITING VMCS_CONTROL_BIT(RDTSC_EXITING) #define CPU_BASED_CR3_LOAD_EXITING VMCS_CONTROL_BIT(CR3_LOAD_EXITING) #define CPU_BASED_CR3_STORE_EXITING VMCS_CONTROL_BIT(CR3_STORE_EXITING) +#define CPU_BASED_ACTIVATE_TERTIARY_CONTROLS VMCS_CONTROL_BIT(TERTIARY_CONTROLS) #define CPU_BASED_CR8_LOAD_EXITING VMCS_CONTROL_BIT(CR8_LOAD_EXITING) #define CPU_BASED_CR8_STORE_EXITING VMCS_CONTROL_BIT(CR8_STORE_EXITING) #define CPU_BASED_TPR_SHADOW VMCS_CONTROL_BIT(VIRTUAL_TPR) @@ -74,6 +75,12 @@ #define SECONDARY_EXEC_TSC_SCALING VMCS_CONTROL_BIT(TSC_SCALING) #define SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE VMCS_CONTROL_BIT(USR_WAIT_PAUSE) #define SECONDARY_EXEC_BUS_LOCK_DETECTION VMCS_CONTROL_BIT(BUS_LOCK_DETECTION) +#define SECONDARY_EXEC_NOTIFY_VM_EXITING VMCS_CONTROL_BIT(NOTIFY_VM_EXITING) + +/* + * Definitions of Tertiary Processor-Based VM-Execution Controls. + */ +#define TERTIARY_EXEC_IPI_VIRT VMCS_CONTROL_BIT(IPI_VIRT) #define PIN_BASED_EXT_INTR_MASK VMCS_CONTROL_BIT(INTR_EXITING) #define PIN_BASED_NMI_EXITING VMCS_CONTROL_BIT(NMI_EXITING) @@ -158,6 +165,7 @@ static inline int vmx_misc_mseg_revid(u64 vmx_misc) enum vmcs_field { VIRTUAL_PROCESSOR_ID = 0x00000000, POSTED_INTR_NV = 0x00000002, + LAST_PID_POINTER_INDEX = 0x00000008, GUEST_ES_SELECTOR = 0x00000800, GUEST_CS_SELECTOR = 0x00000802, GUEST_SS_SELECTOR = 0x00000804, @@ -221,6 +229,10 @@ enum vmcs_field { ENCLS_EXITING_BITMAP_HIGH = 0x0000202F, TSC_MULTIPLIER = 0x00002032, TSC_MULTIPLIER_HIGH = 0x00002033, + TERTIARY_VM_EXEC_CONTROL = 0x00002034, + TERTIARY_VM_EXEC_CONTROL_HIGH = 0x00002035, + PID_POINTER_TABLE = 0x00002042, + PID_POINTER_TABLE_HIGH = 0x00002043, GUEST_PHYSICAL_ADDRESS = 0x00002400, GUEST_PHYSICAL_ADDRESS_HIGH = 0x00002401, VMCS_LINK_POINTER = 0x00002800, @@ -269,6 +281,7 @@ enum vmcs_field { SECONDARY_VM_EXEC_CONTROL = 0x0000401e, PLE_GAP = 0x00004020, PLE_WINDOW = 0x00004022, + NOTIFY_WINDOW = 0x00004024, VM_INSTRUCTION_ERROR = 0x00004400, VM_EXIT_REASON = 0x00004402, VM_EXIT_INTR_INFO = 0x00004404, @@ -553,6 +566,11 @@ enum vm_entry_failure_code { #define EPT_VIOLATION_GVA_IS_VALID (1 << EPT_VIOLATION_GVA_IS_VALID_BIT) #define EPT_VIOLATION_GVA_TRANSLATED (1 << EPT_VIOLATION_GVA_TRANSLATED_BIT) +/* + * Exit Qualifications for NOTIFY VM EXIT + */ +#define NOTIFY_VM_CONTEXT_INVALID BIT(0) + /* * VM-instruction error numbers */ diff --git a/arch/x86/include/asm/vmxfeatures.h b/arch/x86/include/asm/vmxfeatures.h index d9a74681a77d..c6a7eed03914 100644 --- a/arch/x86/include/asm/vmxfeatures.h +++ b/arch/x86/include/asm/vmxfeatures.h @@ -5,7 +5,7 @@ /* * Defines VMX CPU feature bits */ -#define NVMXINTS 3 /* N 32-bit words worth of info */ +#define NVMXINTS 5 /* N 32-bit words worth of info */ /* * Note: If the comment begins with a quoted string, that string is used @@ -43,6 +43,7 @@ #define VMX_FEATURE_RDTSC_EXITING ( 1*32+ 12) /* "" VM-Exit on RDTSC */ #define VMX_FEATURE_CR3_LOAD_EXITING ( 1*32+ 15) /* "" VM-Exit on writes to CR3 */ #define VMX_FEATURE_CR3_STORE_EXITING ( 1*32+ 16) /* "" VM-Exit on reads from CR3 */ +#define VMX_FEATURE_TERTIARY_CONTROLS ( 1*32+ 17) /* "" Enable Tertiary VM-Execution Controls */ #define VMX_FEATURE_CR8_LOAD_EXITING ( 1*32+ 19) /* "" VM-Exit on writes to CR8 */ #define VMX_FEATURE_CR8_STORE_EXITING ( 1*32+ 20) /* "" VM-Exit on reads from CR8 */ #define VMX_FEATURE_VIRTUAL_TPR ( 1*32+ 21) /* "vtpr" TPR virtualization, a.k.a. TPR shadow */ @@ -84,5 +85,8 @@ #define VMX_FEATURE_USR_WAIT_PAUSE ( 2*32+ 26) /* Enable TPAUSE, UMONITOR, UMWAIT in guest */ #define VMX_FEATURE_ENCLV_EXITING ( 2*32+ 28) /* "" VM-Exit on ENCLV (leaf dependent) */ #define VMX_FEATURE_BUS_LOCK_DETECTION ( 2*32+ 30) /* "" VM-Exit when bus lock caused */ +#define VMX_FEATURE_NOTIFY_VM_EXITING ( 2*32+ 31) /* VM-Exit when no event windows after notify window */ +/* Tertiary Processor-Based VM-Execution Controls, word 3 */ +#define VMX_FEATURE_IPI_VIRT ( 3*32+ 4) /* Enable IPI virtualization */ #endif /* _ASM_X86_VMXFEATURES_H */ diff --git a/arch/x86/include/uapi/asm/kvm.h b/arch/x86/include/uapi/asm/kvm.h index ec53c9fa1da9..46de10a809ec 100644 --- a/arch/x86/include/uapi/asm/kvm.h +++ b/arch/x86/include/uapi/asm/kvm.h @@ -306,7 +306,8 @@ struct kvm_pit_state { struct kvm_pit_channel_state channels[3]; }; -#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 +#define KVM_PIT_FLAGS_HPET_LEGACY 0x00000001 +#define KVM_PIT_FLAGS_SPEAKER_DATA_ON 0x00000002 struct kvm_pit_state2 { struct kvm_pit_channel_state channels[3]; @@ -325,6 +326,7 @@ struct kvm_reinject_control { #define KVM_VCPUEVENT_VALID_SHADOW 0x00000004 #define KVM_VCPUEVENT_VALID_SMM 0x00000008 #define KVM_VCPUEVENT_VALID_PAYLOAD 0x00000010 +#define KVM_VCPUEVENT_VALID_TRIPLE_FAULT 0x00000020 /* Interrupt shadow states */ #define KVM_X86_SHADOW_INT_MOV_SS 0x01 @@ -359,7 +361,10 @@ struct kvm_vcpu_events { __u8 smm_inside_nmi; __u8 latched_init; } smi; - __u8 reserved[27]; + struct { + __u8 pending; + } triple_fault; + __u8 reserved[26]; __u8 exception_has_payload; __u64 exception_payload; }; @@ -434,6 +439,7 @@ struct kvm_sync_regs { #define KVM_X86_QUIRK_OUT_7E_INC_RIP (1 << 3) #define KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT (1 << 4) #define KVM_X86_QUIRK_FIX_HYPERCALL_INSN (1 << 5) +#define KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS (1 << 6) #define KVM_STATE_NESTED_FORMAT_VMX 0 #define KVM_STATE_NESTED_FORMAT_SVM 1 diff --git a/arch/x86/include/uapi/asm/vmx.h b/arch/x86/include/uapi/asm/vmx.h index 946d761adbd3..a5faf6d88f1b 100644 --- a/arch/x86/include/uapi/asm/vmx.h +++ b/arch/x86/include/uapi/asm/vmx.h @@ -91,6 +91,7 @@ #define EXIT_REASON_UMWAIT 67 #define EXIT_REASON_TPAUSE 68 #define EXIT_REASON_BUS_LOCK 74 +#define EXIT_REASON_NOTIFY 75 #define VMX_EXIT_REASONS \ { EXIT_REASON_EXCEPTION_NMI, "EXCEPTION_NMI" }, \ @@ -153,7 +154,8 @@ { EXIT_REASON_XRSTORS, "XRSTORS" }, \ { EXIT_REASON_UMWAIT, "UMWAIT" }, \ { EXIT_REASON_TPAUSE, "TPAUSE" }, \ - { EXIT_REASON_BUS_LOCK, "BUS_LOCK" } + { EXIT_REASON_BUS_LOCK, "BUS_LOCK" }, \ + { EXIT_REASON_NOTIFY, "NOTIFY" } #define VMX_EXIT_REASON_FLAGS \ { VMX_EXIT_REASONS_FAILED_VMENTRY, "FAILED_VMENTRY" } diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index 189d3a5e471a..a4347605ab00 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -275,7 +275,7 @@ void native_apic_icr_write(u32 low, u32 id) unsigned long flags; local_irq_save(flags); - apic_write(APIC_ICR2, SET_APIC_DEST_FIELD(id)); + apic_write(APIC_ICR2, SET_XAPIC_DEST_FIELD(id)); apic_write(APIC_ICR, low); local_irq_restore(flags); } diff --git a/arch/x86/kernel/apic/ipi.c b/arch/x86/kernel/apic/ipi.c index d1fb874fbe64..2a6509e8c840 100644 --- a/arch/x86/kernel/apic/ipi.c +++ b/arch/x86/kernel/apic/ipi.c @@ -99,7 +99,7 @@ sendmask: static inline int __prepare_ICR2(unsigned int mask) { - return SET_APIC_DEST_FIELD(mask); + return SET_XAPIC_DEST_FIELD(mask); } static inline void __xapic_wait_icr_idle(void) diff --git a/arch/x86/kernel/cpu/feat_ctl.c b/arch/x86/kernel/cpu/feat_ctl.c index da696eb4821a..993697e71854 100644 --- a/arch/x86/kernel/cpu/feat_ctl.c +++ b/arch/x86/kernel/cpu/feat_ctl.c @@ -15,6 +15,8 @@ enum vmx_feature_leafs { MISC_FEATURES = 0, PRIMARY_CTLS, SECONDARY_CTLS, + TERTIARY_CTLS_LOW, + TERTIARY_CTLS_HIGH, NR_VMX_FEATURE_WORDS, }; @@ -22,7 +24,7 @@ enum vmx_feature_leafs { static void init_vmx_capabilities(struct cpuinfo_x86 *c) { - u32 supported, funcs, ept, vpid, ign; + u32 supported, funcs, ept, vpid, ign, low, high; BUILD_BUG_ON(NVMXINTS != NR_VMX_FEATURE_WORDS); @@ -42,6 +44,11 @@ static void init_vmx_capabilities(struct cpuinfo_x86 *c) rdmsr_safe(MSR_IA32_VMX_PROCBASED_CTLS2, &ign, &supported); c->vmx_capability[SECONDARY_CTLS] = supported; + /* All 64 bits of tertiary controls MSR are allowed-1 settings. */ + rdmsr_safe(MSR_IA32_VMX_PROCBASED_CTLS3, &low, &high); + c->vmx_capability[TERTIARY_CTLS_LOW] = low; + c->vmx_capability[TERTIARY_CTLS_HIGH] = high; + rdmsr(MSR_IA32_VMX_PINBASED_CTLS, ign, supported); rdmsr_safe(MSR_IA32_VMX_VMFUNC, &ign, &funcs); diff --git a/arch/x86/kernel/kvm.c b/arch/x86/kernel/kvm.c index 1a3658f7e6d9..d4e48b4a438b 100644 --- a/arch/x86/kernel/kvm.c +++ b/arch/x86/kernel/kvm.c @@ -236,8 +236,7 @@ again: raw_spin_unlock(&b->lock); /* A dummy token might be allocated and ultimately not used. */ - if (dummy) - kfree(dummy); + kfree(dummy); } EXPORT_SYMBOL_GPL(kvm_async_pf_task_wake); diff --git a/arch/x86/kvm/cpuid.c b/arch/x86/kvm/cpuid.c index de6d44e07e34..75dcf7a72605 100644 --- a/arch/x86/kvm/cpuid.c +++ b/arch/x86/kvm/cpuid.c @@ -67,9 +67,17 @@ u32 xstate_required_size(u64 xstate_bv, bool compacted) #define F feature_bit #define SF(name) (boot_cpu_has(X86_FEATURE_##name) ? F(name) : 0) +/* + * Magic value used by KVM when querying userspace-provided CPUID entries and + * doesn't care about the CPIUD index because the index of the function in + * question is not significant. Note, this magic value must have at least one + * bit set in bits[63:32] and must be consumed as a u64 by cpuid_entry2_find() + * to avoid false positives when processing guest CPUID input. + */ +#define KVM_CPUID_INDEX_NOT_SIGNIFICANT -1ull static inline struct kvm_cpuid_entry2 *cpuid_entry2_find( - struct kvm_cpuid_entry2 *entries, int nent, u32 function, u32 index) + struct kvm_cpuid_entry2 *entries, int nent, u32 function, u64 index) { struct kvm_cpuid_entry2 *e; int i; @@ -77,9 +85,31 @@ static inline struct kvm_cpuid_entry2 *cpuid_entry2_find( for (i = 0; i < nent; i++) { e = &entries[i]; - if (e->function == function && - (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) || e->index == index)) + if (e->function != function) + continue; + + /* + * If the index isn't significant, use the first entry with a + * matching function. It's userspace's responsibilty to not + * provide "duplicate" entries in all cases. + */ + if (!(e->flags & KVM_CPUID_FLAG_SIGNIFCANT_INDEX) || e->index == index) return e; + + + /* + * Similarly, use the first matching entry if KVM is doing a + * lookup (as opposed to emulating CPUID) for a function that's + * architecturally defined as not having a significant index. + */ + if (index == KVM_CPUID_INDEX_NOT_SIGNIFICANT) { + /* + * Direct lookups from KVM should not diverge from what + * KVM defines internally (the architectural behavior). + */ + WARN_ON_ONCE(cpuid_function_is_indexed(function)); + return e; + } } return NULL; @@ -96,7 +126,8 @@ static int kvm_check_cpuid(struct kvm_vcpu *vcpu, * The existing code assumes virtual address is 48-bit or 57-bit in the * canonical address checks; exit if it is ever changed. */ - best = cpuid_entry2_find(entries, nent, 0x80000008, 0); + best = cpuid_entry2_find(entries, nent, 0x80000008, + KVM_CPUID_INDEX_NOT_SIGNIFICANT); if (best) { int vaddr_bits = (best->eax & 0xff00) >> 8; @@ -151,7 +182,7 @@ static void kvm_update_kvm_cpuid_base(struct kvm_vcpu *vcpu) vcpu->arch.kvm_cpuid_base = 0; for_each_possible_hypervisor_cpuid_base(function) { - entry = kvm_find_cpuid_entry(vcpu, function, 0); + entry = kvm_find_cpuid_entry(vcpu, function); if (entry) { u32 signature[3]; @@ -177,7 +208,8 @@ static struct kvm_cpuid_entry2 *__kvm_find_kvm_cpuid_features(struct kvm_vcpu *v if (!base) return NULL; - return cpuid_entry2_find(entries, nent, base | KVM_CPUID_FEATURES, 0); + return cpuid_entry2_find(entries, nent, base | KVM_CPUID_FEATURES, + KVM_CPUID_INDEX_NOT_SIGNIFICANT); } static struct kvm_cpuid_entry2 *kvm_find_kvm_cpuid_features(struct kvm_vcpu *vcpu) @@ -200,7 +232,7 @@ void kvm_update_pv_runtime(struct kvm_vcpu *vcpu) /* * Calculate guest's supported XCR0 taking into account guest CPUID data and - * supported_xcr0 (comprised of host configuration and KVM_SUPPORTED_XCR0). + * KVM's supported XCR0 (comprised of host's XCR0 and KVM_SUPPORTED_XCR0). */ static u64 cpuid_get_supported_xcr0(struct kvm_cpuid_entry2 *entries, int nent) { @@ -210,7 +242,7 @@ static u64 cpuid_get_supported_xcr0(struct kvm_cpuid_entry2 *entries, int nent) if (!best) return 0; - return (best->eax | ((u64)best->edx << 32)) & supported_xcr0; + return (best->eax | ((u64)best->edx << 32)) & kvm_caps.supported_xcr0; } static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_entry2 *entries, @@ -219,7 +251,7 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e struct kvm_cpuid_entry2 *best; u64 guest_supported_xcr0 = cpuid_get_supported_xcr0(entries, nent); - best = cpuid_entry2_find(entries, nent, 1, 0); + best = cpuid_entry2_find(entries, nent, 1, KVM_CPUID_INDEX_NOT_SIGNIFICANT); if (best) { /* Update OSXSAVE bit */ if (boot_cpu_has(X86_FEATURE_XSAVE)) @@ -250,7 +282,7 @@ static void __kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu, struct kvm_cpuid_e best->eax &= ~(1 << KVM_FEATURE_PV_UNHALT); if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT)) { - best = cpuid_entry2_find(entries, nent, 0x1, 0); + best = cpuid_entry2_find(entries, nent, 0x1, KVM_CPUID_INDEX_NOT_SIGNIFICANT); if (best) cpuid_entry_change(best, X86_FEATURE_MWAIT, vcpu->arch.ia32_misc_enable_msr & @@ -285,7 +317,7 @@ static void kvm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) struct kvm_cpuid_entry2 *best; u64 guest_supported_xcr0; - best = kvm_find_cpuid_entry(vcpu, 1, 0); + best = kvm_find_cpuid_entry(vcpu, 1); if (best && apic) { if (cpuid_entry_has(best, X86_FEATURE_TSC_DEADLINE_TIMER)) apic->lapic_timer.timer_mode_mask = 3 << 17; @@ -325,10 +357,10 @@ int cpuid_query_maxphyaddr(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; - best = kvm_find_cpuid_entry(vcpu, 0x80000000, 0); + best = kvm_find_cpuid_entry(vcpu, 0x80000000); if (!best || best->eax < 0x80000008) goto not_found; - best = kvm_find_cpuid_entry(vcpu, 0x80000008, 0); + best = kvm_find_cpuid_entry(vcpu, 0x80000008); if (best) return best->eax & 0xff; not_found: @@ -868,7 +900,6 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) case 9: break; case 0xa: { /* Architectural Performance Monitoring */ - struct x86_pmu_capability cap; union cpuid10_eax eax; union cpuid10_edx edx; @@ -877,30 +908,20 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) break; } - perf_get_x86_pmu_capability(&cap); + eax.split.version_id = kvm_pmu_cap.version; + eax.split.num_counters = kvm_pmu_cap.num_counters_gp; + eax.split.bit_width = kvm_pmu_cap.bit_width_gp; + eax.split.mask_length = kvm_pmu_cap.events_mask_len; + edx.split.num_counters_fixed = kvm_pmu_cap.num_counters_fixed; + edx.split.bit_width_fixed = kvm_pmu_cap.bit_width_fixed; - /* - * The guest architecture pmu is only supported if the architecture - * pmu exists on the host and the module parameters allow it. - */ - if (!cap.version || !enable_pmu) - memset(&cap, 0, sizeof(cap)); - - eax.split.version_id = min(cap.version, 2); - eax.split.num_counters = cap.num_counters_gp; - eax.split.bit_width = cap.bit_width_gp; - eax.split.mask_length = cap.events_mask_len; - - edx.split.num_counters_fixed = - min(cap.num_counters_fixed, KVM_PMC_MAX_FIXED); - edx.split.bit_width_fixed = cap.bit_width_fixed; - if (cap.version) + if (kvm_pmu_cap.version) edx.split.anythread_deprecated = 1; edx.split.reserved1 = 0; edx.split.reserved2 = 0; entry->eax = eax.full; - entry->ebx = cap.events_mask; + entry->ebx = kvm_pmu_cap.events_mask; entry->ecx = 0; entry->edx = edx.full; break; @@ -923,8 +944,8 @@ static inline int __do_cpuid_func(struct kvm_cpuid_array *array, u32 function) } break; case 0xd: { - u64 permitted_xcr0 = supported_xcr0 & xstate_get_guest_group_perm(); - u64 permitted_xss = supported_xss; + u64 permitted_xcr0 = kvm_caps.supported_xcr0 & xstate_get_guest_group_perm(); + u64 permitted_xss = kvm_caps.supported_xss; entry->eax &= permitted_xcr0; entry->ebx = xstate_required_size(permitted_xcr0, false); @@ -1313,12 +1334,20 @@ out_free: return r; } -struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu, - u32 function, u32 index) +struct kvm_cpuid_entry2 *kvm_find_cpuid_entry_index(struct kvm_vcpu *vcpu, + u32 function, u32 index) { return cpuid_entry2_find(vcpu->arch.cpuid_entries, vcpu->arch.cpuid_nent, function, index); } +EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry_index); + +struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu, + u32 function) +{ + return cpuid_entry2_find(vcpu->arch.cpuid_entries, vcpu->arch.cpuid_nent, + function, KVM_CPUID_INDEX_NOT_SIGNIFICANT); +} EXPORT_SYMBOL_GPL(kvm_find_cpuid_entry); /* @@ -1355,7 +1384,7 @@ get_out_of_range_cpuid_entry(struct kvm_vcpu *vcpu, u32 *fn_ptr, u32 index) struct kvm_cpuid_entry2 *basic, *class; u32 function = *fn_ptr; - basic = kvm_find_cpuid_entry(vcpu, 0, 0); + basic = kvm_find_cpuid_entry(vcpu, 0); if (!basic) return NULL; @@ -1364,11 +1393,11 @@ get_out_of_range_cpuid_entry(struct kvm_vcpu *vcpu, u32 *fn_ptr, u32 index) return NULL; if (function >= 0x40000000 && function <= 0x4fffffff) - class = kvm_find_cpuid_entry(vcpu, function & 0xffffff00, 0); + class = kvm_find_cpuid_entry(vcpu, function & 0xffffff00); else if (function >= 0xc0000000) - class = kvm_find_cpuid_entry(vcpu, 0xc0000000, 0); + class = kvm_find_cpuid_entry(vcpu, 0xc0000000); else - class = kvm_find_cpuid_entry(vcpu, function & 0x80000000, 0); + class = kvm_find_cpuid_entry(vcpu, function & 0x80000000); if (class && function <= class->eax) return NULL; @@ -1386,7 +1415,7 @@ get_out_of_range_cpuid_entry(struct kvm_vcpu *vcpu, u32 *fn_ptr, u32 index) * the effective CPUID entry is the max basic leaf. Note, the index of * the original requested leaf is observed! */ - return kvm_find_cpuid_entry(vcpu, basic->eax, index); + return kvm_find_cpuid_entry_index(vcpu, basic->eax, index); } bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, @@ -1396,7 +1425,7 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, struct kvm_cpuid_entry2 *entry; bool exact, used_max_basic = false; - entry = kvm_find_cpuid_entry(vcpu, function, index); + entry = kvm_find_cpuid_entry_index(vcpu, function, index); exact = !!entry; if (!entry && !exact_only) { @@ -1425,7 +1454,7 @@ bool kvm_cpuid(struct kvm_vcpu *vcpu, u32 *eax, u32 *ebx, * exists. EDX can be copied from any existing index. */ if (function == 0xb || function == 0x1f) { - entry = kvm_find_cpuid_entry(vcpu, function, 1); + entry = kvm_find_cpuid_entry_index(vcpu, function, 1); if (entry) { *ecx = index & 0xff; *edx = entry->edx; diff --git a/arch/x86/kvm/cpuid.h b/arch/x86/kvm/cpuid.h index 8a770b481d9d..b1658c0de847 100644 --- a/arch/x86/kvm/cpuid.h +++ b/arch/x86/kvm/cpuid.h @@ -13,8 +13,10 @@ void kvm_set_cpu_caps(void); void kvm_update_cpuid_runtime(struct kvm_vcpu *vcpu); void kvm_update_pv_runtime(struct kvm_vcpu *vcpu); +struct kvm_cpuid_entry2 *kvm_find_cpuid_entry_index(struct kvm_vcpu *vcpu, + u32 function, u32 index); struct kvm_cpuid_entry2 *kvm_find_cpuid_entry(struct kvm_vcpu *vcpu, - u32 function, u32 index); + u32 function); int kvm_dev_ioctl_get_cpuid(struct kvm_cpuid2 *cpuid, struct kvm_cpuid_entry2 __user *entries, unsigned int type); @@ -76,7 +78,7 @@ static __always_inline u32 *guest_cpuid_get_register(struct kvm_vcpu *vcpu, const struct cpuid_reg cpuid = x86_feature_cpuid(x86_feature); struct kvm_cpuid_entry2 *entry; - entry = kvm_find_cpuid_entry(vcpu, cpuid.function, cpuid.index); + entry = kvm_find_cpuid_entry_index(vcpu, cpuid.function, cpuid.index); if (!entry) return NULL; @@ -109,7 +111,7 @@ static inline bool guest_cpuid_is_amd_or_hygon(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; - best = kvm_find_cpuid_entry(vcpu, 0, 0); + best = kvm_find_cpuid_entry(vcpu, 0); return best && (is_guest_vendor_amd(best->ebx, best->ecx, best->edx) || is_guest_vendor_hygon(best->ebx, best->ecx, best->edx)); @@ -119,7 +121,7 @@ static inline bool guest_cpuid_is_intel(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; - best = kvm_find_cpuid_entry(vcpu, 0, 0); + best = kvm_find_cpuid_entry(vcpu, 0); return best && is_guest_vendor_intel(best->ebx, best->ecx, best->edx); } @@ -127,7 +129,7 @@ static inline int guest_cpuid_family(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; - best = kvm_find_cpuid_entry(vcpu, 0x1, 0); + best = kvm_find_cpuid_entry(vcpu, 0x1); if (!best) return -1; @@ -138,18 +140,23 @@ static inline int guest_cpuid_model(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; - best = kvm_find_cpuid_entry(vcpu, 0x1, 0); + best = kvm_find_cpuid_entry(vcpu, 0x1); if (!best) return -1; return x86_model(best->eax); } +static inline bool cpuid_model_is_consistent(struct kvm_vcpu *vcpu) +{ + return boot_cpu_data.x86_model == guest_cpuid_model(vcpu); +} + static inline int guest_cpuid_stepping(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *best; - best = kvm_find_cpuid_entry(vcpu, 0x1, 0); + best = kvm_find_cpuid_entry(vcpu, 0x1); if (!best) return -1; diff --git a/arch/x86/kvm/debugfs.c b/arch/x86/kvm/debugfs.c index 9240b3b7f8dd..cfed36aba2f7 100644 --- a/arch/x86/kvm/debugfs.c +++ b/arch/x86/kvm/debugfs.c @@ -48,7 +48,7 @@ DEFINE_SIMPLE_ATTRIBUTE(vcpu_tsc_scaling_fops, vcpu_get_tsc_scaling_ratio, NULL, static int vcpu_get_tsc_scaling_frac_bits(void *data, u64 *val) { - *val = kvm_tsc_scaling_ratio_frac_bits; + *val = kvm_caps.tsc_scaling_ratio_frac_bits; return 0; } @@ -66,7 +66,7 @@ void kvm_arch_create_vcpu_debugfs(struct kvm_vcpu *vcpu, struct dentry *debugfs_ debugfs_dentry, vcpu, &vcpu_timer_advance_ns_fops); - if (kvm_has_tsc_control) { + if (kvm_caps.has_tsc_control) { debugfs_create_file("tsc-scaling-ratio", 0444, debugfs_dentry, vcpu, &vcpu_tsc_scaling_fops); diff --git a/arch/x86/kvm/emulate.c b/arch/x86/kvm/emulate.c index f8382abe22ff..047c583596bb 100644 --- a/arch/x86/kvm/emulate.c +++ b/arch/x86/kvm/emulate.c @@ -244,6 +244,9 @@ enum x86_transfer_type { static ulong reg_read(struct x86_emulate_ctxt *ctxt, unsigned nr) { + if (KVM_EMULATOR_BUG_ON(nr >= NR_EMULATOR_GPRS, ctxt)) + nr &= NR_EMULATOR_GPRS - 1; + if (!(ctxt->regs_valid & (1 << nr))) { ctxt->regs_valid |= 1 << nr; ctxt->_regs[nr] = ctxt->ops->read_gpr(ctxt, nr); @@ -253,6 +256,12 @@ static ulong reg_read(struct x86_emulate_ctxt *ctxt, unsigned nr) static ulong *reg_write(struct x86_emulate_ctxt *ctxt, unsigned nr) { + if (KVM_EMULATOR_BUG_ON(nr >= NR_EMULATOR_GPRS, ctxt)) + nr &= NR_EMULATOR_GPRS - 1; + + BUILD_BUG_ON(sizeof(ctxt->regs_dirty) * BITS_PER_BYTE < NR_EMULATOR_GPRS); + BUILD_BUG_ON(sizeof(ctxt->regs_valid) * BITS_PER_BYTE < NR_EMULATOR_GPRS); + ctxt->regs_valid |= 1 << nr; ctxt->regs_dirty |= 1 << nr; return &ctxt->_regs[nr]; @@ -266,9 +275,10 @@ static ulong *reg_rmw(struct x86_emulate_ctxt *ctxt, unsigned nr) static void writeback_registers(struct x86_emulate_ctxt *ctxt) { + unsigned long dirty = ctxt->regs_dirty; unsigned reg; - for_each_set_bit(reg, (ulong *)&ctxt->regs_dirty, 16) + for_each_set_bit(reg, &dirty, NR_EMULATOR_GPRS) ctxt->ops->write_gpr(ctxt, reg, ctxt->_regs[reg]); } @@ -615,7 +625,9 @@ static unsigned long seg_base(struct x86_emulate_ctxt *ctxt, int seg) static int emulate_exception(struct x86_emulate_ctxt *ctxt, int vec, u32 error, bool valid) { - WARN_ON(vec > 0x1f); + if (KVM_EMULATOR_BUG_ON(vec > 0x1f, ctxt)) + return X86EMUL_UNHANDLEABLE; + ctxt->exception.vector = vec; ctxt->exception.error_code = error; ctxt->exception.error_code_valid = valid; @@ -1362,7 +1374,8 @@ static int read_emulated(struct x86_emulate_ctxt *ctxt, if (mc->pos < mc->end) goto read_cached; - WARN_ON((mc->end + size) >= sizeof(mc->data)); + if (KVM_EMULATOR_BUG_ON((mc->end + size) >= sizeof(mc->data), ctxt)) + return X86EMUL_UNHANDLEABLE; rc = ctxt->ops->read_emulated(ctxt, addr, mc->data + mc->end, size, &ctxt->exception); @@ -1687,16 +1700,6 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, case VCPU_SREG_TR: if (seg_desc.s || (seg_desc.type != 1 && seg_desc.type != 9)) goto exception; - if (!seg_desc.p) { - err_vec = NP_VECTOR; - goto exception; - } - old_desc = seg_desc; - seg_desc.type |= 2; /* busy */ - ret = ctxt->ops->cmpxchg_emulated(ctxt, desc_addr, &old_desc, &seg_desc, - sizeof(seg_desc), &ctxt->exception); - if (ret != X86EMUL_CONTINUE) - return ret; break; case VCPU_SREG_LDTR: if (seg_desc.s || seg_desc.type != 2) @@ -1734,8 +1737,17 @@ static int __load_segment_descriptor(struct x86_emulate_ctxt *ctxt, if (ret != X86EMUL_CONTINUE) return ret; if (emul_is_noncanonical_address(get_desc_base(&seg_desc) | - ((u64)base3 << 32), ctxt)) - return emulate_gp(ctxt, 0); + ((u64)base3 << 32), ctxt)) + return emulate_gp(ctxt, err_code); + } + + if (seg == VCPU_SREG_TR) { + old_desc = seg_desc; + seg_desc.type |= 2; /* busy */ + ret = ctxt->ops->cmpxchg_emulated(ctxt, desc_addr, &old_desc, &seg_desc, + sizeof(seg_desc), &ctxt->exception); + if (ret != X86EMUL_CONTINUE) + return ret; } load: ctxt->ops->set_segment(ctxt, selector, &seg_desc, base3, seg); @@ -2432,7 +2444,7 @@ static int rsm_load_state_32(struct x86_emulate_ctxt *ctxt, ctxt->eflags = GET_SMSTATE(u32, smstate, 0x7ff4) | X86_EFLAGS_FIXED; ctxt->_eip = GET_SMSTATE(u32, smstate, 0x7ff0); - for (i = 0; i < 8; i++) + for (i = 0; i < NR_EMULATOR_GPRS; i++) *reg_write(ctxt, i) = GET_SMSTATE(u32, smstate, 0x7fd0 + i * 4); val = GET_SMSTATE(u32, smstate, 0x7fcc); @@ -2489,7 +2501,7 @@ static int rsm_load_state_64(struct x86_emulate_ctxt *ctxt, u16 selector; int i, r; - for (i = 0; i < 16; i++) + for (i = 0; i < NR_EMULATOR_GPRS; i++) *reg_write(ctxt, i) = GET_SMSTATE(u64, smstate, 0x7ff8 - i * 8); ctxt->_eip = GET_SMSTATE(u64, smstate, 0x7f78); @@ -5719,7 +5731,8 @@ writeback: done: if (rc == X86EMUL_PROPAGATE_FAULT) { - WARN_ON(ctxt->exception.vector > 0x1f); + if (KVM_EMULATOR_BUG_ON(ctxt->exception.vector > 0x1f, ctxt)) + return EMULATION_FAILED; ctxt->have_exception = true; } if (rc == X86EMUL_INTERCEPTED) diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c index e2e95a6fccfd..ed804447589c 100644 --- a/arch/x86/kvm/hyperv.c +++ b/arch/x86/kvm/hyperv.c @@ -1992,7 +1992,7 @@ void kvm_hv_set_cpuid(struct kvm_vcpu *vcpu) struct kvm_cpuid_entry2 *entry; struct kvm_vcpu_hv *hv_vcpu; - entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_INTERFACE, 0); + entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_INTERFACE); if (entry && entry->eax == HYPERV_CPUID_SIGNATURE_EAX) { vcpu->arch.hyperv_enabled = true; } else { @@ -2005,7 +2005,7 @@ void kvm_hv_set_cpuid(struct kvm_vcpu *vcpu) hv_vcpu = to_hv_vcpu(vcpu); - entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES, 0); + entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES); if (entry) { hv_vcpu->cpuid_cache.features_eax = entry->eax; hv_vcpu->cpuid_cache.features_ebx = entry->ebx; @@ -2016,7 +2016,7 @@ void kvm_hv_set_cpuid(struct kvm_vcpu *vcpu) hv_vcpu->cpuid_cache.features_edx = 0; } - entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO, 0); + entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO); if (entry) { hv_vcpu->cpuid_cache.enlightenments_eax = entry->eax; hv_vcpu->cpuid_cache.enlightenments_ebx = entry->ebx; @@ -2025,7 +2025,7 @@ void kvm_hv_set_cpuid(struct kvm_vcpu *vcpu) hv_vcpu->cpuid_cache.enlightenments_ebx = 0; } - entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES, 0); + entry = kvm_find_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES); if (entry) hv_vcpu->cpuid_cache.syndbg_cap_eax = entry->eax; else diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 1c83076091af..e0a7a0e7a73c 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -591,7 +591,10 @@ static int speaker_ioport_write(struct kvm_vcpu *vcpu, return -EOPNOTSUPP; mutex_lock(&pit_state->lock); - pit_state->speaker_data_on = (val >> 1) & 1; + if (val & (1 << 1)) + pit_state->flags |= KVM_PIT_FLAGS_SPEAKER_DATA_ON; + else + pit_state->flags &= ~KVM_PIT_FLAGS_SPEAKER_DATA_ON; pit_set_gate(pit, 2, val & 1); mutex_unlock(&pit_state->lock); return 0; @@ -612,8 +615,9 @@ static int speaker_ioport_read(struct kvm_vcpu *vcpu, refresh_clock = ((unsigned int)ktime_to_ns(ktime_get()) >> 14) & 1; mutex_lock(&pit_state->lock); - ret = ((pit_state->speaker_data_on << 1) | pit_get_gate(pit, 2) | - (pit_get_out(pit, 2) << 5) | (refresh_clock << 4)); + ret = (!!(pit_state->flags & KVM_PIT_FLAGS_SPEAKER_DATA_ON) << 1) | + pit_get_gate(pit, 2) | (pit_get_out(pit, 2) << 5) | + (refresh_clock << 4); if (len > sizeof(ret)) len = sizeof(ret); memcpy(data, (char *)&ret, len); diff --git a/arch/x86/kvm/i8254.h b/arch/x86/kvm/i8254.h index 394d9527da7e..a768212ba821 100644 --- a/arch/x86/kvm/i8254.h +++ b/arch/x86/kvm/i8254.h @@ -29,7 +29,6 @@ struct kvm_kpit_state { bool is_periodic; s64 period; /* unit: ns */ struct hrtimer timer; - u32 speaker_data_on; struct mutex lock; atomic_t reinject; diff --git a/arch/x86/kvm/kvm_emulate.h b/arch/x86/kvm/kvm_emulate.h index 8dff25d267b7..89246446d6aa 100644 --- a/arch/x86/kvm/kvm_emulate.h +++ b/arch/x86/kvm/kvm_emulate.h @@ -89,6 +89,7 @@ struct x86_instruction_info { #define X86EMUL_INTERCEPTED 6 /* Intercepted by nested VMCB/VMCS */ struct x86_emulate_ops { + void (*vm_bugged)(struct x86_emulate_ctxt *ctxt); /* * read_gpr: read a general purpose register (rax - r15) * @@ -301,6 +302,18 @@ struct fastop; typedef void (*fastop_t)(struct fastop *); +/* + * The emulator's _regs array tracks only the GPRs, i.e. excludes RIP. RIP is + * tracked/accessed via _eip, and except for RIP relative addressing, which + * also uses _eip, RIP cannot be a register operand nor can it be an operand in + * a ModRM or SIB byte. + */ +#ifdef CONFIG_X86_64 +#define NR_EMULATOR_GPRS 16 +#else +#define NR_EMULATOR_GPRS 8 +#endif + struct x86_emulate_ctxt { void *vcpu; const struct x86_emulate_ops *ops; @@ -345,9 +358,9 @@ struct x86_emulate_ctxt { u8 lock_prefix; u8 rep_prefix; /* bitmaps of registers in _regs[] that can be read */ - u32 regs_valid; + u16 regs_valid; /* bitmaps of registers in _regs[] that have been written */ - u32 regs_dirty; + u16 regs_dirty; /* modrm */ u8 modrm; u8 modrm_mod; @@ -363,7 +376,7 @@ struct x86_emulate_ctxt { struct operand src2; struct operand dst; struct operand memop; - unsigned long _regs[NR_VCPU_REGS]; + unsigned long _regs[NR_EMULATOR_GPRS]; struct operand *memopp; struct fetch_cache fetch; struct read_cache io_read; @@ -371,6 +384,15 @@ struct x86_emulate_ctxt { bool is_branch; }; +#define KVM_EMULATOR_BUG_ON(cond, ctxt) \ +({ \ + int __ret = (cond); \ + \ + if (WARN_ON_ONCE(__ret)) \ + ctxt->ops->vm_bugged(ctxt); \ + unlikely(__ret); \ +}) + /* Repeat String Operation Prefix */ #define REPE_PREFIX 0xf3 #define REPNE_PREFIX 0xf2 diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 0e68b4c937fc..e2ce3556915e 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -54,7 +55,7 @@ #define PRIo64 "o" /* 14 is the version for Xeon and Pentium 8.4.8*/ -#define APIC_VERSION (0x14UL | ((KVM_APIC_LVT_NUM - 1) << 16)) +#define APIC_VERSION 0x14UL #define LAPIC_MMIO_LENGTH (1 << 12) /* followed define is not in apicdef.h */ #define MAX_APIC_VECTOR 256 @@ -67,6 +68,8 @@ static bool lapic_timer_advance_dynamic __read_mostly; #define LAPIC_TIMER_ADVANCE_NS_MAX 5000 /* step-by-step approximation to mitigate fluctuation */ #define LAPIC_TIMER_ADVANCE_ADJUST_STEP 8 +static int kvm_lapic_msr_read(struct kvm_lapic *apic, u32 reg, u64 *data); +static int kvm_lapic_msr_write(struct kvm_lapic *apic, u32 reg, u64 data); static inline void __kvm_lapic_set_reg(char *regs, int reg_off, u32 val) { @@ -398,14 +401,26 @@ static inline int apic_lvt_nmi_mode(u32 lvt_val) return (lvt_val & (APIC_MODE_MASK | APIC_LVT_MASKED)) == APIC_DM_NMI; } +static inline bool kvm_lapic_lvt_supported(struct kvm_lapic *apic, int lvt_index) +{ + return apic->nr_lvt_entries > lvt_index; +} + +static inline int kvm_apic_calc_nr_lvt_entries(struct kvm_vcpu *vcpu) +{ + return KVM_APIC_MAX_NR_LVT_ENTRIES - !(vcpu->arch.mcg_cap & MCG_CMCI_P); +} + void kvm_apic_set_version(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic = vcpu->arch.apic; - u32 v = APIC_VERSION; + u32 v = 0; if (!lapic_in_kernel(vcpu)) return; + v = APIC_VERSION | ((apic->nr_lvt_entries - 1) << 16); + /* * KVM emulates 82093AA datasheet (with in-kernel IOAPIC implementation) * which doesn't have EOI register; Some buggy OSes (e.g. Windows with @@ -419,12 +434,33 @@ void kvm_apic_set_version(struct kvm_vcpu *vcpu) kvm_lapic_set_reg(apic, APIC_LVR, v); } -static const unsigned int apic_lvt_mask[KVM_APIC_LVT_NUM] = { - LVT_MASK , /* part LVTT mask, timer mode mask added at runtime */ - LVT_MASK | APIC_MODE_MASK, /* LVTTHMR */ - LVT_MASK | APIC_MODE_MASK, /* LVTPC */ - LINT_MASK, LINT_MASK, /* LVT0-1 */ - LVT_MASK /* LVTERR */ +void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu) +{ + int nr_lvt_entries = kvm_apic_calc_nr_lvt_entries(vcpu); + struct kvm_lapic *apic = vcpu->arch.apic; + int i; + + if (!lapic_in_kernel(vcpu) || nr_lvt_entries == apic->nr_lvt_entries) + return; + + /* Initialize/mask any "new" LVT entries. */ + for (i = apic->nr_lvt_entries; i < nr_lvt_entries; i++) + kvm_lapic_set_reg(apic, APIC_LVTx(i), APIC_LVT_MASKED); + + apic->nr_lvt_entries = nr_lvt_entries; + + /* The number of LVT entries is reflected in the version register. */ + kvm_apic_set_version(vcpu); +} + +static const unsigned int apic_lvt_mask[KVM_APIC_MAX_NR_LVT_ENTRIES] = { + [LVT_TIMER] = LVT_MASK, /* timer mode mask added at runtime */ + [LVT_THERMAL_MONITOR] = LVT_MASK | APIC_MODE_MASK, + [LVT_PERFORMANCE_COUNTER] = LVT_MASK | APIC_MODE_MASK, + [LVT_LINT0] = LINT_MASK, + [LVT_LINT1] = LINT_MASK, + [LVT_ERROR] = LVT_MASK, + [LVT_CMCI] = LVT_MASK | APIC_MODE_MASK }; static int find_highest_vector(void *bitmap) @@ -518,14 +554,11 @@ static inline int apic_find_highest_irr(struct kvm_lapic *apic) static inline void apic_clear_irr(int vec, struct kvm_lapic *apic) { - struct kvm_vcpu *vcpu; - - vcpu = apic->vcpu; - - if (unlikely(vcpu->arch.apicv_active)) { + if (unlikely(apic->apicv_active)) { /* need to update RVI */ kvm_lapic_clear_vector(vec, apic->regs + APIC_IRR); - static_call_cond(kvm_x86_hwapic_irr_update)(vcpu, apic_find_highest_irr(apic)); + static_call_cond(kvm_x86_hwapic_irr_update)(apic->vcpu, + apic_find_highest_irr(apic)); } else { apic->irr_pending = false; kvm_lapic_clear_vector(vec, apic->regs + APIC_IRR); @@ -542,20 +575,16 @@ EXPORT_SYMBOL_GPL(kvm_apic_clear_irr); static inline void apic_set_isr(int vec, struct kvm_lapic *apic) { - struct kvm_vcpu *vcpu; - if (__apic_test_and_set_vector(vec, apic->regs + APIC_ISR)) return; - vcpu = apic->vcpu; - /* * With APIC virtualization enabled, all caching is disabled * because the processor can modify ISR under the hood. Instead * just set SVI. */ - if (unlikely(vcpu->arch.apicv_active)) - static_call_cond(kvm_x86_hwapic_isr_update)(vcpu, vec); + if (unlikely(apic->apicv_active)) + static_call_cond(kvm_x86_hwapic_isr_update)(vec); else { ++apic->isr_count; BUG_ON(apic->isr_count > MAX_APIC_VECTOR); @@ -589,12 +618,9 @@ static inline int apic_find_highest_isr(struct kvm_lapic *apic) static inline void apic_clear_isr(int vec, struct kvm_lapic *apic) { - struct kvm_vcpu *vcpu; if (!__apic_test_and_clear_vector(vec, apic->regs + APIC_ISR)) return; - vcpu = apic->vcpu; - /* * We do get here for APIC virtualization enabled if the guest * uses the Hyper-V APIC enlightenment. In this case we may need @@ -602,8 +628,8 @@ static inline void apic_clear_isr(int vec, struct kvm_lapic *apic) * on the other hand isr_count and highest_isr_cache are unused * and must be left alone. */ - if (unlikely(vcpu->arch.apicv_active)) - static_call_cond(kvm_x86_hwapic_isr_update)(vcpu, apic_find_highest_isr(apic)); + if (unlikely(apic->apicv_active)) + static_call_cond(kvm_x86_hwapic_isr_update)(apic_find_highest_isr(apic)); else { --apic->isr_count; BUG_ON(apic->isr_count < 0); @@ -801,17 +827,17 @@ static bool kvm_apic_match_physical_addr(struct kvm_lapic *apic, u32 mda) if (kvm_apic_broadcast(apic, mda)) return true; - if (apic_x2apic_mode(apic)) - return mda == kvm_x2apic_id(apic); - /* - * Hotplug hack: Make LAPIC in xAPIC mode also accept interrupts as if - * it were in x2APIC mode. Hotplugged VCPUs start in xAPIC mode and - * this allows unique addressing of VCPUs with APIC ID over 0xff. - * The 0xff condition is needed because writeable xAPIC ID. + * Hotplug hack: Accept interrupts for vCPUs in xAPIC mode as if they + * were in x2APIC mode if the target APIC ID can't be encoded as an + * xAPIC ID. This allows unique addressing of hotplugged vCPUs (which + * start in xAPIC mode) with an APIC ID that is unaddressable in xAPIC + * mode. Match the x2APIC ID if and only if the target APIC ID can't + * be encoded in xAPIC to avoid spurious matches against a vCPU that + * changed its (addressable) xAPIC ID (which is writable). */ - if (kvm_x2apic_id(apic) > 0xff && mda == kvm_x2apic_id(apic)) - return true; + if (apic_x2apic_mode(apic) || mda > 0xff) + return mda == kvm_x2apic_id(apic); return mda == kvm_xapic_id(apic); } @@ -1325,7 +1351,7 @@ void kvm_apic_send_ipi(struct kvm_lapic *apic, u32 icr_low, u32 icr_high) if (apic_x2apic_mode(apic)) irq.dest_id = icr_high; else - irq.dest_id = GET_APIC_DEST_FIELD(icr_high); + irq.dest_id = GET_XAPIC_DEST_FIELD(icr_high); trace_kvm_apic_ipi(icr_low, irq.dest_id); @@ -1444,6 +1470,9 @@ static int kvm_lapic_reg_read(struct kvm_lapic *apic, u32 offset, int len, APIC_REG_MASK(APIC_TMCCT) | APIC_REG_MASK(APIC_TDCR); + if (kvm_lapic_lvt_supported(apic, LVT_CMCI)) + valid_reg_mask |= APIC_REG_MASK(APIC_LVTCMCI); + /* * ARBPRI and ICR2 are not valid in x2APIC mode. WARN if KVM reads ICR * in x2APIC mode as it's an 8-byte register in x2APIC and needs to be @@ -1583,7 +1612,7 @@ static bool lapic_timer_int_injected(struct kvm_vcpu *vcpu) int vec = reg & APIC_VECTOR_MASK; void *bitmap = apic->regs + APIC_ISR; - if (vcpu->arch.apicv_active) + if (apic->apicv_active) bitmap = apic->regs + APIC_IRR; if (apic_test_vector(vec, bitmap)) @@ -1602,7 +1631,7 @@ static inline void __wait_lapic_expire(struct kvm_vcpu *vcpu, u64 guest_cycles) * that __delay() uses delay_tsc whenever the hardware has TSC, thus * always for VMX enabled hardware. */ - if (vcpu->arch.tsc_scaling_ratio == kvm_default_tsc_scaling_ratio) { + if (vcpu->arch.tsc_scaling_ratio == kvm_caps.default_tsc_scaling_ratio) { __delay(min(guest_cycles, nsec_to_cycles(vcpu, timer_advance_ns))); } else { @@ -1700,7 +1729,7 @@ static void apic_timer_expired(struct kvm_lapic *apic, bool from_timer_fn) if (apic_lvtt_tscdeadline(apic) || ktimer->hv_timer_in_use) ktimer->expired_tscdeadline = ktimer->tscdeadline; - if (!from_timer_fn && vcpu->arch.apicv_active) { + if (!from_timer_fn && apic->apicv_active) { WARN_ON(kvm_get_running_vcpu() != vcpu); kvm_apic_inject_pending_timer_irqs(apic); return; @@ -2052,6 +2081,16 @@ static void kvm_lapic_xapic_id_updated(struct kvm_lapic *apic) kvm_set_apicv_inhibit(apic->vcpu->kvm, APICV_INHIBIT_REASON_APIC_ID_MODIFIED); } +static int get_lvt_index(u32 reg) +{ + if (reg == APIC_LVTCMCI) + return LVT_CMCI; + if (reg < APIC_LVTT || reg > APIC_LVTERR) + return -1; + return array_index_nospec( + (reg - APIC_LVTT) >> 4, KVM_APIC_MAX_NR_LVT_ENTRIES); +} + static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) { int ret = 0; @@ -2098,13 +2137,10 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) apic_set_spiv(apic, val & mask); if (!(val & APIC_SPIV_APIC_ENABLED)) { int i; - u32 lvt_val; - for (i = 0; i < KVM_APIC_LVT_NUM; i++) { - lvt_val = kvm_lapic_get_reg(apic, - APIC_LVTT + 0x10 * i); - kvm_lapic_set_reg(apic, APIC_LVTT + 0x10 * i, - lvt_val | APIC_LVT_MASKED); + for (i = 0; i < apic->nr_lvt_entries; i++) { + kvm_lapic_set_reg(apic, APIC_LVTx(i), + kvm_lapic_get_reg(apic, APIC_LVTx(i)) | APIC_LVT_MASKED); } apic_update_lvtt(apic); atomic_set(&apic->lapic_timer.pending, 0); @@ -2133,16 +2169,15 @@ static int kvm_lapic_reg_write(struct kvm_lapic *apic, u32 reg, u32 val) case APIC_LVTTHMR: case APIC_LVTPC: case APIC_LVT1: - case APIC_LVTERR: { - /* TODO: Check vector */ - size_t size; - u32 index; - + case APIC_LVTERR: + case APIC_LVTCMCI: { + u32 index = get_lvt_index(reg); + if (!kvm_lapic_lvt_supported(apic, index)) { + ret = 1; + break; + } if (!kvm_apic_sw_enabled(apic)) val |= APIC_LVT_MASKED; - size = ARRAY_SIZE(apic_lvt_mask); - index = array_index_nospec( - (reg - APIC_LVTT) >> 4, size); val &= apic_lvt_mask[index]; kvm_lapic_set_reg(apic, reg, val); break; @@ -2246,10 +2281,26 @@ EXPORT_SYMBOL_GPL(kvm_lapic_set_eoi); /* emulate APIC access in a trap manner */ void kvm_apic_write_nodecode(struct kvm_vcpu *vcpu, u32 offset) { - u32 val = kvm_lapic_get_reg(vcpu->arch.apic, offset); + struct kvm_lapic *apic = vcpu->arch.apic; + u64 val; - /* TODO: optimize to just emulate side effect w/o one more write */ - kvm_lapic_reg_write(vcpu->arch.apic, offset, val); + if (apic_x2apic_mode(apic)) + kvm_lapic_msr_read(apic, offset, &val); + else + val = kvm_lapic_get_reg(apic, offset); + + /* + * ICR is a single 64-bit register when x2APIC is enabled. For legacy + * xAPIC, ICR writes need to go down the common (slightly slower) path + * to get the upper half from ICR2. + */ + if (apic_x2apic_mode(apic) && offset == APIC_ICR) { + kvm_apic_send_ipi(apic, (u32)val, (u32)(val >> 32)); + trace_kvm_apic_write(APIC_ICR, val); + } else { + /* TODO: optimize to just emulate side effect w/o one more write */ + kvm_lapic_reg_write(apic, offset, (u32)val); + } } EXPORT_SYMBOL_GPL(kvm_apic_write_nodecode); @@ -2344,8 +2395,10 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value) if (((old_value ^ value) & X2APIC_ENABLE) && (value & X2APIC_ENABLE)) kvm_apic_set_x2apic_id(apic, vcpu->vcpu_id); - if ((old_value ^ value) & (MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE)) + if ((old_value ^ value) & (MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE)) { + kvm_vcpu_update_apicv(vcpu); static_call_cond(kvm_x86_set_virtual_apic_mode)(vcpu); + } apic->base_address = apic->vcpu->arch.apic_base & MSR_IA32_APICBASE_BASE; @@ -2361,7 +2414,7 @@ void kvm_apic_update_apicv(struct kvm_vcpu *vcpu) { struct kvm_lapic *apic = vcpu->arch.apic; - if (vcpu->arch.apicv_active) { + if (apic->apicv_active) { /* irr_pending is always true when apicv is activated. */ apic->irr_pending = true; apic->isr_count = 1; @@ -2401,8 +2454,8 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event) kvm_apic_set_xapic_id(apic, vcpu->vcpu_id); kvm_apic_set_version(apic->vcpu); - for (i = 0; i < KVM_APIC_LVT_NUM; i++) - kvm_lapic_set_reg(apic, APIC_LVTT + 0x10 * i, APIC_LVT_MASKED); + for (i = 0; i < apic->nr_lvt_entries; i++) + kvm_lapic_set_reg(apic, APIC_LVTx(i), APIC_LVT_MASKED); apic_update_lvtt(apic); if (kvm_vcpu_is_reset_bsp(vcpu) && kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_LINT0_REENABLED)) @@ -2436,10 +2489,10 @@ void kvm_lapic_reset(struct kvm_vcpu *vcpu, bool init_event) vcpu->arch.pv_eoi.msr_val = 0; apic_update_ppr(apic); - if (vcpu->arch.apicv_active) { + if (apic->apicv_active) { static_call_cond(kvm_x86_apicv_post_state_restore)(vcpu); static_call_cond(kvm_x86_hwapic_irr_update)(vcpu, -1); - static_call_cond(kvm_x86_hwapic_isr_update)(vcpu, -1); + static_call_cond(kvm_x86_hwapic_isr_update)(-1); } vcpu->arch.apic_arb_prio = 0; @@ -2532,6 +2585,8 @@ int kvm_create_lapic(struct kvm_vcpu *vcpu, int timer_advance_ns) } apic->vcpu = vcpu; + apic->nr_lvt_entries = kvm_apic_calc_nr_lvt_entries(vcpu); + hrtimer_init(&apic->lapic_timer.timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS_HARD); apic->lapic_timer.timer.function = apic_timer_fn; @@ -2716,10 +2771,10 @@ int kvm_apic_set_state(struct kvm_vcpu *vcpu, struct kvm_lapic_state *s) kvm_lapic_set_reg(apic, APIC_TMCCT, 0); kvm_apic_update_apicv(vcpu); apic->highest_isr_cache = -1; - if (vcpu->arch.apicv_active) { + if (apic->apicv_active) { static_call_cond(kvm_x86_apicv_post_state_restore)(vcpu); static_call_cond(kvm_x86_hwapic_irr_update)(vcpu, apic_find_highest_irr(apic)); - static_call_cond(kvm_x86_hwapic_isr_update)(vcpu, apic_find_highest_isr(apic)); + static_call_cond(kvm_x86_hwapic_isr_update)(apic_find_highest_isr(apic)); } kvm_make_request(KVM_REQ_EVENT, vcpu); if (ioapic_in_kernel(vcpu->kvm)) diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 65bb2a8cf145..117a46df5cc1 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -10,7 +10,6 @@ #define KVM_APIC_INIT 0 #define KVM_APIC_SIPI 1 -#define KVM_APIC_LVT_NUM 6 #define APIC_SHORT_MASK 0xc0000 #define APIC_DEST_NOSHORT 0x0 @@ -29,6 +28,20 @@ enum lapic_mode { LAPIC_MODE_X2APIC = MSR_IA32_APICBASE_ENABLE | X2APIC_ENABLE, }; +enum lapic_lvt_entry { + LVT_TIMER, + LVT_THERMAL_MONITOR, + LVT_PERFORMANCE_COUNTER, + LVT_LINT0, + LVT_LINT1, + LVT_ERROR, + LVT_CMCI, + + KVM_APIC_MAX_NR_LVT_ENTRIES, +}; + +#define APIC_LVTx(x) ((x) == LVT_CMCI ? APIC_LVTCMCI : APIC_LVTT + 0x10 * (x)) + struct kvm_timer { struct hrtimer timer; s64 period; /* unit: ns */ @@ -48,6 +61,7 @@ struct kvm_lapic { struct kvm_timer lapic_timer; u32 divide_count; struct kvm_vcpu *vcpu; + bool apicv_active; bool sw_enabled; bool irr_pending; bool lvt0_in_nmi_mode; @@ -65,6 +79,7 @@ struct kvm_lapic { struct gfn_to_hva_cache vapic_cache; unsigned long pending_events; unsigned int sipi_vector; + int nr_lvt_entries; }; struct dest_map; @@ -84,6 +99,7 @@ void kvm_lapic_set_base(struct kvm_vcpu *vcpu, u64 value); u64 kvm_lapic_get_base(struct kvm_vcpu *vcpu); void kvm_recalculate_apic_map(struct kvm *kvm); void kvm_apic_set_version(struct kvm_vcpu *vcpu); +void kvm_apic_after_set_mcg_cap(struct kvm_vcpu *vcpu); bool kvm_apic_match_dest(struct kvm_vcpu *vcpu, struct kvm_lapic *source, int shorthand, unsigned int dest, int dest_mode); int kvm_apic_compare_prio(struct kvm_vcpu *vcpu1, struct kvm_vcpu *vcpu2); @@ -204,7 +220,7 @@ static inline int apic_x2apic_mode(struct kvm_lapic *apic) static inline bool kvm_vcpu_apicv_active(struct kvm_vcpu *vcpu) { - return vcpu->arch.apic && vcpu->arch.apicv_active; + return lapic_in_kernel(vcpu) && vcpu->arch.apic->apicv_active; } static inline bool kvm_apic_has_events(struct kvm_vcpu *vcpu) diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index f8192864b496..a99acec925eb 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -6,11 +6,6 @@ #include "kvm_cache_regs.h" #include "cpuid.h" -#define PT64_PT_BITS 9 -#define PT64_ENT_PER_PAGE (1 << PT64_PT_BITS) -#define PT32_PT_BITS 10 -#define PT32_ENT_PER_PAGE (1 << PT32_PT_BITS) - #define PT_WRITABLE_SHIFT 1 #define PT_USER_SHIFT 2 @@ -34,11 +29,6 @@ #define PT_DIR_PAT_SHIFT 12 #define PT_DIR_PAT_MASK (1ULL << PT_DIR_PAT_SHIFT) -#define PT32_DIR_PSE36_SIZE 4 -#define PT32_DIR_PSE36_SHIFT 13 -#define PT32_DIR_PSE36_MASK \ - (((1ULL << PT32_DIR_PSE36_SIZE) - 1) << PT32_DIR_PSE36_SHIFT) - #define PT64_ROOT_5LEVEL 5 #define PT64_ROOT_4LEVEL 4 #define PT32_ROOT_LEVEL 2 diff --git a/arch/x86/kvm/mmu/mmu.c b/arch/x86/kvm/mmu/mmu.c index 17252f39bd7c..4236a28b9be5 100644 --- a/arch/x86/kvm/mmu/mmu.c +++ b/arch/x86/kvm/mmu/mmu.c @@ -53,8 +53,6 @@ #include #include "trace.h" -#include "paging.h" - extern bool itlb_multihit_kvm_mitigation; int __read_mostly nx_huge_pages = -1; @@ -111,26 +109,6 @@ module_param(dbg, bool, 0644); #define PTE_PREFETCH_NUM 8 -#define PT32_LEVEL_BITS 10 - -#define PT32_LEVEL_SHIFT(level) \ - (PAGE_SHIFT + (level - 1) * PT32_LEVEL_BITS) - -#define PT32_LVL_OFFSET_MASK(level) \ - (PT32_BASE_ADDR_MASK & ((1ULL << (PAGE_SHIFT + (((level) - 1) \ - * PT32_LEVEL_BITS))) - 1)) - -#define PT32_INDEX(address, level)\ - (((address) >> PT32_LEVEL_SHIFT(level)) & ((1 << PT32_LEVEL_BITS) - 1)) - - -#define PT32_BASE_ADDR_MASK PAGE_MASK -#define PT32_DIR_BASE_ADDR_MASK \ - (PAGE_MASK & ~((1ULL << (PAGE_SHIFT + PT32_LEVEL_BITS)) - 1)) -#define PT32_LVL_ADDR_MASK(level) \ - (PAGE_MASK & ~((1ULL << (PAGE_SHIFT + (((level) - 1) \ - * PT32_LEVEL_BITS))) - 1)) - #include /* make pte_list_desc fit well in cache lines */ @@ -326,13 +304,6 @@ static int is_cpuid_PSE36(void) return 1; } -static gfn_t pse36_gfn_delta(u32 gpte) -{ - int shift = 32 - PT32_DIR_PSE36_SHIFT - PAGE_SHIFT; - - return (gpte & PT32_DIR_PSE36_MASK) << shift; -} - #ifdef CONFIG_X86_64 static void __set_spte(u64 *sptep, u64 spte) { @@ -432,7 +403,7 @@ static u64 __update_clear_spte_slow(u64 *sptep, u64 spte) * The idea using the light way get the spte on x86_32 guest is from * gup_get_pte (mm/gup.c). * - * An spte tlb flush may be pending, because kvm_set_pte_rmapp + * An spte tlb flush may be pending, because kvm_set_pte_rmap * coalesces them and we are running out of the MMU lock. Therefore * we need to protect against in-progress updates of the spte. * @@ -558,11 +529,12 @@ static bool mmu_spte_update(u64 *sptep, u64 new_spte) * state bits, it is used to clear the last level sptep. * Returns the old PTE. */ -static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) +static u64 mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) { kvm_pfn_t pfn; u64 old_spte = *sptep; int level = sptep_to_sp(sptep)->role.level; + struct page *page; if (!is_shadow_present_pte(old_spte) || !spte_has_volatile_bits(old_spte)) @@ -578,11 +550,13 @@ static int mmu_spte_clear_track_bits(struct kvm *kvm, u64 *sptep) pfn = spte_to_pfn(old_spte); /* - * KVM does not hold the refcount of the page used by - * kvm mmu, before reclaiming the page, we should - * unmap it from mmu first. + * KVM doesn't hold a reference to any pages mapped into the guest, and + * instead uses the mmu_notifier to ensure that KVM unmaps any pages + * before they are reclaimed. Sanity check that, if the pfn is backed + * by a refcounted page, the refcount is elevated. */ - WARN_ON(!kvm_is_reserved_pfn(pfn) && !page_count(pfn_to_page(pfn))); + page = kvm_pfn_to_refcounted_page(pfn); + WARN_ON(page && !page_count(page)); if (is_accessed_spte(old_spte)) kvm_set_pfn_accessed(pfn); @@ -682,7 +656,7 @@ static int mmu_topup_memory_caches(struct kvm_vcpu *vcpu, bool maybe_indirect) if (r) return r; if (maybe_indirect) { - r = kvm_mmu_topup_memory_cache(&vcpu->arch.mmu_gfn_array_cache, + r = kvm_mmu_topup_memory_cache(&vcpu->arch.mmu_shadowed_info_cache, PT64_ROOT_MAX_LEVEL); if (r) return r; @@ -695,48 +669,79 @@ static void mmu_free_memory_caches(struct kvm_vcpu *vcpu) { kvm_mmu_free_memory_cache(&vcpu->arch.mmu_pte_list_desc_cache); kvm_mmu_free_memory_cache(&vcpu->arch.mmu_shadow_page_cache); - kvm_mmu_free_memory_cache(&vcpu->arch.mmu_gfn_array_cache); + kvm_mmu_free_memory_cache(&vcpu->arch.mmu_shadowed_info_cache); kvm_mmu_free_memory_cache(&vcpu->arch.mmu_page_header_cache); } -static struct pte_list_desc *mmu_alloc_pte_list_desc(struct kvm_vcpu *vcpu) -{ - return kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_pte_list_desc_cache); -} - static void mmu_free_pte_list_desc(struct pte_list_desc *pte_list_desc) { kmem_cache_free(pte_list_desc_cache, pte_list_desc); } +static bool sp_has_gptes(struct kvm_mmu_page *sp); + static gfn_t kvm_mmu_page_get_gfn(struct kvm_mmu_page *sp, int index) { if (sp->role.passthrough) return sp->gfn; if (!sp->role.direct) - return sp->gfns[index]; + return sp->shadowed_translation[index] >> PAGE_SHIFT; - return sp->gfn + (index << ((sp->role.level - 1) * PT64_LEVEL_BITS)); + return sp->gfn + (index << ((sp->role.level - 1) * SPTE_LEVEL_BITS)); } -static void kvm_mmu_page_set_gfn(struct kvm_mmu_page *sp, int index, gfn_t gfn) +/* + * For leaf SPTEs, fetch the *guest* access permissions being shadowed. Note + * that the SPTE itself may have a more constrained access permissions that + * what the guest enforces. For example, a guest may create an executable + * huge PTE but KVM may disallow execution to mitigate iTLB multihit. + */ +static u32 kvm_mmu_page_get_access(struct kvm_mmu_page *sp, int index) { - if (sp->role.passthrough) { - WARN_ON_ONCE(gfn != sp->gfn); + if (sp_has_gptes(sp)) + return sp->shadowed_translation[index] & ACC_ALL; + + /* + * For direct MMUs (e.g. TDP or non-paging guests) or passthrough SPs, + * KVM is not shadowing any guest page tables, so the "guest access + * permissions" are just ACC_ALL. + * + * For direct SPs in indirect MMUs (shadow paging), i.e. when KVM + * is shadowing a guest huge page with small pages, the guest access + * permissions being shadowed are the access permissions of the huge + * page. + * + * In both cases, sp->role.access contains the correct access bits. + */ + return sp->role.access; +} + +static void kvm_mmu_page_set_translation(struct kvm_mmu_page *sp, int index, + gfn_t gfn, unsigned int access) +{ + if (sp_has_gptes(sp)) { + sp->shadowed_translation[index] = (gfn << PAGE_SHIFT) | access; return; } - if (!sp->role.direct) { - sp->gfns[index] = gfn; - return; - } + WARN_ONCE(access != kvm_mmu_page_get_access(sp, index), + "access mismatch under %s page %llx (expected %u, got %u)\n", + sp->role.passthrough ? "passthrough" : "direct", + sp->gfn, kvm_mmu_page_get_access(sp, index), access); - if (WARN_ON(gfn != kvm_mmu_page_get_gfn(sp, index))) - pr_err_ratelimited("gfn mismatch under direct page %llx " - "(expected %llx, got %llx)\n", - sp->gfn, - kvm_mmu_page_get_gfn(sp, index), gfn); + WARN_ONCE(gfn != kvm_mmu_page_get_gfn(sp, index), + "gfn mismatch under %s page %llx (expected %llx, got %llx)\n", + sp->role.passthrough ? "passthrough" : "direct", + sp->gfn, kvm_mmu_page_get_gfn(sp, index), gfn); +} + +static void kvm_mmu_page_set_access(struct kvm_mmu_page *sp, int index, + unsigned int access) +{ + gfn_t gfn = kvm_mmu_page_get_gfn(sp, index); + + kvm_mmu_page_set_translation(sp, index, gfn, access); } /* @@ -792,6 +797,9 @@ static void account_shadowed(struct kvm *kvm, struct kvm_mmu_page *sp) KVM_PAGE_TRACK_WRITE); kvm_mmu_gfn_disallow_lpage(slot, gfn); + + if (kvm_mmu_slot_gfn_write_protect(kvm, slot, gfn, PG_LEVEL_4K)) + kvm_flush_remote_tlbs_with_address(kvm, gfn, 1); } void account_huge_nx_page(struct kvm *kvm, struct kvm_mmu_page *sp) @@ -855,7 +863,7 @@ gfn_to_memslot_dirty_bitmap(struct kvm_vcpu *vcpu, gfn_t gfn, /* * Returns the number of pointers in the rmap chain, not counting the new one. */ -static int pte_list_add(struct kvm_vcpu *vcpu, u64 *spte, +static int pte_list_add(struct kvm_mmu_memory_cache *cache, u64 *spte, struct kvm_rmap_head *rmap_head) { struct pte_list_desc *desc; @@ -866,7 +874,7 @@ static int pte_list_add(struct kvm_vcpu *vcpu, u64 *spte, rmap_head->val = (unsigned long)spte; } else if (!(rmap_head->val & 1)) { rmap_printk("%p %llx 1->many\n", spte, *spte); - desc = mmu_alloc_pte_list_desc(vcpu); + desc = kvm_mmu_memory_cache_alloc(cache); desc->sptes[0] = (u64 *)rmap_head->val; desc->sptes[1] = spte; desc->spte_count = 2; @@ -878,7 +886,7 @@ static int pte_list_add(struct kvm_vcpu *vcpu, u64 *spte, while (desc->spte_count == PTE_LIST_EXT) { count += PTE_LIST_EXT; if (!desc->more) { - desc->more = mmu_alloc_pte_list_desc(vcpu); + desc->more = kvm_mmu_memory_cache_alloc(cache); desc = desc->more; desc->spte_count = 0; break; @@ -913,7 +921,7 @@ pte_list_desc_remove_entry(struct kvm_rmap_head *rmap_head, mmu_free_pte_list_desc(desc); } -static void __pte_list_remove(u64 *spte, struct kvm_rmap_head *rmap_head) +static void pte_list_remove(u64 *spte, struct kvm_rmap_head *rmap_head) { struct pte_list_desc *desc; struct pte_list_desc *prev_desc; @@ -949,15 +957,16 @@ static void __pte_list_remove(u64 *spte, struct kvm_rmap_head *rmap_head) } } -static void pte_list_remove(struct kvm *kvm, struct kvm_rmap_head *rmap_head, - u64 *sptep) +static void kvm_zap_one_rmap_spte(struct kvm *kvm, + struct kvm_rmap_head *rmap_head, u64 *sptep) { mmu_spte_clear_track_bits(kvm, sptep); - __pte_list_remove(sptep, rmap_head); + pte_list_remove(sptep, rmap_head); } -/* Return true if rmap existed, false otherwise */ -static bool pte_list_destroy(struct kvm *kvm, struct kvm_rmap_head *rmap_head) +/* Return true if at least one SPTE was zapped, false otherwise */ +static bool kvm_zap_all_rmap_sptes(struct kvm *kvm, + struct kvm_rmap_head *rmap_head) { struct pte_list_desc *desc, *next; int i; @@ -1030,7 +1039,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte) struct kvm_rmap_head *rmap_head; sp = sptep_to_sp(spte); - gfn = kvm_mmu_page_get_gfn(sp, spte - sp->spt); + gfn = kvm_mmu_page_get_gfn(sp, spte_index(spte)); /* * Unlike rmap_add, rmap_remove does not run in the context of a vCPU @@ -1042,7 +1051,7 @@ static void rmap_remove(struct kvm *kvm, u64 *spte) slot = __gfn_to_memslot(slots, gfn); rmap_head = gfn_to_rmap(gfn, sp->role.level, slot); - __pte_list_remove(spte, rmap_head); + pte_list_remove(spte, rmap_head); } /* @@ -1129,26 +1138,18 @@ static void drop_spte(struct kvm *kvm, u64 *sptep) rmap_remove(kvm, sptep); } - -static bool __drop_large_spte(struct kvm *kvm, u64 *sptep) +static void drop_large_spte(struct kvm *kvm, u64 *sptep, bool flush) { - if (is_large_pte(*sptep)) { - WARN_ON(sptep_to_sp(sptep)->role.level == PG_LEVEL_4K); - drop_spte(kvm, sptep); - return true; - } + struct kvm_mmu_page *sp; - return false; -} + sp = sptep_to_sp(sptep); + WARN_ON(sp->role.level == PG_LEVEL_4K); -static void drop_large_spte(struct kvm_vcpu *vcpu, u64 *sptep) -{ - if (__drop_large_spte(vcpu->kvm, sptep)) { - struct kvm_mmu_page *sp = sptep_to_sp(sptep); + drop_spte(kvm, sptep); - kvm_flush_remote_tlbs_with_address(vcpu->kvm, sp->gfn, + if (flush) + kvm_flush_remote_tlbs_with_address(kvm, sp->gfn, KVM_PAGES_PER_HPAGE(sp->role.level)); - } } /* @@ -1383,22 +1384,22 @@ static bool kvm_vcpu_write_protect_gfn(struct kvm_vcpu *vcpu, u64 gfn) return kvm_mmu_slot_gfn_write_protect(vcpu->kvm, slot, gfn, PG_LEVEL_4K); } -static bool kvm_zap_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, - const struct kvm_memory_slot *slot) +static bool __kvm_zap_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head, + const struct kvm_memory_slot *slot) { - return pte_list_destroy(kvm, rmap_head); + return kvm_zap_all_rmap_sptes(kvm, rmap_head); } -static bool kvm_unmap_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, - struct kvm_memory_slot *slot, gfn_t gfn, int level, - pte_t unused) +static bool kvm_zap_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head, + struct kvm_memory_slot *slot, gfn_t gfn, int level, + pte_t unused) { - return kvm_zap_rmapp(kvm, rmap_head, slot); + return __kvm_zap_rmap(kvm, rmap_head, slot); } -static bool kvm_set_pte_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, - struct kvm_memory_slot *slot, gfn_t gfn, int level, - pte_t pte) +static bool kvm_set_pte_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head, + struct kvm_memory_slot *slot, gfn_t gfn, int level, + pte_t pte) { u64 *sptep; struct rmap_iterator iter; @@ -1417,7 +1418,7 @@ restart: need_flush = true; if (pte_write(pte)) { - pte_list_remove(kvm, rmap_head, sptep); + kvm_zap_one_rmap_spte(kvm, rmap_head, sptep); goto restart; } else { new_spte = kvm_mmu_changed_pte_notifier_make_spte( @@ -1529,7 +1530,7 @@ bool kvm_unmap_gfn_range(struct kvm *kvm, struct kvm_gfn_range *range) bool flush = false; if (kvm_memslots_have_rmaps(kvm)) - flush = kvm_handle_gfn_range(kvm, range, kvm_unmap_rmapp); + flush = kvm_handle_gfn_range(kvm, range, kvm_zap_rmap); if (is_tdp_mmu_enabled(kvm)) flush = kvm_tdp_mmu_unmap_gfn_range(kvm, range, flush); @@ -1542,7 +1543,7 @@ bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) bool flush = false; if (kvm_memslots_have_rmaps(kvm)) - flush = kvm_handle_gfn_range(kvm, range, kvm_set_pte_rmapp); + flush = kvm_handle_gfn_range(kvm, range, kvm_set_pte_rmap); if (is_tdp_mmu_enabled(kvm)) flush |= kvm_tdp_mmu_set_spte_gfn(kvm, range); @@ -1550,9 +1551,9 @@ bool kvm_set_spte_gfn(struct kvm *kvm, struct kvm_gfn_range *range) return flush; } -static bool kvm_age_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, - struct kvm_memory_slot *slot, gfn_t gfn, int level, - pte_t unused) +static bool kvm_age_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head, + struct kvm_memory_slot *slot, gfn_t gfn, int level, + pte_t unused) { u64 *sptep; struct rmap_iterator iter; @@ -1564,9 +1565,9 @@ static bool kvm_age_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, return young; } -static bool kvm_test_age_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, - struct kvm_memory_slot *slot, gfn_t gfn, - int level, pte_t unused) +static bool kvm_test_age_rmap(struct kvm *kvm, struct kvm_rmap_head *rmap_head, + struct kvm_memory_slot *slot, gfn_t gfn, + int level, pte_t unused) { u64 *sptep; struct rmap_iterator iter; @@ -1579,31 +1580,43 @@ static bool kvm_test_age_rmapp(struct kvm *kvm, struct kvm_rmap_head *rmap_head, #define RMAP_RECYCLE_THRESHOLD 1000 -static void rmap_add(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, - u64 *spte, gfn_t gfn) +static void __rmap_add(struct kvm *kvm, + struct kvm_mmu_memory_cache *cache, + const struct kvm_memory_slot *slot, + u64 *spte, gfn_t gfn, unsigned int access) { struct kvm_mmu_page *sp; struct kvm_rmap_head *rmap_head; int rmap_count; sp = sptep_to_sp(spte); - kvm_mmu_page_set_gfn(sp, spte - sp->spt, gfn); + kvm_mmu_page_set_translation(sp, spte_index(spte), gfn, access); + kvm_update_page_stats(kvm, sp->role.level, 1); + rmap_head = gfn_to_rmap(gfn, sp->role.level, slot); - rmap_count = pte_list_add(vcpu, spte, rmap_head); + rmap_count = pte_list_add(cache, spte, rmap_head); if (rmap_count > RMAP_RECYCLE_THRESHOLD) { - kvm_unmap_rmapp(vcpu->kvm, rmap_head, NULL, gfn, sp->role.level, __pte(0)); + kvm_zap_all_rmap_sptes(kvm, rmap_head); kvm_flush_remote_tlbs_with_address( - vcpu->kvm, sp->gfn, KVM_PAGES_PER_HPAGE(sp->role.level)); + kvm, sp->gfn, KVM_PAGES_PER_HPAGE(sp->role.level)); } } +static void rmap_add(struct kvm_vcpu *vcpu, const struct kvm_memory_slot *slot, + u64 *spte, gfn_t gfn, unsigned int access) +{ + struct kvm_mmu_memory_cache *cache = &vcpu->arch.mmu_pte_list_desc_cache; + + __rmap_add(vcpu->kvm, cache, slot, spte, gfn, access); +} + bool kvm_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) { bool young = false; if (kvm_memslots_have_rmaps(kvm)) - young = kvm_handle_gfn_range(kvm, range, kvm_age_rmapp); + young = kvm_handle_gfn_range(kvm, range, kvm_age_rmap); if (is_tdp_mmu_enabled(kvm)) young |= kvm_tdp_mmu_age_gfn_range(kvm, range); @@ -1616,7 +1629,7 @@ bool kvm_test_age_gfn(struct kvm *kvm, struct kvm_gfn_range *range) bool young = false; if (kvm_memslots_have_rmaps(kvm)) - young = kvm_handle_gfn_range(kvm, range, kvm_test_age_rmapp); + young = kvm_handle_gfn_range(kvm, range, kvm_test_age_rmap); if (is_tdp_mmu_enabled(kvm)) young |= kvm_tdp_mmu_test_age_gfn(kvm, range); @@ -1652,14 +1665,14 @@ static inline void kvm_mod_used_mmu_pages(struct kvm *kvm, long nr) percpu_counter_add(&kvm_total_used_mmu_pages, nr); } -static void kvm_mmu_free_page(struct kvm_mmu_page *sp) +static void kvm_mmu_free_shadow_page(struct kvm_mmu_page *sp) { MMU_WARN_ON(!is_empty_shadow_page(sp->spt)); hlist_del(&sp->hash_link); list_del(&sp->link); free_page((unsigned long)sp->spt); if (!sp->role.direct) - free_page((unsigned long)sp->gfns); + free_page((unsigned long)sp->shadowed_translation); kmem_cache_free(mmu_page_header_cache, sp); } @@ -1668,19 +1681,19 @@ static unsigned kvm_page_table_hashfn(gfn_t gfn) return hash_64(gfn, KVM_MMU_HASH_SHIFT); } -static void mmu_page_add_parent_pte(struct kvm_vcpu *vcpu, +static void mmu_page_add_parent_pte(struct kvm_mmu_memory_cache *cache, struct kvm_mmu_page *sp, u64 *parent_pte) { if (!parent_pte) return; - pte_list_add(vcpu, parent_pte, &sp->parent_ptes); + pte_list_add(cache, parent_pte, &sp->parent_ptes); } static void mmu_page_remove_parent_pte(struct kvm_mmu_page *sp, u64 *parent_pte) { - __pte_list_remove(parent_pte, &sp->parent_ptes); + pte_list_remove(parent_pte, &sp->parent_ptes); } static void drop_parent_pte(struct kvm_mmu_page *sp, @@ -1690,27 +1703,6 @@ static void drop_parent_pte(struct kvm_mmu_page *sp, mmu_spte_clear_no_track(parent_pte); } -static struct kvm_mmu_page *kvm_mmu_alloc_page(struct kvm_vcpu *vcpu, int direct) -{ - struct kvm_mmu_page *sp; - - sp = kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_page_header_cache); - sp->spt = kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_shadow_page_cache); - if (!direct) - sp->gfns = kvm_mmu_memory_cache_alloc(&vcpu->arch.mmu_gfn_array_cache); - set_page_private(virt_to_page(sp->spt), (unsigned long)sp); - - /* - * active_mmu_pages must be a FIFO list, as kvm_zap_obsolete_pages() - * depends on valid pages being added to the head of the list. See - * comments in kvm_zap_obsolete_pages(). - */ - sp->mmu_valid_gen = vcpu->kvm->arch.mmu_valid_gen; - list_add(&sp->link, &vcpu->kvm->arch.active_mmu_pages); - kvm_mod_used_mmu_pages(vcpu->kvm, +1); - return sp; -} - static void mark_unsync(u64 *spte); static void kvm_mmu_mark_parents_unsync(struct kvm_mmu_page *sp) { @@ -1725,11 +1717,9 @@ static void kvm_mmu_mark_parents_unsync(struct kvm_mmu_page *sp) static void mark_unsync(u64 *spte) { struct kvm_mmu_page *sp; - unsigned int index; sp = sptep_to_sp(spte); - index = spte - sp->spt; - if (__test_and_set_bit(index, sp->unsync_child_bitmap)) + if (__test_and_set_bit(spte_index(spte), sp->unsync_child_bitmap)) return; if (sp->unsync_children++) return; @@ -1789,7 +1779,7 @@ static int __mmu_unsync_walk(struct kvm_mmu_page *sp, continue; } - child = to_shadow_page(ent & PT64_BASE_ADDR_MASK); + child = to_shadow_page(ent & SPTE_BASE_ADDR_MASK); if (child->unsync_children) { if (mmu_pages_add(pvec, child, i)) @@ -2019,36 +2009,24 @@ static void clear_sp_write_flooding_count(u64 *spte) __clear_sp_write_flooding_count(sptep_to_sp(spte)); } -static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, - gfn_t gfn, - gva_t gaddr, - unsigned level, - int direct, - unsigned int access) +/* + * The vCPU is required when finding indirect shadow pages; the shadow + * page may already exist and syncing it needs the vCPU pointer in + * order to read guest page tables. Direct shadow pages are never + * unsync, thus @vcpu can be NULL if @role.direct is true. + */ +static struct kvm_mmu_page *kvm_mmu_find_shadow_page(struct kvm *kvm, + struct kvm_vcpu *vcpu, + gfn_t gfn, + struct hlist_head *sp_list, + union kvm_mmu_page_role role) { - bool direct_mmu = vcpu->arch.mmu->root_role.direct; - union kvm_mmu_page_role role; - struct hlist_head *sp_list; - unsigned quadrant; struct kvm_mmu_page *sp; int ret; int collisions = 0; LIST_HEAD(invalid_list); - role = vcpu->arch.mmu->root_role; - role.level = level; - role.direct = direct; - role.access = access; - if (role.has_4_byte_gpte) { - quadrant = gaddr >> (PAGE_SHIFT + (PT64_PT_BITS * level)); - quadrant &= (1 << ((PT32_PT_BITS - PT64_PT_BITS) * level)) - 1; - role.quadrant = quadrant; - } - if (level <= vcpu->arch.mmu->cpu_role.base.level) - role.passthrough = 0; - - sp_list = &vcpu->kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]; - for_each_valid_sp(vcpu->kvm, sp, sp_list) { + for_each_valid_sp(kvm, sp, sp_list) { if (sp->gfn != gfn) { collisions++; continue; @@ -2064,16 +2042,20 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, * Unsync pages must not be left as is, because the new * upper-level page will be write-protected. */ - if (level > PG_LEVEL_4K && sp->unsync) - kvm_mmu_prepare_zap_page(vcpu->kvm, sp, + if (role.level > PG_LEVEL_4K && sp->unsync) + kvm_mmu_prepare_zap_page(kvm, sp, &invalid_list); continue; } - if (direct_mmu) - goto trace_get_page; + /* unsync and write-flooding only apply to indirect SPs. */ + if (sp->role.direct) + goto out; if (sp->unsync) { + if (KVM_BUG_ON(!vcpu, kvm)) + break; + /* * The page is good, but is stale. kvm_sync_page does * get the latest guest state, but (unlike mmu_unsync_children) @@ -2092,37 +2074,160 @@ static struct kvm_mmu_page *kvm_mmu_get_page(struct kvm_vcpu *vcpu, WARN_ON(!list_empty(&invalid_list)); if (ret > 0) - kvm_flush_remote_tlbs(vcpu->kvm); + kvm_flush_remote_tlbs(kvm); } __clear_sp_write_flooding_count(sp); -trace_get_page: - trace_kvm_mmu_get_page(sp, false); goto out; } - ++vcpu->kvm->stat.mmu_cache_miss; + sp = NULL; + ++kvm->stat.mmu_cache_miss; - sp = kvm_mmu_alloc_page(vcpu, direct); +out: + kvm_mmu_commit_zap_page(kvm, &invalid_list); + + if (collisions > kvm->stat.max_mmu_page_hash_collisions) + kvm->stat.max_mmu_page_hash_collisions = collisions; + return sp; +} + +/* Caches used when allocating a new shadow page. */ +struct shadow_page_caches { + struct kvm_mmu_memory_cache *page_header_cache; + struct kvm_mmu_memory_cache *shadow_page_cache; + struct kvm_mmu_memory_cache *shadowed_info_cache; +}; + +static struct kvm_mmu_page *kvm_mmu_alloc_shadow_page(struct kvm *kvm, + struct shadow_page_caches *caches, + gfn_t gfn, + struct hlist_head *sp_list, + union kvm_mmu_page_role role) +{ + struct kvm_mmu_page *sp; + + sp = kvm_mmu_memory_cache_alloc(caches->page_header_cache); + sp->spt = kvm_mmu_memory_cache_alloc(caches->shadow_page_cache); + if (!role.direct) + sp->shadowed_translation = kvm_mmu_memory_cache_alloc(caches->shadowed_info_cache); + + set_page_private(virt_to_page(sp->spt), (unsigned long)sp); + + /* + * active_mmu_pages must be a FIFO list, as kvm_zap_obsolete_pages() + * depends on valid pages being added to the head of the list. See + * comments in kvm_zap_obsolete_pages(). + */ + sp->mmu_valid_gen = kvm->arch.mmu_valid_gen; + list_add(&sp->link, &kvm->arch.active_mmu_pages); + kvm_mod_used_mmu_pages(kvm, +1); sp->gfn = gfn; sp->role = role; hlist_add_head(&sp->hash_link, sp_list); - if (sp_has_gptes(sp)) { - account_shadowed(vcpu->kvm, sp); - if (level == PG_LEVEL_4K && kvm_vcpu_write_protect_gfn(vcpu, gfn)) - kvm_flush_remote_tlbs_with_address(vcpu->kvm, gfn, 1); - } - trace_kvm_mmu_get_page(sp, true); -out: - kvm_mmu_commit_zap_page(vcpu->kvm, &invalid_list); + if (sp_has_gptes(sp)) + account_shadowed(kvm, sp); - if (collisions > vcpu->kvm->stat.max_mmu_page_hash_collisions) - vcpu->kvm->stat.max_mmu_page_hash_collisions = collisions; return sp; } +/* Note, @vcpu may be NULL if @role.direct is true; see kvm_mmu_find_shadow_page. */ +static struct kvm_mmu_page *__kvm_mmu_get_shadow_page(struct kvm *kvm, + struct kvm_vcpu *vcpu, + struct shadow_page_caches *caches, + gfn_t gfn, + union kvm_mmu_page_role role) +{ + struct hlist_head *sp_list; + struct kvm_mmu_page *sp; + bool created = false; + + sp_list = &kvm->arch.mmu_page_hash[kvm_page_table_hashfn(gfn)]; + + sp = kvm_mmu_find_shadow_page(kvm, vcpu, gfn, sp_list, role); + if (!sp) { + created = true; + sp = kvm_mmu_alloc_shadow_page(kvm, caches, gfn, sp_list, role); + } + + trace_kvm_mmu_get_page(sp, created); + return sp; +} + +static struct kvm_mmu_page *kvm_mmu_get_shadow_page(struct kvm_vcpu *vcpu, + gfn_t gfn, + union kvm_mmu_page_role role) +{ + struct shadow_page_caches caches = { + .page_header_cache = &vcpu->arch.mmu_page_header_cache, + .shadow_page_cache = &vcpu->arch.mmu_shadow_page_cache, + .shadowed_info_cache = &vcpu->arch.mmu_shadowed_info_cache, + }; + + return __kvm_mmu_get_shadow_page(vcpu->kvm, vcpu, &caches, gfn, role); +} + +static union kvm_mmu_page_role kvm_mmu_child_role(u64 *sptep, bool direct, + unsigned int access) +{ + struct kvm_mmu_page *parent_sp = sptep_to_sp(sptep); + union kvm_mmu_page_role role; + + role = parent_sp->role; + role.level--; + role.access = access; + role.direct = direct; + role.passthrough = 0; + + /* + * If the guest has 4-byte PTEs then that means it's using 32-bit, + * 2-level, non-PAE paging. KVM shadows such guests with PAE paging + * (i.e. 8-byte PTEs). The difference in PTE size means that KVM must + * shadow each guest page table with multiple shadow page tables, which + * requires extra bookkeeping in the role. + * + * Specifically, to shadow the guest's page directory (which covers a + * 4GiB address space), KVM uses 4 PAE page directories, each mapping + * 1GiB of the address space. @role.quadrant encodes which quarter of + * the address space each maps. + * + * To shadow the guest's page tables (which each map a 4MiB region), KVM + * uses 2 PAE page tables, each mapping a 2MiB region. For these, + * @role.quadrant encodes which half of the region they map. + * + * Concretely, a 4-byte PDE consumes bits 31:22, while an 8-byte PDE + * consumes bits 29:21. To consume bits 31:30, KVM's uses 4 shadow + * PDPTEs; those 4 PAE page directories are pre-allocated and their + * quadrant is assigned in mmu_alloc_root(). A 4-byte PTE consumes + * bits 21:12, while an 8-byte PTE consumes bits 20:12. To consume + * bit 21 in the PTE (the child here), KVM propagates that bit to the + * quadrant, i.e. sets quadrant to '0' or '1'. The parent 8-byte PDE + * covers bit 21 (see above), thus the quadrant is calculated from the + * _least_ significant bit of the PDE index. + */ + if (role.has_4_byte_gpte) { + WARN_ON_ONCE(role.level != PG_LEVEL_4K); + role.quadrant = spte_index(sptep) & 1; + } + + return role; +} + +static struct kvm_mmu_page *kvm_mmu_get_child_sp(struct kvm_vcpu *vcpu, + u64 *sptep, gfn_t gfn, + bool direct, unsigned int access) +{ + union kvm_mmu_page_role role; + + if (is_shadow_present_pte(*sptep) && !is_large_pte(*sptep)) + return ERR_PTR(-EEXIST); + + role = kvm_mmu_child_role(sptep, direct, access); + return kvm_mmu_get_shadow_page(vcpu, gfn, role); +} + static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterator, struct kvm_vcpu *vcpu, hpa_t root, u64 addr) @@ -2145,7 +2250,7 @@ static void shadow_walk_init_using_root(struct kvm_shadow_walk_iterator *iterato iterator->shadow_addr = vcpu->arch.mmu->pae_root[(addr >> 30) & 3]; - iterator->shadow_addr &= PT64_BASE_ADDR_MASK; + iterator->shadow_addr &= SPTE_BASE_ADDR_MASK; --iterator->level; if (!iterator->shadow_addr) iterator->level = 0; @@ -2164,7 +2269,7 @@ static bool shadow_walk_okay(struct kvm_shadow_walk_iterator *iterator) if (iterator->level < PG_LEVEL_4K) return false; - iterator->index = SHADOW_PT_INDEX(iterator->addr, iterator->level); + iterator->index = SPTE_INDEX(iterator->addr, iterator->level); iterator->sptep = ((u64 *)__va(iterator->shadow_addr)) + iterator->index; return true; } @@ -2177,7 +2282,7 @@ static void __shadow_walk_next(struct kvm_shadow_walk_iterator *iterator, return; } - iterator->shadow_addr = spte & PT64_BASE_ADDR_MASK; + iterator->shadow_addr = spte & SPTE_BASE_ADDR_MASK; --iterator->level; } @@ -2186,23 +2291,38 @@ static void shadow_walk_next(struct kvm_shadow_walk_iterator *iterator) __shadow_walk_next(iterator, *iterator->sptep); } -static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep, - struct kvm_mmu_page *sp) +static void __link_shadow_page(struct kvm *kvm, + struct kvm_mmu_memory_cache *cache, u64 *sptep, + struct kvm_mmu_page *sp, bool flush) { u64 spte; BUILD_BUG_ON(VMX_EPT_WRITABLE_MASK != PT_WRITABLE_MASK); + /* + * If an SPTE is present already, it must be a leaf and therefore + * a large one. Drop it, and flush the TLB if needed, before + * installing sp. + */ + if (is_shadow_present_pte(*sptep)) + drop_large_spte(kvm, sptep, flush); + spte = make_nonleaf_spte(sp->spt, sp_ad_disabled(sp)); mmu_spte_set(sptep, spte); - mmu_page_add_parent_pte(vcpu, sp, sptep); + mmu_page_add_parent_pte(cache, sp, sptep); if (sp->unsync_children || sp->unsync) mark_unsync(sptep); } +static void link_shadow_page(struct kvm_vcpu *vcpu, u64 *sptep, + struct kvm_mmu_page *sp) +{ + __link_shadow_page(vcpu->kvm, &vcpu->arch.mmu_pte_list_desc_cache, sptep, sp, true); +} + static void validate_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep, unsigned direct_access) { @@ -2216,7 +2336,7 @@ static void validate_direct_spte(struct kvm_vcpu *vcpu, u64 *sptep, * so we should update the spte at this point to get * a new sp with the correct access. */ - child = to_shadow_page(*sptep & PT64_BASE_ADDR_MASK); + child = to_shadow_page(*sptep & SPTE_BASE_ADDR_MASK); if (child->role.access == direct_access) return; @@ -2237,7 +2357,7 @@ static int mmu_page_zap_pte(struct kvm *kvm, struct kvm_mmu_page *sp, if (is_last_spte(pte, sp->role.level)) { drop_spte(kvm, spte); } else { - child = to_shadow_page(pte & PT64_BASE_ADDR_MASK); + child = to_shadow_page(pte & SPTE_BASE_ADDR_MASK); drop_parent_pte(child, spte); /* @@ -2263,7 +2383,7 @@ static int kvm_mmu_page_unlink_children(struct kvm *kvm, int zapped = 0; unsigned i; - for (i = 0; i < PT64_ENT_PER_PAGE; ++i) + for (i = 0; i < SPTE_ENT_PER_PAGE; ++i) zapped += mmu_page_zap_pte(kvm, sp, sp->spt + i, invalid_list); return zapped; @@ -2396,7 +2516,7 @@ static void kvm_mmu_commit_zap_page(struct kvm *kvm, list_for_each_entry_safe(sp, nsp, invalid_list, link) { WARN_ON(!sp->role.invalid || sp->root_count); - kvm_mmu_free_page(sp); + kvm_mmu_free_shadow_page(sp); } } @@ -2676,7 +2796,7 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, struct kvm_mmu_page *child; u64 pte = *sptep; - child = to_shadow_page(pte & PT64_BASE_ADDR_MASK); + child = to_shadow_page(pte & SPTE_BASE_ADDR_MASK); drop_parent_pte(child, sptep); flush = true; } else if (pfn != spte_to_pfn(*sptep)) { @@ -2711,8 +2831,10 @@ static int mmu_set_spte(struct kvm_vcpu *vcpu, struct kvm_memory_slot *slot, if (!was_rmapped) { WARN_ON_ONCE(ret == RET_PF_SPURIOUS); - kvm_update_page_stats(vcpu->kvm, level, 1); - rmap_add(vcpu, slot, sptep, gfn); + rmap_add(vcpu, slot, sptep, gfn, pte_access); + } else { + /* Already rmapped but the pte_access bits may have changed. */ + kvm_mmu_page_set_access(sp, spte_index(sptep), pte_access); } return ret; @@ -2728,7 +2850,7 @@ static int direct_pte_prefetch_many(struct kvm_vcpu *vcpu, int i, ret; gfn_t gfn; - gfn = kvm_mmu_page_get_gfn(sp, start - sp->spt); + gfn = kvm_mmu_page_get_gfn(sp, spte_index(start)); slot = gfn_to_memslot_dirty_bitmap(vcpu, gfn, access & ACC_WRITE_MASK); if (!slot) return -1; @@ -2754,7 +2876,7 @@ static void __direct_pte_prefetch(struct kvm_vcpu *vcpu, WARN_ON(!sp->role.direct); - i = (sptep - sp->spt) & ~(PTE_PREFETCH_NUM - 1); + i = spte_index(sptep) & ~(PTE_PREFETCH_NUM - 1); spte = sp->spt + i; for (i = 0; i < PTE_PREFETCH_NUM; i++, spte++) { @@ -2798,20 +2920,42 @@ static void direct_pte_prefetch(struct kvm_vcpu *vcpu, u64 *sptep) __direct_pte_prefetch(vcpu, sp, sptep); } -static int host_pfn_mapping_level(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, +/* + * Lookup the mapping level for @gfn in the current mm. + * + * WARNING! Use of host_pfn_mapping_level() requires the caller and the end + * consumer to be tied into KVM's handlers for MMU notifier events! + * + * There are several ways to safely use this helper: + * + * - Check mmu_notifier_retry_hva() after grabbing the mapping level, before + * consuming it. In this case, mmu_lock doesn't need to be held during the + * lookup, but it does need to be held while checking the MMU notifier. + * + * - Hold mmu_lock AND ensure there is no in-progress MMU notifier invalidation + * event for the hva. This can be done by explicit checking the MMU notifier + * or by ensuring that KVM already has a valid mapping that covers the hva. + * + * - Do not use the result to install new mappings, e.g. use the host mapping + * level only to decide whether or not to zap an entry. In this case, it's + * not required to hold mmu_lock (though it's highly likely the caller will + * want to hold mmu_lock anyways, e.g. to modify SPTEs). + * + * Note! The lookup can still race with modifications to host page tables, but + * the above "rules" ensure KVM will not _consume_ the result of the walk if a + * race with the primary MMU occurs. + */ +static int host_pfn_mapping_level(struct kvm *kvm, gfn_t gfn, const struct kvm_memory_slot *slot) { + int level = PG_LEVEL_4K; unsigned long hva; unsigned long flags; - int level = PG_LEVEL_4K; pgd_t pgd; p4d_t p4d; pud_t pud; pmd_t pmd; - if (!PageCompound(pfn_to_page(pfn)) && !kvm_is_zone_device_pfn(pfn)) - return PG_LEVEL_4K; - /* * Note, using the already-retrieved memslot and __gfn_to_hva_memslot() * is not solely for performance, it's also necessary to avoid the @@ -2823,16 +2967,19 @@ static int host_pfn_mapping_level(struct kvm *kvm, gfn_t gfn, kvm_pfn_t pfn, hva = __gfn_to_hva_memslot(slot, gfn); /* - * Lookup the mapping level in the current mm. The information - * may become stale soon, but it is safe to use as long as - * 1) mmu_notifier_retry was checked after taking mmu_lock, and - * 2) mmu_lock is taken now. - * - * We still need to disable IRQs to prevent concurrent tear down - * of page tables. + * Disable IRQs to prevent concurrent tear down of host page tables, + * e.g. if the primary MMU promotes a P*D to a huge page and then frees + * the original page table. */ local_irq_save(flags); + /* + * Read each entry once. As above, a non-leaf entry can be promoted to + * a huge page _during_ this walk. Re-reading the entry could send the + * walk into the weeks, e.g. p*d_large() returns false (sees the old + * value) and then p*d_offset() walks into the target huge page instead + * of the old page table (sees the new value). + */ pgd = READ_ONCE(*pgd_offset(kvm->mm, hva)); if (pgd_none(pgd)) goto out; @@ -2864,7 +3011,7 @@ out: int kvm_mmu_max_mapping_level(struct kvm *kvm, const struct kvm_memory_slot *slot, gfn_t gfn, - kvm_pfn_t pfn, int max_level) + int max_level) { struct kvm_lpage_info *linfo; int host_level; @@ -2879,7 +3026,7 @@ int kvm_mmu_max_mapping_level(struct kvm *kvm, if (max_level == PG_LEVEL_4K) return PG_LEVEL_4K; - host_level = host_pfn_mapping_level(kvm, gfn, pfn, slot); + host_level = host_pfn_mapping_level(kvm, gfn, slot); return min(host_level, max_level); } @@ -2893,7 +3040,7 @@ void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault if (unlikely(fault->max_level == PG_LEVEL_4K)) return; - if (is_error_noslot_pfn(fault->pfn) || kvm_is_reserved_pfn(fault->pfn)) + if (is_error_noslot_pfn(fault->pfn)) return; if (kvm_slot_dirty_track_enabled(slot)) @@ -2904,8 +3051,7 @@ void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault * level, which will be used to do precise, accurate accounting. */ fault->req_level = kvm_mmu_max_mapping_level(vcpu->kvm, slot, - fault->gfn, fault->pfn, - fault->max_level); + fault->gfn, fault->max_level); if (fault->req_level == PG_LEVEL_4K || fault->huge_page_disallowed) return; @@ -2961,13 +3107,10 @@ static int __direct_map(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) if (it.level == fault->goal_level) break; - drop_large_spte(vcpu, it.sptep); - if (is_shadow_present_pte(*it.sptep)) + sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn, true, ACC_ALL); + if (sp == ERR_PTR(-EEXIST)) continue; - sp = kvm_mmu_get_page(vcpu, base_gfn, it.addr, - it.level - 1, true, ACC_ALL); - link_shadow_page(vcpu, it.sptep, sp); if (fault->is_tdp && fault->huge_page_disallowed && fault->req_level >= it.level) @@ -3095,7 +3238,7 @@ fast_pf_fix_direct_spte(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, * * Compare with set_spte where instead shadow_dirty_mask is set. */ - if (cmpxchg64(sptep, old_spte, new_spte) != old_spte) + if (!try_cmpxchg64(sptep, &old_spte, new_spte)) return false; if (is_writable_pte(new_spte) && !is_writable_pte(old_spte)) @@ -3265,7 +3408,7 @@ static void mmu_free_root_page(struct kvm *kvm, hpa_t *root_hpa, if (!VALID_PAGE(*root_hpa)) return; - sp = to_shadow_page(*root_hpa & PT64_BASE_ADDR_MASK); + sp = to_shadow_page(*root_hpa & SPTE_BASE_ADDR_MASK); if (WARN_ON(!sp)) return; @@ -3369,12 +3512,19 @@ static int mmu_check_root(struct kvm_vcpu *vcpu, gfn_t root_gfn) return ret; } -static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, gva_t gva, - u8 level, bool direct) +static hpa_t mmu_alloc_root(struct kvm_vcpu *vcpu, gfn_t gfn, int quadrant, + u8 level) { + union kvm_mmu_page_role role = vcpu->arch.mmu->root_role; struct kvm_mmu_page *sp; - sp = kvm_mmu_get_page(vcpu, gfn, gva, level, direct, ACC_ALL); + role.level = level; + role.quadrant = quadrant; + + WARN_ON_ONCE(quadrant && !role.has_4_byte_gpte); + WARN_ON_ONCE(role.direct && role.has_4_byte_gpte); + + sp = kvm_mmu_get_shadow_page(vcpu, gfn, role); ++sp->root_count; return __pa(sp->spt); @@ -3397,7 +3547,7 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu) root = kvm_tdp_mmu_get_vcpu_root_hpa(vcpu); mmu->root.hpa = root; } else if (shadow_root_level >= PT64_ROOT_4LEVEL) { - root = mmu_alloc_root(vcpu, 0, 0, shadow_root_level, true); + root = mmu_alloc_root(vcpu, 0, 0, shadow_root_level); mmu->root.hpa = root; } else if (shadow_root_level == PT32E_ROOT_LEVEL) { if (WARN_ON_ONCE(!mmu->pae_root)) { @@ -3408,8 +3558,8 @@ static int mmu_alloc_direct_roots(struct kvm_vcpu *vcpu) for (i = 0; i < 4; ++i) { WARN_ON_ONCE(IS_VALID_PAE_ROOT(mmu->pae_root[i])); - root = mmu_alloc_root(vcpu, i << (30 - PAGE_SHIFT), - i << 30, PT32_ROOT_LEVEL, true); + root = mmu_alloc_root(vcpu, i << (30 - PAGE_SHIFT), 0, + PT32_ROOT_LEVEL); mmu->pae_root[i] = root | PT_PRESENT_MASK | shadow_me_value; } @@ -3493,9 +3643,8 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) struct kvm_mmu *mmu = vcpu->arch.mmu; u64 pdptrs[4], pm_mask; gfn_t root_gfn, root_pgd; + int quadrant, i, r; hpa_t root; - unsigned i; - int r; root_pgd = mmu->get_guest_pgd(vcpu); root_gfn = root_pgd >> PAGE_SHIFT; @@ -3533,7 +3682,7 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) */ if (mmu->cpu_role.base.level >= PT64_ROOT_4LEVEL) { root = mmu_alloc_root(vcpu, root_gfn, 0, - mmu->root_role.level, false); + mmu->root_role.level); mmu->root.hpa = root; goto set_root_pgd; } @@ -3578,8 +3727,15 @@ static int mmu_alloc_shadow_roots(struct kvm_vcpu *vcpu) root_gfn = pdptrs[i] >> PAGE_SHIFT; } - root = mmu_alloc_root(vcpu, root_gfn, i << 30, - PT32_ROOT_LEVEL, false); + /* + * If shadowing 32-bit non-PAE page tables, each PAE page + * directory maps one quarter of the guest's non-PAE page + * directory. Othwerise each PAE page direct shadows one guest + * PAE page directory so that quadrant should be 0. + */ + quadrant = (mmu->cpu_role.base.level == PT32_ROOT_LEVEL) ? i : 0; + + root = mmu_alloc_root(vcpu, root_gfn, quadrant, PT32_ROOT_LEVEL); mmu->pae_root[i] = root | pm_mask; } @@ -3737,7 +3893,7 @@ void kvm_mmu_sync_roots(struct kvm_vcpu *vcpu) hpa_t root = vcpu->arch.mmu->pae_root[i]; if (IS_VALID_PAE_ROOT(root)) { - root &= PT64_BASE_ADDR_MASK; + root &= SPTE_BASE_ADDR_MASK; sp = to_shadow_page(root); mmu_sync_children(vcpu, sp, true); } @@ -4155,14 +4311,26 @@ EXPORT_SYMBOL_GPL(kvm_handle_page_fault); int kvm_tdp_page_fault(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault) { - while (fault->max_level > PG_LEVEL_4K) { - int page_num = KVM_PAGES_PER_HPAGE(fault->max_level); - gfn_t base = (fault->addr >> PAGE_SHIFT) & ~(page_num - 1); + /* + * If the guest's MTRRs may be used to compute the "real" memtype, + * restrict the mapping level to ensure KVM uses a consistent memtype + * across the entire mapping. If the host MTRRs are ignored by TDP + * (shadow_memtype_mask is non-zero), and the VM has non-coherent DMA + * (DMA doesn't snoop CPU caches), KVM's ABI is to honor the memtype + * from the guest's MTRRs so that guest accesses to memory that is + * DMA'd aren't cached against the guest's wishes. + * + * Note, KVM may still ultimately ignore guest MTRRs for certain PFNs, + * e.g. KVM will force UC memtype for host MMIO. + */ + if (shadow_memtype_mask && kvm_arch_has_noncoherent_dma(vcpu->kvm)) { + for ( ; fault->max_level > PG_LEVEL_4K; --fault->max_level) { + int page_num = KVM_PAGES_PER_HPAGE(fault->max_level); + gfn_t base = (fault->addr >> PAGE_SHIFT) & ~(page_num - 1); - if (kvm_mtrr_check_gfn_range_consistency(vcpu, base, page_num)) - break; - - --fault->max_level; + if (kvm_mtrr_check_gfn_range_consistency(vcpu, base, page_num)) + break; + } } return direct_page_fault(vcpu, fault); @@ -4567,7 +4735,7 @@ reset_tdp_shadow_zero_bits_mask(struct kvm_mmu *context) if (boot_cpu_is_amd()) __reset_rsvds_bits_mask(shadow_zero_check, reserved_hpa_bits(), - context->root_role.level, false, + context->root_role.level, true, boot_cpu_has(X86_FEATURE_GBPAGES), false, true); else @@ -5199,11 +5367,11 @@ static bool need_remote_flush(u64 old, u64 new) return false; if (!is_shadow_present_pte(new)) return true; - if ((old ^ new) & PT64_BASE_ADDR_MASK) + if ((old ^ new) & SPTE_BASE_ADDR_MASK) return true; old ^= shadow_nx_mask; new ^= shadow_nx_mask; - return (old & ~new & PT64_PERM_MASK) != 0; + return (old & ~new & SPTE_PERM_MASK) != 0; } static u64 mmu_pte_write_fetch_gpte(struct kvm_vcpu *vcpu, gpa_t *gpa, @@ -5328,13 +5496,6 @@ static void kvm_mmu_pte_write(struct kvm_vcpu *vcpu, gpa_t gpa, pgprintk("%s: gpa %llx bytes %d\n", __func__, gpa, bytes); - /* - * No need to care whether allocation memory is successful - * or not since pte prefetch is skipped if it does not have - * enough objects in the cache. - */ - mmu_topup_memory_caches(vcpu, true); - write_lock(&vcpu->kvm->mmu_lock); gentry = mmu_pte_write_fetch_gpte(vcpu, &gpa, &bytes); @@ -5819,9 +5980,25 @@ int kvm_mmu_init_vm(struct kvm *kvm) node->track_write = kvm_mmu_pte_write; node->track_flush_slot = kvm_mmu_invalidate_zap_pages_in_memslot; kvm_page_track_register_notifier(kvm, node); + + kvm->arch.split_page_header_cache.kmem_cache = mmu_page_header_cache; + kvm->arch.split_page_header_cache.gfp_zero = __GFP_ZERO; + + kvm->arch.split_shadow_page_cache.gfp_zero = __GFP_ZERO; + + kvm->arch.split_desc_cache.kmem_cache = pte_list_desc_cache; + kvm->arch.split_desc_cache.gfp_zero = __GFP_ZERO; + return 0; } +static void mmu_free_vm_memory_caches(struct kvm *kvm) +{ + kvm_mmu_free_memory_cache(&kvm->arch.split_desc_cache); + kvm_mmu_free_memory_cache(&kvm->arch.split_page_header_cache); + kvm_mmu_free_memory_cache(&kvm->arch.split_shadow_page_cache); +} + void kvm_mmu_uninit_vm(struct kvm *kvm) { struct kvm_page_track_notifier_node *node = &kvm->arch.mmu_sp_tracker; @@ -5829,9 +6006,11 @@ void kvm_mmu_uninit_vm(struct kvm *kvm) kvm_page_track_unregister_notifier(kvm, node); kvm_mmu_uninit_tdp_mmu(kvm); + + mmu_free_vm_memory_caches(kvm); } -static bool __kvm_zap_rmaps(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) +static bool kvm_rmap_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) { const struct kvm_memory_slot *memslot; struct kvm_memslots *slots; @@ -5853,8 +6032,7 @@ static bool __kvm_zap_rmaps(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) if (WARN_ON_ONCE(start >= end)) continue; - flush = slot_handle_level_range(kvm, memslot, kvm_zap_rmapp, - + flush = slot_handle_level_range(kvm, memslot, __kvm_zap_rmap, PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL, start, end - 1, true, flush); } @@ -5879,7 +6057,7 @@ void kvm_zap_gfn_range(struct kvm *kvm, gfn_t gfn_start, gfn_t gfn_end) kvm_inc_notifier_count(kvm, gfn_start, gfn_end); - flush = __kvm_zap_rmaps(kvm, gfn_start, gfn_end); + flush = kvm_rmap_zap_gfn_range(kvm, gfn_start, gfn_end); if (is_tdp_mmu_enabled(kvm)) { for (i = 0; i < KVM_ADDRESS_SPACE_NUM; i++) @@ -5950,15 +6128,249 @@ void kvm_mmu_slot_remove_write_access(struct kvm *kvm, kvm_arch_flush_remote_tlbs_memslot(kvm, memslot); } +static inline bool need_topup(struct kvm_mmu_memory_cache *cache, int min) +{ + return kvm_mmu_memory_cache_nr_free_objects(cache) < min; +} + +static bool need_topup_split_caches_or_resched(struct kvm *kvm) +{ + if (need_resched() || rwlock_needbreak(&kvm->mmu_lock)) + return true; + + /* + * In the worst case, SPLIT_DESC_CACHE_MIN_NR_OBJECTS descriptors are needed + * to split a single huge page. Calculating how many are actually needed + * is possible but not worth the complexity. + */ + return need_topup(&kvm->arch.split_desc_cache, SPLIT_DESC_CACHE_MIN_NR_OBJECTS) || + need_topup(&kvm->arch.split_page_header_cache, 1) || + need_topup(&kvm->arch.split_shadow_page_cache, 1); +} + +static int topup_split_caches(struct kvm *kvm) +{ + /* + * Allocating rmap list entries when splitting huge pages for nested + * MMUs is uncommon as KVM needs to use a list if and only if there is + * more than one rmap entry for a gfn, i.e. requires an L1 gfn to be + * aliased by multiple L2 gfns and/or from multiple nested roots with + * different roles. Aliasing gfns when using TDP is atypical for VMMs; + * a few gfns are often aliased during boot, e.g. when remapping BIOS, + * but aliasing rarely occurs post-boot or for many gfns. If there is + * only one rmap entry, rmap->val points directly at that one entry and + * doesn't need to allocate a list. Buffer the cache by the default + * capacity so that KVM doesn't have to drop mmu_lock to topup if KVM + * encounters an aliased gfn or two. + */ + const int capacity = SPLIT_DESC_CACHE_MIN_NR_OBJECTS + + KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE; + int r; + + lockdep_assert_held(&kvm->slots_lock); + + r = __kvm_mmu_topup_memory_cache(&kvm->arch.split_desc_cache, capacity, + SPLIT_DESC_CACHE_MIN_NR_OBJECTS); + if (r) + return r; + + r = kvm_mmu_topup_memory_cache(&kvm->arch.split_page_header_cache, 1); + if (r) + return r; + + return kvm_mmu_topup_memory_cache(&kvm->arch.split_shadow_page_cache, 1); +} + +static struct kvm_mmu_page *shadow_mmu_get_sp_for_split(struct kvm *kvm, u64 *huge_sptep) +{ + struct kvm_mmu_page *huge_sp = sptep_to_sp(huge_sptep); + struct shadow_page_caches caches = {}; + union kvm_mmu_page_role role; + unsigned int access; + gfn_t gfn; + + gfn = kvm_mmu_page_get_gfn(huge_sp, spte_index(huge_sptep)); + access = kvm_mmu_page_get_access(huge_sp, spte_index(huge_sptep)); + + /* + * Note, huge page splitting always uses direct shadow pages, regardless + * of whether the huge page itself is mapped by a direct or indirect + * shadow page, since the huge page region itself is being directly + * mapped with smaller pages. + */ + role = kvm_mmu_child_role(huge_sptep, /*direct=*/true, access); + + /* Direct SPs do not require a shadowed_info_cache. */ + caches.page_header_cache = &kvm->arch.split_page_header_cache; + caches.shadow_page_cache = &kvm->arch.split_shadow_page_cache; + + /* Safe to pass NULL for vCPU since requesting a direct SP. */ + return __kvm_mmu_get_shadow_page(kvm, NULL, &caches, gfn, role); +} + +static void shadow_mmu_split_huge_page(struct kvm *kvm, + const struct kvm_memory_slot *slot, + u64 *huge_sptep) + +{ + struct kvm_mmu_memory_cache *cache = &kvm->arch.split_desc_cache; + u64 huge_spte = READ_ONCE(*huge_sptep); + struct kvm_mmu_page *sp; + bool flush = false; + u64 *sptep, spte; + gfn_t gfn; + int index; + + sp = shadow_mmu_get_sp_for_split(kvm, huge_sptep); + + for (index = 0; index < SPTE_ENT_PER_PAGE; index++) { + sptep = &sp->spt[index]; + gfn = kvm_mmu_page_get_gfn(sp, index); + + /* + * The SP may already have populated SPTEs, e.g. if this huge + * page is aliased by multiple sptes with the same access + * permissions. These entries are guaranteed to map the same + * gfn-to-pfn translation since the SP is direct, so no need to + * modify them. + * + * However, if a given SPTE points to a lower level page table, + * that lower level page table may only be partially populated. + * Installing such SPTEs would effectively unmap a potion of the + * huge page. Unmapping guest memory always requires a TLB flush + * since a subsequent operation on the unmapped regions would + * fail to detect the need to flush. + */ + if (is_shadow_present_pte(*sptep)) { + flush |= !is_last_spte(*sptep, sp->role.level); + continue; + } + + spte = make_huge_page_split_spte(kvm, huge_spte, sp->role, index); + mmu_spte_set(sptep, spte); + __rmap_add(kvm, cache, slot, sptep, gfn, sp->role.access); + } + + __link_shadow_page(kvm, cache, huge_sptep, sp, flush); +} + +static int shadow_mmu_try_split_huge_page(struct kvm *kvm, + const struct kvm_memory_slot *slot, + u64 *huge_sptep) +{ + struct kvm_mmu_page *huge_sp = sptep_to_sp(huge_sptep); + int level, r = 0; + gfn_t gfn; + u64 spte; + + /* Grab information for the tracepoint before dropping the MMU lock. */ + gfn = kvm_mmu_page_get_gfn(huge_sp, spte_index(huge_sptep)); + level = huge_sp->role.level; + spte = *huge_sptep; + + if (kvm_mmu_available_pages(kvm) <= KVM_MIN_FREE_MMU_PAGES) { + r = -ENOSPC; + goto out; + } + + if (need_topup_split_caches_or_resched(kvm)) { + write_unlock(&kvm->mmu_lock); + cond_resched(); + /* + * If the topup succeeds, return -EAGAIN to indicate that the + * rmap iterator should be restarted because the MMU lock was + * dropped. + */ + r = topup_split_caches(kvm) ?: -EAGAIN; + write_lock(&kvm->mmu_lock); + goto out; + } + + shadow_mmu_split_huge_page(kvm, slot, huge_sptep); + +out: + trace_kvm_mmu_split_huge_page(gfn, spte, level, r); + return r; +} + +static bool shadow_mmu_try_split_huge_pages(struct kvm *kvm, + struct kvm_rmap_head *rmap_head, + const struct kvm_memory_slot *slot) +{ + struct rmap_iterator iter; + struct kvm_mmu_page *sp; + u64 *huge_sptep; + int r; + +restart: + for_each_rmap_spte(rmap_head, &iter, huge_sptep) { + sp = sptep_to_sp(huge_sptep); + + /* TDP MMU is enabled, so rmap only contains nested MMU SPs. */ + if (WARN_ON_ONCE(!sp->role.guest_mode)) + continue; + + /* The rmaps should never contain non-leaf SPTEs. */ + if (WARN_ON_ONCE(!is_large_pte(*huge_sptep))) + continue; + + /* SPs with level >PG_LEVEL_4K should never by unsync. */ + if (WARN_ON_ONCE(sp->unsync)) + continue; + + /* Don't bother splitting huge pages on invalid SPs. */ + if (sp->role.invalid) + continue; + + r = shadow_mmu_try_split_huge_page(kvm, slot, huge_sptep); + + /* + * The split succeeded or needs to be retried because the MMU + * lock was dropped. Either way, restart the iterator to get it + * back into a consistent state. + */ + if (!r || r == -EAGAIN) + goto restart; + + /* The split failed and shouldn't be retried (e.g. -ENOMEM). */ + break; + } + + return false; +} + +static void kvm_shadow_mmu_try_split_huge_pages(struct kvm *kvm, + const struct kvm_memory_slot *slot, + gfn_t start, gfn_t end, + int target_level) +{ + int level; + + /* + * Split huge pages starting with KVM_MAX_HUGEPAGE_LEVEL and working + * down to the target level. This ensures pages are recursively split + * all the way to the target level. There's no need to split pages + * already at the target level. + */ + for (level = KVM_MAX_HUGEPAGE_LEVEL; level > target_level; level--) { + slot_handle_level_range(kvm, slot, shadow_mmu_try_split_huge_pages, + level, level, start, end - 1, true, false); + } +} + /* Must be called with the mmu_lock held in write-mode. */ void kvm_mmu_try_split_huge_pages(struct kvm *kvm, const struct kvm_memory_slot *memslot, u64 start, u64 end, int target_level) { - if (is_tdp_mmu_enabled(kvm)) - kvm_tdp_mmu_try_split_huge_pages(kvm, memslot, start, end, - target_level, false); + if (!is_tdp_mmu_enabled(kvm)) + return; + + if (kvm_memslots_have_rmaps(kvm)) + kvm_shadow_mmu_try_split_huge_pages(kvm, memslot, start, end, target_level); + + kvm_tdp_mmu_try_split_huge_pages(kvm, memslot, start, end, target_level, false); /* * A TLB flush is unnecessary at this point for the same resons as in @@ -5973,12 +6385,19 @@ void kvm_mmu_slot_try_split_huge_pages(struct kvm *kvm, u64 start = memslot->base_gfn; u64 end = start + memslot->npages; - if (is_tdp_mmu_enabled(kvm)) { - read_lock(&kvm->mmu_lock); - kvm_tdp_mmu_try_split_huge_pages(kvm, memslot, start, end, target_level, true); - read_unlock(&kvm->mmu_lock); + if (!is_tdp_mmu_enabled(kvm)) + return; + + if (kvm_memslots_have_rmaps(kvm)) { + write_lock(&kvm->mmu_lock); + kvm_shadow_mmu_try_split_huge_pages(kvm, memslot, start, end, target_level); + write_unlock(&kvm->mmu_lock); } + read_lock(&kvm->mmu_lock); + kvm_tdp_mmu_try_split_huge_pages(kvm, memslot, start, end, target_level, true); + read_unlock(&kvm->mmu_lock); + /* * No TLB flush is necessary here. KVM will flush TLBs after * write-protecting and/or clearing dirty on the newly split SPTEs to @@ -5997,13 +6416,11 @@ static bool kvm_mmu_zap_collapsible_spte(struct kvm *kvm, u64 *sptep; struct rmap_iterator iter; int need_tlb_flush = 0; - kvm_pfn_t pfn; struct kvm_mmu_page *sp; restart: for_each_rmap_spte(rmap_head, &iter, sptep) { sp = sptep_to_sp(sptep); - pfn = spte_to_pfn(*sptep); /* * We cannot do huge page mapping for indirect shadow pages, @@ -6012,10 +6429,10 @@ restart: * the guest, and the guest page table is using 4K page size * mapping if the indirect sp has level = 1. */ - if (sp->role.direct && !kvm_is_reserved_pfn(pfn) && + if (sp->role.direct && sp->role.level < kvm_mmu_max_mapping_level(kvm, slot, sp->gfn, - pfn, PG_LEVEL_NUM)) { - pte_list_remove(kvm, rmap_head, sptep); + PG_LEVEL_NUM)) { + kvm_zap_one_rmap_spte(kvm, rmap_head, sptep); if (kvm_available_flush_tlb_with_range()) kvm_flush_remote_tlbs_with_address(kvm, sp->gfn, @@ -6030,18 +6447,24 @@ restart: return need_tlb_flush; } +static void kvm_rmap_zap_collapsible_sptes(struct kvm *kvm, + const struct kvm_memory_slot *slot) +{ + /* + * Note, use KVM_MAX_HUGEPAGE_LEVEL - 1 since there's no need to zap + * pages that are already mapped at the maximum hugepage level. + */ + if (slot_handle_level(kvm, slot, kvm_mmu_zap_collapsible_spte, + PG_LEVEL_4K, KVM_MAX_HUGEPAGE_LEVEL - 1, true)) + kvm_arch_flush_remote_tlbs_memslot(kvm, slot); +} + void kvm_mmu_zap_collapsible_sptes(struct kvm *kvm, const struct kvm_memory_slot *slot) { if (kvm_memslots_have_rmaps(kvm)) { write_lock(&kvm->mmu_lock); - /* - * Zap only 4k SPTEs since the legacy MMU only supports dirty - * logging at a 4k granularity and never creates collapsible - * 2m SPTEs during dirty logging. - */ - if (slot_handle_level_4k(kvm, slot, kvm_mmu_zap_collapsible_spte, true)) - kvm_arch_flush_remote_tlbs_memslot(kvm, slot); + kvm_rmap_zap_collapsible_sptes(kvm, slot); write_unlock(&kvm->mmu_lock); } diff --git a/arch/x86/kvm/mmu/mmu_internal.h b/arch/x86/kvm/mmu/mmu_internal.h index bd2a26897b97..582def531d4d 100644 --- a/arch/x86/kvm/mmu/mmu_internal.h +++ b/arch/x86/kvm/mmu/mmu_internal.h @@ -20,6 +20,20 @@ extern bool dbg; #define MMU_WARN_ON(x) do { } while (0) #endif +/* Page table builder macros common to shadow (host) PTEs and guest PTEs. */ +#define __PT_LEVEL_SHIFT(level, bits_per_level) \ + (PAGE_SHIFT + ((level) - 1) * (bits_per_level)) +#define __PT_INDEX(address, level, bits_per_level) \ + (((address) >> __PT_LEVEL_SHIFT(level, bits_per_level)) & ((1 << (bits_per_level)) - 1)) + +#define __PT_LVL_ADDR_MASK(base_addr_mask, level, bits_per_level) \ + ((base_addr_mask) & ~((1ULL << (PAGE_SHIFT + (((level) - 1) * (bits_per_level)))) - 1)) + +#define __PT_LVL_OFFSET_MASK(base_addr_mask, level, bits_per_level) \ + ((base_addr_mask) & ((1ULL << (PAGE_SHIFT + (((level) - 1) * (bits_per_level)))) - 1)) + +#define __PT_ENT_PER_PAGE(bits_per_level) (1 << (bits_per_level)) + /* * Unlike regular MMU roots, PAE "roots", a.k.a. PDPTEs/PDPTRs, have a PRESENT * bit, and thus are guaranteed to be non-zero when valid. And, when a guest @@ -53,8 +67,21 @@ struct kvm_mmu_page { gfn_t gfn; u64 *spt; - /* hold the gfn of each spte inside spt */ - gfn_t *gfns; + + /* + * Stores the result of the guest translation being shadowed by each + * SPTE. KVM shadows two types of guest translations: nGPA -> GPA + * (shadow EPT/NPT) and GVA -> GPA (traditional shadow paging). In both + * cases the result of the translation is a GPA and a set of access + * constraints. + * + * The GFN is stored in the upper bits (PAGE_SHIFT) and the shadowed + * access permissions are stored in the lower bits. Note, for + * convenience and uniformity across guests, the access permissions are + * stored in KVM format (e.g. ACC_EXEC_MASK) not the raw guest format. + */ + u64 *shadowed_translation; + /* Currently serving as active root */ union { int root_count; @@ -141,9 +168,9 @@ void kvm_flush_remote_tlbs_with_address(struct kvm *kvm, unsigned int pte_list_count(struct kvm_rmap_head *rmap_head); extern int nx_huge_pages; -static inline bool is_nx_huge_page_enabled(void) +static inline bool is_nx_huge_page_enabled(struct kvm *kvm) { - return READ_ONCE(nx_huge_pages); + return READ_ONCE(nx_huge_pages) && !kvm->arch.disable_nx_huge_pages; } struct kvm_page_fault { @@ -242,7 +269,8 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, .user = err & PFERR_USER_MASK, .prefetch = prefetch, .is_tdp = likely(vcpu->arch.mmu->page_fault == kvm_tdp_page_fault), - .nx_huge_page_workaround_enabled = is_nx_huge_page_enabled(), + .nx_huge_page_workaround_enabled = + is_nx_huge_page_enabled(vcpu->kvm), .max_level = KVM_MAX_HUGEPAGE_LEVEL, .req_level = PG_LEVEL_4K, @@ -281,7 +309,7 @@ static inline int kvm_mmu_do_page_fault(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, int kvm_mmu_max_mapping_level(struct kvm *kvm, const struct kvm_memory_slot *slot, gfn_t gfn, - kvm_pfn_t pfn, int max_level); + int max_level); void kvm_mmu_hugepage_adjust(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault); void disallowed_hugepage_adjust(struct kvm_page_fault *fault, u64 spte, int cur_level); diff --git a/arch/x86/kvm/mmu/paging.h b/arch/x86/kvm/mmu/paging.h deleted file mode 100644 index de8ab323bb70..000000000000 --- a/arch/x86/kvm/mmu/paging.h +++ /dev/null @@ -1,14 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* Shadow paging constants/helpers that don't need to be #undef'd. */ -#ifndef __KVM_X86_PAGING_H -#define __KVM_X86_PAGING_H - -#define GUEST_PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) -#define PT64_LVL_ADDR_MASK(level) \ - (GUEST_PT64_BASE_ADDR_MASK & ~((1ULL << (PAGE_SHIFT + (((level) - 1) \ - * PT64_LEVEL_BITS))) - 1)) -#define PT64_LVL_OFFSET_MASK(level) \ - (GUEST_PT64_BASE_ADDR_MASK & ((1ULL << (PAGE_SHIFT + (((level) - 1) \ - * PT64_LEVEL_BITS))) - 1)) -#endif /* __KVM_X86_PAGING_H */ - diff --git a/arch/x86/kvm/mmu/paging_tmpl.h b/arch/x86/kvm/mmu/paging_tmpl.h index db80f7ccaa4e..f5958071220c 100644 --- a/arch/x86/kvm/mmu/paging_tmpl.h +++ b/arch/x86/kvm/mmu/paging_tmpl.h @@ -16,25 +16,21 @@ */ /* - * We need the mmu code to access both 32-bit and 64-bit guest ptes, - * so the code in this file is compiled twice, once per pte size. + * The MMU needs to be able to access/walk 32-bit and 64-bit guest page tables, + * as well as guest EPT tables, so the code in this file is compiled thrice, + * once per guest PTE type. The per-type defines are #undef'd at the end. */ #if PTTYPE == 64 #define pt_element_t u64 #define guest_walker guest_walker64 #define FNAME(name) paging##64_##name - #define PT_BASE_ADDR_MASK GUEST_PT64_BASE_ADDR_MASK - #define PT_LVL_ADDR_MASK(lvl) PT64_LVL_ADDR_MASK(lvl) - #define PT_LVL_OFFSET_MASK(lvl) PT64_LVL_OFFSET_MASK(lvl) - #define PT_INDEX(addr, level) PT64_INDEX(addr, level) - #define PT_LEVEL_BITS PT64_LEVEL_BITS + #define PT_LEVEL_BITS 9 #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT #define PT_HAVE_ACCESSED_DIRTY(mmu) true #ifdef CONFIG_X86_64 #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL - #define CMPXCHG "cmpxchgq" #else #define PT_MAX_FULL_LEVELS 2 #endif @@ -42,36 +38,35 @@ #define pt_element_t u32 #define guest_walker guest_walker32 #define FNAME(name) paging##32_##name - #define PT_BASE_ADDR_MASK PT32_BASE_ADDR_MASK - #define PT_LVL_ADDR_MASK(lvl) PT32_LVL_ADDR_MASK(lvl) - #define PT_LVL_OFFSET_MASK(lvl) PT32_LVL_OFFSET_MASK(lvl) - #define PT_INDEX(addr, level) PT32_INDEX(addr, level) - #define PT_LEVEL_BITS PT32_LEVEL_BITS + #define PT_LEVEL_BITS 10 #define PT_MAX_FULL_LEVELS 2 #define PT_GUEST_DIRTY_SHIFT PT_DIRTY_SHIFT #define PT_GUEST_ACCESSED_SHIFT PT_ACCESSED_SHIFT #define PT_HAVE_ACCESSED_DIRTY(mmu) true - #define CMPXCHG "cmpxchgl" + + #define PT32_DIR_PSE36_SIZE 4 + #define PT32_DIR_PSE36_SHIFT 13 + #define PT32_DIR_PSE36_MASK \ + (((1ULL << PT32_DIR_PSE36_SIZE) - 1) << PT32_DIR_PSE36_SHIFT) #elif PTTYPE == PTTYPE_EPT #define pt_element_t u64 #define guest_walker guest_walkerEPT #define FNAME(name) ept_##name - #define PT_BASE_ADDR_MASK GUEST_PT64_BASE_ADDR_MASK - #define PT_LVL_ADDR_MASK(lvl) PT64_LVL_ADDR_MASK(lvl) - #define PT_LVL_OFFSET_MASK(lvl) PT64_LVL_OFFSET_MASK(lvl) - #define PT_INDEX(addr, level) PT64_INDEX(addr, level) - #define PT_LEVEL_BITS PT64_LEVEL_BITS + #define PT_LEVEL_BITS 9 #define PT_GUEST_DIRTY_SHIFT 9 #define PT_GUEST_ACCESSED_SHIFT 8 #define PT_HAVE_ACCESSED_DIRTY(mmu) (!(mmu)->cpu_role.base.ad_disabled) - #ifdef CONFIG_X86_64 - #define CMPXCHG "cmpxchgq" - #endif #define PT_MAX_FULL_LEVELS PT64_ROOT_MAX_LEVEL #else #error Invalid PTTYPE value #endif +/* Common logic, but per-type values. These also need to be undefined. */ +#define PT_BASE_ADDR_MASK ((pt_element_t)(((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1))) +#define PT_LVL_ADDR_MASK(lvl) __PT_LVL_ADDR_MASK(PT_BASE_ADDR_MASK, lvl, PT_LEVEL_BITS) +#define PT_LVL_OFFSET_MASK(lvl) __PT_LVL_OFFSET_MASK(PT_BASE_ADDR_MASK, lvl, PT_LEVEL_BITS) +#define PT_INDEX(addr, lvl) __PT_INDEX(addr, lvl, PT_LEVEL_BITS) + #define PT_GUEST_DIRTY_MASK (1 << PT_GUEST_DIRTY_SHIFT) #define PT_GUEST_ACCESSED_MASK (1 << PT_GUEST_ACCESSED_SHIFT) @@ -97,6 +92,15 @@ struct guest_walker { struct x86_exception fault; }; +#if PTTYPE == 32 +static inline gfn_t pse36_gfn_delta(u32 gpte) +{ + int shift = 32 - PT32_DIR_PSE36_SHIFT - PAGE_SHIFT; + + return (gpte & PT32_DIR_PSE36_MASK) << shift; +} +#endif + static gfn_t gpte_to_gfn_lvl(pt_element_t gpte, int lvl) { return (gpte & PT_LVL_ADDR_MASK(lvl)) >> PAGE_SHIFT; @@ -374,7 +378,7 @@ retry_walk: * information to fix the exit_qualification or exit_info_1 * fields. */ - if (unlikely(real_gpa == UNMAPPED_GVA)) + if (unlikely(real_gpa == INVALID_GPA)) return 0; host_addr = kvm_vcpu_gfn_to_hva_prot(vcpu, gpa_to_gfn(real_gpa), @@ -421,11 +425,13 @@ retry_walk: gfn = gpte_to_gfn_lvl(pte, walker->level); gfn += (addr & PT_LVL_OFFSET_MASK(walker->level)) >> PAGE_SHIFT; - if (PTTYPE == 32 && walker->level > PG_LEVEL_4K && is_cpuid_PSE36()) +#if PTTYPE == 32 + if (walker->level > PG_LEVEL_4K && is_cpuid_PSE36()) gfn += pse36_gfn_delta(pte); +#endif real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(gfn), access, &walker->fault); - if (real_gpa == UNMAPPED_GVA) + if (real_gpa == INVALID_GPA) return 0; walker->gfn = real_gpa >> PAGE_SHIFT; @@ -589,7 +595,7 @@ static void FNAME(pte_prefetch)(struct kvm_vcpu *vcpu, struct guest_walker *gw, if (sp->role.direct) return __direct_pte_prefetch(vcpu, sp, sptep); - i = (sptep - sp->spt) & ~(PTE_PREFETCH_NUM - 1); + i = spte_index(sptep) & ~(PTE_PREFETCH_NUM - 1); spte = sp->spt + i; for (i = 0; i < PTE_PREFETCH_NUM; i++, spte++) { @@ -642,14 +648,13 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, gfn_t table_gfn; clear_sp_write_flooding_count(it.sptep); - drop_large_spte(vcpu, it.sptep); - sp = NULL; - if (!is_shadow_present_pte(*it.sptep)) { - table_gfn = gw->table_gfn[it.level - 2]; - access = gw->pt_access[it.level - 2]; - sp = kvm_mmu_get_page(vcpu, table_gfn, fault->addr, - it.level-1, false, access); + table_gfn = gw->table_gfn[it.level - 2]; + access = gw->pt_access[it.level - 2]; + sp = kvm_mmu_get_child_sp(vcpu, it.sptep, table_gfn, + false, access); + + if (sp != ERR_PTR(-EEXIST)) { /* * We must synchronize the pagetable before linking it * because the guest doesn't need to flush tlb when @@ -678,7 +683,7 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, if (FNAME(gpte_changed)(vcpu, gw, it.level - 1)) goto out_gpte_changed; - if (sp) + if (sp != ERR_PTR(-EEXIST)) link_shadow_page(vcpu, it.sptep, sp); } @@ -702,16 +707,15 @@ static int FNAME(fetch)(struct kvm_vcpu *vcpu, struct kvm_page_fault *fault, validate_direct_spte(vcpu, it.sptep, direct_access); - drop_large_spte(vcpu, it.sptep); + sp = kvm_mmu_get_child_sp(vcpu, it.sptep, base_gfn, + true, direct_access); + if (sp == ERR_PTR(-EEXIST)) + continue; - if (!is_shadow_present_pte(*it.sptep)) { - sp = kvm_mmu_get_page(vcpu, base_gfn, fault->addr, - it.level - 1, true, direct_access); - link_shadow_page(vcpu, it.sptep, sp); - if (fault->huge_page_disallowed && - fault->req_level >= it.level) - account_huge_nx_page(vcpu->kvm, sp); - } + link_shadow_page(vcpu, it.sptep, sp); + if (fault->huge_page_disallowed && + fault->req_level >= it.level) + account_huge_nx_page(vcpu->kvm, sp); } if (WARN_ON_ONCE(it.level != fault->goal_level)) @@ -888,7 +892,7 @@ static gpa_t FNAME(get_level1_sp_gpa)(struct kvm_mmu_page *sp) WARN_ON(sp->role.level != PG_LEVEL_4K); if (PTTYPE == 32) - offset = sp->role.quadrant << PT64_LEVEL_BITS; + offset = sp->role.quadrant << SPTE_LEVEL_BITS; return gfn_to_gpa(sp->gfn) + offset * sizeof(pt_element_t); } @@ -929,7 +933,7 @@ static void FNAME(invlpg)(struct kvm_vcpu *vcpu, gva_t gva, hpa_t root_hpa) break; pte_gpa = FNAME(get_level1_sp_gpa)(sp); - pte_gpa += (sptep - sp->spt) * sizeof(pt_element_t); + pte_gpa += spte_index(sptep) * sizeof(pt_element_t); mmu_page_zap_pte(vcpu->kvm, sp, sptep, NULL); if (is_shadow_present_pte(old_spte)) @@ -958,7 +962,7 @@ static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, struct x86_exception *exception) { struct guest_walker walker; - gpa_t gpa = UNMAPPED_GVA; + gpa_t gpa = INVALID_GPA; int r; #ifndef CONFIG_X86_64 @@ -978,7 +982,8 @@ static gpa_t FNAME(gva_to_gpa)(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, } /* - * Using the cached information from sp->gfns is safe because: + * Using the information in sp->shadowed_translation (kvm_mmu_page_get_gfn()) is + * safe because: * - The spte has a reference to the struct page, so the pfn for a given gfn * can't change unless all sptes pointing to it are nuked first. * @@ -1023,7 +1028,7 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) first_pte_gpa = FNAME(get_level1_sp_gpa)(sp); - for (i = 0; i < PT64_ENT_PER_PAGE; i++) { + for (i = 0; i < SPTE_ENT_PER_PAGE; i++) { u64 *sptep, spte; struct kvm_memory_slot *slot; unsigned pte_access; @@ -1053,12 +1058,23 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) if (sync_mmio_spte(vcpu, &sp->spt[i], gfn, pte_access)) continue; - if (gfn != sp->gfns[i]) { + /* + * Drop the SPTE if the new protections would result in a RWX=0 + * SPTE or if the gfn is changing. The RWX=0 case only affects + * EPT with execute-only support, i.e. EPT without an effective + * "present" bit, as all other paging modes will create a + * read-only SPTE if pte_access is zero. + */ + if ((!pte_access && !shadow_present_mask) || + gfn != kvm_mmu_page_get_gfn(sp, i)) { drop_spte(vcpu->kvm, &sp->spt[i]); flush = true; continue; } + /* Update the shadowed access bits in case they changed. */ + kvm_mmu_page_set_access(sp, i, pte_access); + sptep = &sp->spt[i]; spte = *sptep; host_writable = spte & shadow_host_writable_mask; @@ -1070,6 +1086,15 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) flush |= mmu_spte_update(sptep, spte); } + /* + * Note, any flush is purely for KVM's correctness, e.g. when dropping + * an existing SPTE or clearing W/A/D bits to ensure an mmu_notifier + * unmap or dirty logging event doesn't fail to flush. The guest is + * responsible for flushing the TLB to ensure any changes in protection + * bits are recognized, i.e. until the guest flushes or page faults on + * a relevant address, KVM is architecturally allowed to let vCPUs use + * cached translations with the old protection bits. + */ return flush; } @@ -1084,7 +1109,6 @@ static int FNAME(sync_page)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp) #undef PT_MAX_FULL_LEVELS #undef gpte_to_gfn #undef gpte_to_gfn_lvl -#undef CMPXCHG #undef PT_GUEST_ACCESSED_MASK #undef PT_GUEST_DIRTY_MASK #undef PT_GUEST_DIRTY_SHIFT diff --git a/arch/x86/kvm/mmu/spte.c b/arch/x86/kvm/mmu/spte.c index b5960bbde7f7..7314d27d57a4 100644 --- a/arch/x86/kvm/mmu/spte.c +++ b/arch/x86/kvm/mmu/spte.c @@ -33,6 +33,7 @@ u64 __read_mostly shadow_mmio_value; u64 __read_mostly shadow_mmio_mask; u64 __read_mostly shadow_mmio_access_mask; u64 __read_mostly shadow_present_mask; +u64 __read_mostly shadow_memtype_mask; u64 __read_mostly shadow_me_value; u64 __read_mostly shadow_me_mask; u64 __read_mostly shadow_acc_track_mask; @@ -129,6 +130,8 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, u64 spte = SPTE_MMU_PRESENT_MASK; bool wrprot = false; + WARN_ON_ONCE(!pte_access && !shadow_present_mask); + if (sp->role.ad_disabled) spte |= SPTE_TDP_AD_DISABLED_MASK; else if (kvm_mmu_page_ad_need_write_protect(sp)) @@ -145,7 +148,7 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, spte |= spte_shadow_accessed_mask(spte); if (level > PG_LEVEL_4K && (pte_access & ACC_EXEC_MASK) && - is_nx_huge_page_enabled()) { + is_nx_huge_page_enabled(vcpu->kvm)) { pte_access &= ~ACC_EXEC_MASK; } @@ -159,10 +162,10 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, if (level > PG_LEVEL_4K) spte |= PT_PAGE_SIZE_MASK; - if (tdp_enabled) - spte |= static_call(kvm_x86_get_mt_mask)(vcpu, gfn, - kvm_is_mmio_pfn(pfn)); + if (shadow_memtype_mask) + spte |= static_call(kvm_x86_get_mt_mask)(vcpu, gfn, + kvm_is_mmio_pfn(pfn)); if (host_writable) spte |= shadow_host_writable_mask; else @@ -244,10 +247,10 @@ static u64 make_spte_executable(u64 spte) * This is used during huge page splitting to build the SPTEs that make up the * new page table. */ -u64 make_huge_page_split_spte(u64 huge_spte, int huge_level, int index) +u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, union kvm_mmu_page_role role, + int index) { u64 child_spte; - int child_level; if (WARN_ON_ONCE(!is_shadow_present_pte(huge_spte))) return 0; @@ -256,23 +259,23 @@ u64 make_huge_page_split_spte(u64 huge_spte, int huge_level, int index) return 0; child_spte = huge_spte; - child_level = huge_level - 1; /* * The child_spte already has the base address of the huge page being * split. So we just have to OR in the offset to the page at the next * lower level for the given index. */ - child_spte |= (index * KVM_PAGES_PER_HPAGE(child_level)) << PAGE_SHIFT; + child_spte |= (index * KVM_PAGES_PER_HPAGE(role.level)) << PAGE_SHIFT; - if (child_level == PG_LEVEL_4K) { + if (role.level == PG_LEVEL_4K) { child_spte &= ~PT_PAGE_SIZE_MASK; /* - * When splitting to a 4K page, mark the page executable as the - * NX hugepage mitigation no longer applies. + * When splitting to a 4K page where execution is allowed, mark + * the page executable as the NX hugepage mitigation no longer + * applies. */ - if (is_nx_huge_page_enabled()) + if ((role.access & ACC_EXEC_MASK) && is_nx_huge_page_enabled(kvm)) child_spte = make_spte_executable(child_spte); } @@ -299,7 +302,7 @@ u64 kvm_mmu_changed_pte_notifier_make_spte(u64 old_spte, kvm_pfn_t new_pfn) { u64 new_spte; - new_spte = old_spte & ~PT64_BASE_ADDR_MASK; + new_spte = old_spte & ~SPTE_BASE_ADDR_MASK; new_spte |= (u64)new_pfn << PAGE_SHIFT; new_spte &= ~PT_WRITABLE_MASK; @@ -389,6 +392,13 @@ void kvm_mmu_set_ept_masks(bool has_ad_bits, bool has_exec_only) shadow_nx_mask = 0ull; shadow_x_mask = VMX_EPT_EXECUTABLE_MASK; shadow_present_mask = has_exec_only ? 0ull : VMX_EPT_READABLE_MASK; + /* + * EPT overrides the host MTRRs, and so KVM must program the desired + * memtype directly into the SPTEs. Note, this mask is just the mask + * of all bits that factor into the memtype, the actual memtype must be + * dynamically calculated, e.g. to ensure host MMIO is mapped UC. + */ + shadow_memtype_mask = VMX_EPT_MT_MASK | VMX_EPT_IPAT_BIT; shadow_acc_track_mask = VMX_EPT_RWX_MASK; shadow_host_writable_mask = EPT_SPTE_HOST_WRITABLE; shadow_mmu_writable_mask = EPT_SPTE_MMU_WRITABLE; @@ -439,6 +449,13 @@ void kvm_mmu_reset_all_pte_masks(void) shadow_nx_mask = PT64_NX_MASK; shadow_x_mask = 0; shadow_present_mask = PT_PRESENT_MASK; + + /* + * For shadow paging and NPT, KVM uses PAT entry '0' to encode WB + * memtype in the SPTEs, i.e. relies on host MTRRs to provide the + * correct memtype (WB is the "weakest" memtype). + */ + shadow_memtype_mask = 0; shadow_acc_track_mask = 0; shadow_me_mask = 0; shadow_me_value = 0; diff --git a/arch/x86/kvm/mmu/spte.h b/arch/x86/kvm/mmu/spte.h index 0127bb6e3c7d..cabe3fbb4f39 100644 --- a/arch/x86/kvm/mmu/spte.h +++ b/arch/x86/kvm/mmu/spte.h @@ -36,12 +36,12 @@ extern bool __read_mostly enable_mmio_caching; static_assert(SPTE_TDP_AD_ENABLED_MASK == 0); #ifdef CONFIG_DYNAMIC_PHYSICAL_MASK -#define PT64_BASE_ADDR_MASK (physical_mask & ~(u64)(PAGE_SIZE-1)) +#define SPTE_BASE_ADDR_MASK (physical_mask & ~(u64)(PAGE_SIZE-1)) #else -#define PT64_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) +#define SPTE_BASE_ADDR_MASK (((1ULL << 52) - 1) & ~(u64)(PAGE_SIZE-1)) #endif -#define PT64_PERM_MASK (PT_PRESENT_MASK | PT_WRITABLE_MASK | shadow_user_mask \ +#define SPTE_PERM_MASK (PT_PRESENT_MASK | PT_WRITABLE_MASK | shadow_user_mask \ | shadow_x_mask | shadow_nx_mask | shadow_me_mask) #define ACC_EXEC_MASK 1 @@ -50,17 +50,13 @@ static_assert(SPTE_TDP_AD_ENABLED_MASK == 0); #define ACC_ALL (ACC_EXEC_MASK | ACC_WRITE_MASK | ACC_USER_MASK) /* The mask for the R/X bits in EPT PTEs */ -#define PT64_EPT_READABLE_MASK 0x1ull -#define PT64_EPT_EXECUTABLE_MASK 0x4ull +#define SPTE_EPT_READABLE_MASK 0x1ull +#define SPTE_EPT_EXECUTABLE_MASK 0x4ull -#define PT64_LEVEL_BITS 9 - -#define PT64_LEVEL_SHIFT(level) \ - (PAGE_SHIFT + (level - 1) * PT64_LEVEL_BITS) - -#define PT64_INDEX(address, level)\ - (((address) >> PT64_LEVEL_SHIFT(level)) & ((1 << PT64_LEVEL_BITS) - 1)) -#define SHADOW_PT_INDEX(addr, level) PT64_INDEX(addr, level) +#define SPTE_LEVEL_BITS 9 +#define SPTE_LEVEL_SHIFT(level) __PT_LEVEL_SHIFT(level, SPTE_LEVEL_BITS) +#define SPTE_INDEX(address, level) __PT_INDEX(address, level, SPTE_LEVEL_BITS) +#define SPTE_ENT_PER_PAGE __PT_ENT_PER_PAGE(SPTE_LEVEL_BITS) /* * The mask/shift to use for saving the original R/X bits when marking the PTE @@ -69,8 +65,8 @@ static_assert(SPTE_TDP_AD_ENABLED_MASK == 0); * restored only when a write is attempted to the page. This mask obviously * must not overlap the A/D type mask. */ -#define SHADOW_ACC_TRACK_SAVED_BITS_MASK (PT64_EPT_READABLE_MASK | \ - PT64_EPT_EXECUTABLE_MASK) +#define SHADOW_ACC_TRACK_SAVED_BITS_MASK (SPTE_EPT_READABLE_MASK | \ + SPTE_EPT_EXECUTABLE_MASK) #define SHADOW_ACC_TRACK_SAVED_BITS_SHIFT 54 #define SHADOW_ACC_TRACK_SAVED_MASK (SHADOW_ACC_TRACK_SAVED_BITS_MASK << \ SHADOW_ACC_TRACK_SAVED_BITS_SHIFT) @@ -151,6 +147,7 @@ extern u64 __read_mostly shadow_mmio_value; extern u64 __read_mostly shadow_mmio_mask; extern u64 __read_mostly shadow_mmio_access_mask; extern u64 __read_mostly shadow_present_mask; +extern u64 __read_mostly shadow_memtype_mask; extern u64 __read_mostly shadow_me_value; extern u64 __read_mostly shadow_me_mask; @@ -194,6 +191,12 @@ static inline bool is_removed_spte(u64 spte) return spte == REMOVED_SPTE; } +/* Get an SPTE's index into its parent's page table (and the spt array). */ +static inline int spte_index(u64 *sptep) +{ + return ((unsigned long)sptep / sizeof(*sptep)) & (SPTE_ENT_PER_PAGE - 1); +} + /* * In some cases, we need to preserve the GFN of a non-present or reserved * SPTE when we usurp the upper five bits of the physical address space to @@ -282,7 +285,7 @@ static inline bool is_executable_pte(u64 spte) static inline kvm_pfn_t spte_to_pfn(u64 pte) { - return (pte & PT64_BASE_ADDR_MASK) >> PAGE_SHIFT; + return (pte & SPTE_BASE_ADDR_MASK) >> PAGE_SHIFT; } static inline bool is_accessed_spte(u64 spte) @@ -425,7 +428,8 @@ bool make_spte(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, unsigned int pte_access, gfn_t gfn, kvm_pfn_t pfn, u64 old_spte, bool prefetch, bool can_unsync, bool host_writable, u64 *new_spte); -u64 make_huge_page_split_spte(u64 huge_spte, int huge_level, int index); +u64 make_huge_page_split_spte(struct kvm *kvm, u64 huge_spte, + union kvm_mmu_page_role role, int index); u64 make_nonleaf_spte(u64 *child_pt, bool ad_disabled); u64 make_mmio_spte(struct kvm_vcpu *vcpu, u64 gfn, unsigned int access); u64 mark_spte_for_access_track(u64 spte); diff --git a/arch/x86/kvm/mmu/tdp_iter.c b/arch/x86/kvm/mmu/tdp_iter.c index ee4802d7b36c..39b48e7d7d1a 100644 --- a/arch/x86/kvm/mmu/tdp_iter.c +++ b/arch/x86/kvm/mmu/tdp_iter.c @@ -11,7 +11,7 @@ static void tdp_iter_refresh_sptep(struct tdp_iter *iter) { iter->sptep = iter->pt_path[iter->level - 1] + - SHADOW_PT_INDEX(iter->gfn << PAGE_SHIFT, iter->level); + SPTE_INDEX(iter->gfn << PAGE_SHIFT, iter->level); iter->old_spte = kvm_tdp_mmu_read_spte(iter->sptep); } @@ -116,8 +116,8 @@ static bool try_step_side(struct tdp_iter *iter) * Check if the iterator is already at the end of the current page * table. */ - if (SHADOW_PT_INDEX(iter->gfn << PAGE_SHIFT, iter->level) == - (PT64_ENT_PER_PAGE - 1)) + if (SPTE_INDEX(iter->gfn << PAGE_SHIFT, iter->level) == + (SPTE_ENT_PER_PAGE - 1)) return false; iter->gfn += KVM_PAGES_PER_HPAGE(iter->level); @@ -145,15 +145,6 @@ static bool try_step_up(struct tdp_iter *iter) return true; } -/* - * Step the iterator back up a level in the paging structure. Should only be - * used when the iterator is below the root level. - */ -void tdp_iter_step_up(struct tdp_iter *iter) -{ - WARN_ON(!try_step_up(iter)); -} - /* * Step to the next SPTE in a pre-order traversal of the paging structure. * To get to the next SPTE, the iterator either steps down towards the goal diff --git a/arch/x86/kvm/mmu/tdp_iter.h b/arch/x86/kvm/mmu/tdp_iter.h index adfca0cf94d3..f0af385c56e0 100644 --- a/arch/x86/kvm/mmu/tdp_iter.h +++ b/arch/x86/kvm/mmu/tdp_iter.h @@ -114,6 +114,5 @@ void tdp_iter_start(struct tdp_iter *iter, struct kvm_mmu_page *root, int min_level, gfn_t next_last_level_gfn); void tdp_iter_next(struct tdp_iter *iter); void tdp_iter_restart(struct tdp_iter *iter); -void tdp_iter_step_up(struct tdp_iter *iter); #endif /* __KVM_X86_MMU_TDP_ITER_H */ diff --git a/arch/x86/kvm/mmu/tdp_mmu.c b/arch/x86/kvm/mmu/tdp_mmu.c index 7b9265d67131..bf2ccf9debca 100644 --- a/arch/x86/kvm/mmu/tdp_mmu.c +++ b/arch/x86/kvm/mmu/tdp_mmu.c @@ -425,7 +425,7 @@ static void handle_removed_pt(struct kvm *kvm, tdp_ptep_t pt, bool shared) tdp_mmu_unlink_sp(kvm, sp, shared); - for (i = 0; i < PT64_ENT_PER_PAGE; i++) { + for (i = 0; i < SPTE_ENT_PER_PAGE; i++) { tdp_ptep_t sptep = pt + i; gfn_t gfn = base_gfn + i * KVM_PAGES_PER_HPAGE(level); u64 old_spte; @@ -633,7 +633,6 @@ static inline int tdp_mmu_set_spte_atomic(struct kvm *kvm, u64 new_spte) { u64 *sptep = rcu_dereference(iter->sptep); - u64 old_spte; /* * The caller is responsible for ensuring the old SPTE is not a REMOVED @@ -649,17 +648,8 @@ static inline int tdp_mmu_set_spte_atomic(struct kvm *kvm, * Note, fast_pf_fix_direct_spte() can also modify TDP MMU SPTEs and * does not hold the mmu_lock. */ - old_spte = cmpxchg64(sptep, iter->old_spte, new_spte); - if (old_spte != iter->old_spte) { - /* - * The page table entry was modified by a different logical - * CPU. Refresh iter->old_spte with the current value so the - * caller operates on fresh data, e.g. if it retries - * tdp_mmu_set_spte_atomic(). - */ - iter->old_spte = old_spte; + if (!try_cmpxchg64(sptep, &iter->old_spte, new_spte)) return -EBUSY; - } __handle_changed_spte(kvm, iter->as_id, iter->gfn, iter->old_spte, new_spte, iter->level, true); @@ -934,9 +924,6 @@ bool kvm_tdp_mmu_zap_sp(struct kvm *kvm, struct kvm_mmu_page *sp) } /* - * Zap leafs SPTEs for the range of gfns, [start, end). Returns true if SPTEs - * have been cleared and a TLB flush is needed before releasing the MMU lock. - * * If can_yield is true, will release the MMU lock and reschedule if the * scheduler needs the CPU or there is contention on the MMU lock. If this * function cannot yield, it will not release the MMU lock or reschedule and @@ -979,10 +966,9 @@ static bool tdp_mmu_zap_leafs(struct kvm *kvm, struct kvm_mmu_page *root, } /* - * Tears down the mappings for the range of gfns, [start, end), and frees the - * non-root pages mapping GFNs strictly within that range. Returns true if - * SPTEs have been cleared and a TLB flush is needed before releasing the - * MMU lock. + * Zap leaf SPTEs for the range of gfns, [start, end), for all roots. Returns + * true if a TLB flush is needed before releasing the MMU lock, i.e. if one or + * more SPTEs were zapped since the MMU lock was last acquired. */ bool kvm_tdp_mmu_zap_leafs(struct kvm *kvm, int as_id, gfn_t start, gfn_t end, bool can_yield, bool flush) @@ -1487,8 +1473,8 @@ static int tdp_mmu_split_huge_page(struct kvm *kvm, struct tdp_iter *iter, * No need for atomics when writing to sp->spt since the page table has * not been linked in yet and thus is not reachable from any other CPU. */ - for (i = 0; i < PT64_ENT_PER_PAGE; i++) - sp->spt[i] = make_huge_page_split_spte(huge_spte, level, i); + for (i = 0; i < SPTE_ENT_PER_PAGE; i++) + sp->spt[i] = make_huge_page_split_spte(kvm, huge_spte, sp->role, i); /* * Replace the huge spte with a pointer to the populated lower level @@ -1507,7 +1493,7 @@ static int tdp_mmu_split_huge_page(struct kvm *kvm, struct tdp_iter *iter, * are overwriting from the page stats. But we have to manually update * the page stats with the new present child pages. */ - kvm_update_page_stats(kvm, level - 1, PT64_ENT_PER_PAGE); + kvm_update_page_stats(kvm, level - 1, SPTE_ENT_PER_PAGE); out: trace_kvm_mmu_split_huge_page(iter->gfn, huge_spte, level, ret); @@ -1731,10 +1717,6 @@ void kvm_tdp_mmu_clear_dirty_pt_masked(struct kvm *kvm, clear_dirty_pt_masked(kvm, root, gfn, mask, wrprot); } -/* - * Clear leaf entries which could be replaced by large mappings, for - * GFNs within the slot. - */ static void zap_collapsible_spte_range(struct kvm *kvm, struct kvm_mmu_page *root, const struct kvm_memory_slot *slot) @@ -1743,61 +1725,52 @@ static void zap_collapsible_spte_range(struct kvm *kvm, gfn_t end = start + slot->npages; struct tdp_iter iter; int max_mapping_level; - kvm_pfn_t pfn; rcu_read_lock(); - tdp_root_for_each_pte(iter, root, start, end) { + for_each_tdp_pte_min_level(iter, root, PG_LEVEL_2M, start, end) { +retry: if (tdp_mmu_iter_cond_resched(kvm, &iter, false, true)) continue; - if (!is_shadow_present_pte(iter.old_spte) || - !is_last_spte(iter.old_spte, iter.level)) + if (iter.level > KVM_MAX_HUGEPAGE_LEVEL || + !is_shadow_present_pte(iter.old_spte)) continue; /* - * This is a leaf SPTE. Check if the PFN it maps can - * be mapped at a higher level. + * Don't zap leaf SPTEs, if a leaf SPTE could be replaced with + * a large page size, then its parent would have been zapped + * instead of stepping down. */ - pfn = spte_to_pfn(iter.old_spte); + if (is_last_spte(iter.old_spte, iter.level)) + continue; - if (kvm_is_reserved_pfn(pfn)) + /* + * If iter.gfn resides outside of the slot, i.e. the page for + * the current level overlaps but is not contained by the slot, + * then the SPTE can't be made huge. More importantly, trying + * to query that info from slot->arch.lpage_info will cause an + * out-of-bounds access. + */ + if (iter.gfn < start || iter.gfn >= end) continue; max_mapping_level = kvm_mmu_max_mapping_level(kvm, slot, - iter.gfn, pfn, PG_LEVEL_NUM); - - WARN_ON(max_mapping_level < iter.level); - - /* - * If this page is already mapped at the highest - * viable level, there's nothing more to do. - */ - if (max_mapping_level == iter.level) + iter.gfn, PG_LEVEL_NUM); + if (max_mapping_level < iter.level) continue; - /* - * The page can be remapped at a higher level, so step - * up to zap the parent SPTE. - */ - while (max_mapping_level > iter.level) - tdp_iter_step_up(&iter); - /* Note, a successful atomic zap also does a remote TLB flush. */ - tdp_mmu_zap_spte_atomic(kvm, &iter); - - /* - * If the atomic zap fails, the iter will recurse back into - * the same subtree to retry. - */ + if (tdp_mmu_zap_spte_atomic(kvm, &iter)) + goto retry; } rcu_read_unlock(); } /* - * Clear non-leaf entries (and free associated page tables) which could - * be replaced by large mappings, for GFNs within the slot. + * Zap non-leaf SPTEs (and free their associated page tables) which could + * be replaced by huge pages, for GFNs within the slot. */ void kvm_tdp_mmu_zap_collapsible_sptes(struct kvm *kvm, const struct kvm_memory_slot *slot) diff --git a/arch/x86/kvm/pmu.c b/arch/x86/kvm/pmu.c index 3f868fed9114..02f9e4f245bd 100644 --- a/arch/x86/kvm/pmu.c +++ b/arch/x86/kvm/pmu.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "x86.h" #include "cpuid.h" #include "lapic.h" @@ -24,6 +25,15 @@ /* This is enough to filter the vast majority of currently defined events. */ #define KVM_PMU_EVENT_FILTER_MAX_EVENTS 300 +struct x86_pmu_capability __read_mostly kvm_pmu_cap; +EXPORT_SYMBOL_GPL(kvm_pmu_cap); + +static const struct x86_cpu_id vmx_icl_pebs_cpu[] = { + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_D, NULL), + X86_MATCH_INTEL_FAM6_MODEL(ICELAKE_X, NULL), + {} +}; + /* NOTE: * - Each perf counter is defined as "struct kvm_pmc"; * - There are two types of perf counters: general purpose (gp) and fixed. @@ -34,7 +44,9 @@ * However AMD doesn't support fixed-counters; * - There are three types of index to access perf counters (PMC): * 1. MSR (named msr): For example Intel has MSR_IA32_PERFCTRn and AMD - * has MSR_K7_PERFCTRn. + * has MSR_K7_PERFCTRn and, for families 15H and later, + * MSR_F15H_PERF_CTRn, where MSR_F15H_PERF_CTR[0-3] are + * aliased to MSR_K7_PERFCTRn. * 2. MSR Index (named idx): This normally is used by RDPMC instruction. * For instance AMD RDPMC instruction uses 0000_0003h in ECX to access * C001_0007h (MSR_K7_PERCTR3). Intel has a similar mechanism, except @@ -46,7 +58,8 @@ * between pmc and perf counters is as the following: * * Intel: [0 .. INTEL_PMC_MAX_GENERIC-1] <=> gp counters * [INTEL_PMC_IDX_FIXED .. INTEL_PMC_IDX_FIXED + 2] <=> fixed - * * AMD: [0 .. AMD64_NUM_COUNTERS-1] <=> gp counters + * * AMD: [0 .. AMD64_NUM_COUNTERS-1] and, for families 15H + * and later, [0 .. AMD64_NUM_COUNTERS_CORE-1] <=> gp counters */ static struct kvm_pmu_ops kvm_pmu_ops __read_mostly; @@ -86,15 +99,22 @@ static void kvm_pmi_trigger_fn(struct irq_work *irq_work) static inline void __kvm_perf_overflow(struct kvm_pmc *pmc, bool in_pmi) { struct kvm_pmu *pmu = pmc_to_pmu(pmc); + bool skip_pmi = false; /* Ignore counters that have been reprogrammed already. */ if (test_and_set_bit(pmc->idx, pmu->reprogram_pmi)) return; - __set_bit(pmc->idx, (unsigned long *)&pmu->global_status); + if (pmc->perf_event && pmc->perf_event->attr.precise_ip) { + /* Indicate PEBS overflow PMI to guest. */ + skip_pmi = __test_and_set_bit(GLOBAL_STATUS_BUFFER_OVF_BIT, + (unsigned long *)&pmu->global_status); + } else { + __set_bit(pmc->idx, (unsigned long *)&pmu->global_status); + } kvm_make_request(KVM_REQ_PMU, pmc->vcpu); - if (!pmc->intr) + if (!pmc->intr || skip_pmi) return; /* @@ -124,6 +144,7 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type, u64 config, bool exclude_user, bool exclude_kernel, bool intr) { + struct kvm_pmu *pmu = pmc_to_pmu(pmc); struct perf_event *event; struct perf_event_attr attr = { .type = type, @@ -135,9 +156,7 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type, .exclude_kernel = exclude_kernel, .config = config, }; - - if (type == PERF_TYPE_HARDWARE && config >= PERF_COUNT_HW_MAX) - return; + bool pebs = test_bit(pmc->idx, (unsigned long *)&pmu->pebs_enable); attr.sample_period = get_sample_period(pmc, pmc->counter); @@ -150,6 +169,25 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type, */ attr.sample_period = 0; } + if (pebs) { + /* + * The non-zero precision level of guest event makes the ordinary + * guest event becomes a guest PEBS event and triggers the host + * PEBS PMI handler to determine whether the PEBS overflow PMI + * comes from the host counters or the guest. + * + * For most PEBS hardware events, the difference in the software + * precision levels of guest and host PEBS events will not affect + * the accuracy of the PEBS profiling result, because the "event IP" + * in the PEBS record is calibrated on the guest side. + * + * On Icelake everything is fine. Other hardware (GLC+, TNT+) that + * could possibly care here is unsupported and needs changes. + */ + attr.precise_ip = 1; + if (x86_match_cpu(vmx_icl_pebs_cpu) && pmc->idx == 32) + attr.precise_ip = 3; + } event = perf_event_create_kernel_counter(&attr, -1, current, kvm_perf_overflow, pmc); @@ -163,7 +201,7 @@ static void pmc_reprogram_counter(struct kvm_pmc *pmc, u32 type, pmc_to_pmu(pmc)->event_count++; clear_bit(pmc->idx, pmc_to_pmu(pmc)->reprogram_pmi); pmc->is_paused = false; - pmc->intr = intr; + pmc->intr = intr || pebs; } static void pmc_pause_counter(struct kvm_pmc *pmc) @@ -189,6 +227,10 @@ static bool pmc_resume_counter(struct kvm_pmc *pmc) get_sample_period(pmc, pmc->counter))) return false; + if (!test_bit(pmc->idx, (unsigned long *)&pmc_to_pmu(pmc)->pebs_enable) && + pmc->perf_event->attr.precise_ip) + return false; + /* reuse perf_event to serve as pmc_reprogram_counter() does*/ perf_event_enable(pmc->perf_event); pmc->is_paused = false; @@ -205,115 +247,83 @@ static int cmp_u64(const void *pa, const void *pb) return (a > b) - (a < b); } -void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel) +static bool check_pmu_event_filter(struct kvm_pmc *pmc) { - u64 config; - u32 type = PERF_TYPE_RAW; - struct kvm *kvm = pmc->vcpu->kvm; struct kvm_pmu_event_filter *filter; - struct kvm_pmu *pmu = vcpu_to_pmu(pmc->vcpu); + struct kvm *kvm = pmc->vcpu->kvm; bool allow_event = true; + __u64 key; + int idx; - if (eventsel & ARCH_PERFMON_EVENTSEL_PIN_CONTROL) - printk_once("kvm pmu: pin control bit is ignored\n"); - - pmc->eventsel = eventsel; - - pmc_pause_counter(pmc); - - if (!(eventsel & ARCH_PERFMON_EVENTSEL_ENABLE) || !pmc_is_enabled(pmc)) - return; + if (!static_call(kvm_x86_pmu_hw_event_available)(pmc)) + return false; filter = srcu_dereference(kvm->arch.pmu_event_filter, &kvm->srcu); - if (filter) { - __u64 key = eventsel & AMD64_RAW_EVENT_MASK_NB; + if (!filter) + goto out; + if (pmc_is_gp(pmc)) { + key = pmc->eventsel & AMD64_RAW_EVENT_MASK_NB; if (bsearch(&key, filter->events, filter->nevents, sizeof(__u64), cmp_u64)) allow_event = filter->action == KVM_PMU_EVENT_ALLOW; else allow_event = filter->action == KVM_PMU_EVENT_DENY; - } - if (!allow_event) - return; - - if (!(eventsel & (ARCH_PERFMON_EVENTSEL_EDGE | - ARCH_PERFMON_EVENTSEL_INV | - ARCH_PERFMON_EVENTSEL_CMASK | - HSW_IN_TX | - HSW_IN_TX_CHECKPOINTED))) { - config = static_call(kvm_x86_pmu_pmc_perf_hw_id)(pmc); - if (config != PERF_COUNT_HW_MAX) - type = PERF_TYPE_HARDWARE; + } else { + idx = pmc->idx - INTEL_PMC_IDX_FIXED; + if (filter->action == KVM_PMU_EVENT_DENY && + test_bit(idx, (ulong *)&filter->fixed_counter_bitmap)) + allow_event = false; + if (filter->action == KVM_PMU_EVENT_ALLOW && + !test_bit(idx, (ulong *)&filter->fixed_counter_bitmap)) + allow_event = false; } - if (type == PERF_TYPE_RAW) - config = eventsel & pmu->raw_event_mask; - - if (pmc->current_config == eventsel && pmc_resume_counter(pmc)) - return; - - pmc_release_perf_event(pmc); - - pmc->current_config = eventsel; - pmc_reprogram_counter(pmc, type, config, - !(eventsel & ARCH_PERFMON_EVENTSEL_USR), - !(eventsel & ARCH_PERFMON_EVENTSEL_OS), - eventsel & ARCH_PERFMON_EVENTSEL_INT); +out: + return allow_event; } -EXPORT_SYMBOL_GPL(reprogram_gp_counter); -void reprogram_fixed_counter(struct kvm_pmc *pmc, u8 ctrl, int idx) +void reprogram_counter(struct kvm_pmc *pmc) { - unsigned en_field = ctrl & 0x3; - bool pmi = ctrl & 0x8; - struct kvm_pmu_event_filter *filter; - struct kvm *kvm = pmc->vcpu->kvm; + struct kvm_pmu *pmu = pmc_to_pmu(pmc); + u64 eventsel = pmc->eventsel; + u64 new_config = eventsel; + u8 fixed_ctr_ctrl; pmc_pause_counter(pmc); - if (!en_field || !pmc_is_enabled(pmc)) + if (!pmc_speculative_in_use(pmc) || !pmc_is_enabled(pmc)) return; - filter = srcu_dereference(kvm->arch.pmu_event_filter, &kvm->srcu); - if (filter) { - if (filter->action == KVM_PMU_EVENT_DENY && - test_bit(idx, (ulong *)&filter->fixed_counter_bitmap)) - return; - if (filter->action == KVM_PMU_EVENT_ALLOW && - !test_bit(idx, (ulong *)&filter->fixed_counter_bitmap)) - return; + if (!check_pmu_event_filter(pmc)) + return; + + if (eventsel & ARCH_PERFMON_EVENTSEL_PIN_CONTROL) + printk_once("kvm pmu: pin control bit is ignored\n"); + + if (pmc_is_fixed(pmc)) { + fixed_ctr_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, + pmc->idx - INTEL_PMC_IDX_FIXED); + if (fixed_ctr_ctrl & 0x1) + eventsel |= ARCH_PERFMON_EVENTSEL_OS; + if (fixed_ctr_ctrl & 0x2) + eventsel |= ARCH_PERFMON_EVENTSEL_USR; + if (fixed_ctr_ctrl & 0x8) + eventsel |= ARCH_PERFMON_EVENTSEL_INT; + new_config = (u64)fixed_ctr_ctrl; } - if (pmc->current_config == (u64)ctrl && pmc_resume_counter(pmc)) + if (pmc->current_config == new_config && pmc_resume_counter(pmc)) return; pmc_release_perf_event(pmc); - pmc->current_config = (u64)ctrl; - pmc_reprogram_counter(pmc, PERF_TYPE_HARDWARE, - static_call(kvm_x86_pmu_pmc_perf_hw_id)(pmc), - !(en_field & 0x2), /* exclude user */ - !(en_field & 0x1), /* exclude kernel */ - pmi); -} -EXPORT_SYMBOL_GPL(reprogram_fixed_counter); - -void reprogram_counter(struct kvm_pmu *pmu, int pmc_idx) -{ - struct kvm_pmc *pmc = static_call(kvm_x86_pmu_pmc_idx_to_pmc)(pmu, pmc_idx); - - if (!pmc) - return; - - if (pmc_is_gp(pmc)) - reprogram_gp_counter(pmc, pmc->eventsel); - else { - int idx = pmc_idx - INTEL_PMC_IDX_FIXED; - u8 ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, idx); - - reprogram_fixed_counter(pmc, ctrl, idx); - } + pmc->current_config = new_config; + pmc_reprogram_counter(pmc, PERF_TYPE_RAW, + (eventsel & pmu->raw_event_mask), + !(eventsel & ARCH_PERFMON_EVENTSEL_USR), + !(eventsel & ARCH_PERFMON_EVENTSEL_OS), + eventsel & ARCH_PERFMON_EVENTSEL_INT); } EXPORT_SYMBOL_GPL(reprogram_counter); @@ -329,8 +339,7 @@ void kvm_pmu_handle_event(struct kvm_vcpu *vcpu) clear_bit(bit, pmu->reprogram_pmi); continue; } - - reprogram_counter(pmu, bit); + reprogram_counter(pmc); } /* @@ -471,17 +480,6 @@ void kvm_pmu_init(struct kvm_vcpu *vcpu) kvm_pmu_refresh(vcpu); } -static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) -{ - struct kvm_pmu *pmu = pmc_to_pmu(pmc); - - if (pmc_is_fixed(pmc)) - return fixed_ctrl_field(pmu->fixed_ctr_ctrl, - pmc->idx - INTEL_PMC_IDX_FIXED) & 0x3; - - return pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE; -} - /* Release perf_events for vPMCs that have been unused for a full time slice. */ void kvm_pmu_cleanup(struct kvm_vcpu *vcpu) { @@ -514,13 +512,12 @@ void kvm_pmu_destroy(struct kvm_vcpu *vcpu) static void kvm_pmu_incr_counter(struct kvm_pmc *pmc) { - struct kvm_pmu *pmu = pmc_to_pmu(pmc); u64 prev_count; prev_count = pmc->counter; pmc->counter = (pmc->counter + 1) & pmc_bitmask(pmc); - reprogram_counter(pmu, pmc->idx); + reprogram_counter(pmc); if (pmc->counter < prev_count) __kvm_perf_overflow(pmc, false); } @@ -528,13 +525,8 @@ static void kvm_pmu_incr_counter(struct kvm_pmc *pmc) static inline bool eventsel_match_perf_hw_id(struct kvm_pmc *pmc, unsigned int perf_hw_id) { - u64 old_eventsel = pmc->eventsel; - unsigned int config; - - pmc->eventsel &= (ARCH_PERFMON_EVENTSEL_EVENT | ARCH_PERFMON_EVENTSEL_UMASK); - config = static_call(kvm_x86_pmu_pmc_perf_hw_id)(pmc); - pmc->eventsel = old_eventsel; - return config == perf_hw_id; + return !((pmc->eventsel ^ perf_get_hw_event_config(perf_hw_id)) & + AMD64_RAW_EVENT_MASK_NB); } static inline bool cpl_is_matched(struct kvm_pmc *pmc) diff --git a/arch/x86/kvm/pmu.h b/arch/x86/kvm/pmu.h index e745f443b6a8..5cc5721f260b 100644 --- a/arch/x86/kvm/pmu.h +++ b/arch/x86/kvm/pmu.h @@ -8,6 +8,9 @@ #define pmu_to_vcpu(pmu) (container_of((pmu), struct kvm_vcpu, arch.pmu)) #define pmc_to_pmu(pmc) (&(pmc)->vcpu->arch.pmu) +#define MSR_IA32_MISC_ENABLE_PMU_RO_MASK (MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL | \ + MSR_IA32_MISC_ENABLE_BTS_UNAVAIL) + /* retrieve the 4 bits for EN and PMI out of IA32_FIXED_CTR_CTRL */ #define fixed_ctrl_field(ctrl_reg, idx) (((ctrl_reg) >> ((idx)*4)) & 0xf) @@ -22,7 +25,7 @@ struct kvm_event_hw_type_mapping { }; struct kvm_pmu_ops { - unsigned int (*pmc_perf_hw_id)(struct kvm_pmc *pmc); + bool (*hw_event_available)(struct kvm_pmc *pmc); bool (*pmc_is_enabled)(struct kvm_pmc *pmc); struct kvm_pmc *(*pmc_idx_to_pmc)(struct kvm_pmu *pmu, int pmc_idx); struct kvm_pmc *(*rdpmc_ecx_to_pmc)(struct kvm_vcpu *vcpu, @@ -144,9 +147,43 @@ static inline void pmc_update_sample_period(struct kvm_pmc *pmc) get_sample_period(pmc, pmc->counter)); } -void reprogram_gp_counter(struct kvm_pmc *pmc, u64 eventsel); -void reprogram_fixed_counter(struct kvm_pmc *pmc, u8 ctrl, int fixed_idx); -void reprogram_counter(struct kvm_pmu *pmu, int pmc_idx); +static inline bool pmc_speculative_in_use(struct kvm_pmc *pmc) +{ + struct kvm_pmu *pmu = pmc_to_pmu(pmc); + + if (pmc_is_fixed(pmc)) + return fixed_ctrl_field(pmu->fixed_ctr_ctrl, + pmc->idx - INTEL_PMC_IDX_FIXED) & 0x3; + + return pmc->eventsel & ARCH_PERFMON_EVENTSEL_ENABLE; +} + +extern struct x86_pmu_capability kvm_pmu_cap; + +static inline void kvm_init_pmu_capability(void) +{ + bool is_intel = boot_cpu_data.x86_vendor == X86_VENDOR_INTEL; + + perf_get_x86_pmu_capability(&kvm_pmu_cap); + + /* + * For Intel, only support guest architectural pmu + * on a host with architectural pmu. + */ + if ((is_intel && !kvm_pmu_cap.version) || !kvm_pmu_cap.num_counters_gp) + enable_pmu = false; + + if (!enable_pmu) { + memset(&kvm_pmu_cap, 0, sizeof(kvm_pmu_cap)); + return; + } + + kvm_pmu_cap.version = min(kvm_pmu_cap.version, 2); + kvm_pmu_cap.num_counters_fixed = min(kvm_pmu_cap.num_counters_fixed, + KVM_PMC_MAX_FIXED); +} + +void reprogram_counter(struct kvm_pmc *pmc); void kvm_pmu_deliver_pmi(struct kvm_vcpu *vcpu); void kvm_pmu_handle_event(struct kvm_vcpu *vcpu); diff --git a/arch/x86/kvm/svm/avic.c b/arch/x86/kvm/svm/avic.c index d1bc5820ea46..6919dee69f18 100644 --- a/arch/x86/kvm/svm/avic.c +++ b/arch/x86/kvm/svm/avic.c @@ -40,6 +40,9 @@ #define AVIC_GATAG_TO_VMID(x) ((x >> AVIC_VCPU_ID_BITS) & AVIC_VM_ID_MASK) #define AVIC_GATAG_TO_VCPUID(x) (x & AVIC_VCPU_ID_MASK) +static bool force_avic; +module_param_unsafe(force_avic, bool, 0444); + /* Note: * This hash table is used to map VM_ID to a struct kvm_svm, * when handling AMD IOMMU GALOG notification to schedule in @@ -50,6 +53,7 @@ static DEFINE_HASHTABLE(svm_vm_data_hash, SVM_VM_DATA_HASH_BITS); static u32 next_vm_id = 0; static bool next_vm_id_wrapped = 0; static DEFINE_SPINLOCK(svm_vm_data_hash_lock); +enum avic_modes avic_mode; /* * This is a wrapper of struct amd_iommu_ir_data. @@ -59,6 +63,54 @@ struct amd_svm_iommu_ir { void *data; /* Storing pointer to struct amd_ir_data */ }; +static void avic_activate_vmcb(struct vcpu_svm *svm) +{ + struct vmcb *vmcb = svm->vmcb01.ptr; + + vmcb->control.int_ctl &= ~(AVIC_ENABLE_MASK | X2APIC_MODE_MASK); + vmcb->control.avic_physical_id &= ~AVIC_PHYSICAL_MAX_INDEX_MASK; + + vmcb->control.int_ctl |= AVIC_ENABLE_MASK; + + /* Note: + * KVM can support hybrid-AVIC mode, where KVM emulates x2APIC + * MSR accesses, while interrupt injection to a running vCPU + * can be achieved using AVIC doorbell. The AVIC hardware still + * accelerate MMIO accesses, but this does not cause any harm + * as the guest is not supposed to access xAPIC mmio when uses x2APIC. + */ + if (apic_x2apic_mode(svm->vcpu.arch.apic) && + avic_mode == AVIC_MODE_X2) { + vmcb->control.int_ctl |= X2APIC_MODE_MASK; + vmcb->control.avic_physical_id |= X2AVIC_MAX_PHYSICAL_ID; + /* Disabling MSR intercept for x2APIC registers */ + svm_set_x2apic_msr_interception(svm, false); + } else { + /* For xAVIC and hybrid-xAVIC modes */ + vmcb->control.avic_physical_id |= AVIC_MAX_PHYSICAL_ID; + /* Enabling MSR intercept for x2APIC registers */ + svm_set_x2apic_msr_interception(svm, true); + } +} + +static void avic_deactivate_vmcb(struct vcpu_svm *svm) +{ + struct vmcb *vmcb = svm->vmcb01.ptr; + + vmcb->control.int_ctl &= ~(AVIC_ENABLE_MASK | X2APIC_MODE_MASK); + vmcb->control.avic_physical_id &= ~AVIC_PHYSICAL_MAX_INDEX_MASK; + + /* + * If running nested and the guest uses its own MSR bitmap, there + * is no need to update L0's msr bitmap + */ + if (is_guest_mode(&svm->vcpu) && + vmcb12_is_intercept(&svm->nested.ctl, INTERCEPT_MSR_PROT)) + return; + + /* Enabling MSR intercept for x2APIC registers */ + svm_set_x2apic_msr_interception(svm, true); +} /* Note: * This function is called from IOMMU driver to notify @@ -175,13 +227,12 @@ void avic_init_vmcb(struct vcpu_svm *svm, struct vmcb *vmcb) vmcb->control.avic_backing_page = bpa & AVIC_HPA_MASK; vmcb->control.avic_logical_id = lpa & AVIC_HPA_MASK; vmcb->control.avic_physical_id = ppa & AVIC_HPA_MASK; - vmcb->control.avic_physical_id |= AVIC_MAX_PHYSICAL_ID_COUNT; vmcb->control.avic_vapic_bar = APIC_DEFAULT_PHYS_BASE & VMCB_AVIC_APIC_BAR_MASK; if (kvm_apicv_activated(svm->vcpu.kvm)) - vmcb->control.int_ctl |= AVIC_ENABLE_MASK; + avic_activate_vmcb(svm); else - vmcb->control.int_ctl &= ~AVIC_ENABLE_MASK; + avic_deactivate_vmcb(svm); } static u64 *avic_get_physical_id_entry(struct kvm_vcpu *vcpu, @@ -190,7 +241,8 @@ static u64 *avic_get_physical_id_entry(struct kvm_vcpu *vcpu, u64 *avic_physical_id_table; struct kvm_svm *kvm_svm = to_kvm_svm(vcpu->kvm); - if (index >= AVIC_MAX_PHYSICAL_ID_COUNT) + if ((avic_mode == AVIC_MODE_X1 && index > AVIC_MAX_PHYSICAL_ID) || + (avic_mode == AVIC_MODE_X2 && index > X2AVIC_MAX_PHYSICAL_ID)) return NULL; avic_physical_id_table = page_address(kvm_svm->avic_physical_id_table_page); @@ -237,7 +289,8 @@ static int avic_init_backing_page(struct kvm_vcpu *vcpu) int id = vcpu->vcpu_id; struct vcpu_svm *svm = to_svm(vcpu); - if (id >= AVIC_MAX_PHYSICAL_ID_COUNT) + if ((avic_mode == AVIC_MODE_X1 && id > AVIC_MAX_PHYSICAL_ID) || + (avic_mode == AVIC_MODE_X2 && id > X2AVIC_MAX_PHYSICAL_ID)) return -EINVAL; if (!vcpu->arch.apic->regs) @@ -279,8 +332,10 @@ void avic_ring_doorbell(struct kvm_vcpu *vcpu) */ int cpu = READ_ONCE(vcpu->cpu); - if (cpu != get_cpu()) + if (cpu != get_cpu()) { wrmsrl(MSR_AMD64_SVM_AVIC_DOORBELL, kvm_cpu_get_apicid(cpu)); + trace_kvm_avic_doorbell(vcpu->vcpu_id, kvm_cpu_get_apicid(cpu)); + } put_cpu(); } @@ -303,7 +358,7 @@ static int avic_kick_target_vcpus_fast(struct kvm *kvm, struct kvm_lapic *source if (apic_x2apic_mode(source)) dest = icrh; else - dest = GET_APIC_DEST_FIELD(icrh); + dest = GET_XAPIC_DEST_FIELD(icrh); if (dest_mode == APIC_DEST_PHYSICAL) { /* broadcast destination, use slow path */ @@ -345,9 +400,7 @@ static int avic_kick_target_vcpus_fast(struct kvm *kvm, struct kvm_lapic *source logid_index = cluster + __ffs(bitmap); - if (apic_x2apic_mode(source)) { - l1_physical_id = logid_index; - } else { + if (!apic_x2apic_mode(source)) { u32 *avic_logical_id_table = page_address(kvm_svm->avic_logical_id_table_page); @@ -362,6 +415,23 @@ static int avic_kick_target_vcpus_fast(struct kvm *kvm, struct kvm_lapic *source l1_physical_id = logid_entry & AVIC_LOGICAL_ID_ENTRY_GUEST_PHYSICAL_ID_MASK; + } else { + /* + * For x2APIC logical mode, cannot leverage the index. + * Instead, calculate physical ID from logical ID in ICRH. + */ + int cluster = (icrh & 0xffff0000) >> 16; + int apic = ffs(icrh & 0xffff) - 1; + + /* + * If the x2APIC logical ID sub-field (i.e. icrh[15:0]) + * contains anything but a single bit, we cannot use the + * fast path, because it is limited to a single vCPU. + */ + if (apic < 0 || icrh != (1 << apic)) + return -EINVAL; + + l1_physical_id = (cluster << 4) + apic; } } @@ -396,9 +466,15 @@ static void avic_kick_target_vcpus(struct kvm *kvm, struct kvm_lapic *source, * since entered the guest will have processed pending IRQs at VMRUN. */ kvm_for_each_vcpu(i, vcpu, kvm) { + u32 dest; + + if (apic_x2apic_mode(vcpu->arch.apic)) + dest = icrh; + else + dest = GET_XAPIC_DEST_FIELD(icrh); + if (kvm_apic_match_dest(vcpu, source, icrl & APIC_SHORT_MASK, - GET_APIC_DEST_FIELD(icrh), - icrl & APIC_DEST_MASK)) { + dest, icrl & APIC_DEST_MASK)) { vcpu->arch.apic->irr_pending = true; svm_complete_interrupt_delivery(vcpu, icrl & APIC_MODE_MASK, @@ -514,8 +590,13 @@ static void avic_invalidate_logical_id_entry(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); bool flat = svm->dfr_reg == APIC_DFR_FLAT; - u32 *entry = avic_get_logical_id_entry(vcpu, svm->ldr_reg, flat); + u32 *entry; + /* Note: x2AVIC does not use logical APIC ID table */ + if (apic_x2apic_mode(vcpu->arch.apic)) + return; + + entry = avic_get_logical_id_entry(vcpu, svm->ldr_reg, flat); if (entry) clear_bit(AVIC_LOGICAL_ID_ENTRY_VALID_BIT, (unsigned long *)entry); } @@ -527,6 +608,10 @@ static int avic_handle_ldr_update(struct kvm_vcpu *vcpu) u32 ldr = kvm_lapic_get_reg(vcpu->arch.apic, APIC_LDR); u32 id = kvm_xapic_id(vcpu->arch.apic); + /* AVIC does not support LDR update for x2APIC */ + if (apic_x2apic_mode(vcpu->arch.apic)) + return 0; + if (ldr == svm->ldr_reg) return 0; @@ -654,6 +739,18 @@ void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu) avic_handle_ldr_update(vcpu); } +void avic_set_virtual_apic_mode(struct kvm_vcpu *vcpu) +{ + if (!lapic_in_kernel(vcpu) || avic_mode == AVIC_MODE_NONE) + return; + + if (kvm_get_apic_mode(vcpu) == LAPIC_MODE_INVALID) { + WARN_ONCE(true, "Invalid local APIC state (vcpu_id=%d)", vcpu->vcpu_id); + return; + } + avic_refresh_apicv_exec_ctrl(vcpu); +} + static int avic_set_pi_irte_mode(struct kvm_vcpu *vcpu, bool activate) { int ret = 0; @@ -906,7 +1003,6 @@ bool avic_check_apicv_inhibit_reasons(enum kvm_apicv_inhibit reason) BIT(APICV_INHIBIT_REASON_NESTED) | BIT(APICV_INHIBIT_REASON_IRQWIN) | BIT(APICV_INHIBIT_REASON_PIT_REINJ) | - BIT(APICV_INHIBIT_REASON_X2APIC) | BIT(APICV_INHIBIT_REASON_BLOCKIRQ) | BIT(APICV_INHIBIT_REASON_SEV) | BIT(APICV_INHIBIT_REASON_APIC_ID_MODIFIED) | @@ -968,7 +1064,6 @@ void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu) return; entry = READ_ONCE(*(svm->avic_physical_id_cache)); - WARN_ON(entry & AVIC_PHYSICAL_ID_ENTRY_IS_RUNNING_MASK); entry &= ~AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK; entry |= (h_physical_id & AVIC_PHYSICAL_ID_ENTRY_HOST_PHYSICAL_ID_MASK); @@ -1016,9 +1111,9 @@ void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) * accordingly before re-activating. */ avic_apicv_post_state_restore(vcpu); - vmcb->control.int_ctl |= AVIC_ENABLE_MASK; + avic_activate_vmcb(svm); } else { - vmcb->control.int_ctl &= ~AVIC_ENABLE_MASK; + avic_deactivate_vmcb(svm); } vmcb_mark_dirty(vmcb, VMCB_AVIC); @@ -1058,3 +1153,44 @@ void avic_vcpu_unblocking(struct kvm_vcpu *vcpu) avic_vcpu_load(vcpu, vcpu->cpu); } + +/* + * Note: + * - The module param avic enable both xAPIC and x2APIC mode. + * - Hypervisor can support both xAVIC and x2AVIC in the same guest. + * - The mode can be switched at run-time. + */ +bool avic_hardware_setup(struct kvm_x86_ops *x86_ops) +{ + if (!npt_enabled) + return false; + + if (boot_cpu_has(X86_FEATURE_AVIC)) { + avic_mode = AVIC_MODE_X1; + pr_info("AVIC enabled\n"); + } else if (force_avic) { + /* + * Some older systems does not advertise AVIC support. + * See Revision Guide for specific AMD processor for more detail. + */ + avic_mode = AVIC_MODE_X1; + pr_warn("AVIC is not supported in CPUID but force enabled"); + pr_warn("Your system might crash and burn"); + } + + /* AVIC is a prerequisite for x2AVIC. */ + if (boot_cpu_has(X86_FEATURE_X2AVIC)) { + if (avic_mode == AVIC_MODE_X1) { + avic_mode = AVIC_MODE_X2; + pr_info("x2AVIC enabled\n"); + } else { + pr_warn(FW_BUG "Cannot support x2AVIC due to AVIC is disabled"); + pr_warn(FW_BUG "Try enable AVIC using force_avic option"); + } + } + + if (avic_mode != AVIC_MODE_NONE) + amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier); + + return !!avic_mode; +} diff --git a/arch/x86/kvm/svm/nested.c b/arch/x86/kvm/svm/nested.c index ba7cd26f438f..76dcc8a3e849 100644 --- a/arch/x86/kvm/svm/nested.c +++ b/arch/x86/kvm/svm/nested.c @@ -230,6 +230,11 @@ static bool nested_svm_vmrun_msrpm(struct vcpu_svm *svm) break; p = msrpm_offsets[i]; + + /* x2apic msrs are intercepted always for the nested guest */ + if (is_x2apic_msrpm_offset(p)) + continue; + offset = svm->nested.ctl.msrpm_base_pa + (p * 4); if (kvm_vcpu_read_guest(&svm->vcpu, offset, &value, 4)) @@ -320,7 +325,8 @@ static bool __nested_vmcb_check_save(struct kvm_vcpu *vcpu, return false; } - if (CC(!kvm_is_valid_cr4(vcpu, save->cr4))) + /* Note, SVM doesn't have any additional restrictions on CR4. */ + if (CC(!__kvm_is_valid_cr4(vcpu, save->cr4))) return false; if (CC(!kvm_valid_efer(vcpu, save->efer))) @@ -371,6 +377,7 @@ void __nested_copy_vmcb_control_to_cache(struct kvm_vcpu *vcpu, to->nested_ctl = from->nested_ctl; to->event_inj = from->event_inj; to->event_inj_err = from->event_inj_err; + to->next_rip = from->next_rip; to->nested_cr3 = from->nested_cr3; to->virt_ext = from->virt_ext; to->pause_filter_count = from->pause_filter_count; @@ -608,7 +615,33 @@ static void nested_vmcb02_prepare_save(struct vcpu_svm *svm, struct vmcb *vmcb12 } } -static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) +static inline bool is_evtinj_soft(u32 evtinj) +{ + u32 type = evtinj & SVM_EVTINJ_TYPE_MASK; + u8 vector = evtinj & SVM_EVTINJ_VEC_MASK; + + if (!(evtinj & SVM_EVTINJ_VALID)) + return false; + + if (type == SVM_EVTINJ_TYPE_SOFT) + return true; + + return type == SVM_EVTINJ_TYPE_EXEPT && kvm_exception_is_soft(vector); +} + +static bool is_evtinj_nmi(u32 evtinj) +{ + u32 type = evtinj & SVM_EVTINJ_TYPE_MASK; + + if (!(evtinj & SVM_EVTINJ_VALID)) + return false; + + return type == SVM_EVTINJ_TYPE_NMI; +} + +static void nested_vmcb02_prepare_control(struct vcpu_svm *svm, + unsigned long vmcb12_rip, + unsigned long vmcb12_csbase) { u32 int_ctl_vmcb01_bits = V_INTR_MASKING_MASK; u32 int_ctl_vmcb12_bits = V_TPR_MASK | V_IRQ_INJECTION_BITS_MASK; @@ -650,7 +683,7 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) vmcb02->control.tsc_offset = vcpu->arch.tsc_offset; - if (svm->tsc_ratio_msr != kvm_default_tsc_scaling_ratio) { + if (svm->tsc_ratio_msr != kvm_caps.default_tsc_scaling_ratio) { WARN_ON(!svm->tsc_scaling_enabled); nested_svm_update_tsc_ratio_msr(vcpu); } @@ -664,6 +697,30 @@ static void nested_vmcb02_prepare_control(struct vcpu_svm *svm) vmcb02->control.event_inj = svm->nested.ctl.event_inj; vmcb02->control.event_inj_err = svm->nested.ctl.event_inj_err; + /* + * next_rip is consumed on VMRUN as the return address pushed on the + * stack for injected soft exceptions/interrupts. If nrips is exposed + * to L1, take it verbatim from vmcb12. If nrips is supported in + * hardware but not exposed to L1, stuff the actual L2 RIP to emulate + * what a nrips=0 CPU would do (L1 is responsible for advancing RIP + * prior to injecting the event). + */ + if (svm->nrips_enabled) + vmcb02->control.next_rip = svm->nested.ctl.next_rip; + else if (boot_cpu_has(X86_FEATURE_NRIPS)) + vmcb02->control.next_rip = vmcb12_rip; + + svm->nmi_l1_to_l2 = is_evtinj_nmi(vmcb02->control.event_inj); + if (is_evtinj_soft(vmcb02->control.event_inj)) { + svm->soft_int_injected = true; + svm->soft_int_csbase = vmcb12_csbase; + svm->soft_int_old_rip = vmcb12_rip; + if (svm->nrips_enabled) + svm->soft_int_next_rip = svm->nested.ctl.next_rip; + else + svm->soft_int_next_rip = vmcb12_rip; + } + vmcb02->control.virt_ext = vmcb01->control.virt_ext & LBR_CTL_ENABLE_MASK; if (svm->lbrv_enabled) @@ -745,7 +802,7 @@ int enter_svm_guest_mode(struct kvm_vcpu *vcpu, u64 vmcb12_gpa, nested_svm_copy_common_state(svm->vmcb01.ptr, svm->nested.vmcb02.ptr); svm_switch_vmcb(svm, &svm->nested.vmcb02); - nested_vmcb02_prepare_control(svm); + nested_vmcb02_prepare_control(svm, vmcb12->save.rip, vmcb12->save.cs.base); nested_vmcb02_prepare_save(svm, vmcb12); ret = nested_svm_load_cr3(&svm->vcpu, svm->nested.save.cr3, @@ -834,6 +891,8 @@ int nested_svm_vmrun(struct kvm_vcpu *vcpu) out_exit_err: svm->nested.nested_run_pending = 0; + svm->nmi_l1_to_l2 = false; + svm->soft_int_injected = false; svm->vmcb->control.exit_code = SVM_EXIT_ERR; svm->vmcb->control.exit_code_hi = 0; @@ -982,7 +1041,7 @@ int nested_svm_vmexit(struct vcpu_svm *svm) vmcb_mark_dirty(vmcb01, VMCB_INTERCEPTS); } - if (svm->tsc_ratio_msr != kvm_default_tsc_scaling_ratio) { + if (svm->tsc_ratio_msr != kvm_caps.default_tsc_scaling_ratio) { WARN_ON(!svm->tsc_scaling_enabled); vcpu->arch.tsc_scaling_ratio = vcpu->arch.l1_tsc_scaling_ratio; __svm_write_tsc_multiplier(vcpu->arch.tsc_scaling_ratio); @@ -1421,6 +1480,7 @@ static void nested_copy_vmcb_cache_to_control(struct vmcb_control_area *dst, dst->nested_ctl = from->nested_ctl; dst->event_inj = from->event_inj; dst->event_inj_err = from->event_inj_err; + dst->next_rip = from->next_rip; dst->nested_cr3 = from->nested_cr3; dst->virt_ext = from->virt_ext; dst->pause_filter_count = from->pause_filter_count; @@ -1605,7 +1665,7 @@ static int svm_set_nested_state(struct kvm_vcpu *vcpu, nested_copy_vmcb_control_to_cache(svm, ctl); svm_switch_vmcb(svm, &svm->nested.vmcb02); - nested_vmcb02_prepare_control(svm); + nested_vmcb02_prepare_control(svm, svm->vmcb->save.rip, svm->vmcb->save.cs.base); /* * While the nested guest CR3 is already checked and set by diff --git a/arch/x86/kvm/svm/pmu.c b/arch/x86/kvm/svm/pmu.c index 136039fc6d01..f24613a108c5 100644 --- a/arch/x86/kvm/svm/pmu.c +++ b/arch/x86/kvm/svm/pmu.c @@ -33,34 +33,6 @@ enum index { INDEX_ERROR, }; -/* duplicated from amd_perfmon_event_map, K7 and above should work. */ -static struct kvm_event_hw_type_mapping amd_event_mapping[] = { - [0] = { 0x76, 0x00, PERF_COUNT_HW_CPU_CYCLES }, - [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS }, - [2] = { 0x7d, 0x07, PERF_COUNT_HW_CACHE_REFERENCES }, - [3] = { 0x7e, 0x07, PERF_COUNT_HW_CACHE_MISSES }, - [4] = { 0xc2, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, - [5] = { 0xc3, 0x00, PERF_COUNT_HW_BRANCH_MISSES }, - [6] = { 0xd0, 0x00, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, - [7] = { 0xd1, 0x00, PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, -}; - -/* duplicated from amd_f17h_perfmon_event_map. */ -static struct kvm_event_hw_type_mapping amd_f17h_event_mapping[] = { - [0] = { 0x76, 0x00, PERF_COUNT_HW_CPU_CYCLES }, - [1] = { 0xc0, 0x00, PERF_COUNT_HW_INSTRUCTIONS }, - [2] = { 0x60, 0xff, PERF_COUNT_HW_CACHE_REFERENCES }, - [3] = { 0x64, 0x09, PERF_COUNT_HW_CACHE_MISSES }, - [4] = { 0xc2, 0x00, PERF_COUNT_HW_BRANCH_INSTRUCTIONS }, - [5] = { 0xc3, 0x00, PERF_COUNT_HW_BRANCH_MISSES }, - [6] = { 0x87, 0x02, PERF_COUNT_HW_STALLED_CYCLES_FRONTEND }, - [7] = { 0x87, 0x01, PERF_COUNT_HW_STALLED_CYCLES_BACKEND }, -}; - -/* amd_pmc_perf_hw_id depends on these being the same size */ -static_assert(ARRAY_SIZE(amd_event_mapping) == - ARRAY_SIZE(amd_f17h_event_mapping)); - static unsigned int get_msr_base(struct kvm_pmu *pmu, enum pmu_type type) { struct kvm_vcpu *vcpu = pmu_to_vcpu(pmu); @@ -154,31 +126,9 @@ static inline struct kvm_pmc *get_gp_pmc_amd(struct kvm_pmu *pmu, u32 msr, return &pmu->gp_counters[msr_to_index(msr)]; } -static unsigned int amd_pmc_perf_hw_id(struct kvm_pmc *pmc) +static bool amd_hw_event_available(struct kvm_pmc *pmc) { - struct kvm_event_hw_type_mapping *event_mapping; - u8 event_select = pmc->eventsel & ARCH_PERFMON_EVENTSEL_EVENT; - u8 unit_mask = (pmc->eventsel & ARCH_PERFMON_EVENTSEL_UMASK) >> 8; - int i; - - /* return PERF_COUNT_HW_MAX as AMD doesn't have fixed events */ - if (WARN_ON(pmc_is_fixed(pmc))) - return PERF_COUNT_HW_MAX; - - if (guest_cpuid_family(pmc->vcpu) >= 0x17) - event_mapping = amd_f17h_event_mapping; - else - event_mapping = amd_event_mapping; - - for (i = 0; i < ARRAY_SIZE(amd_event_mapping); i++) - if (event_mapping[i].eventsel == event_select - && event_mapping[i].unit_mask == unit_mask) - break; - - if (i == ARRAY_SIZE(amd_event_mapping)) - return PERF_COUNT_HW_MAX; - - return event_mapping[i].event_type; + return true; } /* check if a PMC is enabled by comparing it against global_ctrl bits. Because @@ -286,8 +236,10 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) pmc = get_gp_pmc_amd(pmu, msr, PMU_TYPE_EVNTSEL); if (pmc) { data &= ~pmu->reserved_bits; - if (data != pmc->eventsel) - reprogram_gp_counter(pmc, data); + if (data != pmc->eventsel) { + pmc->eventsel = data; + reprogram_counter(pmc); + } return 0; } @@ -343,7 +295,7 @@ static void amd_pmu_reset(struct kvm_vcpu *vcpu) } struct kvm_pmu_ops amd_pmu_ops __initdata = { - .pmc_perf_hw_id = amd_pmc_perf_hw_id, + .hw_event_available = amd_hw_event_available, .pmc_is_enabled = amd_pmc_is_enabled, .pmc_idx_to_pmc = amd_pmc_idx_to_pmc, .rdpmc_ecx_to_pmc = amd_rdpmc_ecx_to_pmc, diff --git a/arch/x86/kvm/svm/sev.c b/arch/x86/kvm/svm/sev.c index 0c240ed04f96..b0e793e7d85c 100644 --- a/arch/x86/kvm/svm/sev.c +++ b/arch/x86/kvm/svm/sev.c @@ -603,6 +603,9 @@ static int sev_es_sync_vmsa(struct vcpu_svm *svm) save->xss = svm->vcpu.arch.ia32_xss; save->dr6 = svm->vcpu.arch.dr6; + pr_debug("Virtual Machine Save Area (VMSA):\n"); + print_hex_dump(KERN_CONT, "", DUMP_PREFIX_NONE, 16, 1, save, sizeof(*save), false); + return 0; } @@ -1606,38 +1609,35 @@ static int sev_lock_vcpus_for_migration(struct kvm *kvm, { struct kvm_vcpu *vcpu; unsigned long i, j; - bool first = true; kvm_for_each_vcpu(i, vcpu, kvm) { if (mutex_lock_killable_nested(&vcpu->mutex, role)) goto out_unlock; - if (first) { +#ifdef CONFIG_PROVE_LOCKING + if (!i) /* * Reset the role to one that avoids colliding with * the role used for the first vcpu mutex. */ role = SEV_NR_MIGRATION_ROLES; - first = false; - } else { + else mutex_release(&vcpu->mutex.dep_map, _THIS_IP_); - } +#endif } return 0; out_unlock: - first = true; kvm_for_each_vcpu(j, vcpu, kvm) { if (i == j) break; - if (first) - first = false; - else +#ifdef CONFIG_PROVE_LOCKING + if (j) mutex_acquire(&vcpu->mutex.dep_map, role, 0, _THIS_IP_); - +#endif mutex_unlock(&vcpu->mutex); } diff --git a/arch/x86/kvm/svm/svm.c b/arch/x86/kvm/svm/svm.c index 44bbf25dfeb9..38f873cb6f2c 100644 --- a/arch/x86/kvm/svm/svm.c +++ b/arch/x86/kvm/svm/svm.c @@ -74,6 +74,8 @@ static uint64_t osvw_len = 4, osvw_status; static DEFINE_PER_CPU(u64, current_tsc_ratio); +#define X2APIC_MSR(x) (APIC_BASE_MSR + (x >> 4)) + static const struct svm_direct_access_msrs { u32 index; /* Index of the MSR */ bool always; /* True if intercept is initially cleared */ @@ -100,6 +102,38 @@ static const struct svm_direct_access_msrs { { .index = MSR_IA32_CR_PAT, .always = false }, { .index = MSR_AMD64_SEV_ES_GHCB, .always = true }, { .index = MSR_TSC_AUX, .always = false }, + { .index = X2APIC_MSR(APIC_ID), .always = false }, + { .index = X2APIC_MSR(APIC_LVR), .always = false }, + { .index = X2APIC_MSR(APIC_TASKPRI), .always = false }, + { .index = X2APIC_MSR(APIC_ARBPRI), .always = false }, + { .index = X2APIC_MSR(APIC_PROCPRI), .always = false }, + { .index = X2APIC_MSR(APIC_EOI), .always = false }, + { .index = X2APIC_MSR(APIC_RRR), .always = false }, + { .index = X2APIC_MSR(APIC_LDR), .always = false }, + { .index = X2APIC_MSR(APIC_DFR), .always = false }, + { .index = X2APIC_MSR(APIC_SPIV), .always = false }, + { .index = X2APIC_MSR(APIC_ISR), .always = false }, + { .index = X2APIC_MSR(APIC_TMR), .always = false }, + { .index = X2APIC_MSR(APIC_IRR), .always = false }, + { .index = X2APIC_MSR(APIC_ESR), .always = false }, + { .index = X2APIC_MSR(APIC_ICR), .always = false }, + { .index = X2APIC_MSR(APIC_ICR2), .always = false }, + + /* + * Note: + * AMD does not virtualize APIC TSC-deadline timer mode, but it is + * emulated by KVM. When setting APIC LVTT (0x832) register bit 18, + * the AVIC hardware would generate GP fault. Therefore, always + * intercept the MSR 0x832, and do not setup direct_access_msr. + */ + { .index = X2APIC_MSR(APIC_LVTTHMR), .always = false }, + { .index = X2APIC_MSR(APIC_LVTPC), .always = false }, + { .index = X2APIC_MSR(APIC_LVT0), .always = false }, + { .index = X2APIC_MSR(APIC_LVT1), .always = false }, + { .index = X2APIC_MSR(APIC_LVTERR), .always = false }, + { .index = X2APIC_MSR(APIC_TMICT), .always = false }, + { .index = X2APIC_MSR(APIC_TMCCT), .always = false }, + { .index = X2APIC_MSR(APIC_TDCR), .always = false }, { .index = MSR_INVALID, .always = false }, }; @@ -188,9 +222,6 @@ module_param(tsc_scaling, int, 0444); static bool avic; module_param(avic, bool, 0444); -static bool force_avic; -module_param_unsafe(force_avic, bool, 0444); - bool __read_mostly dump_invalid_vmcb; module_param(dump_invalid_vmcb, bool, 0644); @@ -342,9 +373,11 @@ static void svm_set_interrupt_shadow(struct kvm_vcpu *vcpu, int mask) } -static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) +static int __svm_skip_emulated_instruction(struct kvm_vcpu *vcpu, + bool commit_side_effects) { struct vcpu_svm *svm = to_svm(vcpu); + unsigned long old_rflags; /* * SEV-ES does not expose the next RIP. The RIP update is controlled by @@ -359,18 +392,75 @@ static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) } if (!svm->next_rip) { + if (unlikely(!commit_side_effects)) + old_rflags = svm->vmcb->save.rflags; + if (!kvm_emulate_instruction(vcpu, EMULTYPE_SKIP)) return 0; + + if (unlikely(!commit_side_effects)) + svm->vmcb->save.rflags = old_rflags; } else { kvm_rip_write(vcpu, svm->next_rip); } done: - svm_set_interrupt_shadow(vcpu, 0); + if (likely(commit_side_effects)) + svm_set_interrupt_shadow(vcpu, 0); return 1; } +static int svm_skip_emulated_instruction(struct kvm_vcpu *vcpu) +{ + return __svm_skip_emulated_instruction(vcpu, true); +} + +static int svm_update_soft_interrupt_rip(struct kvm_vcpu *vcpu) +{ + unsigned long rip, old_rip = kvm_rip_read(vcpu); + struct vcpu_svm *svm = to_svm(vcpu); + + /* + * Due to architectural shortcomings, the CPU doesn't always provide + * NextRIP, e.g. if KVM intercepted an exception that occurred while + * the CPU was vectoring an INTO/INT3 in the guest. Temporarily skip + * the instruction even if NextRIP is supported to acquire the next + * RIP so that it can be shoved into the NextRIP field, otherwise + * hardware will fail to advance guest RIP during event injection. + * Drop the exception/interrupt if emulation fails and effectively + * retry the instruction, it's the least awful option. If NRIPS is + * in use, the skip must not commit any side effects such as clearing + * the interrupt shadow or RFLAGS.RF. + */ + if (!__svm_skip_emulated_instruction(vcpu, !nrips)) + return -EIO; + + rip = kvm_rip_read(vcpu); + + /* + * Save the injection information, even when using next_rip, as the + * VMCB's next_rip will be lost (cleared on VM-Exit) if the injection + * doesn't complete due to a VM-Exit occurring while the CPU is + * vectoring the event. Decoding the instruction isn't guaranteed to + * work as there may be no backing instruction, e.g. if the event is + * being injected by L1 for L2, or if the guest is patching INT3 into + * a different instruction. + */ + svm->soft_int_injected = true; + svm->soft_int_csbase = svm->vmcb->save.cs.base; + svm->soft_int_old_rip = old_rip; + svm->soft_int_next_rip = rip; + + if (nrips) + kvm_rip_write(vcpu, old_rip); + + if (static_cpu_has(X86_FEATURE_NRIPS)) + svm->vmcb->control.next_rip = rip; + + return 0; +} + static void svm_queue_exception(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); @@ -380,21 +470,9 @@ static void svm_queue_exception(struct kvm_vcpu *vcpu) kvm_deliver_exception_payload(vcpu); - if (nr == BP_VECTOR && !nrips) { - unsigned long rip, old_rip = kvm_rip_read(vcpu); - - /* - * For guest debugging where we have to reinject #BP if some - * INT3 is guest-owned: - * Emulate nRIP by moving RIP forward. Will fail if injection - * raises a fault that is not intercepted. Still better than - * failing in all cases. - */ - (void)svm_skip_emulated_instruction(vcpu); - rip = kvm_rip_read(vcpu); - svm->int3_rip = rip + svm->vmcb->save.cs.base; - svm->int3_injected = rip - old_rip; - } + if (kvm_exception_is_soft(nr) && + svm_update_soft_interrupt_rip(vcpu)) + return; svm->vmcb->control.event_inj = nr | SVM_EVTINJ_VALID @@ -736,6 +814,29 @@ void svm_vcpu_init_msrpm(struct kvm_vcpu *vcpu, u32 *msrpm) } } +void svm_set_x2apic_msr_interception(struct vcpu_svm *svm, bool intercept) +{ + int i; + + if (intercept == svm->x2avic_msrs_intercepted) + return; + + if (avic_mode != AVIC_MODE_X2 || + !apic_x2apic_mode(svm->vcpu.arch.apic)) + return; + + for (i = 0; i < MAX_DIRECT_ACCESS_MSRS; i++) { + int index = direct_access_msrs[i].index; + + if ((index < APIC_BASE_MSR) || + (index > APIC_BASE_MSR + 0xff)) + continue; + set_msr_interception(&svm->vcpu, svm->msrpm, index, + !intercept, !intercept); + } + + svm->x2avic_msrs_intercepted = intercept; +} void svm_vcpu_free_msrpm(u32 *msrpm) { @@ -1231,7 +1332,7 @@ static void __svm_vcpu_reset(struct kvm_vcpu *vcpu) svm_init_osvw(vcpu); vcpu->arch.microcode_version = 0x01000065; - svm->tsc_ratio_msr = kvm_default_tsc_scaling_ratio; + svm->tsc_ratio_msr = kvm_caps.default_tsc_scaling_ratio; if (sev_es_guest(vcpu->kvm)) sev_es_vcpu_reset(svm); @@ -1299,6 +1400,8 @@ static int svm_vcpu_create(struct kvm_vcpu *vcpu) goto error_free_vmsa_page; } + svm->x2avic_msrs_intercepted = true; + svm->vmcb01.ptr = page_address(vmcb01_page); svm->vmcb01.pa = __sme_set(page_to_pfn(vmcb01_page) << PAGE_SHIFT); svm_switch_vmcb(svm, &svm->vmcb01); @@ -2345,6 +2448,7 @@ static int task_switch_interception(struct kvm_vcpu *vcpu) kvm_clear_exception_queue(vcpu); break; case SVM_EXITINTINFO_TYPE_INTR: + case SVM_EXITINTINFO_TYPE_SOFT: kvm_clear_interrupt_queue(vcpu); break; default: @@ -3375,35 +3479,49 @@ static void svm_inject_nmi(struct kvm_vcpu *vcpu) struct vcpu_svm *svm = to_svm(vcpu); svm->vmcb->control.event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_NMI; + + if (svm->nmi_l1_to_l2) + return; + vcpu->arch.hflags |= HF_NMI_MASK; if (!sev_es_guest(vcpu->kvm)) svm_set_intercept(svm, INTERCEPT_IRET); ++vcpu->stat.nmi_injections; } -static void svm_inject_irq(struct kvm_vcpu *vcpu) +static void svm_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) { struct vcpu_svm *svm = to_svm(vcpu); + u32 type; - BUG_ON(!(gif_set(svm))); + if (vcpu->arch.interrupt.soft) { + if (svm_update_soft_interrupt_rip(vcpu)) + return; - trace_kvm_inj_virq(vcpu->arch.interrupt.nr); + type = SVM_EVTINJ_TYPE_SOFT; + } else { + type = SVM_EVTINJ_TYPE_INTR; + } + + trace_kvm_inj_virq(vcpu->arch.interrupt.nr, + vcpu->arch.interrupt.soft, reinjected); ++vcpu->stat.irq_injections; svm->vmcb->control.event_inj = vcpu->arch.interrupt.nr | - SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_INTR; + SVM_EVTINJ_VALID | type; } void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, int trig_mode, int vector) { /* - * vcpu->arch.apicv_active must be read after vcpu->mode. + * apic->apicv_active must be read after vcpu->mode. * Pairs with smp_store_release in vcpu_enter_guest. */ bool in_guest_mode = (smp_load_acquire(&vcpu->mode) == IN_GUEST_MODE); - if (!READ_ONCE(vcpu->arch.apicv_active)) { + /* Note, this is called iff the local APIC is in-kernel. */ + if (!READ_ONCE(vcpu->arch.apic->apicv_active)) { /* Process the interrupt via inject_pending_event */ kvm_make_request(KVM_REQ_EVENT, vcpu); kvm_vcpu_kick(vcpu); @@ -3668,15 +3786,49 @@ static inline void sync_lapic_to_cr8(struct kvm_vcpu *vcpu) svm->vmcb->control.int_ctl |= cr8 & V_TPR_MASK; } +static void svm_complete_soft_interrupt(struct kvm_vcpu *vcpu, u8 vector, + int type) +{ + bool is_exception = (type == SVM_EXITINTINFO_TYPE_EXEPT); + bool is_soft = (type == SVM_EXITINTINFO_TYPE_SOFT); + struct vcpu_svm *svm = to_svm(vcpu); + + /* + * If NRIPS is enabled, KVM must snapshot the pre-VMRUN next_rip that's + * associated with the original soft exception/interrupt. next_rip is + * cleared on all exits that can occur while vectoring an event, so KVM + * needs to manually set next_rip for re-injection. Unlike the !nrips + * case below, this needs to be done if and only if KVM is re-injecting + * the same event, i.e. if the event is a soft exception/interrupt, + * otherwise next_rip is unused on VMRUN. + */ + if (nrips && (is_soft || (is_exception && kvm_exception_is_soft(vector))) && + kvm_is_linear_rip(vcpu, svm->soft_int_old_rip + svm->soft_int_csbase)) + svm->vmcb->control.next_rip = svm->soft_int_next_rip; + /* + * If NRIPS isn't enabled, KVM must manually advance RIP prior to + * injecting the soft exception/interrupt. That advancement needs to + * be unwound if vectoring didn't complete. Note, the new event may + * not be the injected event, e.g. if KVM injected an INTn, the INTn + * hit a #NP in the guest, and the #NP encountered a #PF, the #NP will + * be the reported vectored event, but RIP still needs to be unwound. + */ + else if (!nrips && (is_soft || is_exception) && + kvm_is_linear_rip(vcpu, svm->soft_int_next_rip + svm->soft_int_csbase)) + kvm_rip_write(vcpu, svm->soft_int_old_rip); +} + static void svm_complete_interrupts(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); u8 vector; int type; u32 exitintinfo = svm->vmcb->control.exit_int_info; - unsigned int3_injected = svm->int3_injected; + bool nmi_l1_to_l2 = svm->nmi_l1_to_l2; + bool soft_int_injected = svm->soft_int_injected; - svm->int3_injected = 0; + svm->nmi_l1_to_l2 = false; + svm->soft_int_injected = false; /* * If we've made progress since setting HF_IRET_MASK, we've @@ -3701,9 +3853,13 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) vector = exitintinfo & SVM_EXITINTINFO_VEC_MASK; type = exitintinfo & SVM_EXITINTINFO_TYPE_MASK; + if (soft_int_injected) + svm_complete_soft_interrupt(vcpu, vector, type); + switch (type) { case SVM_EXITINTINFO_TYPE_NMI: vcpu->arch.nmi_injected = true; + svm->nmi_l1_to_l2 = nmi_l1_to_l2; break; case SVM_EXITINTINFO_TYPE_EXEPT: /* @@ -3712,18 +3868,6 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) if (vector == X86_TRAP_VC) break; - /* - * In case of software exceptions, do not reinject the vector, - * but re-execute the instruction instead. Rewind RIP first - * if we emulated INT3 before. - */ - if (kvm_exception_is_soft(vector)) { - if (vector == BP_VECTOR && int3_injected && - kvm_is_linear_rip(vcpu, svm->int3_rip)) - kvm_rip_write(vcpu, - kvm_rip_read(vcpu) - int3_injected); - break; - } if (exitintinfo & SVM_EXITINTINFO_VALID_ERR) { u32 err = svm->vmcb->control.exit_int_info_err; kvm_requeue_exception_e(vcpu, vector, err); @@ -3734,9 +3878,13 @@ static void svm_complete_interrupts(struct kvm_vcpu *vcpu) case SVM_EXITINTINFO_TYPE_INTR: kvm_queue_interrupt(vcpu, vector, false); break; + case SVM_EXITINTINFO_TYPE_SOFT: + kvm_queue_interrupt(vcpu, vector, true); + break; default: break; } + } static void svm_cancel_injection(struct kvm_vcpu *vcpu) @@ -3952,7 +4100,7 @@ static void svm_load_mmu_pgd(struct kvm_vcpu *vcpu, hpa_t root_hpa, hv_track_root_tdp(vcpu, root_hpa); cr3 = vcpu->arch.cr3; - } else if (vcpu->arch.mmu->root_role.level >= PT64_ROOT_4LEVEL) { + } else if (root_level >= PT64_ROOT_4LEVEL) { cr3 = __sme_set(root_hpa) | kvm_get_active_pcid(vcpu); } else { /* PCID in the guest should be impossible with a 32-bit MMU. */ @@ -4013,16 +4161,10 @@ static bool svm_has_emulated_msr(struct kvm *kvm, u32 index) return true; } -static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) -{ - return 0; -} - static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) { struct vcpu_svm *svm = to_svm(vcpu); struct kvm_cpuid_entry2 *best; - struct kvm *kvm = vcpu->kvm; vcpu->arch.xsaves_enabled = guest_cpuid_has(vcpu, X86_FEATURE_XSAVE) && boot_cpu_has(X86_FEATURE_XSAVE) && @@ -4049,19 +4191,11 @@ static void svm_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) /* For sev guests, the memory encryption bit is not reserved in CR3. */ if (sev_guest(vcpu->kvm)) { - best = kvm_find_cpuid_entry(vcpu, 0x8000001F, 0); + best = kvm_find_cpuid_entry(vcpu, 0x8000001F); if (best) vcpu->arch.reserved_gpa_bits &= ~(1UL << (best->ebx & 0x3f)); } - if (kvm_vcpu_apicv_active(vcpu)) { - /* - * AVIC does not work with an x2APIC mode guest. If the X2APIC feature - * is exposed to the guest, disable AVIC. - */ - if (guest_cpuid_has(vcpu, X86_FEATURE_X2APIC)) - kvm_set_apicv_inhibit(kvm, APICV_INHIBIT_REASON_X2APIC); - } init_vmcb_after_set_cpuid(vcpu); } @@ -4673,11 +4807,11 @@ static struct kvm_x86_ops svm_x86_ops __initdata = { .enable_nmi_window = svm_enable_nmi_window, .enable_irq_window = svm_enable_irq_window, .update_cr8_intercept = svm_update_cr8_intercept, + .set_virtual_apic_mode = avic_set_virtual_apic_mode, .refresh_apicv_exec_ctrl = avic_refresh_apicv_exec_ctrl, .check_apicv_inhibit_reasons = avic_check_apicv_inhibit_reasons, .apicv_post_state_restore = avic_apicv_post_state_restore, - .get_mt_mask = svm_get_mt_mask, .get_exit_info = svm_get_exit_info, .vcpu_after_set_cpuid = svm_vcpu_after_set_cpuid, @@ -4773,7 +4907,7 @@ static __init void svm_set_cpu_caps(void) { kvm_set_cpu_caps(); - supported_xss = 0; + kvm_caps.supported_xss = 0; /* CPUID 0x80000001 and 0x8000000A (SVM features) */ if (nested) { @@ -4849,7 +4983,8 @@ static __init int svm_hardware_setup(void) init_msrpm_offsets(); - supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); + kvm_caps.supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | + XFEATURE_MASK_BNDCSR); if (boot_cpu_has(X86_FEATURE_FXSR_OPT)) kvm_enable_efer_bits(EFER_FFXSR); @@ -4859,11 +4994,11 @@ static __init int svm_hardware_setup(void) tsc_scaling = false; } else { pr_info("TSC scaling supported\n"); - kvm_has_tsc_control = true; + kvm_caps.has_tsc_control = true; } } - kvm_max_tsc_scaling_ratio = SVM_TSC_RATIO_MAX; - kvm_tsc_scaling_ratio_frac_bits = 32; + kvm_caps.max_tsc_scaling_ratio = SVM_TSC_RATIO_MAX; + kvm_caps.tsc_scaling_ratio_frac_bits = 32; tsc_aux_uret_slot = kvm_add_user_return_msr(MSR_TSC_AUX); @@ -4917,17 +5052,9 @@ static __init int svm_hardware_setup(void) nrips = false; } - enable_apicv = avic = avic && npt_enabled && (boot_cpu_has(X86_FEATURE_AVIC) || force_avic); + enable_apicv = avic = avic && avic_hardware_setup(&svm_x86_ops); - if (enable_apicv) { - if (!boot_cpu_has(X86_FEATURE_AVIC)) { - pr_warn("AVIC is not supported in CPUID but force enabled"); - pr_warn("Your system might crash and burn"); - } else - pr_info("AVIC enabled\n"); - - amd_iommu_register_ga_log_notifier(&avic_ga_log_notifier); - } else { + if (!enable_apicv) { svm_x86_ops.vcpu_blocking = NULL; svm_x86_ops.vcpu_unblocking = NULL; svm_x86_ops.vcpu_get_apicv_inhibit_reasons = NULL; diff --git a/arch/x86/kvm/svm/svm.h b/arch/x86/kvm/svm/svm.h index 9223ac100ef5..6a7686bf6900 100644 --- a/arch/x86/kvm/svm/svm.h +++ b/arch/x86/kvm/svm/svm.h @@ -29,13 +29,21 @@ #define IOPM_SIZE PAGE_SIZE * 3 #define MSRPM_SIZE PAGE_SIZE * 2 -#define MAX_DIRECT_ACCESS_MSRS 21 -#define MSRPM_OFFSETS 16 +#define MAX_DIRECT_ACCESS_MSRS 46 +#define MSRPM_OFFSETS 32 extern u32 msrpm_offsets[MSRPM_OFFSETS] __read_mostly; extern bool npt_enabled; extern int vgif; extern bool intercept_smi; +enum avic_modes { + AVIC_MODE_NONE = 0, + AVIC_MODE_X1, + AVIC_MODE_X2, +}; + +extern enum avic_modes avic_mode; + /* * Clean bits in VMCB. * VMCB_ALL_CLEAN_MASK might also need to @@ -139,6 +147,7 @@ struct vmcb_ctrl_area_cached { u64 nested_ctl; u32 event_inj; u32 event_inj_err; + u64 next_rip; u64 nested_cr3; u64 virt_ext; u32 clean; @@ -228,9 +237,12 @@ struct vcpu_svm { bool nmi_singlestep; u64 nmi_singlestep_guest_rflags; + bool nmi_l1_to_l2; - unsigned int3_injected; - unsigned long int3_rip; + unsigned long soft_int_csbase; + unsigned long soft_int_old_rip; + unsigned long soft_int_next_rip; + bool soft_int_injected; /* optional nested SVM features that are enabled for this guest */ bool nrips_enabled : 1; @@ -264,6 +276,8 @@ struct vcpu_svm { struct vcpu_sev_es_state sev_es; bool guest_state_loaded; + + bool x2avic_msrs_intercepted; }; struct svm_cpu_data { @@ -509,6 +523,15 @@ static inline bool nested_npt_enabled(struct vcpu_svm *svm) return svm->nested.ctl.nested_ctl & SVM_NESTED_CTL_NP_ENABLE; } +static inline bool is_x2apic_msrpm_offset(u32 offset) +{ + /* 4 msrs per u8, and 4 u8 in u32 */ + u32 msr = offset * 16; + + return (msr >= APIC_BASE_MSR) && + (msr < (APIC_BASE_MSR + 0x100)); +} + /* svm.c */ #define MSR_INVALID 0xffffffffU @@ -534,6 +557,7 @@ void svm_set_gif(struct vcpu_svm *svm, bool value); int svm_invoke_exit_handler(struct kvm_vcpu *vcpu, u64 exit_code); void set_msr_interception(struct kvm_vcpu *vcpu, u32 *msrpm, u32 msr, int read, int write); +void svm_set_x2apic_msr_interception(struct vcpu_svm *svm, bool disable); void svm_complete_interrupt_delivery(struct kvm_vcpu *vcpu, int delivery_mode, int trig_mode, int vec); @@ -603,6 +627,7 @@ extern struct kvm_x86_nested_ops svm_nested_ops; /* avic.c */ +bool avic_hardware_setup(struct kvm_x86_ops *ops); int avic_ga_log_notifier(u32 ga_tag); void avic_vm_destroy(struct kvm *kvm); int avic_vm_init(struct kvm *kvm); @@ -613,18 +638,16 @@ int avic_init_vcpu(struct vcpu_svm *svm); void avic_vcpu_load(struct kvm_vcpu *vcpu, int cpu); void avic_vcpu_put(struct kvm_vcpu *vcpu); void avic_apicv_post_state_restore(struct kvm_vcpu *vcpu); -void avic_set_virtual_apic_mode(struct kvm_vcpu *vcpu); void avic_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu); bool avic_check_apicv_inhibit_reasons(enum kvm_apicv_inhibit reason); -void avic_hwapic_irr_update(struct kvm_vcpu *vcpu, int max_irr); -void avic_hwapic_isr_update(struct kvm_vcpu *vcpu, int max_isr); -bool avic_dy_apicv_has_pending_interrupt(struct kvm_vcpu *vcpu); int avic_pi_update_irte(struct kvm *kvm, unsigned int host_irq, uint32_t guest_irq, bool set); void avic_vcpu_blocking(struct kvm_vcpu *vcpu); void avic_vcpu_unblocking(struct kvm_vcpu *vcpu); void avic_ring_doorbell(struct kvm_vcpu *vcpu); unsigned long avic_vcpu_get_apicv_inhibit_reasons(struct kvm_vcpu *vcpu); +void avic_set_virtual_apic_mode(struct kvm_vcpu *vcpu); + /* sev.c */ diff --git a/arch/x86/kvm/trace.h b/arch/x86/kvm/trace.h index de4762517569..2120d7c060a9 100644 --- a/arch/x86/kvm/trace.h +++ b/arch/x86/kvm/trace.h @@ -154,7 +154,7 @@ TRACE_EVENT(kvm_xen_hypercall, TRACE_EVENT(kvm_pio, TP_PROTO(unsigned int rw, unsigned int port, unsigned int size, - unsigned int count, void *data), + unsigned int count, const void *data), TP_ARGS(rw, port, size, count, data), TP_STRUCT__entry( @@ -333,18 +333,24 @@ TRACE_EVENT_KVM_EXIT(kvm_exit); * Tracepoint for kvm interrupt injection: */ TRACE_EVENT(kvm_inj_virq, - TP_PROTO(unsigned int irq), - TP_ARGS(irq), + TP_PROTO(unsigned int vector, bool soft, bool reinjected), + TP_ARGS(vector, soft, reinjected), TP_STRUCT__entry( - __field( unsigned int, irq ) + __field( unsigned int, vector ) + __field( bool, soft ) + __field( bool, reinjected ) ), TP_fast_assign( - __entry->irq = irq; + __entry->vector = vector; + __entry->soft = soft; + __entry->reinjected = reinjected; ), - TP_printk("irq %u", __entry->irq) + TP_printk("%s 0x%x%s", + __entry->soft ? "Soft/INTn" : "IRQ", __entry->vector, + __entry->reinjected ? " [reinjected]" : "") ); #define EXS(x) { x##_VECTOR, "#" #x } @@ -358,25 +364,30 @@ TRACE_EVENT(kvm_inj_virq, * Tracepoint for kvm interrupt injection: */ TRACE_EVENT(kvm_inj_exception, - TP_PROTO(unsigned exception, bool has_error, unsigned error_code), - TP_ARGS(exception, has_error, error_code), + TP_PROTO(unsigned exception, bool has_error, unsigned error_code, + bool reinjected), + TP_ARGS(exception, has_error, error_code, reinjected), TP_STRUCT__entry( __field( u8, exception ) __field( u8, has_error ) __field( u32, error_code ) + __field( bool, reinjected ) ), TP_fast_assign( __entry->exception = exception; __entry->has_error = has_error; __entry->error_code = error_code; + __entry->reinjected = reinjected; ), - TP_printk("%s (0x%x)", + TP_printk("%s%s%s%s%s", __print_symbolic(__entry->exception, kvm_trace_sym_exc), - /* FIXME: don't print error_code if not present */ - __entry->has_error ? __entry->error_code : 0) + !__entry->has_error ? "" : " (", + !__entry->has_error ? "" : __print_symbolic(__entry->error_code, { }), + !__entry->has_error ? "" : ")", + __entry->reinjected ? " [reinjected]" : "") ); /* @@ -1479,6 +1490,24 @@ TRACE_EVENT(kvm_avic_kick_vcpu_slowpath, __entry->icrh, __entry->icrl, __entry->index) ); +TRACE_EVENT(kvm_avic_doorbell, + TP_PROTO(u32 vcpuid, u32 apicid), + TP_ARGS(vcpuid, apicid), + + TP_STRUCT__entry( + __field(u32, vcpuid) + __field(u32, apicid) + ), + + TP_fast_assign( + __entry->vcpuid = vcpuid; + __entry->apicid = apicid; + ), + + TP_printk("vcpuid=%u, apicid=%u", + __entry->vcpuid, __entry->apicid) +); + TRACE_EVENT(kvm_hv_timer_state, TP_PROTO(unsigned int vcpu_id, unsigned int hv_timer_in_use), TP_ARGS(vcpu_id, hv_timer_in_use), diff --git a/arch/x86/kvm/vmx/capabilities.h b/arch/x86/kvm/vmx/capabilities.h index c0e24826a86f..c5e5dfef69c7 100644 --- a/arch/x86/kvm/vmx/capabilities.h +++ b/arch/x86/kvm/vmx/capabilities.h @@ -6,6 +6,8 @@ #include "../lapic.h" #include "../x86.h" +#include "../pmu.h" +#include "../cpuid.h" extern bool __read_mostly enable_vpid; extern bool __read_mostly flexpriority_enabled; @@ -13,6 +15,7 @@ extern bool __read_mostly enable_ept; extern bool __read_mostly enable_unrestricted_guest; extern bool __read_mostly enable_ept_ad_bits; extern bool __read_mostly enable_pml; +extern bool __read_mostly enable_ipiv; extern int __read_mostly pt_mode; #define PT_MODE_SYSTEM 0 @@ -59,6 +62,7 @@ struct vmcs_config { u32 pin_based_exec_ctrl; u32 cpu_based_exec_ctrl; u32 cpu_based_2nd_exec_ctrl; + u64 cpu_based_3rd_exec_ctrl; u32 vmexit_ctrl; u32 vmentry_ctrl; struct nested_vmx_msrs nested; @@ -94,20 +98,17 @@ static inline bool cpu_has_vmx_posted_intr(void) static inline bool cpu_has_load_ia32_efer(void) { - return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_EFER) && - (vmcs_config.vmexit_ctrl & VM_EXIT_LOAD_IA32_EFER); + return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_EFER; } static inline bool cpu_has_load_perf_global_ctrl(void) { - return (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL) && - (vmcs_config.vmexit_ctrl & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL); + return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; } static inline bool cpu_has_vmx_mpx(void) { - return (vmcs_config.vmexit_ctrl & VM_EXIT_CLEAR_BNDCFGS) && - (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS); + return vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_BNDCFGS; } static inline bool cpu_has_vmx_tpr_shadow(void) @@ -131,6 +132,12 @@ static inline bool cpu_has_secondary_exec_ctrls(void) CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; } +static inline bool cpu_has_tertiary_exec_ctrls(void) +{ + return vmcs_config.cpu_based_exec_ctrl & + CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; +} + static inline bool cpu_has_vmx_virtualize_apic_accesses(void) { return vmcs_config.cpu_based_2nd_exec_ctrl & @@ -276,6 +283,11 @@ static inline bool cpu_has_vmx_apicv(void) cpu_has_vmx_posted_intr(); } +static inline bool cpu_has_vmx_ipiv(void) +{ + return vmcs_config.cpu_based_3rd_exec_ctrl & TERTIARY_EXEC_IPI_VIRT; +} + static inline bool cpu_has_vmx_flexpriority(void) { return cpu_has_vmx_tpr_shadow() && @@ -363,7 +375,6 @@ static inline bool cpu_has_vmx_intel_pt(void) rdmsrl(MSR_IA32_VMX_MISC, vmx_msr); return (vmx_msr & MSR_IA32_VMX_MISC_INTEL_PT) && (vmcs_config.cpu_based_2nd_exec_ctrl & SECONDARY_EXEC_PT_USE_GPA) && - (vmcs_config.vmexit_ctrl & VM_EXIT_CLEAR_IA32_RTIT_CTL) && (vmcs_config.vmentry_ctrl & VM_ENTRY_LOAD_IA32_RTIT_CTL); } @@ -385,23 +396,31 @@ static inline bool vmx_pt_mode_is_host_guest(void) return pt_mode == PT_MODE_HOST_GUEST; } +static inline bool vmx_pebs_supported(void) +{ + return boot_cpu_has(X86_FEATURE_PEBS) && kvm_pmu_cap.pebs_ept; +} + static inline u64 vmx_get_perf_capabilities(void) { - u64 perf_cap = 0; + u64 perf_cap = PMU_CAP_FW_WRITES; + u64 host_perf_cap = 0; if (!enable_pmu) - return perf_cap; + return 0; if (boot_cpu_has(X86_FEATURE_PDCM)) - rdmsrl(MSR_IA32_PERF_CAPABILITIES, perf_cap); + rdmsrl(MSR_IA32_PERF_CAPABILITIES, host_perf_cap); - perf_cap &= PMU_CAP_LBR_FMT; + perf_cap |= host_perf_cap & PMU_CAP_LBR_FMT; - /* - * Since counters are virtualized, KVM would support full - * width counting unconditionally, even if the host lacks it. - */ - return PMU_CAP_FW_WRITES | perf_cap; + if (vmx_pebs_supported()) { + perf_cap |= host_perf_cap & PERF_CAP_PEBS_MASK; + if ((perf_cap & PERF_CAP_PEBS_FORMAT) < 4) + perf_cap &= ~PERF_CAP_PEBS_BASELINE; + } + + return perf_cap; } static inline u64 vmx_supported_debugctl(void) @@ -417,4 +436,10 @@ static inline u64 vmx_supported_debugctl(void) return debugctl; } +static inline bool cpu_has_notify_vmexit(void) +{ + return vmcs_config.cpu_based_2nd_exec_ctrl & + SECONDARY_EXEC_NOTIFY_VM_EXITING; +} + #endif /* __KVM_X86_VMX_CAPS_H */ diff --git a/arch/x86/kvm/vmx/evmcs.c b/arch/x86/kvm/vmx/evmcs.c index 87e3dc10edf4..6a61b1ae7942 100644 --- a/arch/x86/kvm/vmx/evmcs.c +++ b/arch/x86/kvm/vmx/evmcs.c @@ -297,8 +297,10 @@ const unsigned int nr_evmcs_1_fields = ARRAY_SIZE(vmcs_field_to_evmcs_1); #if IS_ENABLED(CONFIG_HYPERV) __init void evmcs_sanitize_exec_ctrls(struct vmcs_config *vmcs_conf) { + vmcs_conf->cpu_based_exec_ctrl &= ~EVMCS1_UNSUPPORTED_EXEC_CTRL; vmcs_conf->pin_based_exec_ctrl &= ~EVMCS1_UNSUPPORTED_PINCTRL; vmcs_conf->cpu_based_2nd_exec_ctrl &= ~EVMCS1_UNSUPPORTED_2NDEXEC; + vmcs_conf->cpu_based_3rd_exec_ctrl = 0; vmcs_conf->vmexit_ctrl &= ~EVMCS1_UNSUPPORTED_VMEXIT_CTRL; vmcs_conf->vmentry_ctrl &= ~EVMCS1_UNSUPPORTED_VMENTRY_CTRL; diff --git a/arch/x86/kvm/vmx/evmcs.h b/arch/x86/kvm/vmx/evmcs.h index 8d70f9aea94b..f886a8ff0342 100644 --- a/arch/x86/kvm/vmx/evmcs.h +++ b/arch/x86/kvm/vmx/evmcs.h @@ -50,6 +50,7 @@ DECLARE_STATIC_KEY_FALSE(enable_evmcs); */ #define EVMCS1_UNSUPPORTED_PINCTRL (PIN_BASED_POSTED_INTR | \ PIN_BASED_VMX_PREEMPTION_TIMER) +#define EVMCS1_UNSUPPORTED_EXEC_CTRL (CPU_BASED_ACTIVATE_TERTIARY_CONTROLS) #define EVMCS1_UNSUPPORTED_2NDEXEC \ (SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY | \ SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES | \ diff --git a/arch/x86/kvm/vmx/nested.c b/arch/x86/kvm/vmx/nested.c index ab135f9ef52f..ddd4367d4826 100644 --- a/arch/x86/kvm/vmx/nested.c +++ b/arch/x86/kvm/vmx/nested.c @@ -311,11 +311,12 @@ static void free_nested(struct kvm_vcpu *vcpu) vmx->nested.cached_vmcs12 = NULL; kfree(vmx->nested.cached_shadow_vmcs12); vmx->nested.cached_shadow_vmcs12 = NULL; - /* Unpin physical memory we referred to in the vmcs02 */ - if (vmx->nested.apic_access_page) { - kvm_release_page_clean(vmx->nested.apic_access_page); - vmx->nested.apic_access_page = NULL; - } + /* + * Unpin physical memory we referred to in the vmcs02. The APIC access + * page's backing page (yeah, confusing) shouldn't actually be accessed, + * and if it is written, the contents are irrelevant. + */ + kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false); kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true); kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true); vmx->nested.pi_desc = NULL; @@ -1223,7 +1224,7 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) BIT_ULL(49) | BIT_ULL(54) | BIT_ULL(55) | /* reserved */ BIT_ULL(31) | GENMASK_ULL(47, 45) | GENMASK_ULL(63, 56); - u64 vmx_basic = vmx->nested.msrs.basic; + u64 vmx_basic = vmcs_config.nested.basic; if (!is_bitwise_subset(vmx_basic, data, feature_and_reserved)) return -EINVAL; @@ -1246,36 +1247,42 @@ static int vmx_restore_vmx_basic(struct vcpu_vmx *vmx, u64 data) return 0; } -static int -vmx_restore_control_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) +static void vmx_get_control_msr(struct nested_vmx_msrs *msrs, u32 msr_index, + u32 **low, u32 **high) { - u64 supported; - u32 *lowp, *highp; - switch (msr_index) { case MSR_IA32_VMX_TRUE_PINBASED_CTLS: - lowp = &vmx->nested.msrs.pinbased_ctls_low; - highp = &vmx->nested.msrs.pinbased_ctls_high; + *low = &msrs->pinbased_ctls_low; + *high = &msrs->pinbased_ctls_high; break; case MSR_IA32_VMX_TRUE_PROCBASED_CTLS: - lowp = &vmx->nested.msrs.procbased_ctls_low; - highp = &vmx->nested.msrs.procbased_ctls_high; + *low = &msrs->procbased_ctls_low; + *high = &msrs->procbased_ctls_high; break; case MSR_IA32_VMX_TRUE_EXIT_CTLS: - lowp = &vmx->nested.msrs.exit_ctls_low; - highp = &vmx->nested.msrs.exit_ctls_high; + *low = &msrs->exit_ctls_low; + *high = &msrs->exit_ctls_high; break; case MSR_IA32_VMX_TRUE_ENTRY_CTLS: - lowp = &vmx->nested.msrs.entry_ctls_low; - highp = &vmx->nested.msrs.entry_ctls_high; + *low = &msrs->entry_ctls_low; + *high = &msrs->entry_ctls_high; break; case MSR_IA32_VMX_PROCBASED_CTLS2: - lowp = &vmx->nested.msrs.secondary_ctls_low; - highp = &vmx->nested.msrs.secondary_ctls_high; + *low = &msrs->secondary_ctls_low; + *high = &msrs->secondary_ctls_high; break; default: BUG(); } +} + +static int +vmx_restore_control_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) +{ + u32 *lowp, *highp; + u64 supported; + + vmx_get_control_msr(&vmcs_config.nested, msr_index, &lowp, &highp); supported = vmx_control_msr(*lowp, *highp); @@ -1287,6 +1294,7 @@ vmx_restore_control_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) if (!is_bitwise_subset(supported, data, GENMASK_ULL(63, 32))) return -EINVAL; + vmx_get_control_msr(&vmx->nested.msrs, msr_index, &lowp, &highp); *lowp = data; *highp = data >> 32; return 0; @@ -1300,10 +1308,8 @@ static int vmx_restore_vmx_misc(struct vcpu_vmx *vmx, u64 data) BIT_ULL(28) | BIT_ULL(29) | BIT_ULL(30) | /* reserved */ GENMASK_ULL(13, 9) | BIT_ULL(31); - u64 vmx_misc; - - vmx_misc = vmx_control_msr(vmx->nested.msrs.misc_low, - vmx->nested.msrs.misc_high); + u64 vmx_misc = vmx_control_msr(vmcs_config.nested.misc_low, + vmcs_config.nested.misc_high); if (!is_bitwise_subset(vmx_misc, data, feature_and_reserved_bits)) return -EINVAL; @@ -1331,10 +1337,8 @@ static int vmx_restore_vmx_misc(struct vcpu_vmx *vmx, u64 data) static int vmx_restore_vmx_ept_vpid_cap(struct vcpu_vmx *vmx, u64 data) { - u64 vmx_ept_vpid_cap; - - vmx_ept_vpid_cap = vmx_control_msr(vmx->nested.msrs.ept_caps, - vmx->nested.msrs.vpid_caps); + u64 vmx_ept_vpid_cap = vmx_control_msr(vmcs_config.nested.ept_caps, + vmcs_config.nested.vpid_caps); /* Every bit is either reserved or a feature bit. */ if (!is_bitwise_subset(vmx_ept_vpid_cap, data, -1ULL)) @@ -1345,20 +1349,21 @@ static int vmx_restore_vmx_ept_vpid_cap(struct vcpu_vmx *vmx, u64 data) return 0; } -static int vmx_restore_fixed0_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) +static u64 *vmx_get_fixed0_msr(struct nested_vmx_msrs *msrs, u32 msr_index) { - u64 *msr; - switch (msr_index) { case MSR_IA32_VMX_CR0_FIXED0: - msr = &vmx->nested.msrs.cr0_fixed0; - break; + return &msrs->cr0_fixed0; case MSR_IA32_VMX_CR4_FIXED0: - msr = &vmx->nested.msrs.cr4_fixed0; - break; + return &msrs->cr4_fixed0; default: BUG(); } +} + +static int vmx_restore_fixed0_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) +{ + const u64 *msr = vmx_get_fixed0_msr(&vmcs_config.nested, msr_index); /* * 1 bits (which indicates bits which "must-be-1" during VMX operation) @@ -1367,7 +1372,7 @@ static int vmx_restore_fixed0_msr(struct vcpu_vmx *vmx, u32 msr_index, u64 data) if (!is_bitwise_subset(data, *msr, -1ULL)) return -EINVAL; - *msr = data; + *vmx_get_fixed0_msr(&vmx->nested.msrs, msr_index) = data; return 0; } @@ -1428,7 +1433,7 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data) vmx->nested.msrs.vmcs_enum = data; return 0; case MSR_IA32_VMX_VMFUNC: - if (data & ~vmx->nested.msrs.vmfunc_controls) + if (data & ~vmcs_config.nested.vmfunc_controls) return -EINVAL; vmx->nested.msrs.vmfunc_controls = data; return 0; @@ -2133,6 +2138,8 @@ static u64 nested_vmx_calc_efer(struct vcpu_vmx *vmx, struct vmcs12 *vmcs12) static void prepare_vmcs02_constant_state(struct vcpu_vmx *vmx) { + struct kvm *kvm = vmx->vcpu.kvm; + /* * If vmcs02 hasn't been initialized, set the constant vmcs02 state * according to L0's settings (vmcs12 is irrelevant here). Host @@ -2175,6 +2182,9 @@ static void prepare_vmcs02_constant_state(struct vcpu_vmx *vmx) if (cpu_has_vmx_encls_vmexit()) vmcs_write64(ENCLS_EXITING_BITMAP, INVALID_GPA); + if (kvm_notify_vmexit_enabled(kvm)) + vmcs_write32(NOTIFY_WINDOW, kvm->arch.notify_window); + /* * Set the MSR load/store lists to match L0's settings. Only the * addresses are constant (for vmcs02), the counts can change based @@ -2514,11 +2524,11 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, vmcs_write64(GUEST_IA32_DEBUGCTL, vmcs12->guest_ia32_debugctl); } else { kvm_set_dr(vcpu, 7, vcpu->arch.dr7); - vmcs_write64(GUEST_IA32_DEBUGCTL, vmx->nested.vmcs01_debugctl); + vmcs_write64(GUEST_IA32_DEBUGCTL, vmx->nested.pre_vmenter_debugctl); } if (kvm_mpx_supported() && (!vmx->nested.nested_run_pending || !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) - vmcs_write64(GUEST_BNDCFGS, vmx->nested.vmcs01_guest_bndcfgs); + vmcs_write64(GUEST_BNDCFGS, vmx->nested.pre_vmenter_bndcfgs); vmx_set_rflags(vcpu, vmcs12->guest_rflags); /* EXCEPTION_BITMAP and CR0_GUEST_HOST_MASK should basically be the @@ -2547,7 +2557,7 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, vmx_get_l2_tsc_multiplier(vcpu)); vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset); - if (kvm_has_tsc_control) + if (kvm_caps.has_tsc_control) vmcs_write64(TSC_MULTIPLIER, vcpu->arch.tsc_scaling_ratio); nested_vmx_transition_tlb_flush(vcpu, vmcs12, true); @@ -2613,6 +2623,7 @@ static int prepare_vmcs02(struct kvm_vcpu *vcpu, struct vmcs12 *vmcs12, } if ((vmcs12->vm_entry_controls & VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL) && + intel_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu)) && WARN_ON_ONCE(kvm_set_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, vmcs12->guest_ia32_perf_global_ctrl))) { *entry_failure_code = ENTRY_FAIL_DEFAULT; @@ -3158,8 +3169,6 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu) struct vmcs12 *vmcs12 = get_vmcs12(vcpu); struct vcpu_vmx *vmx = to_vmx(vcpu); struct kvm_host_map *map; - struct page *page; - u64 hpa; if (!vcpu->arch.pdptrs_from_userspace && !nested_cpu_has_ept(vmcs12) && is_pae_paging(vcpu)) { @@ -3174,23 +3183,12 @@ static bool nested_get_vmcs12_pages(struct kvm_vcpu *vcpu) if (nested_cpu_has2(vmcs12, SECONDARY_EXEC_VIRTUALIZE_APIC_ACCESSES)) { - /* - * Translate L1 physical address to host physical - * address for vmcs02. Keep the page pinned, so this - * physical address remains valid. We keep a reference - * to it so we can release it later. - */ - if (vmx->nested.apic_access_page) { /* shouldn't happen */ - kvm_release_page_clean(vmx->nested.apic_access_page); - vmx->nested.apic_access_page = NULL; - } - page = kvm_vcpu_gpa_to_page(vcpu, vmcs12->apic_access_addr); - if (!is_error_page(page)) { - vmx->nested.apic_access_page = page; - hpa = page_to_phys(vmx->nested.apic_access_page); - vmcs_write64(APIC_ACCESS_ADDR, hpa); + map = &vmx->nested.apic_access_page_map; + + if (!kvm_vcpu_map(vcpu, gpa_to_gfn(vmcs12->apic_access_addr), map)) { + vmcs_write64(APIC_ACCESS_ADDR, pfn_to_hpa(map->pfn)); } else { - pr_debug_ratelimited("%s: no backing 'struct page' for APIC-access address in vmcs12\n", + pr_debug_ratelimited("%s: no backing for APIC-access address in vmcs12\n", __func__); vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; vcpu->run->internal.suberror = @@ -3373,11 +3371,13 @@ enum nvmx_vmentry_status nested_vmx_enter_non_root_mode(struct kvm_vcpu *vcpu, if (likely(!evaluate_pending_interrupts) && kvm_vcpu_apicv_active(vcpu)) evaluate_pending_interrupts |= vmx_has_apicv_interrupt(vcpu); - if (!(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS)) - vmx->nested.vmcs01_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL); + if (!vmx->nested.nested_run_pending || + !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_DEBUG_CONTROLS)) + vmx->nested.pre_vmenter_debugctl = vmcs_read64(GUEST_IA32_DEBUGCTL); if (kvm_mpx_supported() && - !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS)) - vmx->nested.vmcs01_guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS); + (!vmx->nested.nested_run_pending || + !(vmcs12->vm_entry_controls & VM_ENTRY_LOAD_BNDCFGS))) + vmx->nested.pre_vmenter_bndcfgs = vmcs_read64(GUEST_BNDCFGS); /* * Overwrite vmcs01.GUEST_CR3 with L1's CR3 if EPT is disabled *and* @@ -4096,8 +4096,6 @@ static void sync_vmcs02_to_vmcs12_rare(struct kvm_vcpu *vcpu, vmcs12->guest_idtr_base = vmcs_readl(GUEST_IDTR_BASE); vmcs12->guest_pending_dbg_exceptions = vmcs_readl(GUEST_PENDING_DBG_EXCEPTIONS); - if (kvm_mpx_supported()) - vmcs12->guest_bndcfgs = vmcs_read64(GUEST_BNDCFGS); vmx->nested.need_sync_vmcs02_to_vmcs12_rare = false; } @@ -4336,7 +4334,8 @@ static void load_vmcs12_host_state(struct kvm_vcpu *vcpu, vmcs_write64(GUEST_IA32_PAT, vmcs12->host_ia32_pat); vcpu->arch.pat = vmcs12->host_ia32_pat; } - if (vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL) + if ((vmcs12->vm_exit_controls & VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL) && + intel_pmu_has_perf_global_ctrl(vcpu_to_pmu(vcpu))) WARN_ON_ONCE(kvm_set_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL, vmcs12->host_ia32_perf_global_ctrl)); @@ -4609,7 +4608,7 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, vmcs_write32(VM_EXIT_MSR_LOAD_COUNT, vmx->msr_autoload.host.nr); vmcs_write32(VM_ENTRY_MSR_LOAD_COUNT, vmx->msr_autoload.guest.nr); vmcs_write64(TSC_OFFSET, vcpu->arch.tsc_offset); - if (kvm_has_tsc_control) + if (kvm_caps.has_tsc_control) vmcs_write64(TSC_MULTIPLIER, vcpu->arch.tsc_scaling_ratio); if (vmx->nested.l1_tpr_threshold != -1) @@ -4626,10 +4625,7 @@ void nested_vmx_vmexit(struct kvm_vcpu *vcpu, u32 vm_exit_reason, } /* Unpin physical memory we referred to in vmcs02 */ - if (vmx->nested.apic_access_page) { - kvm_release_page_clean(vmx->nested.apic_access_page); - vmx->nested.apic_access_page = NULL; - } + kvm_vcpu_unmap(vcpu, &vmx->nested.apic_access_page_map, false); kvm_vcpu_unmap(vcpu, &vmx->nested.virtual_apic_map, true); kvm_vcpu_unmap(vcpu, &vmx->nested.pi_desc_map, true); vmx->nested.pi_desc = NULL; @@ -4828,28 +4824,6 @@ int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification, return 0; } -void nested_vmx_pmu_refresh(struct kvm_vcpu *vcpu, - bool vcpu_has_perf_global_ctrl) -{ - struct vcpu_vmx *vmx; - - if (!nested_vmx_allowed(vcpu)) - return; - - vmx = to_vmx(vcpu); - if (vcpu_has_perf_global_ctrl) { - vmx->nested.msrs.entry_ctls_high |= - VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; - vmx->nested.msrs.exit_ctls_high |= - VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL; - } else { - vmx->nested.msrs.entry_ctls_high &= - ~VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL; - vmx->nested.msrs.exit_ctls_high &= - ~VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL; - } -} - static int nested_vmx_get_vmptr(struct kvm_vcpu *vcpu, gpa_t *vmpointer, int *ret) { @@ -4952,7 +4926,7 @@ out_vmcs02: } /* Emulate the VMXON instruction. */ -static int handle_vmon(struct kvm_vcpu *vcpu) +static int handle_vmxon(struct kvm_vcpu *vcpu) { int ret; gpa_t vmptr; @@ -4962,20 +4936,25 @@ static int handle_vmon(struct kvm_vcpu *vcpu) | FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX; /* - * The Intel VMX Instruction Reference lists a bunch of bits that are - * prerequisite to running VMXON, most notably cr4.VMXE must be set to - * 1 (see vmx_is_valid_cr4() for when we allow the guest to set this). - * Otherwise, we should fail with #UD. But most faulting conditions - * have already been checked by hardware, prior to the VM-exit for - * VMXON. We do test guest cr4.VMXE because processor CR4 always has - * that bit set to 1 in non-root mode. + * Note, KVM cannot rely on hardware to perform the CR0/CR4 #UD checks + * that have higher priority than VM-Exit (see Intel SDM's pseudocode + * for VMXON), as KVM must load valid CR0/CR4 values into hardware while + * running the guest, i.e. KVM needs to check the _guest_ values. + * + * Rely on hardware for the other two pre-VM-Exit checks, !VM86 and + * !COMPATIBILITY modes. KVM may run the guest in VM86 to emulate Real + * Mode, but KVM will never take the guest out of those modes. */ - if (!kvm_read_cr4_bits(vcpu, X86_CR4_VMXE)) { + if (!nested_host_cr0_valid(vcpu, kvm_read_cr0(vcpu)) || + !nested_host_cr4_valid(vcpu, kvm_read_cr4(vcpu))) { kvm_queue_exception(vcpu, UD_VECTOR); return 1; } - /* CPL=0 must be checked manually. */ + /* + * CPL=0 and all other checks that are lower priority than VM-Exit must + * be checked manually. + */ if (vmx_get_cpl(vcpu)) { kvm_inject_gp(vcpu, 0); return 1; @@ -5044,7 +5023,7 @@ static inline void nested_release_vmcs12(struct kvm_vcpu *vcpu) } /* Emulate the VMXOFF instruction */ -static int handle_vmoff(struct kvm_vcpu *vcpu) +static int handle_vmxoff(struct kvm_vcpu *vcpu) { if (!nested_vmx_check_permission(vcpu)) return 1; @@ -6111,6 +6090,9 @@ static bool nested_vmx_l1_wants_exit(struct kvm_vcpu *vcpu, SECONDARY_EXEC_ENABLE_USR_WAIT_PAUSE); case EXIT_REASON_ENCLS: return nested_vmx_exit_handled_encls(vcpu, vmcs12); + case EXIT_REASON_NOTIFY: + /* Notify VM exit is not exposed to L1 */ + return false; default: return true; } @@ -6775,6 +6757,9 @@ void nested_vmx_setup_ctls_msrs(struct nested_vmx_msrs *msrs, u32 ept_caps) rdmsrl(MSR_IA32_VMX_CR0_FIXED1, msrs->cr0_fixed1); rdmsrl(MSR_IA32_VMX_CR4_FIXED1, msrs->cr4_fixed1); + if (vmx_umip_emulated()) + msrs->cr4_fixed1 |= X86_CR4_UMIP; + msrs->vmcs_enum = nested_vmx_calc_vmcs_enum_msr(); } @@ -6818,8 +6803,8 @@ __init int nested_vmx_hardware_setup(int (*exit_handlers[])(struct kvm_vcpu *)) exit_handlers[EXIT_REASON_VMREAD] = handle_vmread; exit_handlers[EXIT_REASON_VMRESUME] = handle_vmresume; exit_handlers[EXIT_REASON_VMWRITE] = handle_vmwrite; - exit_handlers[EXIT_REASON_VMOFF] = handle_vmoff; - exit_handlers[EXIT_REASON_VMON] = handle_vmon; + exit_handlers[EXIT_REASON_VMOFF] = handle_vmxoff; + exit_handlers[EXIT_REASON_VMON] = handle_vmxon; exit_handlers[EXIT_REASON_INVEPT] = handle_invept; exit_handlers[EXIT_REASON_INVVPID] = handle_invvpid; exit_handlers[EXIT_REASON_VMFUNC] = handle_vmfunc; diff --git a/arch/x86/kvm/vmx/nested.h b/arch/x86/kvm/vmx/nested.h index c92cea0b8ccc..88b00a7359e4 100644 --- a/arch/x86/kvm/vmx/nested.h +++ b/arch/x86/kvm/vmx/nested.h @@ -32,8 +32,6 @@ int vmx_set_vmx_msr(struct kvm_vcpu *vcpu, u32 msr_index, u64 data); int vmx_get_vmx_msr(struct nested_vmx_msrs *msrs, u32 msr_index, u64 *pdata); int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification, u32 vmx_instruction_info, bool wr, int len, gva_t *ret); -void nested_vmx_pmu_refresh(struct kvm_vcpu *vcpu, - bool vcpu_has_perf_global_ctrl); void nested_mark_vmcs12_pages_dirty(struct kvm_vcpu *vcpu); bool nested_vmx_check_io_bitmaps(struct kvm_vcpu *vcpu, unsigned int port, int size); @@ -281,7 +279,8 @@ static inline bool nested_cr4_valid(struct kvm_vcpu *vcpu, unsigned long val) u64 fixed0 = to_vmx(vcpu)->nested.msrs.cr4_fixed0; u64 fixed1 = to_vmx(vcpu)->nested.msrs.cr4_fixed1; - return fixed_bits_valid(val, fixed0, fixed1); + return fixed_bits_valid(val, fixed0, fixed1) && + __kvm_is_valid_cr4(vcpu, val); } /* No difference in the restrictions on guest and host CR4 in VMX operation. */ diff --git a/arch/x86/kvm/vmx/pmu_intel.c b/arch/x86/kvm/vmx/pmu_intel.c index 37e9eb32e3d9..862c1a4d971b 100644 --- a/arch/x86/kvm/vmx/pmu_intel.c +++ b/arch/x86/kvm/vmx/pmu_intel.c @@ -37,23 +37,35 @@ static int fixed_pmc_events[] = {1, 0, 7}; static void reprogram_fixed_counters(struct kvm_pmu *pmu, u64 data) { + struct kvm_pmc *pmc; + u8 old_fixed_ctr_ctrl = pmu->fixed_ctr_ctrl; int i; + pmu->fixed_ctr_ctrl = data; for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { u8 new_ctrl = fixed_ctrl_field(data, i); - u8 old_ctrl = fixed_ctrl_field(pmu->fixed_ctr_ctrl, i); - struct kvm_pmc *pmc; - - pmc = get_fixed_pmc(pmu, MSR_CORE_PERF_FIXED_CTR0 + i); + u8 old_ctrl = fixed_ctrl_field(old_fixed_ctr_ctrl, i); if (old_ctrl == new_ctrl) continue; - __set_bit(INTEL_PMC_IDX_FIXED + i, pmu->pmc_in_use); - reprogram_fixed_counter(pmc, new_ctrl, i); - } + pmc = get_fixed_pmc(pmu, MSR_CORE_PERF_FIXED_CTR0 + i); - pmu->fixed_ctr_ctrl = data; + __set_bit(INTEL_PMC_IDX_FIXED + i, pmu->pmc_in_use); + reprogram_counter(pmc); + } +} + +static struct kvm_pmc *intel_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx) +{ + if (pmc_idx < INTEL_PMC_IDX_FIXED) { + return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + pmc_idx, + MSR_P6_EVNTSEL0); + } else { + u32 idx = pmc_idx - INTEL_PMC_IDX_FIXED; + + return get_fixed_pmc(pmu, idx + MSR_CORE_PERF_FIXED_CTR0); + } } /* function is called when global control register has been updated. */ @@ -61,14 +73,18 @@ static void global_ctrl_changed(struct kvm_pmu *pmu, u64 data) { int bit; u64 diff = pmu->global_ctrl ^ data; + struct kvm_pmc *pmc; pmu->global_ctrl = data; - for_each_set_bit(bit, (unsigned long *)&diff, X86_PMC_IDX_MAX) - reprogram_counter(pmu, bit); + for_each_set_bit(bit, (unsigned long *)&diff, X86_PMC_IDX_MAX) { + pmc = intel_pmc_idx_to_pmc(pmu, bit); + if (pmc) + reprogram_counter(pmc); + } } -static unsigned int intel_pmc_perf_hw_id(struct kvm_pmc *pmc) +static bool intel_hw_event_available(struct kvm_pmc *pmc) { struct kvm_pmu *pmu = pmc_to_pmu(pmc); u8 event_select = pmc->eventsel & ARCH_PERFMON_EVENTSEL_EVENT; @@ -82,15 +98,12 @@ static unsigned int intel_pmc_perf_hw_id(struct kvm_pmc *pmc) /* disable event that reported as not present by cpuid */ if ((i < 7) && !(pmu->available_event_types & (1 << i))) - return PERF_COUNT_HW_MAX + 1; + return false; break; } - if (i == ARRAY_SIZE(intel_arch_events)) - return PERF_COUNT_HW_MAX; - - return intel_arch_events[i].event_type; + return true; } /* check if a PMC is enabled by comparing it with globl_ctrl bits. */ @@ -98,21 +111,12 @@ static bool intel_pmc_is_enabled(struct kvm_pmc *pmc) { struct kvm_pmu *pmu = pmc_to_pmu(pmc); + if (!intel_pmu_has_perf_global_ctrl(pmu)) + return true; + return test_bit(pmc->idx, (unsigned long *)&pmu->global_ctrl); } -static struct kvm_pmc *intel_pmc_idx_to_pmc(struct kvm_pmu *pmu, int pmc_idx) -{ - if (pmc_idx < INTEL_PMC_IDX_FIXED) - return get_gp_pmc(pmu, MSR_P6_EVNTSEL0 + pmc_idx, - MSR_P6_EVNTSEL0); - else { - u32 idx = pmc_idx - INTEL_PMC_IDX_FIXED; - - return get_fixed_pmc(pmu, idx + MSR_CORE_PERF_FIXED_CTR0); - } -} - static bool intel_is_valid_rdpmc_ecx(struct kvm_vcpu *vcpu, unsigned int idx) { struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); @@ -167,16 +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_compatible(struct kvm_vcpu *vcpu) -{ - /* - * As a first step, a guest could only enable LBR feature if its - * cpu model is the same as the host because the LBR registers - * would be pass-through to the guest and they're model specific. - */ - return boot_cpu_data.x86_model == guest_cpuid_model(vcpu); -} - bool intel_pmu_lbr_is_enabled(struct kvm_vcpu *vcpu) { struct x86_pmu_lbr *lbr = vcpu_to_lbr_records(vcpu); @@ -205,6 +199,7 @@ static bool intel_pmu_is_valid_lbr_msr(struct kvm_vcpu *vcpu, u32 index) static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr) { struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); + u64 perf_capabilities; int ret; switch (msr) { @@ -212,7 +207,18 @@ static bool intel_is_valid_msr(struct kvm_vcpu *vcpu, u32 msr) case MSR_CORE_PERF_GLOBAL_STATUS: case MSR_CORE_PERF_GLOBAL_CTRL: case MSR_CORE_PERF_GLOBAL_OVF_CTRL: - ret = pmu->version > 1; + return intel_pmu_has_perf_global_ctrl(pmu); + break; + case MSR_IA32_PEBS_ENABLE: + ret = vcpu_get_perf_capabilities(vcpu) & PERF_CAP_PEBS_FORMAT; + break; + case MSR_IA32_DS_AREA: + ret = guest_cpuid_has(vcpu, X86_FEATURE_DS); + break; + case MSR_PEBS_DATA_CFG: + perf_capabilities = vcpu_get_perf_capabilities(vcpu); + ret = (perf_capabilities & PERF_CAP_PEBS_BASELINE) && + ((perf_capabilities & PERF_CAP_PEBS_FORMAT) > 3); break; default: ret = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0) || @@ -361,6 +367,15 @@ static int intel_pmu_get_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_CORE_PERF_GLOBAL_OVF_CTRL: msr_info->data = 0; return 0; + case MSR_IA32_PEBS_ENABLE: + msr_info->data = pmu->pebs_enable; + return 0; + case MSR_IA32_DS_AREA: + msr_info->data = pmu->ds_area; + return 0; + case MSR_PEBS_DATA_CFG: + msr_info->data = pmu->pebs_data_cfg; + return 0; default: if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) || (pmc = get_gp_pmc(pmu, msr, MSR_IA32_PMC0))) { @@ -395,7 +410,7 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_CORE_PERF_FIXED_CTR_CTRL: if (pmu->fixed_ctr_ctrl == data) return 0; - if (!(data & 0xfffffffffffff444ull)) { + if (!(data & pmu->fixed_ctr_ctrl_mask)) { reprogram_fixed_counters(pmu, data); return 0; } @@ -421,6 +436,29 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 0; } break; + case MSR_IA32_PEBS_ENABLE: + if (pmu->pebs_enable == data) + return 0; + if (!(data & pmu->pebs_enable_mask)) { + pmu->pebs_enable = data; + return 0; + } + break; + case MSR_IA32_DS_AREA: + if (msr_info->host_initiated && data && !guest_cpuid_has(vcpu, X86_FEATURE_DS)) + return 1; + if (is_noncanonical_address(data, vcpu)) + return 1; + pmu->ds_area = data; + return 0; + case MSR_PEBS_DATA_CFG: + if (pmu->pebs_data_cfg == data) + return 0; + if (!(data & pmu->pebs_data_cfg_mask)) { + pmu->pebs_data_cfg = data; + return 0; + } + break; default: if ((pmc = get_gp_pmc(pmu, msr, MSR_IA32_PERFCTR0)) || (pmc = get_gp_pmc(pmu, msr, MSR_IA32_PMC0))) { @@ -445,7 +483,8 @@ static int intel_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) (pmu->raw_event_mask & HSW_IN_TX_CHECKPOINTED)) reserved_bits ^= HSW_IN_TX_CHECKPOINTED; if (!(data & reserved_bits)) { - reprogram_gp_counter(pmc, data); + pmc->eventsel = data; + reprogram_counter(pmc); return 0; } } else if (intel_pmu_handle_lbr_msrs_access(vcpu, msr_info, false)) @@ -474,11 +513,12 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) { struct kvm_pmu *pmu = vcpu_to_pmu(vcpu); struct lbr_desc *lbr_desc = vcpu_to_lbr_desc(vcpu); - - struct x86_pmu_capability x86_pmu; struct kvm_cpuid_entry2 *entry; union cpuid10_eax eax; union cpuid10_edx edx; + u64 perf_capabilities; + u64 counter_mask; + int i; pmu->nr_arch_gp_counters = 0; pmu->nr_arch_fixed_counters = 0; @@ -487,8 +527,13 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) pmu->version = 0; pmu->reserved_bits = 0xffffffff00200000ull; pmu->raw_event_mask = X86_RAW_EVENT_MASK; + pmu->global_ctrl_mask = ~0ull; + pmu->global_ovf_ctrl_mask = ~0ull; + pmu->fixed_ctr_ctrl_mask = ~0ull; + pmu->pebs_enable_mask = ~0ull; + pmu->pebs_data_cfg_mask = ~0ull; - entry = kvm_find_cpuid_entry(vcpu, 0xa, 0); + entry = kvm_find_cpuid_entry(vcpu, 0xa); if (!entry || !vcpu->kvm->arch.enable_pmu) return; eax.full = entry->eax; @@ -498,13 +543,13 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) if (!pmu->version) return; - perf_get_x86_pmu_capability(&x86_pmu); - pmu->nr_arch_gp_counters = min_t(int, eax.split.num_counters, - x86_pmu.num_counters_gp); - eax.split.bit_width = min_t(int, eax.split.bit_width, x86_pmu.bit_width_gp); + kvm_pmu_cap.num_counters_gp); + eax.split.bit_width = min_t(int, eax.split.bit_width, + kvm_pmu_cap.bit_width_gp); pmu->counter_bitmask[KVM_PMC_GP] = ((u64)1 << eax.split.bit_width) - 1; - eax.split.mask_length = min_t(int, eax.split.mask_length, x86_pmu.events_mask_len); + eax.split.mask_length = min_t(int, eax.split.mask_length, + kvm_pmu_cap.events_mask_len); pmu->available_event_types = ~entry->ebx & ((1ull << eax.split.mask_length) - 1); @@ -514,17 +559,19 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) pmu->nr_arch_fixed_counters = min3(ARRAY_SIZE(fixed_pmc_events), (size_t) edx.split.num_counters_fixed, - (size_t) x86_pmu.num_counters_fixed); - edx.split.bit_width_fixed = min_t(int, - edx.split.bit_width_fixed, x86_pmu.bit_width_fixed); + (size_t)kvm_pmu_cap.num_counters_fixed); + edx.split.bit_width_fixed = min_t(int, edx.split.bit_width_fixed, + kvm_pmu_cap.bit_width_fixed); pmu->counter_bitmask[KVM_PMC_FIXED] = ((u64)1 << edx.split.bit_width_fixed) - 1; setup_fixed_pmc_eventsel(pmu); } - pmu->global_ctrl = ((1ull << pmu->nr_arch_gp_counters) - 1) | - (((1ull << pmu->nr_arch_fixed_counters) - 1) << INTEL_PMC_IDX_FIXED); - pmu->global_ctrl_mask = ~pmu->global_ctrl; + for (i = 0; i < pmu->nr_arch_fixed_counters; i++) + pmu->fixed_ctr_ctrl_mask &= ~(0xbull << (i * 4)); + counter_mask = ~(((1ull << pmu->nr_arch_gp_counters) - 1) | + (((1ull << pmu->nr_arch_fixed_counters) - 1) << INTEL_PMC_IDX_FIXED)); + pmu->global_ctrl_mask = counter_mask; pmu->global_ovf_ctrl_mask = pmu->global_ctrl_mask & ~(MSR_CORE_PERF_GLOBAL_OVF_CTRL_OVF_BUF | MSR_CORE_PERF_GLOBAL_OVF_CTRL_COND_CHGD); @@ -532,7 +579,7 @@ static void intel_pmu_refresh(struct kvm_vcpu *vcpu) pmu->global_ovf_ctrl_mask &= ~MSR_CORE_PERF_GLOBAL_OVF_CTRL_TRACE_TOPA_PMI; - entry = kvm_find_cpuid_entry(vcpu, 7, 0); + entry = kvm_find_cpuid_entry_index(vcpu, 7, 0); if (entry && (boot_cpu_has(X86_FEATURE_HLE) || boot_cpu_has(X86_FEATURE_RTM)) && (entry->ebx & (X86_FEATURE_HLE|X86_FEATURE_RTM))) { @@ -545,16 +592,29 @@ 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); - nested_vmx_pmu_refresh(vcpu, - intel_is_valid_msr(vcpu, MSR_CORE_PERF_GLOBAL_CTRL)); - - if (intel_pmu_lbr_is_compatible(vcpu)) + if (cpuid_model_is_consistent(vcpu)) x86_perf_get_lbr(&lbr_desc->records); else lbr_desc->records.nr = 0; 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; + pmu->reserved_bits &= ~ICL_EVENTSEL_ADAPTIVE; + for (i = 0; i < pmu->nr_arch_fixed_counters; i++) { + pmu->fixed_ctr_ctrl_mask &= + ~(1ULL << (INTEL_PMC_IDX_FIXED + i * 4)); + } + pmu->pebs_data_cfg_mask = ~0xff00000full; + } else { + pmu->pebs_enable_mask = + ~((1ull << pmu->nr_arch_gp_counters) - 1); + } + } } static void intel_pmu_init(struct kvm_vcpu *vcpu) @@ -719,8 +779,28 @@ static void intel_pmu_cleanup(struct kvm_vcpu *vcpu) intel_pmu_release_guest_lbr_event(vcpu); } +void intel_pmu_cross_mapped_check(struct kvm_pmu *pmu) +{ + struct kvm_pmc *pmc = NULL; + int bit; + + for_each_set_bit(bit, (unsigned long *)&pmu->global_ctrl, + X86_PMC_IDX_MAX) { + pmc = intel_pmc_idx_to_pmc(pmu, bit); + + if (!pmc || !pmc_speculative_in_use(pmc) || + !intel_pmc_is_enabled(pmc)) + continue; + + if (pmc->perf_event && pmc->idx != pmc->perf_event->hw.idx) { + pmu->host_cross_mapped_mask |= + BIT_ULL(pmc->perf_event->hw.idx); + } + } +} + struct kvm_pmu_ops intel_pmu_ops __initdata = { - .pmc_perf_hw_id = intel_pmc_perf_hw_id, + .hw_event_available = intel_hw_event_available, .pmc_is_enabled = intel_pmc_is_enabled, .pmc_idx_to_pmc = intel_pmc_idx_to_pmc, .rdpmc_ecx_to_pmc = intel_rdpmc_ecx_to_pmc, diff --git a/arch/x86/kvm/vmx/posted_intr.c b/arch/x86/kvm/vmx/posted_intr.c index 07e5fcf5a5aa..1b56c5e5c9fb 100644 --- a/arch/x86/kvm/vmx/posted_intr.c +++ b/arch/x86/kvm/vmx/posted_intr.c @@ -34,7 +34,7 @@ static inline struct pi_desc *vcpu_to_pi_desc(struct kvm_vcpu *vcpu) return &(to_vmx(vcpu)->pi_desc); } -static int pi_try_set_control(struct pi_desc *pi_desc, u64 old, u64 new) +static int pi_try_set_control(struct pi_desc *pi_desc, u64 *pold, u64 new) { /* * PID.ON can be set at any time by a different vCPU or by hardware, @@ -42,7 +42,7 @@ static int pi_try_set_control(struct pi_desc *pi_desc, u64 old, u64 new) * update must be retried with a fresh snapshot an ON change causes * the cmpxchg to fail. */ - if (cmpxchg64(&pi_desc->control, old, new) != old) + if (!try_cmpxchg64(&pi_desc->control, pold, new)) return -EBUSY; return 0; @@ -96,8 +96,9 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu) if (!x2apic_mode) dest = (dest << 8) & 0xFF00; + old.control = READ_ONCE(pi_desc->control); do { - old.control = new.control = READ_ONCE(pi_desc->control); + new.control = old.control; /* * Clear SN (as above) and refresh the destination APIC ID to @@ -111,7 +112,7 @@ void vmx_vcpu_pi_load(struct kvm_vcpu *vcpu, int cpu) * descriptor was modified on "put" to use the wakeup vector. */ new.nv = POSTED_INTR_VECTOR; - } while (pi_try_set_control(pi_desc, old.control, new.control)); + } while (pi_try_set_control(pi_desc, &old.control, new.control)); local_irq_restore(flags); @@ -156,12 +157,12 @@ static void pi_enable_wakeup_handler(struct kvm_vcpu *vcpu) WARN(pi_desc->sn, "PI descriptor SN field set before blocking"); + old.control = READ_ONCE(pi_desc->control); do { - old.control = new.control = READ_ONCE(pi_desc->control); - /* set 'NV' to 'wakeup vector' */ + new.control = old.control; new.nv = POSTED_INTR_WAKEUP_VECTOR; - } while (pi_try_set_control(pi_desc, old.control, new.control)); + } while (pi_try_set_control(pi_desc, &old.control, new.control)); /* * Send a wakeup IPI to this CPU if an interrupt may have been posted @@ -177,11 +178,24 @@ static void pi_enable_wakeup_handler(struct kvm_vcpu *vcpu) local_irq_restore(flags); } +static bool vmx_needs_pi_wakeup(struct kvm_vcpu *vcpu) +{ + /* + * The default posted interrupt vector does nothing when + * invoked outside guest mode. Return whether a blocked vCPU + * can be the target of posted interrupts, as is the case when + * using either IPI virtualization or VT-d PI, so that the + * notification vector is switched to the one that calls + * back to the pi_wakeup_handler() function. + */ + return vmx_can_use_ipiv(vcpu) || vmx_can_use_vtd_pi(vcpu->kvm); +} + void vmx_vcpu_pi_put(struct kvm_vcpu *vcpu) { struct pi_desc *pi_desc = vcpu_to_pi_desc(vcpu); - if (!vmx_can_use_vtd_pi(vcpu->kvm)) + if (!vmx_needs_pi_wakeup(vcpu)) return; if (kvm_vcpu_is_blocking(vcpu) && !vmx_interrupt_blocked(vcpu)) diff --git a/arch/x86/kvm/vmx/posted_intr.h b/arch/x86/kvm/vmx/posted_intr.h index 9a45d5c9f116..26992076552e 100644 --- a/arch/x86/kvm/vmx/posted_intr.h +++ b/arch/x86/kvm/vmx/posted_intr.h @@ -5,6 +5,8 @@ #define POSTED_INTR_ON 0 #define POSTED_INTR_SN 1 +#define PID_TABLE_ENTRY_VALID 1 + /* Posted-Interrupt Descriptor */ struct pi_desc { u32 pir[8]; /* Posted interrupt requested */ diff --git a/arch/x86/kvm/vmx/sgx.c b/arch/x86/kvm/vmx/sgx.c index 35e7ec91ae86..aba8cebdc587 100644 --- a/arch/x86/kvm/vmx/sgx.c +++ b/arch/x86/kvm/vmx/sgx.c @@ -79,7 +79,7 @@ static int sgx_gva_to_gpa(struct kvm_vcpu *vcpu, gva_t gva, bool write, else *gpa = kvm_mmu_gva_to_gpa_read(vcpu, gva, &ex); - if (*gpa == UNMAPPED_GVA) { + if (*gpa == INVALID_GPA) { kvm_inject_emulated_page_fault(vcpu, &ex); return -EFAULT; } @@ -148,8 +148,8 @@ static int __handle_encls_ecreate(struct kvm_vcpu *vcpu, u8 max_size_log2; int trapnr, ret; - sgx_12_0 = kvm_find_cpuid_entry(vcpu, 0x12, 0); - sgx_12_1 = kvm_find_cpuid_entry(vcpu, 0x12, 1); + sgx_12_0 = kvm_find_cpuid_entry_index(vcpu, 0x12, 0); + sgx_12_1 = kvm_find_cpuid_entry_index(vcpu, 0x12, 1); if (!sgx_12_0 || !sgx_12_1) { kvm_prepare_emulation_failure_exit(vcpu); return 0; @@ -431,7 +431,7 @@ static bool sgx_intercept_encls_ecreate(struct kvm_vcpu *vcpu) if (!vcpu->kvm->arch.sgx_provisioning_allowed) return true; - guest_cpuid = kvm_find_cpuid_entry(vcpu, 0x12, 0); + guest_cpuid = kvm_find_cpuid_entry_index(vcpu, 0x12, 0); if (!guest_cpuid) return true; @@ -439,7 +439,7 @@ static bool sgx_intercept_encls_ecreate(struct kvm_vcpu *vcpu) if (guest_cpuid->ebx != ebx || guest_cpuid->edx != edx) return true; - guest_cpuid = kvm_find_cpuid_entry(vcpu, 0x12, 1); + guest_cpuid = kvm_find_cpuid_entry_index(vcpu, 0x12, 1); if (!guest_cpuid) return true; diff --git a/arch/x86/kvm/vmx/vmcs.h b/arch/x86/kvm/vmx/vmcs.h index 2b9d7a7e83f7..ac290a44a693 100644 --- a/arch/x86/kvm/vmx/vmcs.h +++ b/arch/x86/kvm/vmx/vmcs.h @@ -50,6 +50,7 @@ struct vmcs_controls_shadow { u32 pin; u32 exec; u32 secondary_exec; + u64 tertiary_exec; }; /* diff --git a/arch/x86/kvm/vmx/vmx.c b/arch/x86/kvm/vmx/vmx.c index be7c19374fdd..d7f8331d6f7e 100644 --- a/arch/x86/kvm/vmx/vmx.c +++ b/arch/x86/kvm/vmx/vmx.c @@ -105,6 +105,9 @@ module_param(fasteoi, bool, S_IRUGO); module_param(enable_apicv, bool, S_IRUGO); +bool __read_mostly enable_ipiv = true; +module_param(enable_ipiv, bool, 0444); + /* * If nested=1, nested virtualization is supported, i.e., guests may use * VMX and be a hypervisor for its own guests. If nested=0, guests may not @@ -116,6 +119,9 @@ module_param(nested, bool, S_IRUGO); bool __read_mostly enable_pml = 1; module_param_named(pml, enable_pml, bool, S_IRUGO); +static bool __read_mostly error_on_inconsistent_vmcs_config = true; +module_param(error_on_inconsistent_vmcs_config, bool, 0444); + static bool __read_mostly dump_invalid_vmcs = 0; module_param(dump_invalid_vmcs, bool, 0644); @@ -443,18 +449,20 @@ asmlinkage void vmread_error(unsigned long field, bool fault) noinline void vmwrite_error(unsigned long field, unsigned long value) { - vmx_insn_failed("kvm: vmwrite failed: field=%lx val=%lx err=%d\n", + vmx_insn_failed("kvm: vmwrite failed: field=%lx val=%lx err=%u\n", field, value, vmcs_read32(VM_INSTRUCTION_ERROR)); } noinline void vmclear_error(struct vmcs *vmcs, u64 phys_addr) { - vmx_insn_failed("kvm: vmclear failed: %p/%llx\n", vmcs, phys_addr); + vmx_insn_failed("kvm: vmclear failed: %p/%llx err=%u\n", + vmcs, phys_addr, vmcs_read32(VM_INSTRUCTION_ERROR)); } noinline void vmptrld_error(struct vmcs *vmcs, u64 phys_addr) { - vmx_insn_failed("kvm: vmptrld failed: %p/%llx\n", vmcs, phys_addr); + vmx_insn_failed("kvm: vmptrld failed: %p/%llx err=%u\n", + vmcs, phys_addr, vmcs_read32(VM_INSTRUCTION_ERROR)); } noinline void invvpid_error(unsigned long ext, u16 vpid, gva_t gva) @@ -1787,7 +1795,7 @@ u64 vmx_get_l2_tsc_multiplier(struct kvm_vcpu *vcpu) nested_cpu_has2(vmcs12, SECONDARY_EXEC_TSC_SCALING)) return vmcs12->tsc_multiplier; - return kvm_default_tsc_scaling_ratio; + return kvm_caps.default_tsc_scaling_ratio; } static void vmx_write_tsc_offset(struct kvm_vcpu *vcpu, u64 offset) @@ -2111,6 +2119,12 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (is_noncanonical_address(data & PAGE_MASK, vcpu) || (data & MSR_IA32_BNDCFGS_RSVD)) return 1; + + if (is_guest_mode(vcpu) && + ((vmx->nested.msrs.entry_ctls_high & VM_ENTRY_LOAD_BNDCFGS) || + (vmx->nested.msrs.exit_ctls_high & VM_EXIT_CLEAR_BNDCFGS))) + get_vmcs12(vcpu)->guest_bndcfgs = data; + vmcs_write64(GUEST_BNDCFGS, data); break; case MSR_IA32_UMWAIT_CONTROL: @@ -2312,7 +2326,18 @@ static int vmx_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if ((data & PMU_CAP_LBR_FMT) != (vmx_get_perf_capabilities() & PMU_CAP_LBR_FMT)) return 1; - if (!intel_pmu_lbr_is_compatible(vcpu)) + if (!cpuid_model_is_consistent(vcpu)) + return 1; + } + if (data & PERF_CAP_PEBS_FORMAT) { + if ((data & PERF_CAP_PEBS_MASK) != + (vmx_get_perf_capabilities() & PERF_CAP_PEBS_MASK)) + return 1; + if (!guest_cpuid_has(vcpu, X86_FEATURE_DS)) + return 1; + if (!guest_cpuid_has(vcpu, X86_FEATURE_DTES64)) + return 1; + if (!cpuid_model_is_consistent(vcpu)) return 1; } ret = kvm_set_msr_common(vcpu, msr_info); @@ -2489,6 +2514,15 @@ static __init int adjust_vmx_controls(u32 ctl_min, u32 ctl_opt, return 0; } +static __init u64 adjust_vmx_controls64(u64 ctl_opt, u32 msr) +{ + u64 allowed; + + rdmsrl(msr, allowed); + + return ctl_opt & allowed; +} + static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, struct vmx_capability *vmx_cap) { @@ -2497,8 +2531,26 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, u32 _pin_based_exec_control = 0; u32 _cpu_based_exec_control = 0; u32 _cpu_based_2nd_exec_control = 0; + u64 _cpu_based_3rd_exec_control = 0; u32 _vmexit_control = 0; u32 _vmentry_control = 0; + int i; + + /* + * LOAD/SAVE_DEBUG_CONTROLS are absent because both are mandatory. + * SAVE_IA32_PAT and SAVE_IA32_EFER are absent because KVM always + * intercepts writes to PAT and EFER, i.e. never enables those controls. + */ + struct { + u32 entry_control; + u32 exit_control; + } const vmcs_entry_exit_pairs[] = { + { VM_ENTRY_LOAD_IA32_PERF_GLOBAL_CTRL, VM_EXIT_LOAD_IA32_PERF_GLOBAL_CTRL }, + { VM_ENTRY_LOAD_IA32_PAT, VM_EXIT_LOAD_IA32_PAT }, + { VM_ENTRY_LOAD_IA32_EFER, VM_EXIT_LOAD_IA32_EFER }, + { VM_ENTRY_LOAD_BNDCFGS, VM_EXIT_CLEAR_BNDCFGS }, + { VM_ENTRY_LOAD_IA32_RTIT_CTL, VM_EXIT_CLEAR_IA32_RTIT_CTL }, + }; memset(vmcs_conf, 0, sizeof(*vmcs_conf)); min = CPU_BASED_HLT_EXITING | @@ -2518,7 +2570,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, opt = CPU_BASED_TPR_SHADOW | CPU_BASED_USE_MSR_BITMAPS | - CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; + CPU_BASED_ACTIVATE_SECONDARY_CONTROLS | + CPU_BASED_ACTIVATE_TERTIARY_CONTROLS; if (adjust_vmx_controls(min, opt, MSR_IA32_VMX_PROCBASED_CTLS, &_cpu_based_exec_control) < 0) return -EIO; @@ -2551,7 +2604,8 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, SECONDARY_EXEC_PT_USE_GPA | SECONDARY_EXEC_PT_CONCEAL_VMX | SECONDARY_EXEC_ENABLE_VMFUNC | - SECONDARY_EXEC_BUS_LOCK_DETECTION; + SECONDARY_EXEC_BUS_LOCK_DETECTION | + SECONDARY_EXEC_NOTIFY_VM_EXITING; if (cpu_has_sgx()) opt2 |= SECONDARY_EXEC_ENCLS_EXITING; if (adjust_vmx_controls(min2, opt2, @@ -2581,15 +2635,30 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, CPU_BASED_CR3_STORE_EXITING | CPU_BASED_INVLPG_EXITING); } else if (vmx_cap->ept) { - vmx_cap->ept = 0; pr_warn_once("EPT CAP should not exist if not support " "1-setting enable EPT VM-execution control\n"); + + if (error_on_inconsistent_vmcs_config) + return -EIO; + + vmx_cap->ept = 0; } if (!(_cpu_based_2nd_exec_control & SECONDARY_EXEC_ENABLE_VPID) && - vmx_cap->vpid) { - vmx_cap->vpid = 0; + vmx_cap->vpid) { pr_warn_once("VPID CAP should not exist if not support " "1-setting enable VPID VM-execution control\n"); + + if (error_on_inconsistent_vmcs_config) + return -EIO; + + vmx_cap->vpid = 0; + } + + if (_cpu_based_exec_control & CPU_BASED_ACTIVATE_TERTIARY_CONTROLS) { + u64 opt3 = TERTIARY_EXEC_IPI_VIRT; + + _cpu_based_3rd_exec_control = adjust_vmx_controls64(opt3, + MSR_IA32_VMX_PROCBASED_CTLS3); } min = VM_EXIT_SAVE_DEBUG_CONTROLS | VM_EXIT_ACK_INTR_ON_EXIT; @@ -2630,6 +2699,23 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, &_vmentry_control) < 0) return -EIO; + for (i = 0; i < ARRAY_SIZE(vmcs_entry_exit_pairs); i++) { + u32 n_ctrl = vmcs_entry_exit_pairs[i].entry_control; + u32 x_ctrl = vmcs_entry_exit_pairs[i].exit_control; + + if (!(_vmentry_control & n_ctrl) == !(_vmexit_control & x_ctrl)) + continue; + + pr_warn_once("Inconsistent VM-Entry/VM-Exit pair, entry = %x, exit = %x\n", + _vmentry_control & n_ctrl, _vmexit_control & x_ctrl); + + if (error_on_inconsistent_vmcs_config) + return -EIO; + + _vmentry_control &= ~n_ctrl; + _vmexit_control &= ~x_ctrl; + } + /* * Some cpus support VM_{ENTRY,EXIT}_IA32_PERF_GLOBAL_CTRL but they * can't be used due to an errata where VM Exit may incorrectly clear @@ -2678,6 +2764,7 @@ static __init int setup_vmcs_config(struct vmcs_config *vmcs_conf, vmcs_conf->pin_based_exec_ctrl = _pin_based_exec_control; vmcs_conf->cpu_based_exec_ctrl = _cpu_based_exec_control; vmcs_conf->cpu_based_2nd_exec_ctrl = _cpu_based_2nd_exec_control; + vmcs_conf->cpu_based_3rd_exec_ctrl = _cpu_based_3rd_exec_control; vmcs_conf->vmexit_ctrl = _vmexit_control; vmcs_conf->vmentry_ctrl = _vmentry_control; @@ -3230,8 +3317,8 @@ static bool vmx_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) { /* * We operate under the default treatment of SMM, so VMX cannot be - * enabled under SMM. Note, whether or not VMXE is allowed at all is - * handled by kvm_is_valid_cr4(). + * enabled under SMM. Note, whether or not VMXE is allowed at all, + * i.e. is a reserved bit, is handled by common x86 code. */ if ((cr4 & X86_CR4_VMXE) && is_smm(vcpu)) return false; @@ -3702,7 +3789,7 @@ static int init_rmode_identity_map(struct kvm *kvm) } /* Set up identity-mapping pagetable for EPT in real mode */ - for (i = 0; i < PT32_ENT_PER_PAGE; i++) { + for (i = 0; i < (PAGE_SIZE / sizeof(tmp)); i++) { tmp = (i << 22) + (_PAGE_PRESENT | _PAGE_RW | _PAGE_USER | _PAGE_ACCESSED | _PAGE_DIRTY | _PAGE_PSE); if (__copy_to_user(uaddr + i * sizeof(tmp), &tmp, sizeof(tmp))) { @@ -3932,6 +4019,8 @@ static void vmx_update_msr_bitmap_x2apic(struct kvm_vcpu *vcpu) vmx_enable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_TMCCT), MSR_TYPE_RW); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_EOI), MSR_TYPE_W); vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_SELF_IPI), MSR_TYPE_W); + if (enable_ipiv) + vmx_disable_intercept_for_msr(vcpu, X2APIC_MSR(APIC_ICR), MSR_TYPE_RW); } } @@ -3977,20 +4066,26 @@ static void vmx_msr_filter_changed(struct kvm_vcpu *vcpu) u32 i; /* - * Set intercept permissions for all potentially passed through MSRs - * again. They will automatically get filtered through the MSR filter, - * so we are back in sync after this. + * Redo intercept permissions for MSRs that KVM is passing through to + * the guest. Disabling interception will check the new MSR filter and + * ensure that KVM enables interception if usersepace wants to filter + * the MSR. MSRs that KVM is already intercepting don't need to be + * refreshed since KVM is going to intercept them regardless of what + * userspace wants. */ for (i = 0; i < ARRAY_SIZE(vmx_possible_passthrough_msrs); i++) { u32 msr = vmx_possible_passthrough_msrs[i]; - bool read = test_bit(i, vmx->shadow_msr_intercept.read); - bool write = test_bit(i, vmx->shadow_msr_intercept.write); - vmx_set_intercept_for_msr(vcpu, msr, MSR_TYPE_R, read); - vmx_set_intercept_for_msr(vcpu, msr, MSR_TYPE_W, write); + if (!test_bit(i, vmx->shadow_msr_intercept.read)) + vmx_disable_intercept_for_msr(vcpu, msr, MSR_TYPE_R); + + if (!test_bit(i, vmx->shadow_msr_intercept.write)) + vmx_disable_intercept_for_msr(vcpu, msr, MSR_TYPE_W); } - pt_update_intercept_for_msr(vcpu); + /* PT MSRs can be passed through iff PT is exposed to the guest. */ + if (vmx_pt_mode_is_host_guest()) + pt_update_intercept_for_msr(vcpu); } static inline void kvm_vcpu_trigger_posted_interrupt(struct kvm_vcpu *vcpu, @@ -4085,7 +4180,8 @@ static int vmx_deliver_posted_interrupt(struct kvm_vcpu *vcpu, int vector) if (!r) return 0; - if (!vcpu->arch.apicv_active) + /* Note, this is called iff the local APIC is in-kernel. */ + if (!vcpu->arch.apic->apicv_active) return -1; if (pi_test_and_set_pir(vector, &vmx->pi_desc)) @@ -4259,15 +4355,19 @@ static void vmx_refresh_apicv_exec_ctrl(struct kvm_vcpu *vcpu) } pin_controls_set(vmx, vmx_pin_based_exec_ctrl(vmx)); - if (cpu_has_secondary_exec_ctrls()) { - if (kvm_vcpu_apicv_active(vcpu)) - secondary_exec_controls_setbit(vmx, - SECONDARY_EXEC_APIC_REGISTER_VIRT | - SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); - else - secondary_exec_controls_clearbit(vmx, - SECONDARY_EXEC_APIC_REGISTER_VIRT | - SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); + + if (kvm_vcpu_apicv_active(vcpu)) { + secondary_exec_controls_setbit(vmx, + SECONDARY_EXEC_APIC_REGISTER_VIRT | + SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); + if (enable_ipiv) + tertiary_exec_controls_setbit(vmx, TERTIARY_EXEC_IPI_VIRT); + } else { + secondary_exec_controls_clearbit(vmx, + SECONDARY_EXEC_APIC_REGISTER_VIRT | + SECONDARY_EXEC_VIRTUAL_INTR_DELIVERY); + if (enable_ipiv) + tertiary_exec_controls_clearbit(vmx, TERTIARY_EXEC_IPI_VIRT); } vmx_update_msr_bitmap_x2apic(vcpu); @@ -4299,6 +4399,20 @@ static u32 vmx_exec_control(struct vcpu_vmx *vmx) return exec_control; } +static u64 vmx_tertiary_exec_control(struct vcpu_vmx *vmx) +{ + u64 exec_control = vmcs_config.cpu_based_3rd_exec_ctrl; + + /* + * IPI virtualization relies on APICv. Disable IPI virtualization if + * APICv is inhibited. + */ + if (!enable_ipiv || !kvm_vcpu_apicv_active(&vmx->vcpu)) + exec_control &= ~TERTIARY_EXEC_IPI_VIRT; + + return exec_control; +} + /* * Adjust a single secondary execution control bit to intercept/allow an * instruction in the guest. This is usually done based on whether or not a @@ -4441,13 +4555,48 @@ static u32 vmx_secondary_exec_control(struct vcpu_vmx *vmx) if (!vcpu->kvm->arch.bus_lock_detection_enabled) exec_control &= ~SECONDARY_EXEC_BUS_LOCK_DETECTION; + if (!kvm_notify_vmexit_enabled(vcpu->kvm)) + exec_control &= ~SECONDARY_EXEC_NOTIFY_VM_EXITING; + return exec_control; } +static inline int vmx_get_pid_table_order(struct kvm *kvm) +{ + return get_order(kvm->arch.max_vcpu_ids * sizeof(*to_kvm_vmx(kvm)->pid_table)); +} + +static int vmx_alloc_ipiv_pid_table(struct kvm *kvm) +{ + struct page *pages; + struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm); + + if (!irqchip_in_kernel(kvm) || !enable_ipiv) + return 0; + + if (kvm_vmx->pid_table) + return 0; + + pages = alloc_pages(GFP_KERNEL | __GFP_ZERO, vmx_get_pid_table_order(kvm)); + if (!pages) + return -ENOMEM; + + kvm_vmx->pid_table = (void *)page_address(pages); + return 0; +} + +static int vmx_vcpu_precreate(struct kvm *kvm) +{ + return vmx_alloc_ipiv_pid_table(kvm); +} + #define VMX_XSS_EXIT_BITMAP 0 static void init_vmcs(struct vcpu_vmx *vmx) { + struct kvm *kvm = vmx->vcpu.kvm; + struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm); + if (nested) nested_vmx_set_vmcs_shadowing_bitmap(); @@ -4464,6 +4613,9 @@ static void init_vmcs(struct vcpu_vmx *vmx) if (cpu_has_secondary_exec_ctrls()) secondary_exec_controls_set(vmx, vmx_secondary_exec_control(vmx)); + if (cpu_has_tertiary_exec_ctrls()) + tertiary_exec_controls_set(vmx, vmx_tertiary_exec_control(vmx)); + if (enable_apicv && lapic_in_kernel(&vmx->vcpu)) { vmcs_write64(EOI_EXIT_BITMAP0, 0); vmcs_write64(EOI_EXIT_BITMAP1, 0); @@ -4476,12 +4628,20 @@ static void init_vmcs(struct vcpu_vmx *vmx) vmcs_write64(POSTED_INTR_DESC_ADDR, __pa((&vmx->pi_desc))); } - if (!kvm_pause_in_guest(vmx->vcpu.kvm)) { + if (vmx_can_use_ipiv(&vmx->vcpu)) { + vmcs_write64(PID_POINTER_TABLE, __pa(kvm_vmx->pid_table)); + vmcs_write16(LAST_PID_POINTER_INDEX, kvm->arch.max_vcpu_ids - 1); + } + + if (!kvm_pause_in_guest(kvm)) { vmcs_write32(PLE_GAP, ple_gap); vmx->ple_window = ple_window; vmx->ple_window_dirty = true; } + if (kvm_notify_vmexit_enabled(kvm)) + vmcs_write32(NOTIFY_WINDOW, kvm->arch.notify_window); + vmcs_write32(PAGE_FAULT_ERROR_CODE_MASK, 0); vmcs_write32(PAGE_FAULT_ERROR_CODE_MATCH, 0); vmcs_write32(CR3_TARGET_COUNT, 0); /* 22.2.1 */ @@ -4652,13 +4812,13 @@ static void vmx_enable_nmi_window(struct kvm_vcpu *vcpu) exec_controls_setbit(to_vmx(vcpu), CPU_BASED_NMI_WINDOW_EXITING); } -static void vmx_inject_irq(struct kvm_vcpu *vcpu) +static void vmx_inject_irq(struct kvm_vcpu *vcpu, bool reinjected) { struct vcpu_vmx *vmx = to_vmx(vcpu); uint32_t intr; int irq = vcpu->arch.interrupt.nr; - trace_kvm_inj_virq(irq); + trace_kvm_inj_virq(irq, vcpu->arch.interrupt.soft, reinjected); ++vcpu->stat.irq_injections; if (vmx->rmode.vm86_active) { @@ -5770,6 +5930,32 @@ static int handle_bus_lock_vmexit(struct kvm_vcpu *vcpu) return 1; } +static int handle_notify(struct kvm_vcpu *vcpu) +{ + unsigned long exit_qual = vmx_get_exit_qual(vcpu); + bool context_invalid = exit_qual & NOTIFY_VM_CONTEXT_INVALID; + + ++vcpu->stat.notify_window_exits; + + /* + * Notify VM exit happened while executing iret from NMI, + * "blocked by NMI" bit has to be set before next VM entry. + */ + if (enable_vnmi && (exit_qual & INTR_INFO_UNBLOCK_NMI)) + vmcs_set_bits(GUEST_INTERRUPTIBILITY_INFO, + GUEST_INTR_STATE_NMI); + + if (vcpu->kvm->arch.notify_vmexit_flags & KVM_X86_NOTIFY_VMEXIT_USER || + context_invalid) { + vcpu->run->exit_reason = KVM_EXIT_NOTIFY; + vcpu->run->notify.flags = context_invalid ? + KVM_NOTIFY_CONTEXT_INVALID : 0; + return 0; + } + + return 1; +} + /* * The exit handlers return 1 if the exit was handled fully and guest execution * may resume. Otherwise they set the kvm_run parameter to indicate what needs @@ -5827,6 +6013,7 @@ static int (*kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { [EXIT_REASON_PREEMPTION_TIMER] = handle_preemption_timer, [EXIT_REASON_ENCLS] = handle_encls, [EXIT_REASON_BUS_LOCK] = handle_bus_lock_vmexit, + [EXIT_REASON_NOTIFY] = handle_notify, }; static const int kvm_vmx_max_exit_handlers = @@ -5924,6 +6111,7 @@ void dump_vmcs(struct kvm_vcpu *vcpu) struct vcpu_vmx *vmx = to_vmx(vcpu); u32 vmentry_ctl, vmexit_ctl; u32 cpu_based_exec_ctrl, pin_based_exec_ctrl, secondary_exec_control; + u64 tertiary_exec_control; unsigned long cr4; int efer_slot; @@ -5937,9 +6125,16 @@ void dump_vmcs(struct kvm_vcpu *vcpu) cpu_based_exec_ctrl = vmcs_read32(CPU_BASED_VM_EXEC_CONTROL); pin_based_exec_ctrl = vmcs_read32(PIN_BASED_VM_EXEC_CONTROL); cr4 = vmcs_readl(GUEST_CR4); - secondary_exec_control = 0; + if (cpu_has_secondary_exec_ctrls()) secondary_exec_control = vmcs_read32(SECONDARY_VM_EXEC_CONTROL); + else + secondary_exec_control = 0; + + if (cpu_has_tertiary_exec_ctrls()) + tertiary_exec_control = vmcs_read64(TERTIARY_VM_EXEC_CONTROL); + else + tertiary_exec_control = 0; pr_err("VMCS %p, last attempted VM-entry on CPU %d\n", vmx->loaded_vmcs->vmcs, vcpu->arch.last_vmentry_cpu); @@ -6039,9 +6234,10 @@ void dump_vmcs(struct kvm_vcpu *vcpu) vmx_dump_msrs("host autoload", &vmx->msr_autoload.host); pr_err("*** Control State ***\n"); - pr_err("PinBased=%08x CPUBased=%08x SecondaryExec=%08x\n", - pin_based_exec_ctrl, cpu_based_exec_ctrl, secondary_exec_control); - pr_err("EntryControls=%08x ExitControls=%08x\n", vmentry_ctl, vmexit_ctl); + pr_err("CPUBased=0x%08x SecondaryExec=0x%08x TertiaryExec=0x%016llx\n", + cpu_based_exec_ctrl, secondary_exec_control, tertiary_exec_control); + pr_err("PinBased=0x%08x EntryControls=%08x ExitControls=%08x\n", + pin_based_exec_ctrl, vmentry_ctl, vmexit_ctl); pr_err("ExceptionBitmap=%08x PFECmask=%08x PFECmatch=%08x\n", vmcs_read32(EXCEPTION_BITMAP), vmcs_read32(PAGE_FAULT_ERROR_CODE_MASK), @@ -6191,7 +6387,8 @@ static int __vmx_handle_exit(struct kvm_vcpu *vcpu, fastpath_t exit_fastpath) exit_reason.basic != EXIT_REASON_EPT_VIOLATION && exit_reason.basic != EXIT_REASON_PML_FULL && exit_reason.basic != EXIT_REASON_APIC_ACCESS && - exit_reason.basic != EXIT_REASON_TASK_SWITCH)) { + exit_reason.basic != EXIT_REASON_TASK_SWITCH && + exit_reason.basic != EXIT_REASON_NOTIFY)) { int ndata = 3; vcpu->run->exit_reason = KVM_EXIT_INTERNAL_ERROR; @@ -6453,7 +6650,7 @@ static void vmx_set_apic_access_page_addr(struct kvm_vcpu *vcpu) put_page(page); } -static void vmx_hwapic_isr_update(struct kvm_vcpu *vcpu, int max_isr) +static void vmx_hwapic_isr_update(int max_isr) { u16 status; u8 old; @@ -6783,9 +6980,14 @@ static void atomic_switch_perf_msrs(struct vcpu_vmx *vmx) { int i, nr_msrs; struct perf_guest_switch_msr *msrs; + struct kvm_pmu *pmu = vcpu_to_pmu(&vmx->vcpu); + + pmu->host_cross_mapped_mask = 0; + if (pmu->pebs_enable & pmu->global_ctrl) + intel_pmu_cross_mapped_check(pmu); /* Note, nr_msrs may be garbage if perf_guest_get_msrs() returns NULL. */ - msrs = perf_guest_get_msrs(&nr_msrs); + msrs = perf_guest_get_msrs(&nr_msrs, (void *)pmu); if (!msrs) return; @@ -7166,6 +7368,10 @@ static int vmx_vcpu_create(struct kvm_vcpu *vcpu) goto free_vmcs; } + if (vmx_can_use_ipiv(vcpu)) + WRITE_ONCE(to_kvm_vmx(vcpu->kvm)->pid_table[vcpu->vcpu_id], + __pa(&vmx->pi_desc) | PID_TABLE_ENTRY_VALID); + return 0; free_vmcs: @@ -7234,7 +7440,7 @@ static int __init vmx_check_processor_compat(void) return 0; } -static u64 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) +static u8 vmx_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) { u8 cache; @@ -7310,7 +7516,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) vmx->nested.msrs.cr4_fixed1 |= (_cr4_mask); \ } while (0) - entry = kvm_find_cpuid_entry(vcpu, 0x1, 0); + entry = kvm_find_cpuid_entry(vcpu, 0x1); cr4_fixed1_update(X86_CR4_VME, edx, feature_bit(VME)); cr4_fixed1_update(X86_CR4_PVI, edx, feature_bit(VME)); cr4_fixed1_update(X86_CR4_TSD, edx, feature_bit(TSC)); @@ -7326,7 +7532,7 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) cr4_fixed1_update(X86_CR4_PCIDE, ecx, feature_bit(PCID)); cr4_fixed1_update(X86_CR4_OSXSAVE, ecx, feature_bit(XSAVE)); - entry = kvm_find_cpuid_entry(vcpu, 0x7, 0); + entry = kvm_find_cpuid_entry_index(vcpu, 0x7, 0); cr4_fixed1_update(X86_CR4_FSGSBASE, ebx, feature_bit(FSGSBASE)); cr4_fixed1_update(X86_CR4_SMEP, ebx, feature_bit(SMEP)); cr4_fixed1_update(X86_CR4_SMAP, ebx, feature_bit(SMAP)); @@ -7337,23 +7543,6 @@ static void nested_vmx_cr_fixed1_bits_update(struct kvm_vcpu *vcpu) #undef cr4_fixed1_update } -static void nested_vmx_entry_exit_ctls_update(struct kvm_vcpu *vcpu) -{ - struct vcpu_vmx *vmx = to_vmx(vcpu); - - if (kvm_mpx_supported()) { - bool mpx_enabled = guest_cpuid_has(vcpu, X86_FEATURE_MPX); - - if (mpx_enabled) { - vmx->nested.msrs.entry_ctls_high |= VM_ENTRY_LOAD_BNDCFGS; - vmx->nested.msrs.exit_ctls_high |= VM_EXIT_CLEAR_BNDCFGS; - } else { - vmx->nested.msrs.entry_ctls_high &= ~VM_ENTRY_LOAD_BNDCFGS; - vmx->nested.msrs.exit_ctls_high &= ~VM_EXIT_CLEAR_BNDCFGS; - } - } -} - static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) { struct vcpu_vmx *vmx = to_vmx(vcpu); @@ -7361,7 +7550,7 @@ static void update_intel_pt_cfg(struct kvm_vcpu *vcpu) int i; for (i = 0; i < PT_CPUID_LEAVES; i++) { - best = kvm_find_cpuid_entry(vcpu, 0x14, i); + best = kvm_find_cpuid_entry_index(vcpu, 0x14, i); if (!best) return; vmx->pt_desc.caps[CPUID_EAX + i*PT_CPUID_REGS_NUM] = best->eax; @@ -7445,10 +7634,8 @@ static void vmx_vcpu_after_set_cpuid(struct kvm_vcpu *vcpu) ~(FEAT_CTL_VMX_ENABLED_INSIDE_SMX | FEAT_CTL_VMX_ENABLED_OUTSIDE_SMX); - if (nested_vmx_allowed(vcpu)) { + if (nested_vmx_allowed(vcpu)) nested_vmx_cr_fixed1_bits_update(vcpu); - nested_vmx_entry_exit_ctls_update(vcpu); - } if (boot_cpu_has(X86_FEATURE_INTEL_PT) && guest_cpuid_has(vcpu, X86_FEATURE_INTEL_PT)) @@ -7502,6 +7689,13 @@ static __init void vmx_set_cpu_caps(void) kvm_cpu_cap_clear(X86_FEATURE_INVPCID); if (vmx_pt_mode_is_host_guest()) kvm_cpu_cap_check_and_set(X86_FEATURE_INTEL_PT); + if (vmx_pebs_supported()) { + kvm_cpu_cap_check_and_set(X86_FEATURE_DS); + kvm_cpu_cap_check_and_set(X86_FEATURE_DTES64); + } + + if (!enable_pmu) + kvm_cpu_cap_clear(X86_FEATURE_PDCM); if (!enable_sgx) { kvm_cpu_cap_clear(X86_FEATURE_SGX); @@ -7514,7 +7708,7 @@ static __init void vmx_set_cpu_caps(void) kvm_cpu_cap_set(X86_FEATURE_UMIP); /* CPUID 0xD.1 */ - supported_xss = 0; + kvm_caps.supported_xss = 0; if (!cpu_has_vmx_xsaves()) kvm_cpu_cap_clear(X86_FEATURE_XSAVES); @@ -7655,9 +7849,9 @@ static int vmx_set_hv_timer(struct kvm_vcpu *vcpu, u64 guest_deadline_tsc, delta_tsc = 0; /* Convert to host delta tsc if tsc scaling is enabled */ - if (vcpu->arch.l1_tsc_scaling_ratio != kvm_default_tsc_scaling_ratio && + if (vcpu->arch.l1_tsc_scaling_ratio != kvm_caps.default_tsc_scaling_ratio && delta_tsc && u64_shl_div_u64(delta_tsc, - kvm_tsc_scaling_ratio_frac_bits, + kvm_caps.tsc_scaling_ratio_frac_bits, vcpu->arch.l1_tsc_scaling_ratio, &delta_tsc)) return -ERANGE; @@ -7729,6 +7923,13 @@ static int vmx_enter_smm(struct kvm_vcpu *vcpu, char *smstate) { struct vcpu_vmx *vmx = to_vmx(vcpu); + /* + * TODO: Implement custom flows for forcing the vCPU out/in of L2 on + * SMI and RSM. Using the common VM-Exit + VM-Enter routines is wrong + * SMI and RSM only modify state that is saved and restored via SMRAM. + * E.g. most MSRs are left untouched, but many are modified by VM-Exit + * and VM-Enter, and thus L2's values may be corrupted on SMI+RSM. + */ vmx->nested.smm.guest_mode = is_guest_mode(vcpu); if (vmx->nested.smm.guest_mode) nested_vmx_vmexit(vcpu, -1, 0, 0); @@ -7802,6 +8003,13 @@ static bool vmx_check_apicv_inhibit_reasons(enum kvm_apicv_inhibit reason) return supported & BIT(reason); } +static void vmx_vm_destroy(struct kvm *kvm) +{ + struct kvm_vmx *kvm_vmx = to_kvm_vmx(kvm); + + free_pages((unsigned long)kvm_vmx->pid_table, vmx_get_pid_table_order(kvm)); +} + static struct kvm_x86_ops vmx_x86_ops __initdata = { .name = "kvm_intel", @@ -7813,7 +8021,9 @@ static struct kvm_x86_ops vmx_x86_ops __initdata = { .vm_size = sizeof(struct kvm_vmx), .vm_init = vmx_vm_init, + .vm_destroy = vmx_vm_destroy, + .vcpu_precreate = vmx_vcpu_precreate, .vcpu_create = vmx_vcpu_create, .vcpu_free = vmx_vcpu_free, .vcpu_reset = vmx_vcpu_reset, @@ -8027,8 +8237,8 @@ static __init int hardware_setup(void) } if (!cpu_has_vmx_mpx()) - supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | - XFEATURE_MASK_BNDCSR); + kvm_caps.supported_xcr0 &= ~(XFEATURE_MASK_BNDREGS | + XFEATURE_MASK_BNDCSR); if (!cpu_has_vmx_vpid() || !cpu_has_vmx_invvpid() || !(cpu_has_vmx_invvpid_single() || cpu_has_vmx_invvpid_global())) @@ -8091,12 +8301,16 @@ static __init int hardware_setup(void) if (!enable_apicv) vmx_x86_ops.sync_pir_to_irr = NULL; - if (cpu_has_vmx_tsc_scaling()) - kvm_has_tsc_control = true; + if (!enable_apicv || !cpu_has_vmx_ipiv()) + enable_ipiv = false; - kvm_max_tsc_scaling_ratio = KVM_VMX_TSC_MULTIPLIER_MAX; - kvm_tsc_scaling_ratio_frac_bits = 48; - kvm_has_bus_lock_exit = cpu_has_vmx_bus_lock_detection(); + if (cpu_has_vmx_tsc_scaling()) + kvm_caps.has_tsc_control = true; + + kvm_caps.max_tsc_scaling_ratio = KVM_VMX_TSC_MULTIPLIER_MAX; + kvm_caps.tsc_scaling_ratio_frac_bits = 48; + kvm_caps.has_bus_lock_exit = cpu_has_vmx_bus_lock_detection(); + kvm_caps.has_notify_vmexit = cpu_has_notify_vmexit(); set_bit(0, vmx_vpid_bitmap); /* 0 is reserved for host */ @@ -8153,11 +8367,12 @@ static __init int hardware_setup(void) vmx_x86_ops.request_immediate_exit = __kvm_request_immediate_exit; } - kvm_mce_cap_supported |= MCG_LMCE_P; + kvm_caps.supported_mce_cap |= MCG_LMCE_P; + kvm_caps.supported_mce_cap |= MCG_CMCI_P; if (pt_mode != PT_MODE_SYSTEM && pt_mode != PT_MODE_HOST_GUEST) return -EINVAL; - if (!enable_ept || !cpu_has_vmx_intel_pt()) + if (!enable_ept || !enable_pmu || !cpu_has_vmx_intel_pt()) pt_mode = PT_MODE_SYSTEM; if (pt_mode == PT_MODE_HOST_GUEST) vmx_init_ops.handle_intel_pt_intr = vmx_handle_intel_pt_intr; diff --git a/arch/x86/kvm/vmx/vmx.h b/arch/x86/kvm/vmx/vmx.h index 1e7f9453894b..fb8e3480a9d7 100644 --- a/arch/x86/kvm/vmx/vmx.h +++ b/arch/x86/kvm/vmx/vmx.h @@ -92,10 +92,22 @@ union vmx_exit_reason { u32 full; }; +static inline bool intel_pmu_has_perf_global_ctrl(struct kvm_pmu *pmu) +{ + /* + * Architecturally, Intel's SDM states that IA32_PERF_GLOBAL_CTRL is + * supported if "CPUID.0AH: EAX[7:0] > 0", i.e. if the PMU version is + * greater than zero. However, KVM only exposes and emulates the MSR + * to/for the guest if the guest PMU supports at least "Architectural + * Performance Monitoring Version 2". + */ + 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) -bool intel_pmu_lbr_is_compatible(struct kvm_vcpu *vcpu); +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); @@ -205,7 +217,7 @@ struct nested_vmx { * Guest pages referred to in the vmcs02 with host-physical * pointers, so we must keep them pinned while L2 runs. */ - struct page *apic_access_page; + struct kvm_host_map apic_access_page_map; struct kvm_host_map virtual_apic_map; struct kvm_host_map pi_desc_map; @@ -220,9 +232,18 @@ struct nested_vmx { bool has_preemption_timer_deadline; bool preemption_timer_expired; - /* to migrate it to L2 if VM_ENTRY_LOAD_DEBUG_CONTROLS is off */ - u64 vmcs01_debugctl; - u64 vmcs01_guest_bndcfgs; + /* + * Used to snapshot MSRs that are conditionally loaded on VM-Enter in + * order to propagate the guest's pre-VM-Enter value into vmcs02. For + * emulation of VMLAUNCH/VMRESUME, the snapshot will be of L1's value. + * For KVM_SET_NESTED_STATE, the snapshot is of L2's value, _if_ + * userspace restores MSRs before nested state. If userspace restores + * MSRs after nested state, the snapshot holds garbage, but KVM can't + * detect that, and the garbage value in vmcs02 will be overwritten by + * MSR restoration in any case. + */ + u64 pre_vmenter_debugctl; + u64 pre_vmenter_bndcfgs; /* to migrate it to L1 if L2 writes to L1's CR8 directly */ int l1_tpr_threshold; @@ -369,6 +390,8 @@ struct kvm_vmx { unsigned int tss_addr; bool ept_identity_pagetable_done; gpa_t ept_identity_map_addr; + /* Posted Interrupt Descriptor (PID) table for IPI virtualization */ + u64 *pid_table; }; bool nested_vmx_allowed(struct kvm_vcpu *vcpu); @@ -462,35 +485,36 @@ static inline u8 vmx_get_rvi(void) return vmcs_read16(GUEST_INTR_STATUS) & 0xff; } -#define BUILD_CONTROLS_SHADOW(lname, uname) \ -static inline void lname##_controls_set(struct vcpu_vmx *vmx, u32 val) \ -{ \ - if (vmx->loaded_vmcs->controls_shadow.lname != val) { \ - vmcs_write32(uname, val); \ - vmx->loaded_vmcs->controls_shadow.lname = val; \ - } \ -} \ -static inline u32 __##lname##_controls_get(struct loaded_vmcs *vmcs) \ -{ \ - return vmcs->controls_shadow.lname; \ -} \ -static inline u32 lname##_controls_get(struct vcpu_vmx *vmx) \ -{ \ - return __##lname##_controls_get(vmx->loaded_vmcs); \ -} \ -static inline void lname##_controls_setbit(struct vcpu_vmx *vmx, u32 val) \ -{ \ - lname##_controls_set(vmx, lname##_controls_get(vmx) | val); \ -} \ -static inline void lname##_controls_clearbit(struct vcpu_vmx *vmx, u32 val) \ -{ \ - lname##_controls_set(vmx, lname##_controls_get(vmx) & ~val); \ +#define BUILD_CONTROLS_SHADOW(lname, uname, bits) \ +static inline void lname##_controls_set(struct vcpu_vmx *vmx, u##bits val) \ +{ \ + if (vmx->loaded_vmcs->controls_shadow.lname != val) { \ + vmcs_write##bits(uname, val); \ + vmx->loaded_vmcs->controls_shadow.lname = val; \ + } \ +} \ +static inline u##bits __##lname##_controls_get(struct loaded_vmcs *vmcs) \ +{ \ + return vmcs->controls_shadow.lname; \ +} \ +static inline u##bits lname##_controls_get(struct vcpu_vmx *vmx) \ +{ \ + return __##lname##_controls_get(vmx->loaded_vmcs); \ +} \ +static inline void lname##_controls_setbit(struct vcpu_vmx *vmx, u##bits val) \ +{ \ + lname##_controls_set(vmx, lname##_controls_get(vmx) | val); \ +} \ +static inline void lname##_controls_clearbit(struct vcpu_vmx *vmx, u##bits val) \ +{ \ + lname##_controls_set(vmx, lname##_controls_get(vmx) & ~val); \ } -BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS) -BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS) -BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL) -BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL) -BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL) +BUILD_CONTROLS_SHADOW(vm_entry, VM_ENTRY_CONTROLS, 32) +BUILD_CONTROLS_SHADOW(vm_exit, VM_EXIT_CONTROLS, 32) +BUILD_CONTROLS_SHADOW(pin, PIN_BASED_VM_EXEC_CONTROL, 32) +BUILD_CONTROLS_SHADOW(exec, CPU_BASED_VM_EXEC_CONTROL, 32) +BUILD_CONTROLS_SHADOW(secondary_exec, SECONDARY_VM_EXEC_CONTROL, 32) +BUILD_CONTROLS_SHADOW(tertiary_exec, TERTIARY_VM_EXEC_CONTROL, 64) /* * VMX_REGS_LAZY_LOAD_SET - The set of registers that will be updated in the @@ -586,4 +610,9 @@ static inline int vmx_get_instr_info_reg2(u32 vmx_instr_info) return (vmx_instr_info >> 28) & 0xf; } +static inline bool vmx_can_use_ipiv(struct kvm_vcpu *vcpu) +{ + return lapic_in_kernel(vcpu) && enable_ipiv; +} + #endif /* __KVM_X86_VMX_H */ diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index e5fa335a4ea7..33560bfa0cac 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -87,8 +87,11 @@ #define MAX_IO_MSRS 256 #define KVM_MAX_MCE_BANKS 32 -u64 __read_mostly kvm_mce_cap_supported = MCG_CTL_P | MCG_SER_P; -EXPORT_SYMBOL_GPL(kvm_mce_cap_supported); + +struct kvm_caps kvm_caps __read_mostly = { + .supported_mce_cap = MCG_CTL_P | MCG_SER_P, +}; +EXPORT_SYMBOL_GPL(kvm_caps); #define ERR_PTR_USR(e) ((void __user *)ERR_PTR(e)) @@ -151,19 +154,6 @@ module_param(min_timer_period_us, uint, S_IRUGO | S_IWUSR); static bool __read_mostly kvmclock_periodic_sync = true; module_param(kvmclock_periodic_sync, bool, S_IRUGO); -bool __read_mostly kvm_has_tsc_control; -EXPORT_SYMBOL_GPL(kvm_has_tsc_control); -u32 __read_mostly kvm_max_guest_tsc_khz; -EXPORT_SYMBOL_GPL(kvm_max_guest_tsc_khz); -u8 __read_mostly kvm_tsc_scaling_ratio_frac_bits; -EXPORT_SYMBOL_GPL(kvm_tsc_scaling_ratio_frac_bits); -u64 __read_mostly kvm_max_tsc_scaling_ratio; -EXPORT_SYMBOL_GPL(kvm_max_tsc_scaling_ratio); -u64 __read_mostly kvm_default_tsc_scaling_ratio; -EXPORT_SYMBOL_GPL(kvm_default_tsc_scaling_ratio); -bool __read_mostly kvm_has_bus_lock_exit; -EXPORT_SYMBOL_GPL(kvm_has_bus_lock_exit); - /* tsc tolerance in parts per million - default to 1/2 of the NTP threshold */ static u32 __read_mostly tsc_tolerance_ppm = 250; module_param(tsc_tolerance_ppm, uint, S_IRUGO | S_IWUSR); @@ -235,8 +225,6 @@ EXPORT_SYMBOL_GPL(enable_apicv); u64 __read_mostly host_xss; EXPORT_SYMBOL_GPL(host_xss); -u64 __read_mostly supported_xss; -EXPORT_SYMBOL_GPL(supported_xss); const struct _kvm_stats_desc kvm_vm_stats_desc[] = { KVM_GENERIC_VM_STATS(), @@ -298,7 +286,8 @@ const struct _kvm_stats_desc kvm_vcpu_stats_desc[] = { STATS_DESC_COUNTER(VCPU, directed_yield_successful), STATS_DESC_COUNTER(VCPU, preemption_reported), STATS_DESC_COUNTER(VCPU, preemption_other), - STATS_DESC_IBOOLEAN(VCPU, guest_mode) + STATS_DESC_IBOOLEAN(VCPU, guest_mode), + STATS_DESC_COUNTER(VCPU, notify_window_exits), }; const struct kvm_stats_header kvm_vcpu_stats_header = { @@ -311,8 +300,6 @@ const struct kvm_stats_header kvm_vcpu_stats_header = { }; u64 __read_mostly host_xcr0; -u64 __read_mostly supported_xcr0; -EXPORT_SYMBOL_GPL(supported_xcr0); static struct kmem_cache *x86_emulator_cache; @@ -862,7 +849,7 @@ int load_pdptrs(struct kvm_vcpu *vcpu, unsigned long cr3) */ real_gpa = kvm_translate_gpa(vcpu, mmu, gfn_to_gpa(pdpt_gfn), PFERR_USER_MASK | PFERR_WRITE_MASK, NULL); - if (real_gpa == UNMAPPED_GVA) + if (real_gpa == INVALID_GPA) return 0; /* Note the offset, PDPTRs are 32 byte aligned when using PAE paging. */ @@ -1094,7 +1081,7 @@ int kvm_emulate_xsetbv(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_emulate_xsetbv); -bool kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) +bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) { if (cr4 & cr4_reserved_bits) return false; @@ -1102,9 +1089,15 @@ bool kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) if (cr4 & vcpu->arch.cr4_guest_rsvd_bits) return false; - return static_call(kvm_x86_is_valid_cr4)(vcpu, cr4); + return true; +} +EXPORT_SYMBOL_GPL(__kvm_is_valid_cr4); + +static bool kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4) +{ + return __kvm_is_valid_cr4(vcpu, cr4) && + static_call(kvm_x86_is_valid_cr4)(vcpu, cr4); } -EXPORT_SYMBOL_GPL(kvm_is_valid_cr4); void kvm_post_set_cr4(struct kvm_vcpu *vcpu, unsigned long old_cr4, unsigned long cr4) { @@ -1450,6 +1443,7 @@ static const u32 msrs_to_save_all[] = { MSR_ARCH_PERFMON_EVENTSEL0 + 12, MSR_ARCH_PERFMON_EVENTSEL0 + 13, MSR_ARCH_PERFMON_EVENTSEL0 + 14, MSR_ARCH_PERFMON_EVENTSEL0 + 15, MSR_ARCH_PERFMON_EVENTSEL0 + 16, MSR_ARCH_PERFMON_EVENTSEL0 + 17, + MSR_IA32_PEBS_ENABLE, MSR_IA32_DS_AREA, MSR_PEBS_DATA_CFG, MSR_K7_EVNTSEL0, MSR_K7_EVNTSEL1, MSR_K7_EVNTSEL2, MSR_K7_EVNTSEL3, MSR_K7_PERFCTR0, MSR_K7_PERFCTR1, MSR_K7_PERFCTR2, MSR_K7_PERFCTR3, @@ -2051,13 +2045,6 @@ int kvm_emulate_invd(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_emulate_invd); -int kvm_emulate_mwait(struct kvm_vcpu *vcpu) -{ - pr_warn_once("kvm: MWAIT instruction emulated as NOP!\n"); - return kvm_emulate_as_nop(vcpu); -} -EXPORT_SYMBOL_GPL(kvm_emulate_mwait); - int kvm_handle_invalid_op(struct kvm_vcpu *vcpu) { kvm_queue_exception(vcpu, UD_VECTOR); @@ -2065,10 +2052,25 @@ int kvm_handle_invalid_op(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_handle_invalid_op); + +static int kvm_emulate_monitor_mwait(struct kvm_vcpu *vcpu, const char *insn) +{ + if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS) && + !guest_cpuid_has(vcpu, X86_FEATURE_MWAIT)) + return kvm_handle_invalid_op(vcpu); + + pr_warn_once("kvm: %s instruction emulated as NOP!\n", insn); + return kvm_emulate_as_nop(vcpu); +} +int kvm_emulate_mwait(struct kvm_vcpu *vcpu) +{ + return kvm_emulate_monitor_mwait(vcpu, "MWAIT"); +} +EXPORT_SYMBOL_GPL(kvm_emulate_mwait); + int kvm_emulate_monitor(struct kvm_vcpu *vcpu) { - pr_warn_once("kvm: MONITOR instruction emulated as NOP!\n"); - return kvm_emulate_as_nop(vcpu); + return kvm_emulate_monitor_mwait(vcpu, "MONITOR"); } EXPORT_SYMBOL_GPL(kvm_emulate_monitor); @@ -2349,12 +2351,12 @@ static int set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz, bool scale) /* Guest TSC same frequency as host TSC? */ if (!scale) { - kvm_vcpu_write_tsc_multiplier(vcpu, kvm_default_tsc_scaling_ratio); + kvm_vcpu_write_tsc_multiplier(vcpu, kvm_caps.default_tsc_scaling_ratio); return 0; } /* TSC scaling supported? */ - if (!kvm_has_tsc_control) { + if (!kvm_caps.has_tsc_control) { if (user_tsc_khz > tsc_khz) { vcpu->arch.tsc_catchup = 1; vcpu->arch.tsc_always_catchup = 1; @@ -2366,10 +2368,10 @@ static int set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz, bool scale) } /* TSC scaling required - calculate ratio */ - ratio = mul_u64_u32_div(1ULL << kvm_tsc_scaling_ratio_frac_bits, + ratio = mul_u64_u32_div(1ULL << kvm_caps.tsc_scaling_ratio_frac_bits, user_tsc_khz, tsc_khz); - if (ratio == 0 || ratio >= kvm_max_tsc_scaling_ratio) { + if (ratio == 0 || ratio >= kvm_caps.max_tsc_scaling_ratio) { pr_warn_ratelimited("Invalid TSC scaling ratio - virtual-tsc-khz=%u\n", user_tsc_khz); return -1; @@ -2387,7 +2389,7 @@ static int kvm_set_tsc_khz(struct kvm_vcpu *vcpu, u32 user_tsc_khz) /* tsc_khz can be zero if TSC calibration fails */ if (user_tsc_khz == 0) { /* set tsc_scaling_ratio to a safe value */ - kvm_vcpu_write_tsc_multiplier(vcpu, kvm_default_tsc_scaling_ratio); + kvm_vcpu_write_tsc_multiplier(vcpu, kvm_caps.default_tsc_scaling_ratio); return -1; } @@ -2464,18 +2466,18 @@ static void kvm_track_tsc_matching(struct kvm_vcpu *vcpu) * (frac) represent the fractional part, ie. ratio represents a fixed * point number (mult + frac * 2^(-N)). * - * N equals to kvm_tsc_scaling_ratio_frac_bits. + * N equals to kvm_caps.tsc_scaling_ratio_frac_bits. */ static inline u64 __scale_tsc(u64 ratio, u64 tsc) { - return mul_u64_u64_shr(tsc, ratio, kvm_tsc_scaling_ratio_frac_bits); + return mul_u64_u64_shr(tsc, ratio, kvm_caps.tsc_scaling_ratio_frac_bits); } u64 kvm_scale_tsc(u64 tsc, u64 ratio) { u64 _tsc = tsc; - if (ratio != kvm_default_tsc_scaling_ratio) + if (ratio != kvm_caps.default_tsc_scaling_ratio) _tsc = __scale_tsc(ratio, tsc); return _tsc; @@ -2502,11 +2504,11 @@ u64 kvm_calc_nested_tsc_offset(u64 l1_offset, u64 l2_offset, u64 l2_multiplier) { u64 nested_offset; - if (l2_multiplier == kvm_default_tsc_scaling_ratio) + if (l2_multiplier == kvm_caps.default_tsc_scaling_ratio) nested_offset = l1_offset; else nested_offset = mul_s64_u64_shr((s64) l1_offset, l2_multiplier, - kvm_tsc_scaling_ratio_frac_bits); + kvm_caps.tsc_scaling_ratio_frac_bits); nested_offset += l2_offset; return nested_offset; @@ -2515,9 +2517,9 @@ EXPORT_SYMBOL_GPL(kvm_calc_nested_tsc_offset); u64 kvm_calc_nested_tsc_multiplier(u64 l1_multiplier, u64 l2_multiplier) { - if (l2_multiplier != kvm_default_tsc_scaling_ratio) + if (l2_multiplier != kvm_caps.default_tsc_scaling_ratio) return mul_u64_u64_shr(l1_multiplier, l2_multiplier, - kvm_tsc_scaling_ratio_frac_bits); + kvm_caps.tsc_scaling_ratio_frac_bits); return l1_multiplier; } @@ -2559,7 +2561,7 @@ static void kvm_vcpu_write_tsc_multiplier(struct kvm_vcpu *vcpu, u64 l1_multipli else vcpu->arch.tsc_scaling_ratio = l1_multiplier; - if (kvm_has_tsc_control) + if (kvm_caps.has_tsc_control) static_call(kvm_x86_write_tsc_multiplier)( vcpu, vcpu->arch.tsc_scaling_ratio); } @@ -2695,7 +2697,7 @@ static inline void adjust_tsc_offset_guest(struct kvm_vcpu *vcpu, static inline void adjust_tsc_offset_host(struct kvm_vcpu *vcpu, s64 adjustment) { - if (vcpu->arch.l1_tsc_scaling_ratio != kvm_default_tsc_scaling_ratio) + if (vcpu->arch.l1_tsc_scaling_ratio != kvm_caps.default_tsc_scaling_ratio) WARN_ON(adjustment < 0); adjustment = kvm_scale_tsc((u64) adjustment, vcpu->arch.l1_tsc_scaling_ratio); @@ -3108,7 +3110,7 @@ static int kvm_guest_time_update(struct kvm_vcpu *v) /* With all the info we got, fill in the values */ - if (kvm_has_tsc_control) + if (kvm_caps.has_tsc_control) tgt_tsc_khz = kvm_scale_tsc(tgt_tsc_khz, v->arch.l1_tsc_scaling_ratio); @@ -3198,6 +3200,16 @@ static void kvmclock_sync_fn(struct work_struct *work) KVMCLOCK_SYNC_PERIOD); } +/* These helpers are safe iff @msr is known to be an MCx bank MSR. */ +static bool is_mci_control_msr(u32 msr) +{ + return (msr & 3) == 0; +} +static bool is_mci_status_msr(u32 msr) +{ + return (msr & 3) == 1; +} + /* * On AMD, HWCR[McStatusWrEn] controls whether setting MCi_STATUS results in #GP. */ @@ -3216,6 +3228,7 @@ static int set_msr_mce(struct kvm_vcpu *vcpu, struct msr_data *msr_info) unsigned bank_num = mcg_cap & 0xff; u32 msr = msr_info->index; u64 data = msr_info->data; + u32 offset, last_msr; switch (msr) { case MSR_IA32_MCG_STATUS: @@ -3229,32 +3242,53 @@ static int set_msr_mce(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; vcpu->arch.mcg_ctl = data; break; + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: + last_msr = MSR_IA32_MCx_CTL2(bank_num) - 1; + if (msr > last_msr) + return 1; + + if (!(mcg_cap & MCG_CMCI_P) && (data || !msr_info->host_initiated)) + return 1; + /* An attempt to write a 1 to a reserved bit raises #GP */ + if (data & ~(MCI_CTL2_CMCI_EN | MCI_CTL2_CMCI_THRESHOLD_MASK)) + return 1; + offset = array_index_nospec(msr - MSR_IA32_MC0_CTL2, + last_msr + 1 - MSR_IA32_MC0_CTL2); + vcpu->arch.mci_ctl2_banks[offset] = data; + break; + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + last_msr = MSR_IA32_MCx_CTL(bank_num) - 1; + if (msr > last_msr) + return 1; + + /* + * Only 0 or all 1s can be written to IA32_MCi_CTL, all other + * values are architecturally undefined. But, some Linux + * kernels clear bit 10 in bank 4 to workaround a BIOS/GART TLB + * issue on AMD K8s, allow bit 10 to be clear when setting all + * other bits in order to avoid an uncaught #GP in the guest. + * + * UNIXWARE clears bit 0 of MC1_CTL to ignore correctable, + * single-bit ECC data errors. + */ + if (is_mci_control_msr(msr) && + data != 0 && (data | (1 << 10) | 1) != ~(u64)0) + return 1; + + /* + * All CPUs allow writing 0 to MCi_STATUS MSRs to clear the MSR. + * AMD-based CPUs allow non-zero values, but if and only if + * HWCR[McStatusWrEn] is set. + */ + if (!msr_info->host_initiated && is_mci_status_msr(msr) && + data != 0 && !can_set_mci_status(vcpu)) + return 1; + + offset = array_index_nospec(msr - MSR_IA32_MC0_CTL, + last_msr + 1 - MSR_IA32_MC0_CTL); + vcpu->arch.mce_banks[offset] = data; + break; default: - if (msr >= MSR_IA32_MC0_CTL && - msr < MSR_IA32_MCx_CTL(bank_num)) { - u32 offset = array_index_nospec( - msr - MSR_IA32_MC0_CTL, - MSR_IA32_MCx_CTL(bank_num) - MSR_IA32_MC0_CTL); - - /* only 0 or all 1s can be written to IA32_MCi_CTL - * some Linux kernels though clear bit 10 in bank 4 to - * workaround a BIOS/GART TBL issue on AMD K8s, ignore - * this to avoid an uncatched #GP in the guest - */ - if ((offset & 0x3) == 0 && - data != 0 && (data | (1 << 10)) != ~(u64)0) - return -1; - - /* MCi_STATUS */ - if (!msr_info->host_initiated && - (offset & 0x3) == 1 && data != 0) { - if (!can_set_mci_status(vcpu)) - return -1; - } - - vcpu->arch.mce_banks[offset] = data; - break; - } return 1; } return 0; @@ -3538,7 +3572,8 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) return 1; } break; - case 0x200 ... 0x2ff: + case 0x200 ... MSR_IA32_MC0_CTL2 - 1: + case MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) ... 0x2ff: return kvm_mtrr_set_msr(vcpu, msr, data); case MSR_IA32_APICBASE: return kvm_set_apic_base(vcpu, msr_info); @@ -3560,9 +3595,21 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vcpu->arch.ia32_tsc_adjust_msr = data; } break; - case MSR_IA32_MISC_ENABLE: + case MSR_IA32_MISC_ENABLE: { + u64 old_val = vcpu->arch.ia32_misc_enable_msr; + + if (!msr_info->host_initiated) { + /* RO bits */ + if ((old_val ^ data) & MSR_IA32_MISC_ENABLE_PMU_RO_MASK) + return 1; + + /* R bits, i.e. writes are ignored, but don't fault. */ + data = data & ~MSR_IA32_MISC_ENABLE_EMON; + data |= old_val & MSR_IA32_MISC_ENABLE_EMON; + } + if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT) && - ((vcpu->arch.ia32_misc_enable_msr ^ data) & MSR_IA32_MISC_ENABLE_MWAIT)) { + ((old_val ^ data) & MSR_IA32_MISC_ENABLE_MWAIT)) { if (!guest_cpuid_has(vcpu, X86_FEATURE_XMM3)) return 1; vcpu->arch.ia32_misc_enable_msr = data; @@ -3571,6 +3618,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vcpu->arch.ia32_misc_enable_msr = data; } break; + } case MSR_IA32_SMBASE: if (!msr_info->host_initiated) return 1; @@ -3597,7 +3645,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) * IA32_XSS[bit 8]. Guests have to use RDMSR/WRMSR rather than * XSAVES/XRSTORS to save/restore PT MSRs. */ - if (data & ~supported_xss) + if (data & ~kvm_caps.supported_xss) return 1; vcpu->arch.ia32_xss = data; kvm_update_cpuid_runtime(vcpu); @@ -3695,6 +3743,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_IA32_MCG_CTL: case MSR_IA32_MCG_STATUS: case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: return set_msr_mce(vcpu, msr_info); case MSR_K7_PERFCTR0 ... MSR_K7_PERFCTR3: @@ -3785,6 +3834,17 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) vcpu->arch.guest_fpu.xfd_err = data; break; #endif + case MSR_IA32_PEBS_ENABLE: + case MSR_IA32_DS_AREA: + case MSR_PEBS_DATA_CFG: + case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5: + if (kvm_pmu_is_valid_msr(vcpu, msr)) + return kvm_pmu_set_msr(vcpu, msr_info); + /* + * Userspace is allowed to write '0' to MSRs that KVM reports + * as to-be-saved, even if an MSRs isn't fully supported. + */ + return !msr_info->host_initiated || data; default: if (kvm_pmu_is_valid_msr(vcpu, msr)) return kvm_pmu_set_msr(vcpu, msr_info); @@ -3799,6 +3859,7 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host) u64 data; u64 mcg_cap = vcpu->arch.mcg_cap; unsigned bank_num = mcg_cap & 0xff; + u32 offset, last_msr; switch (msr) { case MSR_IA32_P5_MC_ADDR: @@ -3816,16 +3877,27 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata, bool host) case MSR_IA32_MCG_STATUS: data = vcpu->arch.mcg_status; break; - default: - if (msr >= MSR_IA32_MC0_CTL && - msr < MSR_IA32_MCx_CTL(bank_num)) { - u32 offset = array_index_nospec( - msr - MSR_IA32_MC0_CTL, - MSR_IA32_MCx_CTL(bank_num) - MSR_IA32_MC0_CTL); + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: + last_msr = MSR_IA32_MCx_CTL2(bank_num) - 1; + if (msr > last_msr) + return 1; - data = vcpu->arch.mce_banks[offset]; - break; - } + if (!(mcg_cap & MCG_CMCI_P) && !host) + return 1; + offset = array_index_nospec(msr - MSR_IA32_MC0_CTL2, + last_msr + 1 - MSR_IA32_MC0_CTL2); + data = vcpu->arch.mci_ctl2_banks[offset]; + break; + case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + last_msr = MSR_IA32_MCx_CTL(bank_num) - 1; + if (msr > last_msr) + return 1; + + offset = array_index_nospec(msr - MSR_IA32_MC0_CTL, + last_msr + 1 - MSR_IA32_MC0_CTL); + data = vcpu->arch.mce_banks[offset]; + break; + default: return 1; } *pdata = data; @@ -3865,9 +3937,16 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_DRAM_ENERGY_STATUS: /* DRAM controller */ msr_info->data = 0; break; + case MSR_IA32_PEBS_ENABLE: + case MSR_IA32_DS_AREA: + case MSR_PEBS_DATA_CFG: case MSR_F15H_PERF_CTL0 ... MSR_F15H_PERF_CTR5: if (kvm_pmu_is_valid_msr(vcpu, msr_info->index)) return kvm_pmu_get_msr(vcpu, msr_info); + /* + * Userspace is allowed to read MSRs that KVM reports as + * to-be-saved, even if an MSR isn't fully supported. + */ if (!msr_info->host_initiated) return 1; msr_info->data = 0; @@ -3922,7 +4001,8 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) break; } case MSR_MTRRcap: - case 0x200 ... 0x2ff: + case 0x200 ... MSR_IA32_MC0_CTL2 - 1: + case MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) ... 0x2ff: return kvm_mtrr_get_msr(vcpu, msr_info->index, &msr_info->data); case 0xcd: /* fsb frequency */ msr_info->data = 3; @@ -4038,6 +4118,7 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) case MSR_IA32_MCG_CTL: case MSR_IA32_MCG_STATUS: case MSR_IA32_MC0_CTL ... MSR_IA32_MCx_CTL(KVM_MAX_MCE_BANKS) - 1: + case MSR_IA32_MC0_CTL2 ... MSR_IA32_MCx_CTL2(KVM_MAX_MCE_BANKS) - 1: return get_msr_mce(vcpu, msr_info->index, &msr_info->data, msr_info->host_initiated); case MSR_IA32_XSS: @@ -4280,6 +4361,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_GET_MSR_FEATURES: case KVM_CAP_MSR_PLATFORM_INFO: case KVM_CAP_EXCEPTION_PAYLOAD: + case KVM_CAP_X86_TRIPLE_FAULT_EVENT: case KVM_CAP_SET_GUEST_DEBUG: case KVM_CAP_LAST_CPU: case KVM_CAP_X86_USER_SPACE_MSR: @@ -4296,6 +4378,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_SYS_ATTRIBUTES: case KVM_CAP_VAPIC: case KVM_CAP_ENABLE_CAP: + case KVM_CAP_VM_DISABLE_NX_HUGE_PAGES: r = 1; break; case KVM_CAP_EXIT_HYPERCALL: @@ -4357,7 +4440,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) break; case KVM_CAP_TSC_CONTROL: case KVM_CAP_VM_TSC_CONTROL: - r = kvm_has_tsc_control; + r = kvm_caps.has_tsc_control; break; case KVM_CAP_X2APIC_API: r = KVM_X2APIC_API_VALID_FLAGS; @@ -4379,7 +4462,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) r = sched_info_on(); break; case KVM_CAP_X86_BUS_LOCK_EXIT: - if (kvm_has_bus_lock_exit) + if (kvm_caps.has_bus_lock_exit) r = KVM_BUS_LOCK_DETECTION_OFF | KVM_BUS_LOCK_DETECTION_EXIT; else @@ -4388,17 +4471,20 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_XSAVE2: { u64 guest_perm = xstate_get_guest_group_perm(); - r = xstate_required_size(supported_xcr0 & guest_perm, false); + r = xstate_required_size(kvm_caps.supported_xcr0 & guest_perm, false); if (r < sizeof(struct kvm_xsave)) r = sizeof(struct kvm_xsave); break; + } case KVM_CAP_PMU_CAPABILITY: r = enable_pmu ? KVM_CAP_PMU_VALID_MASK : 0; break; - } case KVM_CAP_DISABLE_QUIRKS2: r = KVM_X86_VALID_QUIRKS; break; + case KVM_CAP_X86_NOTIFY_VMEXIT: + r = kvm_caps.has_notify_vmexit; + break; default: break; } @@ -4426,7 +4512,7 @@ static int kvm_x86_dev_get_attr(struct kvm_device_attr *attr) switch (attr->attr) { case KVM_X86_XCOMP_GUEST_SUPP: - if (put_user(supported_xcr0, uaddr)) + if (put_user(kvm_caps.supported_xcr0, uaddr)) return -EFAULT; return 0; default: @@ -4503,8 +4589,8 @@ long kvm_arch_dev_ioctl(struct file *filp, } case KVM_X86_GET_MCE_CAP_SUPPORTED: r = -EFAULT; - if (copy_to_user(argp, &kvm_mce_cap_supported, - sizeof(kvm_mce_cap_supported))) + if (copy_to_user(argp, &kvm_caps.supported_mce_cap, + sizeof(kvm_caps.supported_mce_cap))) goto out; r = 0; break; @@ -4803,22 +4889,63 @@ static int kvm_vcpu_ioctl_x86_setup_mce(struct kvm_vcpu *vcpu, r = -EINVAL; if (!bank_num || bank_num > KVM_MAX_MCE_BANKS) goto out; - if (mcg_cap & ~(kvm_mce_cap_supported | 0xff | 0xff0000)) + if (mcg_cap & ~(kvm_caps.supported_mce_cap | 0xff | 0xff0000)) goto out; r = 0; vcpu->arch.mcg_cap = mcg_cap; /* Init IA32_MCG_CTL to all 1s */ if (mcg_cap & MCG_CTL_P) vcpu->arch.mcg_ctl = ~(u64)0; - /* Init IA32_MCi_CTL to all 1s */ - for (bank = 0; bank < bank_num; bank++) + /* Init IA32_MCi_CTL to all 1s, IA32_MCi_CTL2 to all 0s */ + for (bank = 0; bank < bank_num; bank++) { vcpu->arch.mce_banks[bank*4] = ~(u64)0; + if (mcg_cap & MCG_CMCI_P) + vcpu->arch.mci_ctl2_banks[bank] = 0; + } + + kvm_apic_after_set_mcg_cap(vcpu); static_call(kvm_x86_setup_mce)(vcpu); out: return r; } +/* + * Validate this is an UCNA (uncorrectable no action) error by checking the + * MCG_STATUS and MCi_STATUS registers: + * - none of the bits for Machine Check Exceptions are set + * - both the VAL (valid) and UC (uncorrectable) bits are set + * MCI_STATUS_PCC - Processor Context Corrupted + * MCI_STATUS_S - Signaled as a Machine Check Exception + * MCI_STATUS_AR - Software recoverable Action Required + */ +static bool is_ucna(struct kvm_x86_mce *mce) +{ + return !mce->mcg_status && + !(mce->status & (MCI_STATUS_PCC | MCI_STATUS_S | MCI_STATUS_AR)) && + (mce->status & MCI_STATUS_VAL) && + (mce->status & MCI_STATUS_UC); +} + +static int kvm_vcpu_x86_set_ucna(struct kvm_vcpu *vcpu, struct kvm_x86_mce *mce, u64* banks) +{ + u64 mcg_cap = vcpu->arch.mcg_cap; + + banks[1] = mce->status; + banks[2] = mce->addr; + banks[3] = mce->misc; + vcpu->arch.mcg_status = mce->mcg_status; + + if (!(mcg_cap & MCG_CMCI_P) || + !(vcpu->arch.mci_ctl2_banks[mce->bank] & MCI_CTL2_CMCI_EN)) + return 0; + + if (lapic_in_kernel(vcpu)) + kvm_apic_local_deliver(vcpu->arch.apic, APIC_LVTCMCI); + + return 0; +} + static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu, struct kvm_x86_mce *mce) { @@ -4828,6 +4955,12 @@ static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu, if (mce->bank >= bank_num || !(mce->status & MCI_STATUS_VAL)) return -EINVAL; + + banks += array_index_nospec(4 * mce->bank, 4 * bank_num); + + if (is_ucna(mce)) + return kvm_vcpu_x86_set_ucna(vcpu, mce, banks); + /* * if IA32_MCG_CTL is not all 1s, the uncorrected error * reporting is disabled @@ -4835,7 +4968,6 @@ static int kvm_vcpu_ioctl_x86_set_mce(struct kvm_vcpu *vcpu, if ((mce->status & MCI_STATUS_UC) && (mcg_cap & MCG_CTL_P) && vcpu->arch.mcg_ctl != ~(u64)0) return 0; - banks += 4 * mce->bank; /* * if IA32_MCi_CTL is not all 1s, the uncorrected error * reporting is disabled for the bank @@ -4941,6 +5073,10 @@ static void kvm_vcpu_ioctl_x86_get_vcpu_events(struct kvm_vcpu *vcpu, | KVM_VCPUEVENT_VALID_SMM); if (vcpu->kvm->arch.exception_payload_enabled) events->flags |= KVM_VCPUEVENT_VALID_PAYLOAD; + if (vcpu->kvm->arch.triple_fault_event) { + events->triple_fault.pending = kvm_test_request(KVM_REQ_TRIPLE_FAULT, vcpu); + events->flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; + } memset(&events->reserved, 0, sizeof(events->reserved)); } @@ -4954,7 +5090,8 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, | KVM_VCPUEVENT_VALID_SIPI_VECTOR | KVM_VCPUEVENT_VALID_SHADOW | KVM_VCPUEVENT_VALID_SMM - | KVM_VCPUEVENT_VALID_PAYLOAD)) + | KVM_VCPUEVENT_VALID_PAYLOAD + | KVM_VCPUEVENT_VALID_TRIPLE_FAULT)) return -EINVAL; if (events->flags & KVM_VCPUEVENT_VALID_PAYLOAD) { @@ -5027,6 +5164,15 @@ static int kvm_vcpu_ioctl_x86_set_vcpu_events(struct kvm_vcpu *vcpu, } } + if (events->flags & KVM_VCPUEVENT_VALID_TRIPLE_FAULT) { + if (!vcpu->kvm->arch.triple_fault_event) + return -EINVAL; + if (events->triple_fault.pending) + kvm_make_request(KVM_REQ_TRIPLE_FAULT, vcpu); + else + kvm_clear_request(KVM_REQ_TRIPLE_FAULT, vcpu); + } + kvm_make_request(KVM_REQ_EVENT, vcpu); return 0; @@ -5095,7 +5241,8 @@ static int kvm_vcpu_ioctl_x86_set_xsave(struct kvm_vcpu *vcpu, return fpu_copy_uabi_to_guest_fpstate(&vcpu->arch.guest_fpu, guest_xsave->region, - supported_xcr0, &vcpu->arch.pkru); + kvm_caps.supported_xcr0, + &vcpu->arch.pkru); } static void kvm_vcpu_ioctl_x86_get_xcrs(struct kvm_vcpu *vcpu, @@ -5600,8 +5747,8 @@ long kvm_arch_vcpu_ioctl(struct file *filp, r = -EINVAL; user_tsc_khz = (u32)arg; - if (kvm_has_tsc_control && - user_tsc_khz >= kvm_max_guest_tsc_khz) + if (kvm_caps.has_tsc_control && + user_tsc_khz >= kvm_caps.max_guest_tsc_khz) goto out; if (user_tsc_khz == 0) @@ -6028,6 +6175,10 @@ split_irqchip_unlock: kvm->arch.exception_payload_enabled = cap->args[0]; r = 0; break; + case KVM_CAP_X86_TRIPLE_FAULT_EVENT: + kvm->arch.triple_fault_event = cap->args[0]; + r = 0; + break; case KVM_CAP_X86_USER_SPACE_MSR: r = -EINVAL; if (cap->args[0] & ~(KVM_MSR_EXIT_REASON_INVAL | @@ -6046,7 +6197,7 @@ split_irqchip_unlock: (cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT)) break; - if (kvm_has_bus_lock_exit && + if (kvm_caps.has_bus_lock_exit && cap->args[0] & KVM_BUS_LOCK_DETECTION_EXIT) kvm->arch.bus_lock_detection_enabled = true; r = 0; @@ -6109,6 +6260,65 @@ split_irqchip_unlock: } mutex_unlock(&kvm->lock); break; + case KVM_CAP_MAX_VCPU_ID: + r = -EINVAL; + if (cap->args[0] > KVM_MAX_VCPU_IDS) + break; + + mutex_lock(&kvm->lock); + if (kvm->arch.max_vcpu_ids == cap->args[0]) { + r = 0; + } else if (!kvm->arch.max_vcpu_ids) { + kvm->arch.max_vcpu_ids = cap->args[0]; + r = 0; + } + mutex_unlock(&kvm->lock); + break; + case KVM_CAP_X86_NOTIFY_VMEXIT: + r = -EINVAL; + if ((u32)cap->args[0] & ~KVM_X86_NOTIFY_VMEXIT_VALID_BITS) + break; + if (!kvm_caps.has_notify_vmexit) + break; + if (!((u32)cap->args[0] & KVM_X86_NOTIFY_VMEXIT_ENABLED)) + break; + mutex_lock(&kvm->lock); + if (!kvm->created_vcpus) { + kvm->arch.notify_window = cap->args[0] >> 32; + kvm->arch.notify_vmexit_flags = (u32)cap->args[0]; + r = 0; + } + mutex_unlock(&kvm->lock); + break; + case KVM_CAP_VM_DISABLE_NX_HUGE_PAGES: + r = -EINVAL; + + /* + * Since the risk of disabling NX hugepages is a guest crashing + * the system, ensure the userspace process has permission to + * reboot the system. + * + * Note that unlike the reboot() syscall, the process must have + * this capability in the root namespace because exposing + * /dev/kvm into a container does not limit the scope of the + * iTLB multihit bug to that container. In other words, + * this must use capable(), not ns_capable(). + */ + if (!capable(CAP_SYS_BOOT)) { + r = -EPERM; + break; + } + + if (cap->args[0]) + break; + + mutex_lock(&kvm->lock); + if (!kvm->created_vcpus) { + kvm->arch.disable_nx_huge_pages = true; + r = 0; + } + mutex_unlock(&kvm->lock); + break; default: r = -EINVAL; break; @@ -6584,8 +6794,8 @@ set_pit2_out: r = -EINVAL; user_tsc_khz = (u32)arg; - if (kvm_has_tsc_control && - user_tsc_khz >= kvm_max_guest_tsc_khz) + if (kvm_caps.has_tsc_control && + user_tsc_khz >= kvm_caps.max_guest_tsc_khz) goto out; if (user_tsc_khz == 0) @@ -6660,15 +6870,12 @@ out: static void kvm_init_msr_list(void) { - struct x86_pmu_capability x86_pmu; u32 dummy[2]; unsigned i; BUILD_BUG_ON_MSG(KVM_PMC_MAX_FIXED != 3, "Please update the fixed PMCs in msrs_to_saved_all[]"); - perf_get_x86_pmu_capability(&x86_pmu); - num_msrs_to_save = 0; num_emulated_msrs = 0; num_msr_based_features = 0; @@ -6720,12 +6927,12 @@ static void kvm_init_msr_list(void) break; case MSR_ARCH_PERFMON_PERFCTR0 ... MSR_ARCH_PERFMON_PERFCTR0 + 17: if (msrs_to_save_all[i] - MSR_ARCH_PERFMON_PERFCTR0 >= - min(INTEL_PMC_MAX_GENERIC, x86_pmu.num_counters_gp)) + min(INTEL_PMC_MAX_GENERIC, kvm_pmu_cap.num_counters_gp)) continue; break; case MSR_ARCH_PERFMON_EVENTSEL0 ... MSR_ARCH_PERFMON_EVENTSEL0 + 17: if (msrs_to_save_all[i] - MSR_ARCH_PERFMON_EVENTSEL0 >= - min(INTEL_PMC_MAX_GENERIC, x86_pmu.num_counters_gp)) + min(INTEL_PMC_MAX_GENERIC, kvm_pmu_cap.num_counters_gp)) continue; break; case MSR_IA32_XFD: @@ -6882,7 +7089,7 @@ static int kvm_read_guest_virt_helper(gva_t addr, void *val, unsigned int bytes, unsigned toread = min(bytes, (unsigned)PAGE_SIZE - offset); int ret; - if (gpa == UNMAPPED_GVA) + if (gpa == INVALID_GPA) return X86EMUL_PROPAGATE_FAULT; ret = kvm_vcpu_read_guest_page(vcpu, gpa >> PAGE_SHIFT, data, offset, toread); @@ -6913,7 +7120,7 @@ static int kvm_fetch_guest_virt(struct x86_emulate_ctxt *ctxt, /* Inline kvm_read_guest_virt_helper for speed. */ gpa_t gpa = mmu->gva_to_gpa(vcpu, mmu, addr, access|PFERR_FETCH_MASK, exception); - if (unlikely(gpa == UNMAPPED_GVA)) + if (unlikely(gpa == INVALID_GPA)) return X86EMUL_PROPAGATE_FAULT; offset = addr & (PAGE_SIZE-1); @@ -6983,7 +7190,7 @@ static int kvm_write_guest_virt_helper(gva_t addr, void *val, unsigned int bytes unsigned towrite = min(bytes, (unsigned)PAGE_SIZE - offset); int ret; - if (gpa == UNMAPPED_GVA) + if (gpa == INVALID_GPA) return X86EMUL_PROPAGATE_FAULT; ret = kvm_vcpu_write_guest(vcpu, gpa, data, towrite); if (ret < 0) { @@ -7094,7 +7301,7 @@ static int vcpu_mmio_gva_to_gpa(struct kvm_vcpu *vcpu, unsigned long gva, *gpa = mmu->gva_to_gpa(vcpu, mmu, gva, access, exception); - if (*gpa == UNMAPPED_GVA) + if (*gpa == INVALID_GPA) return -1; return vcpu_is_mmio_gpa(vcpu, gva, *gpa, write); @@ -7331,7 +7538,7 @@ static int emulator_cmpxchg_emulated(struct x86_emulate_ctxt *ctxt, gpa = kvm_mmu_gva_to_gpa_write(vcpu, addr, NULL); - if (gpa == UNMAPPED_GVA || + if (gpa == INVALID_GPA || (gpa & PAGE_MASK) == APIC_DEFAULT_PHYS_BASE) goto emul_write; @@ -7385,36 +7592,47 @@ emul_write: return emulator_write_emulated(ctxt, addr, new, bytes, exception); } -static int kernel_pio(struct kvm_vcpu *vcpu, void *pd) -{ - int r = 0, i; - - for (i = 0; i < vcpu->arch.pio.count; i++) { - if (vcpu->arch.pio.in) - r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, vcpu->arch.pio.port, - vcpu->arch.pio.size, pd); - else - r = kvm_io_bus_write(vcpu, KVM_PIO_BUS, - vcpu->arch.pio.port, vcpu->arch.pio.size, - pd); - if (r) - break; - pd += vcpu->arch.pio.size; - } - return r; -} - static int emulator_pio_in_out(struct kvm_vcpu *vcpu, int size, - unsigned short port, + unsigned short port, void *data, unsigned int count, bool in) { + unsigned i; + int r; + + WARN_ON_ONCE(vcpu->arch.pio.count); + for (i = 0; i < count; i++) { + if (in) + r = kvm_io_bus_read(vcpu, KVM_PIO_BUS, port, size, data); + else + r = kvm_io_bus_write(vcpu, KVM_PIO_BUS, port, size, data); + + if (r) { + if (i == 0) + goto userspace_io; + + /* + * Userspace must have unregistered the device while PIO + * was running. Drop writes / read as 0. + */ + if (in) + memset(data, 0, size * (count - i)); + break; + } + + data += size; + } + return 1; + +userspace_io: vcpu->arch.pio.port = port; vcpu->arch.pio.in = in; - vcpu->arch.pio.count = count; + vcpu->arch.pio.count = count; vcpu->arch.pio.size = size; - if (!kernel_pio(vcpu, vcpu->arch.pio_data)) - return 1; + if (in) + memset(vcpu->arch.pio_data, 0, size * count); + else + memcpy(vcpu->arch.pio_data, data, size * count); vcpu->run->exit_reason = KVM_EXIT_IO; vcpu->run->io.direction = in ? KVM_EXIT_IO_IN : KVM_EXIT_IO_OUT; @@ -7422,30 +7640,33 @@ static int emulator_pio_in_out(struct kvm_vcpu *vcpu, int size, vcpu->run->io.data_offset = KVM_PIO_PAGE_OFFSET * PAGE_SIZE; vcpu->run->io.count = count; vcpu->run->io.port = port; - return 0; } -static int __emulator_pio_in(struct kvm_vcpu *vcpu, int size, - unsigned short port, unsigned int count) +static int emulator_pio_in(struct kvm_vcpu *vcpu, int size, + unsigned short port, void *val, unsigned int count) { - WARN_ON(vcpu->arch.pio.count); - memset(vcpu->arch.pio_data, 0, size * count); - return emulator_pio_in_out(vcpu, size, port, count, true); + int r = emulator_pio_in_out(vcpu, size, port, val, count, true); + if (r) + trace_kvm_pio(KVM_PIO_IN, port, size, count, val); + + return r; } static void complete_emulator_pio_in(struct kvm_vcpu *vcpu, void *val) { int size = vcpu->arch.pio.size; - unsigned count = vcpu->arch.pio.count; + unsigned int count = vcpu->arch.pio.count; memcpy(val, vcpu->arch.pio_data, size * count); trace_kvm_pio(KVM_PIO_IN, vcpu->arch.pio.port, size, count, vcpu->arch.pio_data); vcpu->arch.pio.count = 0; } -static int emulator_pio_in(struct kvm_vcpu *vcpu, int size, - unsigned short port, void *val, unsigned int count) +static int emulator_pio_in_emulated(struct x86_emulate_ctxt *ctxt, + int size, unsigned short port, void *val, + unsigned int count) { + struct kvm_vcpu *vcpu = emul_to_vcpu(ctxt); if (vcpu->arch.pio.count) { /* * Complete a previous iteration that required userspace I/O. @@ -7454,39 +7675,19 @@ static int emulator_pio_in(struct kvm_vcpu *vcpu, int size, * shenanigans as KVM doesn't support modifying the rep count, * and the emulator ensures @count doesn't overflow the buffer. */ - } else { - int r = __emulator_pio_in(vcpu, size, port, count); - if (!r) - return r; - - /* Results already available, fall through. */ + complete_emulator_pio_in(vcpu, val); + return 1; } - complete_emulator_pio_in(vcpu, val); - return 1; -} - -static int emulator_pio_in_emulated(struct x86_emulate_ctxt *ctxt, - int size, unsigned short port, void *val, - unsigned int count) -{ - return emulator_pio_in(emul_to_vcpu(ctxt), size, port, val, count); - + return emulator_pio_in(vcpu, size, port, val, count); } static int emulator_pio_out(struct kvm_vcpu *vcpu, int size, unsigned short port, const void *val, unsigned int count) { - int ret; - - memcpy(vcpu->arch.pio_data, val, size * count); - trace_kvm_pio(KVM_PIO_OUT, port, size, count, vcpu->arch.pio_data); - ret = emulator_pio_in_out(vcpu, size, port, count, false); - if (ret) - vcpu->arch.pio.count = 0; - - return ret; + trace_kvm_pio(KVM_PIO_OUT, port, size, count, val); + return emulator_pio_in_out(vcpu, size, port, (void *)val, count, false); } static int emulator_pio_out_emulated(struct x86_emulate_ctxt *ctxt, @@ -7868,7 +8069,16 @@ static int emulator_set_xcr(struct x86_emulate_ctxt *ctxt, u32 index, u64 xcr) return __kvm_set_xcr(emul_to_vcpu(ctxt), index, xcr); } +static void emulator_vm_bugged(struct x86_emulate_ctxt *ctxt) +{ + struct kvm *kvm = emul_to_vcpu(ctxt)->kvm; + + if (!kvm->vm_bugged) + kvm_vm_bugged(kvm); +} + static const struct x86_emulate_ops emulate_ops = { + .vm_bugged = emulator_vm_bugged, .read_gpr = emulator_read_gpr, .write_gpr = emulator_write_gpr, .read_std = emulator_read_std, @@ -8145,7 +8355,7 @@ static bool reexecute_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, * If the mapping is invalid in guest, let cpu retry * it to generate fault. */ - if (gpa == UNMAPPED_GVA) + if (gpa == INVALID_GPA) return true; } @@ -8672,11 +8882,7 @@ static int complete_fast_pio_in(struct kvm_vcpu *vcpu) /* For size less than 4 we merge, else we zero extend */ val = (vcpu->arch.pio.size < 4) ? kvm_rax_read(vcpu) : 0; - /* - * Since vcpu->arch.pio.count == 1 let emulator_pio_in perform - * the copy and tracing - */ - emulator_pio_in(vcpu, vcpu->arch.pio.size, vcpu->arch.pio.port, &val, 1); + complete_emulator_pio_in(vcpu, &val); kvm_rax_write(vcpu, val); return kvm_skip_emulated_instruction(vcpu); @@ -8751,7 +8957,7 @@ static void kvm_hyperv_tsc_notifier(void) /* TSC frequency always matches when on Hyper-V */ for_each_present_cpu(cpu) per_cpu(cpu_tsc_khz, cpu) = tsc_khz; - kvm_max_guest_tsc_khz = tsc_khz; + kvm_caps.max_guest_tsc_khz = tsc_khz; list_for_each_entry(kvm, &vm_list, vm_list) { __kvm_start_pvclock_update(kvm); @@ -8952,25 +9158,23 @@ static struct notifier_block pvclock_gtod_notifier = { int kvm_arch_init(void *opaque) { struct kvm_x86_init_ops *ops = opaque; + u64 host_pat; int r; if (kvm_x86_ops.hardware_enable) { pr_err("kvm: already loaded vendor module '%s'\n", kvm_x86_ops.name); - r = -EEXIST; - goto out; + return -EEXIST; } if (!ops->cpu_has_kvm_support()) { pr_err_ratelimited("kvm: no hardware support for '%s'\n", ops->runtime_ops->name); - r = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; } if (ops->disabled_by_bios()) { pr_err_ratelimited("kvm: support for '%s' disabled by bios\n", ops->runtime_ops->name); - r = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; } /* @@ -8980,27 +9184,37 @@ int kvm_arch_init(void *opaque) */ if (!boot_cpu_has(X86_FEATURE_FPU) || !boot_cpu_has(X86_FEATURE_FXSR)) { printk(KERN_ERR "kvm: inadequate fpu\n"); - r = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; } if (IS_ENABLED(CONFIG_PREEMPT_RT) && !boot_cpu_has(X86_FEATURE_CONSTANT_TSC)) { pr_err("RT requires X86_FEATURE_CONSTANT_TSC\n"); - r = -EOPNOTSUPP; - goto out; + return -EOPNOTSUPP; } - r = -ENOMEM; + /* + * KVM assumes that PAT entry '0' encodes WB memtype and simply zeroes + * the PAT bits in SPTEs. Bail if PAT[0] is programmed to something + * other than WB. Note, EPT doesn't utilize the PAT, but don't bother + * with an exception. PAT[0] is set to WB on RESET and also by the + * kernel, i.e. failure indicates a kernel bug or broken firmware. + */ + if (rdmsrl_safe(MSR_IA32_CR_PAT, &host_pat) || + (host_pat & GENMASK(2, 0)) != 6) { + pr_err("kvm: host PAT[0] is not WB\n"); + return -EIO; + } x86_emulator_cache = kvm_alloc_emulator_cache(); if (!x86_emulator_cache) { pr_err("kvm: failed to allocate cache for x86 emulator\n"); - goto out; + return -ENOMEM; } user_return_msrs = alloc_percpu(struct kvm_user_return_msrs); if (!user_return_msrs) { printk(KERN_ERR "kvm: failed to allocate percpu kvm_user_return_msrs\n"); + r = -ENOMEM; goto out_free_x86_emulator_cache; } kvm_nr_uret_msrs = 0; @@ -9013,7 +9227,7 @@ int kvm_arch_init(void *opaque) if (boot_cpu_has(X86_FEATURE_XSAVE)) { host_xcr0 = xgetbv(XCR_XFEATURE_ENABLED_MASK); - supported_xcr0 = host_xcr0 & KVM_SUPPORTED_XCR0; + kvm_caps.supported_xcr0 = host_xcr0 & KVM_SUPPORTED_XCR0; } if (pi_inject_timer == -1) @@ -9031,7 +9245,6 @@ out_free_percpu: free_percpu(user_return_msrs); out_free_x86_emulator_cache: kmem_cache_destroy(x86_emulator_cache); -out: return r; } @@ -9406,7 +9619,7 @@ static void update_cr8_intercept(struct kvm_vcpu *vcpu) if (!lapic_in_kernel(vcpu)) return; - if (vcpu->arch.apicv_active) + if (vcpu->arch.apic->apicv_active) return; if (!vcpu->arch.apic->vapic_addr) @@ -9435,6 +9648,11 @@ int kvm_check_nested_events(struct kvm_vcpu *vcpu) static void kvm_inject_exception(struct kvm_vcpu *vcpu) { + trace_kvm_inj_exception(vcpu->arch.exception.nr, + vcpu->arch.exception.has_error_code, + vcpu->arch.exception.error_code, + vcpu->arch.exception.injected); + if (vcpu->arch.exception.error_code && !is_protmode(vcpu)) vcpu->arch.exception.error_code = false; static_call(kvm_x86_queue_exception)(vcpu); @@ -9470,7 +9688,7 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool *req_immediate_exit) static_call(kvm_x86_inject_nmi)(vcpu); can_inject = false; } else if (vcpu->arch.interrupt.injected) { - static_call(kvm_x86_inject_irq)(vcpu); + static_call(kvm_x86_inject_irq)(vcpu, true); can_inject = false; } } @@ -9492,13 +9710,6 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool *req_immediate_exit) /* try to inject new event if pending */ if (vcpu->arch.exception.pending) { - trace_kvm_inj_exception(vcpu->arch.exception.nr, - vcpu->arch.exception.has_error_code, - vcpu->arch.exception.error_code); - - vcpu->arch.exception.pending = false; - vcpu->arch.exception.injected = true; - if (exception_type(vcpu->arch.exception.nr) == EXCPT_FAULT) __kvm_set_rflags(vcpu, kvm_get_rflags(vcpu) | X86_EFLAGS_RF); @@ -9512,6 +9723,10 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool *req_immediate_exit) } kvm_inject_exception(vcpu); + + vcpu->arch.exception.pending = false; + vcpu->arch.exception.injected = true; + can_inject = false; } @@ -9564,7 +9779,7 @@ static int inject_pending_event(struct kvm_vcpu *vcpu, bool *req_immediate_exit) goto out; if (r) { kvm_queue_interrupt(vcpu, kvm_cpu_get_interrupt(vcpu), false); - static_call(kvm_x86_inject_irq)(vcpu); + static_call(kvm_x86_inject_irq)(vcpu, false); WARN_ON(static_call(kvm_x86_interrupt_allowed)(vcpu, true) < 0); } if (kvm_cpu_has_injectable_intr(vcpu)) @@ -9857,6 +10072,7 @@ void kvm_make_scan_ioapic_request(struct kvm *kvm) void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu) { + struct kvm_lapic *apic = vcpu->arch.apic; bool activate; if (!lapic_in_kernel(vcpu)) @@ -9865,12 +10081,14 @@ void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu) down_read(&vcpu->kvm->arch.apicv_update_lock); preempt_disable(); - activate = kvm_vcpu_apicv_activated(vcpu); + /* Do not activate APICV when APIC is disabled */ + activate = kvm_vcpu_apicv_activated(vcpu) && + (kvm_get_apic_mode(vcpu) != LAPIC_MODE_DISABLED); - if (vcpu->arch.apicv_active == activate) + if (apic->apicv_active == activate) goto out; - vcpu->arch.apicv_active = activate; + apic->apicv_active = activate; kvm_apic_update_apicv(vcpu); static_call(kvm_x86_refresh_apicv_exec_ctrl)(vcpu); @@ -9880,7 +10098,7 @@ void kvm_vcpu_update_apicv(struct kvm_vcpu *vcpu) * still active when the interrupt got accepted. Make sure * inject_pending_event() is called to check for that. */ - if (!vcpu->arch.apicv_active) + if (!apic->apicv_active) kvm_make_request(KVM_REQ_EVENT, vcpu); out: @@ -10275,7 +10493,8 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) * per-VM state, and responsing vCPUs must wait for the update * to complete before servicing KVM_REQ_APICV_UPDATE. */ - WARN_ON_ONCE(kvm_vcpu_apicv_activated(vcpu) != kvm_vcpu_apicv_active(vcpu)); + WARN_ON_ONCE((kvm_vcpu_apicv_activated(vcpu) != kvm_vcpu_apicv_active(vcpu)) && + (kvm_get_apic_mode(vcpu) != LAPIC_MODE_DISABLED)); exit_fastpath = static_call(kvm_x86_vcpu_run)(vcpu); if (likely(exit_fastpath != EXIT_FASTPATH_REENTER_GUEST)) @@ -10654,8 +10873,10 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu) r = cui(vcpu); if (r <= 0) goto out; - } else - WARN_ON(vcpu->arch.pio.count || vcpu->mmio_needed); + } else { + WARN_ON_ONCE(vcpu->arch.pio.count); + WARN_ON_ONCE(vcpu->mmio_needed); + } if (kvm_run->immediate_exit) { r = -EINTR; @@ -11183,7 +11404,7 @@ int kvm_arch_vcpu_ioctl_translate(struct kvm_vcpu *vcpu, gpa = kvm_mmu_gva_to_gpa_system(vcpu, vaddr, NULL); srcu_read_unlock(&vcpu->kvm->srcu, idx); tr->physical_address = gpa; - tr->valid = gpa != UNMAPPED_GVA; + tr->valid = gpa != INVALID_GPA; tr->writeable = 1; tr->usermode = 0; @@ -11276,11 +11497,17 @@ static int sync_regs(struct kvm_vcpu *vcpu) int kvm_arch_vcpu_precreate(struct kvm *kvm, unsigned int id) { - if (kvm_check_tsc_unstable() && atomic_read(&kvm->online_vcpus) != 0) + if (kvm_check_tsc_unstable() && kvm->created_vcpus) pr_warn_once("kvm: SMP vm created on host with unstable TSC; " "guest TSC will not be reliable\n"); - return 0; + if (!kvm->arch.max_vcpu_ids) + kvm->arch.max_vcpu_ids = KVM_MAX_VCPU_IDS; + + if (id >= kvm->arch.max_vcpu_ids) + return -EINVAL; + + return static_call(kvm_x86_vcpu_precreate)(kvm); } int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) @@ -11317,7 +11544,7 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) * will ensure the vCPU gets the correct state before VM-Entry. */ if (enable_apicv) { - vcpu->arch.apicv_active = true; + vcpu->arch.apic->apicv_active = true; kvm_make_request(KVM_REQ_APICV_UPDATE, vcpu); } } else @@ -11330,9 +11557,11 @@ int kvm_arch_vcpu_create(struct kvm_vcpu *vcpu) goto fail_free_lapic; vcpu->arch.pio_data = page_address(page); - vcpu->arch.mce_banks = kzalloc(KVM_MAX_MCE_BANKS * sizeof(u64) * 4, + vcpu->arch.mce_banks = kcalloc(KVM_MAX_MCE_BANKS * 4, sizeof(u64), GFP_KERNEL_ACCOUNT); - if (!vcpu->arch.mce_banks) + vcpu->arch.mci_ctl2_banks = kcalloc(KVM_MAX_MCE_BANKS, sizeof(u64), + GFP_KERNEL_ACCOUNT); + if (!vcpu->arch.mce_banks || !vcpu->arch.mci_ctl2_banks) goto fail_free_pio_data; vcpu->arch.mcg_cap = KVM_MAX_MCE_BANKS; @@ -11386,6 +11615,7 @@ free_wbinvd_dirty_mask: free_cpumask_var(vcpu->arch.wbinvd_dirty_mask); fail_free_mce_banks: kfree(vcpu->arch.mce_banks); + kfree(vcpu->arch.mci_ctl2_banks); fail_free_pio_data: free_page((unsigned long)vcpu->arch.pio_data); fail_free_lapic: @@ -11431,6 +11661,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) kvm_hv_vcpu_uninit(vcpu); kvm_pmu_destroy(vcpu); kfree(vcpu->arch.mce_banks); + kfree(vcpu->arch.mci_ctl2_banks); kvm_free_lapic(vcpu); idx = srcu_read_lock(&vcpu->kvm->srcu); kvm_mmu_destroy(vcpu); @@ -11510,6 +11741,8 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) vcpu->arch.smbase = 0x30000; vcpu->arch.msr_misc_features_enables = 0; + vcpu->arch.ia32_misc_enable_msr = MSR_IA32_MISC_ENABLE_PEBS_UNAVAIL | + MSR_IA32_MISC_ENABLE_BTS_UNAVAIL; __kvm_set_xcr(vcpu, 0, XFEATURE_MASK_FP); __kvm_set_msr(vcpu, MSR_IA32_XSS, 0, true); @@ -11526,7 +11759,7 @@ void kvm_vcpu_reset(struct kvm_vcpu *vcpu, bool init_event) * i.e. it's impossible for kvm_find_cpuid_entry() to find a valid entry * on RESET. But, go through the motions in case that's ever remedied. */ - cpuid_0x1 = kvm_find_cpuid_entry(vcpu, 1, 0); + cpuid_0x1 = kvm_find_cpuid_entry(vcpu, 1); kvm_rdx_write(vcpu, cpuid_0x1 ? cpuid_0x1->eax : 0x600); static_call(kvm_x86_vcpu_reset)(vcpu, init_event); @@ -11717,6 +11950,8 @@ int kvm_arch_hardware_setup(void *opaque) if (boot_cpu_has(X86_FEATURE_XSAVES)) rdmsrl(MSR_IA32_XSS, host_xss); + kvm_init_pmu_capability(); + r = ops->hardware_setup(); if (r != 0) return r; @@ -11726,13 +11961,13 @@ int kvm_arch_hardware_setup(void *opaque) kvm_register_perf_callbacks(ops->handle_intel_pt_intr); if (!kvm_cpu_cap_has(X86_FEATURE_XSAVES)) - supported_xss = 0; + kvm_caps.supported_xss = 0; #define __kvm_cpu_cap_has(UNUSED_, f) kvm_cpu_cap_has(f) cr4_reserved_bits = __cr4_reserved_bits(__kvm_cpu_cap_has, UNUSED_); #undef __kvm_cpu_cap_has - if (kvm_has_tsc_control) { + if (kvm_caps.has_tsc_control) { /* * Make sure the user can only configure tsc_khz values that * fit into a signed integer. @@ -11740,10 +11975,10 @@ int kvm_arch_hardware_setup(void *opaque) * be 1 on all machines. */ u64 max = min(0x7fffffffULL, - __scale_tsc(kvm_max_tsc_scaling_ratio, tsc_khz)); - kvm_max_guest_tsc_khz = max; + __scale_tsc(kvm_caps.max_tsc_scaling_ratio, tsc_khz)); + kvm_caps.max_guest_tsc_khz = max; } - kvm_default_tsc_scaling_ratio = 1ULL << kvm_tsc_scaling_ratio_frac_bits; + kvm_caps.default_tsc_scaling_ratio = 1ULL << kvm_caps.tsc_scaling_ratio_frac_bits; kvm_init_msr_list(); return 0; } @@ -12331,7 +12566,8 @@ int kvm_arch_vcpu_runnable(struct kvm_vcpu *vcpu) bool kvm_arch_dy_has_pending_interrupt(struct kvm_vcpu *vcpu) { - if (vcpu->arch.apicv_active && static_call(kvm_x86_dy_apicv_has_pending_interrupt)(vcpu)) + if (kvm_vcpu_apicv_active(vcpu) && + static_call(kvm_x86_dy_apicv_has_pending_interrupt)(vcpu)) return true; return false; @@ -12773,7 +13009,7 @@ void kvm_fixup_and_inject_pf_error(struct kvm_vcpu *vcpu, gva_t gva, u16 error_c (PFERR_WRITE_MASK | PFERR_FETCH_MASK | PFERR_USER_MASK); if (!(error_code & PFERR_PRESENT_MASK) || - mmu->gva_to_gpa(vcpu, mmu, gva, access, &fault) != UNMAPPED_GVA) { + mmu->gva_to_gpa(vcpu, mmu, gva, access, &fault) != INVALID_GPA) { /* * If vcpu->arch.walk_mmu->gva_to_gpa succeeded, the page * tables probably do not match the TLB. Just proceed @@ -12998,6 +13234,12 @@ int kvm_sev_es_mmio_read(struct kvm_vcpu *vcpu, gpa_t gpa, unsigned int bytes, } EXPORT_SYMBOL_GPL(kvm_sev_es_mmio_read); +static void advance_sev_es_emulated_pio(struct kvm_vcpu *vcpu, unsigned count, int size) +{ + vcpu->arch.sev_pio_count -= count; + vcpu->arch.sev_pio_data += count * size; +} + static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size, unsigned int port); @@ -13021,8 +13263,7 @@ static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size, int ret = emulator_pio_out(vcpu, size, port, vcpu->arch.sev_pio_data, count); /* memcpy done already by emulator_pio_out. */ - vcpu->arch.sev_pio_count -= count; - vcpu->arch.sev_pio_data += count * vcpu->arch.pio.size; + advance_sev_es_emulated_pio(vcpu, count, size); if (!ret) break; @@ -13038,20 +13279,14 @@ static int kvm_sev_es_outs(struct kvm_vcpu *vcpu, unsigned int size, static int kvm_sev_es_ins(struct kvm_vcpu *vcpu, unsigned int size, unsigned int port); -static void advance_sev_es_emulated_ins(struct kvm_vcpu *vcpu) -{ - unsigned count = vcpu->arch.pio.count; - complete_emulator_pio_in(vcpu, vcpu->arch.sev_pio_data); - vcpu->arch.sev_pio_count -= count; - vcpu->arch.sev_pio_data += count * vcpu->arch.pio.size; -} - static int complete_sev_es_emulated_ins(struct kvm_vcpu *vcpu) { + unsigned count = vcpu->arch.pio.count; int size = vcpu->arch.pio.size; int port = vcpu->arch.pio.port; - advance_sev_es_emulated_ins(vcpu); + complete_emulator_pio_in(vcpu, vcpu->arch.sev_pio_data); + advance_sev_es_emulated_pio(vcpu, count, size); if (vcpu->arch.sev_pio_count) return kvm_sev_es_ins(vcpu, size, port); return 1; @@ -13063,11 +13298,11 @@ static int kvm_sev_es_ins(struct kvm_vcpu *vcpu, unsigned int size, for (;;) { unsigned int count = min_t(unsigned int, PAGE_SIZE / size, vcpu->arch.sev_pio_count); - if (!__emulator_pio_in(vcpu, size, port, count)) + if (!emulator_pio_in(vcpu, size, port, vcpu->arch.sev_pio_data, count)) break; /* Emulation done by the kernel. */ - advance_sev_es_emulated_ins(vcpu); + advance_sev_es_emulated_pio(vcpu, count, size); if (!vcpu->arch.sev_pio_count) return 1; } @@ -13110,6 +13345,7 @@ EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_unaccelerated_access); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_incomplete_ipi); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_ga_log); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_kick_vcpu_slowpath); +EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_avic_doorbell); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_apicv_accept_irq); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_vmgexit_enter); EXPORT_TRACEPOINT_SYMBOL_GPL(kvm_vmgexit_exit); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 588792f00334..1926d2cb8e79 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -8,6 +8,27 @@ #include "kvm_cache_regs.h" #include "kvm_emulate.h" +struct kvm_caps { + /* control of guest tsc rate supported? */ + bool has_tsc_control; + /* maximum supported tsc_khz for guests */ + u32 max_guest_tsc_khz; + /* number of bits of the fractional part of the TSC scaling ratio */ + u8 tsc_scaling_ratio_frac_bits; + /* maximum allowed value of TSC scaling ratio */ + u64 max_tsc_scaling_ratio; + /* 1ull << kvm_caps.tsc_scaling_ratio_frac_bits */ + u64 default_tsc_scaling_ratio; + /* bus lock detection supported? */ + bool has_bus_lock_exit; + /* notify VM exit supported? */ + bool has_notify_vmexit; + + u64 supported_mce_cap; + u64 supported_xcr0; + u64 supported_xss; +}; + void kvm_spurious_fault(void); #define KVM_NESTED_VMENTER_CONSISTENCY_CHECK(consistency_check) \ @@ -283,14 +304,15 @@ int x86_emulate_instruction(struct kvm_vcpu *vcpu, gpa_t cr2_or_gpa, fastpath_t handle_fastpath_set_msr_irqoff(struct kvm_vcpu *vcpu); extern u64 host_xcr0; -extern u64 supported_xcr0; extern u64 host_xss; -extern u64 supported_xss; + +extern struct kvm_caps kvm_caps; + extern bool enable_pmu; static inline bool kvm_mpx_supported(void) { - return (supported_xcr0 & (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR)) + return (kvm_caps.supported_xcr0 & (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR)) == (XFEATURE_MASK_BNDREGS | XFEATURE_MASK_BNDCSR); } @@ -344,6 +366,11 @@ static inline bool kvm_cstate_in_guest(struct kvm *kvm) return kvm->arch.cstate_in_guest; } +static inline bool kvm_notify_vmexit_enabled(struct kvm *kvm) +{ + return kvm->arch.notify_vmexit_flags & KVM_X86_NOTIFY_VMEXIT_ENABLED; +} + enum kvm_intr_type { /* Values are arbitrary, but must be non-zero. */ KVM_HANDLING_IRQ = 1, @@ -407,7 +434,7 @@ static inline void kvm_machine_check(void) void kvm_load_guest_xsave_state(struct kvm_vcpu *vcpu); void kvm_load_host_xsave_state(struct kvm_vcpu *vcpu); int kvm_spec_ctrl_test_value(u64 value); -bool kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4); +bool __kvm_is_valid_cr4(struct kvm_vcpu *vcpu, unsigned long cr4); int kvm_handle_memory_failure(struct kvm_vcpu *vcpu, int r, struct x86_exception *e); int kvm_handle_invpcid(struct kvm_vcpu *vcpu, unsigned long type, gva_t gva); diff --git a/arch/x86/kvm/xen.c b/arch/x86/kvm/xen.c index 610beba35907..a0c05ccbf4b1 100644 --- a/arch/x86/kvm/xen.c +++ b/arch/x86/kvm/xen.c @@ -1049,7 +1049,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode, else vcpu->arch.xen.poll_evtchn = -1; - set_bit(kvm_vcpu_get_idx(vcpu), vcpu->kvm->arch.xen.poll_mask); + set_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask); if (!wait_pending_event(vcpu, sched_poll.nr_ports, ports)) { vcpu->arch.mp_state = KVM_MP_STATE_HALTED; @@ -1071,7 +1071,7 @@ static bool kvm_xen_schedop_poll(struct kvm_vcpu *vcpu, bool longmode, *r = 0; out: /* Really, this is only needed in case of timeout */ - clear_bit(kvm_vcpu_get_idx(vcpu), vcpu->kvm->arch.xen.poll_mask); + clear_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask); if (unlikely(sched_poll.nr_ports > 1)) kfree(ports); @@ -1311,7 +1311,7 @@ static void kvm_xen_check_poller(struct kvm_vcpu *vcpu, int port) int poll_evtchn = vcpu->arch.xen.poll_evtchn; if ((poll_evtchn == port || poll_evtchn == -1) && - test_and_clear_bit(kvm_vcpu_get_idx(vcpu), vcpu->kvm->arch.xen.poll_mask)) { + test_and_clear_bit(vcpu->vcpu_idx, vcpu->kvm->arch.xen.poll_mask)) { kvm_make_request(KVM_REQ_UNBLOCK, vcpu); kvm_vcpu_kick(vcpu); } @@ -1344,7 +1344,7 @@ int kvm_xen_set_evtchn_fast(struct kvm_xen_evtchn *xe, struct kvm *kvm) vcpu = kvm_get_vcpu_by_id(kvm, xe->vcpu_id); if (!vcpu) return -EINVAL; - WRITE_ONCE(xe->vcpu_idx, kvm_vcpu_get_idx(vcpu)); + WRITE_ONCE(xe->vcpu_idx, vcpu->vcpu_idx); } if (!vcpu->arch.xen.vcpu_info_cache.active) @@ -1540,7 +1540,7 @@ int kvm_xen_setup_evtchn(struct kvm *kvm, */ vcpu = kvm_get_vcpu_by_id(kvm, ue->u.xen_evtchn.vcpu); if (vcpu) - e->xen_evtchn.vcpu_idx = kvm_vcpu_get_idx(vcpu); + e->xen_evtchn.vcpu_idx = vcpu->vcpu_idx; else e->xen_evtchn.vcpu_idx = -1; diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c index f6d038e2cd8e..97452688f99f 100644 --- a/arch/x86/mm/mem_encrypt_amd.c +++ b/arch/x86/mm/mem_encrypt_amd.c @@ -20,8 +20,8 @@ #include #include #include +#include #include -#include #include #include @@ -245,7 +245,7 @@ void __init sev_setup_arch(void) swiotlb_adjust_size(size); /* Set restricted memory access for virtio. */ - platform_set(PLATFORM_VIRTIO_RESTRICTED_MEM_ACCESS); + virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); } static unsigned long pg_level_to_pfn(int level, pte_t *kpte, pgprot_t *ret_prot) diff --git a/arch/x86/xen/enlighten_hvm.c b/arch/x86/xen/enlighten_hvm.c index 8b71b1dd7639..28762f800596 100644 --- a/arch/x86/xen/enlighten_hvm.c +++ b/arch/x86/xen/enlighten_hvm.c @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -195,7 +196,8 @@ static void __init xen_hvm_guest_init(void) if (xen_pv_domain()) return; - xen_set_restricted_virtio_memory_access(); + if (IS_ENABLED(CONFIG_XEN_VIRTIO_FORCE_GRANT)) + virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); init_hvm_pv_info(); diff --git a/arch/x86/xen/enlighten_pv.c b/arch/x86/xen/enlighten_pv.c index 70fb2ea85e90..0ed2e487a693 100644 --- a/arch/x86/xen/enlighten_pv.c +++ b/arch/x86/xen/enlighten_pv.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include @@ -109,7 +110,9 @@ static DEFINE_PER_CPU(struct tls_descs, shadow_tls_desc); static void __init xen_pv_init_platform(void) { - xen_set_restricted_virtio_memory_access(); + /* PV guests can't operate virtio devices without grants. */ + if (IS_ENABLED(CONFIG_XEN_VIRTIO)) + virtio_set_mem_acc_cb(virtio_require_restricted_mem_acc); populate_extra_pte(fix_to_virt(FIX_PARAVIRT_BOOTMAP)); diff --git a/arch/xtensa/Kconfig b/arch/xtensa/Kconfig index 7927fed7bc83..14e547ca2af8 100644 --- a/arch/xtensa/Kconfig +++ b/arch/xtensa/Kconfig @@ -6,6 +6,8 @@ config XTENSA select ARCH_HAS_CURRENT_STACK_POINTER select ARCH_HAS_DEBUG_VM_PGTABLE select ARCH_HAS_DMA_PREP_COHERENT if MMU + select ARCH_HAS_GCOV_PROFILE_ALL + select ARCH_HAS_KCOV select ARCH_HAS_SYNC_DMA_FOR_CPU if MMU select ARCH_HAS_SYNC_DMA_FOR_DEVICE if MMU select ARCH_HAS_DMA_SET_UNCACHED if MMU diff --git a/arch/xtensa/boot/lib/Makefile b/arch/xtensa/boot/lib/Makefile index 162d10af36f3..0378a22a08e3 100644 --- a/arch/xtensa/boot/lib/Makefile +++ b/arch/xtensa/boot/lib/Makefile @@ -17,6 +17,8 @@ endif KASAN_SANITIZE := n KCSAN_SANITIZE := n +KCOV_INSTRUMENT := n +GCOV_PROFILE := n CFLAGS_REMOVE_inflate.o += -fstack-protector -fstack-protector-strong CFLAGS_REMOVE_zmem.o += -fstack-protector -fstack-protector-strong diff --git a/arch/xtensa/platforms/iss/network.c b/arch/xtensa/platforms/iss/network.c index fd84d4891758..9ac46ab3a296 100644 --- a/arch/xtensa/platforms/iss/network.c +++ b/arch/xtensa/platforms/iss/network.c @@ -37,10 +37,6 @@ #define ETH_HEADER_OTHER 14 #define ISS_NET_TIMER_VALUE (HZ / 10) - -static DEFINE_SPINLOCK(devices_lock); -static LIST_HEAD(devices); - /* ------------------------------------------------------------------------- */ /* We currently only support the TUNTAP transport protocol. */ @@ -70,8 +66,6 @@ struct iss_net_ops { /* This structure contains out private information for the driver. */ struct iss_net_private { - struct list_head device_list; - spinlock_t lock; struct net_device *dev; struct platform_device pdev; @@ -472,23 +466,30 @@ static const struct net_device_ops iss_netdev_ops = { .ndo_set_rx_mode = iss_net_set_multicast_list, }; -static int iss_net_configure(int index, char *init) +static void iss_net_pdev_release(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct iss_net_private *lp = + container_of(pdev, struct iss_net_private, pdev); + + free_netdev(lp->dev); +} + +static void iss_net_configure(int index, char *init) { struct net_device *dev; struct iss_net_private *lp; - int err; dev = alloc_etherdev(sizeof(*lp)); if (dev == NULL) { pr_err("eth_configure: failed to allocate device\n"); - return 1; + return; } /* Initialize private element. */ lp = netdev_priv(dev); *lp = (struct iss_net_private) { - .device_list = LIST_HEAD_INIT(lp->device_list), .dev = dev, .index = index, }; @@ -509,7 +510,7 @@ static int iss_net_configure(int index, char *init) if (!tuntap_probe(lp, index, init)) { pr_err("%s: invalid arguments. Skipping device!\n", dev->name); - goto errout; + goto err_free_netdev; } pr_info("Netdevice %d (%pM)\n", index, dev->dev_addr); @@ -517,17 +518,16 @@ static int iss_net_configure(int index, char *init) /* sysfs register */ if (!driver_registered) { - platform_driver_register(&iss_net_driver); + if (platform_driver_register(&iss_net_driver)) + goto err_free_netdev; driver_registered = 1; } - spin_lock(&devices_lock); - list_add(&lp->device_list, &devices); - spin_unlock(&devices_lock); - lp->pdev.id = index; lp->pdev.name = DRIVER_NAME; - platform_device_register(&lp->pdev); + lp->pdev.dev.release = iss_net_pdev_release; + if (platform_device_register(&lp->pdev)) + goto err_free_netdev; SET_NETDEV_DEV(dev, &lp->pdev.dev); dev->netdev_ops = &iss_netdev_ops; @@ -536,23 +536,20 @@ static int iss_net_configure(int index, char *init) dev->irq = -1; rtnl_lock(); - err = register_netdevice(dev); - rtnl_unlock(); - - if (err) { + if (register_netdevice(dev)) { + rtnl_unlock(); pr_err("%s: error registering net device!\n", dev->name); - /* XXX: should we call ->remove() here? */ - free_netdev(dev); - return 1; + platform_device_unregister(&lp->pdev); + return; } + rtnl_unlock(); timer_setup(&lp->tl, iss_net_user_timer_expire, 0); - return 0; + return; -errout: - /* FIXME: unregister; free, etc.. */ - return -EIO; +err_free_netdev: + free_netdev(dev); } /* ------------------------------------------------------------------------- */ @@ -574,7 +571,7 @@ struct iss_net_init { static int __init iss_net_setup(char *str) { - struct iss_net_private *device = NULL; + struct iss_net_init *device = NULL; struct iss_net_init *new; struct list_head *ele; char *end; @@ -595,16 +592,12 @@ static int __init iss_net_setup(char *str) } str = end; - spin_lock(&devices_lock); - - list_for_each(ele, &devices) { - device = list_entry(ele, struct iss_net_private, device_list); + list_for_each(ele, ð_cmd_line) { + device = list_entry(ele, struct iss_net_init, list); if (device->index == n) break; } - spin_unlock(&devices_lock); - if (device && device->index == n) { pr_err("Device %u already configured\n", n); return 1; diff --git a/block/bio-integrity.c b/block/bio-integrity.c index 32929c89ba8a..3f5685c00e36 100644 --- a/block/bio-integrity.c +++ b/block/bio-integrity.c @@ -134,7 +134,7 @@ int bio_integrity_add_page(struct bio *bio, struct page *page, iv = bip->bip_vec + bip->bip_vcnt; if (bip->bip_vcnt && - bvec_gap_to_prev(bdev_get_queue(bio->bi_bdev), + bvec_gap_to_prev(&bdev_get_queue(bio->bi_bdev)->limits, &bip->bip_vec[bip->bip_vcnt - 1], offset)) return 0; diff --git a/block/bio.c b/block/bio.c index 9a14f37c609f..96bca5b8cc92 100644 --- a/block/bio.c +++ b/block/bio.c @@ -968,7 +968,7 @@ int bio_add_hw_page(struct request_queue *q, struct bio *bio, * would create a gap, disallow it. */ bvec = &bio->bi_io_vec[bio->bi_vcnt - 1]; - if (bvec_gap_to_prev(q, bvec, offset)) + if (bvec_gap_to_prev(&q->limits, bvec, offset)) return 0; } @@ -1154,22 +1154,12 @@ void bio_iov_bvec_set(struct bio *bio, struct iov_iter *iter) bio_set_flag(bio, BIO_CLONED); } -static void bio_put_pages(struct page **pages, size_t size, size_t off) -{ - size_t i, nr = DIV_ROUND_UP(size + (off & ~PAGE_MASK), PAGE_SIZE); - - for (i = 0; i < nr; i++) - put_page(pages[i]); -} - static int bio_iov_add_page(struct bio *bio, struct page *page, unsigned int len, unsigned int offset) { bool same_page = false; if (!__bio_try_merge_page(bio, page, len, offset, &same_page)) { - if (WARN_ON_ONCE(bio_full(bio, len))) - return -EINVAL; __bio_add_page(bio, page, len, offset); return 0; } @@ -1212,8 +1202,9 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) struct bio_vec *bv = bio->bi_io_vec + bio->bi_vcnt; struct page **pages = (struct page **)bv; ssize_t size, left; - unsigned len, i; + unsigned len, i = 0; size_t offset; + int ret = 0; /* * Move page array up in the allocated memory for the bio vecs as far as @@ -1230,32 +1221,40 @@ static int __bio_iov_iter_get_pages(struct bio *bio, struct iov_iter *iter) * result to ensure the bio's total size is correct. The remainder of * the iov data will be picked up in the next bio iteration. */ - size = iov_iter_get_pages(iter, pages, LONG_MAX, nr_pages, &offset); - if (size > 0) + size = iov_iter_get_pages(iter, pages, UINT_MAX - bio->bi_iter.bi_size, + nr_pages, &offset); + if (size > 0) { + nr_pages = DIV_ROUND_UP(offset + size, PAGE_SIZE); size = ALIGN_DOWN(size, bdev_logical_block_size(bio->bi_bdev)); - if (unlikely(size <= 0)) - return size ? size : -EFAULT; + } else + nr_pages = 0; + + if (unlikely(size <= 0)) { + ret = size ? size : -EFAULT; + goto out; + } for (left = size, i = 0; left > 0; left -= len, i++) { struct page *page = pages[i]; - int ret; len = min_t(size_t, PAGE_SIZE - offset, left); - if (bio_op(bio) == REQ_OP_ZONE_APPEND) + if (bio_op(bio) == REQ_OP_ZONE_APPEND) { ret = bio_iov_add_zone_append_page(bio, page, len, offset); - else - ret = bio_iov_add_page(bio, page, len, offset); + if (ret) + break; + } else + bio_iov_add_page(bio, page, len, offset); - if (ret) { - bio_put_pages(pages + i, left, offset); - return ret; - } offset = 0; } - iov_iter_advance(iter, size); - return 0; + iov_iter_advance(iter, size - left); +out: + while (i < nr_pages) + put_page(pages[i++]); + + return ret; } /** diff --git a/block/blk-core.c b/block/blk-core.c index 3d286a256d3d..a0d1104c5590 100644 --- a/block/blk-core.c +++ b/block/blk-core.c @@ -377,7 +377,6 @@ static void blk_timeout_work(struct work_struct *work) struct request_queue *blk_alloc_queue(int node_id, bool alloc_srcu) { struct request_queue *q; - int ret; q = kmem_cache_alloc_node(blk_get_queue_kmem_cache(alloc_srcu), GFP_KERNEL | __GFP_ZERO, node_id); @@ -396,13 +395,9 @@ struct request_queue *blk_alloc_queue(int node_id, bool alloc_srcu) if (q->id < 0) goto fail_srcu; - ret = bioset_init(&q->bio_split, BIO_POOL_SIZE, 0, 0); - if (ret) - goto fail_id; - q->stats = blk_alloc_queue_stats(); if (!q->stats) - goto fail_split; + goto fail_id; q->node = node_id; @@ -439,8 +434,6 @@ struct request_queue *blk_alloc_queue(int node_id, bool alloc_srcu) fail_stats: blk_free_queue_stats(q->stats); -fail_split: - bioset_exit(&q->bio_split); fail_id: ida_free(&blk_queue_ida, q->id); fail_srcu: diff --git a/block/blk-merge.c b/block/blk-merge.c index 4c8a699754c9..ff04e9290715 100644 --- a/block/blk-merge.c +++ b/block/blk-merge.c @@ -82,7 +82,7 @@ static inline bool bio_will_gap(struct request_queue *q, bio_get_first_bvec(next, &nb); if (biovec_phys_mergeable(q, &pb, &nb)) return false; - return __bvec_gap_to_prev(q, &pb, nb.bv_offset); + return __bvec_gap_to_prev(&q->limits, &pb, nb.bv_offset); } static inline bool req_gap_back_merge(struct request *req, struct bio *bio) @@ -95,23 +95,30 @@ static inline bool req_gap_front_merge(struct request *req, struct bio *bio) return bio_will_gap(req->q, NULL, bio, req->bio); } -static struct bio *blk_bio_discard_split(struct request_queue *q, - struct bio *bio, - struct bio_set *bs, - unsigned *nsegs) +/* + * The max size one bio can handle is UINT_MAX becasue bvec_iter.bi_size + * is defined as 'unsigned int', meantime it has to be aligned to with the + * logical block size, which is the minimum accepted unit by hardware. + */ +static unsigned int bio_allowed_max_sectors(struct queue_limits *lim) +{ + return round_down(UINT_MAX, lim->logical_block_size) >> SECTOR_SHIFT; +} + +static struct bio *bio_split_discard(struct bio *bio, struct queue_limits *lim, + unsigned *nsegs, struct bio_set *bs) { unsigned int max_discard_sectors, granularity; - int alignment; sector_t tmp; unsigned split_sectors; *nsegs = 1; /* Zero-sector (unknown) and one-sector granularities are the same. */ - granularity = max(q->limits.discard_granularity >> 9, 1U); + granularity = max(lim->discard_granularity >> 9, 1U); - max_discard_sectors = min(q->limits.max_discard_sectors, - bio_allowed_max_sectors(q)); + max_discard_sectors = + min(lim->max_discard_sectors, bio_allowed_max_sectors(lim)); max_discard_sectors -= max_discard_sectors % granularity; if (unlikely(!max_discard_sectors)) { @@ -128,9 +135,8 @@ static struct bio *blk_bio_discard_split(struct request_queue *q, * If the next starting sector would be misaligned, stop the discard at * the previous aligned sector. */ - alignment = (q->limits.discard_alignment >> 9) % granularity; - - tmp = bio->bi_iter.bi_sector + split_sectors - alignment; + tmp = bio->bi_iter.bi_sector + split_sectors - + ((lim->discard_alignment >> 9) % granularity); tmp = sector_div(tmp, granularity); if (split_sectors > tmp) @@ -139,18 +145,15 @@ static struct bio *blk_bio_discard_split(struct request_queue *q, return bio_split(bio, split_sectors, GFP_NOIO, bs); } -static struct bio *blk_bio_write_zeroes_split(struct request_queue *q, - struct bio *bio, struct bio_set *bs, unsigned *nsegs) +static struct bio *bio_split_write_zeroes(struct bio *bio, + struct queue_limits *lim, unsigned *nsegs, struct bio_set *bs) { *nsegs = 0; - - if (!q->limits.max_write_zeroes_sectors) + if (!lim->max_write_zeroes_sectors) return NULL; - - if (bio_sectors(bio) <= q->limits.max_write_zeroes_sectors) + if (bio_sectors(bio) <= lim->max_write_zeroes_sectors) return NULL; - - return bio_split(bio, q->limits.max_write_zeroes_sectors, GFP_NOIO, bs); + return bio_split(bio, lim->max_write_zeroes_sectors, GFP_NOIO, bs); } /* @@ -161,17 +164,17 @@ static struct bio *blk_bio_write_zeroes_split(struct request_queue *q, * requests that are submitted to a block device if the start of a bio is not * aligned to a physical block boundary. */ -static inline unsigned get_max_io_size(struct request_queue *q, - struct bio *bio) +static inline unsigned get_max_io_size(struct bio *bio, + struct queue_limits *lim) { - unsigned pbs = queue_physical_block_size(q) >> SECTOR_SHIFT; - unsigned lbs = queue_logical_block_size(q) >> SECTOR_SHIFT; - unsigned max_sectors = queue_max_sectors(q), start, end; + unsigned pbs = lim->physical_block_size >> SECTOR_SHIFT; + unsigned lbs = lim->logical_block_size >> SECTOR_SHIFT; + unsigned max_sectors = lim->max_sectors, start, end; - if (q->limits.chunk_sectors) { + if (lim->chunk_sectors) { max_sectors = min(max_sectors, blk_chunk_sectors_left(bio->bi_iter.bi_sector, - q->limits.chunk_sectors)); + lim->chunk_sectors)); } start = bio->bi_iter.bi_sector & (pbs - 1); @@ -181,11 +184,10 @@ static inline unsigned get_max_io_size(struct request_queue *q, return max_sectors & ~(lbs - 1); } -static inline unsigned get_max_segment_size(const struct request_queue *q, - struct page *start_page, - unsigned long offset) +static inline unsigned get_max_segment_size(struct queue_limits *lim, + struct page *start_page, unsigned long offset) { - unsigned long mask = queue_segment_boundary(q); + unsigned long mask = lim->seg_boundary_mask; offset = mask & (page_to_phys(start_page) + offset); @@ -194,12 +196,12 @@ static inline unsigned get_max_segment_size(const struct request_queue *q, * on 32bit arch, use queue's max segment size when that happens. */ return min_not_zero(mask - offset + 1, - (unsigned long)queue_max_segment_size(q)); + (unsigned long)lim->max_segment_size); } /** * bvec_split_segs - verify whether or not a bvec should be split in the middle - * @q: [in] request queue associated with the bio associated with @bv + * @lim: [in] queue limits to split based on * @bv: [in] bvec to examine * @nsegs: [in,out] Number of segments in the bio being built. Incremented * by the number of segments from @bv that may be appended to that @@ -217,10 +219,9 @@ static inline unsigned get_max_segment_size(const struct request_queue *q, * *@nsegs segments and *@sectors sectors would make that bio unacceptable for * the block driver. */ -static bool bvec_split_segs(const struct request_queue *q, - const struct bio_vec *bv, unsigned *nsegs, - unsigned *bytes, unsigned max_segs, - unsigned max_bytes) +static bool bvec_split_segs(struct queue_limits *lim, const struct bio_vec *bv, + unsigned *nsegs, unsigned *bytes, unsigned max_segs, + unsigned max_bytes) { unsigned max_len = min(max_bytes, UINT_MAX) - *bytes; unsigned len = min(bv->bv_len, max_len); @@ -228,7 +229,7 @@ static bool bvec_split_segs(const struct request_queue *q, unsigned seg_size = 0; while (len && *nsegs < max_segs) { - seg_size = get_max_segment_size(q, bv->bv_page, + seg_size = get_max_segment_size(lim, bv->bv_page, bv->bv_offset + total_len); seg_size = min(seg_size, len); @@ -236,7 +237,7 @@ static bool bvec_split_segs(const struct request_queue *q, total_len += seg_size; len -= seg_size; - if ((bv->bv_offset + total_len) & queue_virt_boundary(q)) + if ((bv->bv_offset + total_len) & lim->virt_boundary_mask) break; } @@ -247,16 +248,17 @@ static bool bvec_split_segs(const struct request_queue *q, } /** - * blk_bio_segment_split - split a bio in two bios - * @q: [in] request queue pointer + * bio_split_rw - split a bio in two bios * @bio: [in] bio to be split - * @bs: [in] bio set to allocate the clone from + * @lim: [in] queue limits to split based on * @segs: [out] number of segments in the bio with the first half of the sectors + * @bs: [in] bio set to allocate the clone from + * @max_bytes: [in] maximum number of bytes per bio * * Clone @bio, update the bi_iter of the clone to represent the first sectors * of @bio and update @bio->bi_iter to represent the remaining sectors. The * following is guaranteed for the cloned bio: - * - That it has at most get_max_io_size(@q, @bio) sectors. + * - That it has at most @max_bytes worth of data * - That it has at most queue_max_segments(@q) segments. * * Except for discard requests the cloned bio will point at the bi_io_vec of @@ -265,33 +267,30 @@ static bool bvec_split_segs(const struct request_queue *q, * responsible for ensuring that @bs is only destroyed after processing of the * split bio has finished. */ -static struct bio *blk_bio_segment_split(struct request_queue *q, - struct bio *bio, - struct bio_set *bs, - unsigned *segs) +static struct bio *bio_split_rw(struct bio *bio, struct queue_limits *lim, + unsigned *segs, struct bio_set *bs, unsigned max_bytes) { struct bio_vec bv, bvprv, *bvprvp = NULL; struct bvec_iter iter; unsigned nsegs = 0, bytes = 0; - const unsigned max_bytes = get_max_io_size(q, bio) << 9; - const unsigned max_segs = queue_max_segments(q); bio_for_each_bvec(bv, bio, iter) { /* * If the queue doesn't support SG gaps and adding this * offset would create a gap, disallow it. */ - if (bvprvp && bvec_gap_to_prev(q, bvprvp, bv.bv_offset)) + if (bvprvp && bvec_gap_to_prev(lim, bvprvp, bv.bv_offset)) goto split; - if (nsegs < max_segs && + if (nsegs < lim->max_segments && bytes + bv.bv_len <= max_bytes && bv.bv_offset + bv.bv_len <= PAGE_SIZE) { nsegs++; bytes += bv.bv_len; - } else if (bvec_split_segs(q, &bv, &nsegs, &bytes, max_segs, - max_bytes)) { - goto split; + } else { + if (bvec_split_segs(lim, &bv, &nsegs, &bytes, + lim->max_segments, max_bytes)) + goto split; } bvprv = bv; @@ -308,7 +307,7 @@ split: * split size so that each bio is properly block size aligned, even if * we do not use the full hardware limits. */ - bytes = ALIGN_DOWN(bytes, queue_logical_block_size(q)); + bytes = ALIGN_DOWN(bytes, lim->logical_block_size); /* * Bio splitting may cause subtle trouble such as hang when doing sync @@ -320,34 +319,35 @@ split: } /** - * __blk_queue_split - split a bio and submit the second half - * @q: [in] request_queue new bio is being queued at - * @bio: [in, out] bio to be split - * @nr_segs: [out] number of segments in the first bio + * __bio_split_to_limits - split a bio to fit the queue limits + * @bio: bio to be split + * @lim: queue limits to split based on + * @nr_segs: returns the number of segments in the returned bio * - * Split a bio into two bios, chain the two bios, submit the second half and - * store a pointer to the first half in *@bio. If the second bio is still too - * big it will be split by a recursive call to this function. Since this - * function may allocate a new bio from q->bio_split, it is the responsibility - * of the caller to ensure that q->bio_split is only released after processing - * of the split bio has finished. + * Check if @bio needs splitting based on the queue limits, and if so split off + * a bio fitting the limits from the beginning of @bio and return it. @bio is + * shortened to the remainder and re-submitted. + * + * The split bio is allocated from @q->bio_split, which is provided by the + * block layer. */ -void __blk_queue_split(struct request_queue *q, struct bio **bio, +struct bio *__bio_split_to_limits(struct bio *bio, struct queue_limits *lim, unsigned int *nr_segs) { - struct bio *split = NULL; + struct bio_set *bs = &bio->bi_bdev->bd_disk->bio_split; + struct bio *split; - switch (bio_op(*bio)) { + switch (bio_op(bio)) { case REQ_OP_DISCARD: case REQ_OP_SECURE_ERASE: - split = blk_bio_discard_split(q, *bio, &q->bio_split, nr_segs); + split = bio_split_discard(bio, lim, nr_segs, bs); break; case REQ_OP_WRITE_ZEROES: - split = blk_bio_write_zeroes_split(q, *bio, &q->bio_split, - nr_segs); + split = bio_split_write_zeroes(bio, lim, nr_segs, bs); break; default: - split = blk_bio_segment_split(q, *bio, &q->bio_split, nr_segs); + split = bio_split_rw(bio, lim, nr_segs, bs, + get_max_io_size(bio, lim) << SECTOR_SHIFT); break; } @@ -356,32 +356,35 @@ void __blk_queue_split(struct request_queue *q, struct bio **bio, split->bi_opf |= REQ_NOMERGE; blkcg_bio_issue_init(split); - bio_chain(split, *bio); - trace_block_split(split, (*bio)->bi_iter.bi_sector); - submit_bio_noacct(*bio); - *bio = split; + bio_chain(split, bio); + trace_block_split(split, bio->bi_iter.bi_sector); + submit_bio_noacct(bio); + return split; } + return bio; } /** - * blk_queue_split - split a bio and submit the second half - * @bio: [in, out] bio to be split + * bio_split_to_limits - split a bio to fit the queue limits + * @bio: bio to be split * - * Split a bio into two bios, chains the two bios, submit the second half and - * store a pointer to the first half in *@bio. Since this function may allocate - * a new bio from q->bio_split, it is the responsibility of the caller to ensure - * that q->bio_split is only released after processing of the split bio has - * finished. + * Check if @bio needs splitting based on the queue limits of @bio->bi_bdev, and + * if so split off a bio fitting the limits from the beginning of @bio and + * return it. @bio is shortened to the remainder and re-submitted. + * + * The split bio is allocated from @q->bio_split, which is provided by the + * block layer. */ -void blk_queue_split(struct bio **bio) +struct bio *bio_split_to_limits(struct bio *bio) { - struct request_queue *q = bdev_get_queue((*bio)->bi_bdev); + struct queue_limits *lim = &bdev_get_queue(bio->bi_bdev)->limits; unsigned int nr_segs; - if (blk_may_split(q, *bio)) - __blk_queue_split(q, bio, &nr_segs); + if (bio_may_exceed_limits(bio, lim)) + return __bio_split_to_limits(bio, lim, &nr_segs); + return bio; } -EXPORT_SYMBOL(blk_queue_split); +EXPORT_SYMBOL(bio_split_to_limits); unsigned int blk_recalc_rq_segments(struct request *rq) { @@ -411,7 +414,7 @@ unsigned int blk_recalc_rq_segments(struct request *rq) } rq_for_each_bvec(bv, rq, iter) - bvec_split_segs(rq->q, &bv, &nr_phys_segs, &bytes, + bvec_split_segs(&rq->q->limits, &bv, &nr_phys_segs, &bytes, UINT_MAX, UINT_MAX); return nr_phys_segs; } @@ -442,8 +445,8 @@ static unsigned blk_bvec_map_sg(struct request_queue *q, while (nbytes > 0) { unsigned offset = bvec->bv_offset + total; - unsigned len = min(get_max_segment_size(q, bvec->bv_page, - offset), nbytes); + unsigned len = min(get_max_segment_size(&q->limits, + bvec->bv_page, offset), nbytes); struct page *page = bvec->bv_page; /* diff --git a/block/blk-mq.c b/block/blk-mq.c index 70177ee74295..5ee62b95f3e5 100644 --- a/block/blk-mq.c +++ b/block/blk-mq.c @@ -2815,9 +2815,9 @@ void blk_mq_submit_bio(struct bio *bio) unsigned int nr_segs = 1; blk_status_t ret; - blk_queue_bounce(q, &bio); - if (blk_may_split(q, bio)) - __blk_queue_split(q, &bio, &nr_segs); + bio = blk_queue_bounce(bio, q); + if (bio_may_exceed_limits(bio, &q->limits)) + bio = __bio_split_to_limits(bio, &q->limits, &nr_segs); if (!bio_integrity_prep(bio)) return; diff --git a/block/blk-sysfs.c b/block/blk-sysfs.c index c0303026752d..e1f009aba6fd 100644 --- a/block/blk-sysfs.c +++ b/block/blk-sysfs.c @@ -779,8 +779,6 @@ static void blk_release_queue(struct kobject *kobj) if (queue_is_mq(q)) blk_mq_release(q); - bioset_exit(&q->bio_split); - if (blk_queue_has_srcu(q)) cleanup_srcu_struct(q->srcu); diff --git a/block/blk.h b/block/blk.h index 1d83b1d41cd1..d7142c4d2fef 100644 --- a/block/blk.h +++ b/block/blk.h @@ -97,23 +97,23 @@ static inline bool biovec_phys_mergeable(struct request_queue *q, return true; } -static inline bool __bvec_gap_to_prev(struct request_queue *q, +static inline bool __bvec_gap_to_prev(struct queue_limits *lim, struct bio_vec *bprv, unsigned int offset) { - return (offset & queue_virt_boundary(q)) || - ((bprv->bv_offset + bprv->bv_len) & queue_virt_boundary(q)); + return (offset & lim->virt_boundary_mask) || + ((bprv->bv_offset + bprv->bv_len) & lim->virt_boundary_mask); } /* * Check if adding a bio_vec after bprv with offset would create a gap in * the SG list. Most drivers don't care about this, but some do. */ -static inline bool bvec_gap_to_prev(struct request_queue *q, +static inline bool bvec_gap_to_prev(struct queue_limits *lim, struct bio_vec *bprv, unsigned int offset) { - if (!queue_virt_boundary(q)) + if (!lim->virt_boundary_mask) return false; - return __bvec_gap_to_prev(q, bprv, offset); + return __bvec_gap_to_prev(lim, bprv, offset); } static inline bool rq_mergeable(struct request *rq) @@ -189,7 +189,8 @@ static inline bool integrity_req_gap_back_merge(struct request *req, struct bio_integrity_payload *bip = bio_integrity(req->bio); struct bio_integrity_payload *bip_next = bio_integrity(next); - return bvec_gap_to_prev(req->q, &bip->bip_vec[bip->bip_vcnt - 1], + return bvec_gap_to_prev(&req->q->limits, + &bip->bip_vec[bip->bip_vcnt - 1], bip_next->bip_vec[0].bv_offset); } @@ -199,7 +200,8 @@ static inline bool integrity_req_gap_front_merge(struct request *req, struct bio_integrity_payload *bip = bio_integrity(bio); struct bio_integrity_payload *bip_next = bio_integrity(req->bio); - return bvec_gap_to_prev(req->q, &bip->bip_vec[bip->bip_vcnt - 1], + return bvec_gap_to_prev(&req->q->limits, + &bip->bip_vec[bip->bip_vcnt - 1], bip_next->bip_vec[0].bv_offset); } @@ -288,7 +290,8 @@ ssize_t part_timeout_show(struct device *, struct device_attribute *, char *); ssize_t part_timeout_store(struct device *, struct device_attribute *, const char *, size_t); -static inline bool blk_may_split(struct request_queue *q, struct bio *bio) +static inline bool bio_may_exceed_limits(struct bio *bio, + struct queue_limits *lim) { switch (bio_op(bio)) { case REQ_OP_DISCARD: @@ -307,12 +310,12 @@ static inline bool blk_may_split(struct request_queue *q, struct bio *bio) * to the performance impact of cloned bios themselves the loop below * doesn't matter anyway. */ - return q->limits.chunk_sectors || bio->bi_vcnt != 1 || + return lim->chunk_sectors || bio->bi_vcnt != 1 || bio->bi_io_vec->bv_len + bio->bi_io_vec->bv_offset > PAGE_SIZE; } -void __blk_queue_split(struct request_queue *q, struct bio **bio, - unsigned int *nr_segs); +struct bio *__bio_split_to_limits(struct bio *bio, struct queue_limits *lim, + unsigned int *nr_segs); int ll_back_merge_fn(struct request *req, struct bio *bio, unsigned int nr_segs); bool blk_attempt_req_merge(struct request_queue *q, struct request *rq, @@ -344,16 +347,6 @@ static inline void req_set_nomerge(struct request_queue *q, struct request *req) q->last_merge = NULL; } -/* - * The max size one bio can handle is UINT_MAX becasue bvec_iter.bi_size - * is defined as 'unsigned int', meantime it has to aligned to with logical - * block size which is the minimum accepted unit by hardware. - */ -static inline unsigned int bio_allowed_max_sectors(struct request_queue *q) -{ - return round_down(UINT_MAX, queue_logical_block_size(q)) >> 9; -} - /* * Internal io_context interface */ @@ -378,7 +371,7 @@ static inline void blk_throtl_bio_endio(struct bio *bio) { } static inline void blk_throtl_stat_add(struct request *rq, u64 time) { } #endif -void __blk_queue_bounce(struct request_queue *q, struct bio **bio); +struct bio *__blk_queue_bounce(struct bio *bio, struct request_queue *q); static inline bool blk_queue_may_bounce(struct request_queue *q) { @@ -387,10 +380,12 @@ static inline bool blk_queue_may_bounce(struct request_queue *q) max_low_pfn >= max_pfn; } -static inline void blk_queue_bounce(struct request_queue *q, struct bio **bio) +static inline struct bio *blk_queue_bounce(struct bio *bio, + struct request_queue *q) { - if (unlikely(blk_queue_may_bounce(q) && bio_has_data(*bio))) - __blk_queue_bounce(q, bio); + if (unlikely(blk_queue_may_bounce(q) && bio_has_data(bio))) + return __blk_queue_bounce(bio, q); + return bio; } #ifdef CONFIG_BLK_CGROUP_IOLATENCY diff --git a/block/bounce.c b/block/bounce.c index c8f487af7be3..7cfcb242f9a1 100644 --- a/block/bounce.c +++ b/block/bounce.c @@ -199,24 +199,24 @@ err_put: return NULL; } -void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig) +struct bio *__blk_queue_bounce(struct bio *bio_orig, struct request_queue *q) { struct bio *bio; - int rw = bio_data_dir(*bio_orig); + int rw = bio_data_dir(bio_orig); struct bio_vec *to, from; struct bvec_iter iter; unsigned i = 0, bytes = 0; bool bounce = false; int sectors; - bio_for_each_segment(from, *bio_orig, iter) { + bio_for_each_segment(from, bio_orig, iter) { if (i++ < BIO_MAX_VECS) bytes += from.bv_len; if (PageHighMem(from.bv_page)) bounce = true; } if (!bounce) - return; + return bio_orig; /* * Individual bvecs might not be logical block aligned. Round down @@ -225,13 +225,13 @@ void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig) */ sectors = ALIGN_DOWN(bytes, queue_logical_block_size(q)) >> SECTOR_SHIFT; - if (sectors < bio_sectors(*bio_orig)) { - bio = bio_split(*bio_orig, sectors, GFP_NOIO, &bounce_bio_split); - bio_chain(bio, *bio_orig); - submit_bio_noacct(*bio_orig); - *bio_orig = bio; + if (sectors < bio_sectors(bio_orig)) { + bio = bio_split(bio_orig, sectors, GFP_NOIO, &bounce_bio_split); + bio_chain(bio, bio_orig); + submit_bio_noacct(bio_orig); + bio_orig = bio; } - bio = bounce_clone_bio(*bio_orig); + bio = bounce_clone_bio(bio_orig); /* * Bvec table can't be updated by bio_for_each_segment_all(), @@ -254,7 +254,7 @@ void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig) to->bv_page = bounce_page; } - trace_block_bio_bounce(*bio_orig); + trace_block_bio_bounce(bio_orig); bio->bi_flags |= (1 << BIO_BOUNCED); @@ -263,6 +263,6 @@ void __blk_queue_bounce(struct request_queue *q, struct bio **bio_orig) else bio->bi_end_io = bounce_end_io_write; - bio->bi_private = *bio_orig; - *bio_orig = bio; + bio->bi_private = bio_orig; + return bio; } diff --git a/block/bsg.c b/block/bsg.c index 882f56bff14f..2ab1351eb082 100644 --- a/block/bsg.c +++ b/block/bsg.c @@ -169,7 +169,7 @@ static void bsg_device_release(struct device *dev) { struct bsg_device *bd = container_of(dev, struct bsg_device, device); - ida_simple_remove(&bsg_minor_ida, MINOR(bd->device.devt)); + ida_free(&bsg_minor_ida, MINOR(bd->device.devt)); kfree(bd); } @@ -196,7 +196,7 @@ struct bsg_device *bsg_register_queue(struct request_queue *q, bd->queue = q; bd->sg_io_fn = sg_io_fn; - ret = ida_simple_get(&bsg_minor_ida, 0, BSG_MAX_DEVS, GFP_KERNEL); + ret = ida_alloc_max(&bsg_minor_ida, BSG_MAX_DEVS - 1, GFP_KERNEL); if (ret < 0) { if (ret == -ENOSPC) dev_err(parent, "bsg: too many bsg devices\n"); diff --git a/block/genhd.c b/block/genhd.c index e1d5b10ac193..b901fea1d55a 100644 --- a/block/genhd.c +++ b/block/genhd.c @@ -1151,6 +1151,7 @@ static void disk_release(struct device *dev) blk_mq_exit_queue(disk->queue); blkcg_exit_queue(disk->queue); + bioset_exit(&disk->bio_split); disk_release_events(disk); kfree(disk->random); @@ -1342,9 +1343,12 @@ struct gendisk *__alloc_disk_node(struct request_queue *q, int node_id, if (!disk) goto out_put_queue; + if (bioset_init(&disk->bio_split, BIO_POOL_SIZE, 0, 0)) + goto out_free_disk; + disk->bdi = bdi_alloc(node_id); if (!disk->bdi) - goto out_free_disk; + goto out_free_bioset; /* bdev_alloc() might need the queue, set before the first call */ disk->queue = q; @@ -1382,6 +1386,8 @@ out_destroy_part_tbl: iput(disk->part0->bd_inode); out_free_bdi: bdi_put(disk->bdi); +out_free_bioset: + bioset_exit(&disk->bio_split); out_free_disk: kfree(disk); out_put_queue: diff --git a/crypto/kpp.c b/crypto/kpp.c index 7aa6ba4b60a4..678e871ce418 100644 --- a/crypto/kpp.c +++ b/crypto/kpp.c @@ -104,6 +104,12 @@ int crypto_grab_kpp(struct crypto_kpp_spawn *spawn, } EXPORT_SYMBOL_GPL(crypto_grab_kpp); +int crypto_has_kpp(const char *alg_name, u32 type, u32 mask) +{ + return crypto_type_has_alg(alg_name, &crypto_kpp_type, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_has_kpp); + static void kpp_prepare_alg(struct kpp_alg *alg) { struct crypto_alg *base = &alg->base; diff --git a/crypto/shash.c b/crypto/shash.c index 0a0a50cb694f..4c88e63b3350 100644 --- a/crypto/shash.c +++ b/crypto/shash.c @@ -521,6 +521,12 @@ struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type, } EXPORT_SYMBOL_GPL(crypto_alloc_shash); +int crypto_has_shash(const char *alg_name, u32 type, u32 mask) +{ + return crypto_type_has_alg(alg_name, &crypto_shash_type, type, mask); +} +EXPORT_SYMBOL_GPL(crypto_has_shash); + static int shash_prepare_alg(struct shash_alg *alg) { struct crypto_alg *base = &alg->base; diff --git a/drivers/Kconfig b/drivers/Kconfig index b6a172d32a7d..19ee995bd0ae 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -183,8 +183,6 @@ source "drivers/iio/Kconfig" source "drivers/ntb/Kconfig" -source "drivers/vme/Kconfig" - source "drivers/pwm/Kconfig" source "drivers/irqchip/Kconfig" diff --git a/drivers/Makefile b/drivers/Makefile index 123dce286758..057857258bfd 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -165,7 +165,6 @@ obj-$(CONFIG_PM_DEVFREQ) += devfreq/ obj-$(CONFIG_EXTCON) += extcon/ obj-$(CONFIG_MEMORY) += memory/ obj-$(CONFIG_IIO) += iio/ -obj-$(CONFIG_VME_BUS) += vme/ obj-$(CONFIG_IPACK_BUS) += ipack/ obj-$(CONFIG_NTB) += ntb/ obj-$(CONFIG_POWERCAP) += powercap/ diff --git a/drivers/acpi/pptt.c b/drivers/acpi/pptt.c index 701f61c01359..c91342dcbcd6 100644 --- a/drivers/acpi/pptt.c +++ b/drivers/acpi/pptt.c @@ -437,7 +437,8 @@ static void cache_setup_acpi_cpu(struct acpi_table_header *table, pr_debug("found = %p %p\n", found_cache, cpu_node); if (found_cache) update_cache_properties(this_leaf, found_cache, - cpu_node, table->revision); + ACPI_TO_POINTER(ACPI_PTR_DIFF(cpu_node, table)), + table->revision); index++; } @@ -532,21 +533,37 @@ static int topology_get_acpi_cpu_tag(struct acpi_table_header *table, return -ENOENT; } + +static struct acpi_table_header *acpi_get_pptt(void) +{ + static struct acpi_table_header *pptt; + acpi_status status; + + /* + * PPTT will be used at runtime on every CPU hotplug in path, so we + * don't need to call acpi_put_table() to release the table mapping. + */ + if (!pptt) { + status = acpi_get_table(ACPI_SIG_PPTT, 0, &pptt); + if (ACPI_FAILURE(status)) + acpi_pptt_warn_missing(); + } + + return pptt; +} + static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag) { struct acpi_table_header *table; - acpi_status status; int retval; - status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); - if (ACPI_FAILURE(status)) { - acpi_pptt_warn_missing(); + table = acpi_get_pptt(); + if (!table) return -ENOENT; - } + retval = topology_get_acpi_cpu_tag(table, cpu, level, flag); pr_debug("Topology Setup ACPI CPU %d, level %d ret = %d\n", cpu, level, retval); - acpi_put_table(table); return retval; } @@ -567,16 +584,13 @@ static int find_acpi_cpu_topology_tag(unsigned int cpu, int level, int flag) static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag) { struct acpi_table_header *table; - acpi_status status; u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu); struct acpi_pptt_processor *cpu_node = NULL; int ret = -ENOENT; - status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); - if (ACPI_FAILURE(status)) { - acpi_pptt_warn_missing(); - return ret; - } + table = acpi_get_pptt(); + if (!table) + return -ENOENT; if (table->revision >= rev) cpu_node = acpi_find_processor_node(table, acpi_cpu_id); @@ -584,8 +598,6 @@ static int check_acpi_cpu_flag(unsigned int cpu, int rev, u32 flag) if (cpu_node) ret = (cpu_node->flags & flag) != 0; - acpi_put_table(table); - return ret; } @@ -604,18 +616,15 @@ int acpi_find_last_cache_level(unsigned int cpu) u32 acpi_cpu_id; struct acpi_table_header *table; int number_of_levels = 0; - acpi_status status; + + table = acpi_get_pptt(); + if (!table) + return -ENOENT; pr_debug("Cache Setup find last level CPU=%d\n", cpu); acpi_cpu_id = get_acpi_id_for_cpu(cpu); - status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); - if (ACPI_FAILURE(status)) { - acpi_pptt_warn_missing(); - } else { - number_of_levels = acpi_find_cache_levels(table, acpi_cpu_id); - acpi_put_table(table); - } + number_of_levels = acpi_find_cache_levels(table, acpi_cpu_id); pr_debug("Cache Setup find last level level=%d\n", number_of_levels); return number_of_levels; @@ -637,20 +646,16 @@ int acpi_find_last_cache_level(unsigned int cpu) int cache_setup_acpi(unsigned int cpu) { struct acpi_table_header *table; - acpi_status status; + + table = acpi_get_pptt(); + if (!table) + return -ENOENT; pr_debug("Cache Setup ACPI CPU %d\n", cpu); - status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); - if (ACPI_FAILURE(status)) { - acpi_pptt_warn_missing(); - return -ENOENT; - } - cache_setup_acpi_cpu(table, cpu); - acpi_put_table(table); - return status; + return 0; } /** @@ -690,43 +695,6 @@ int find_acpi_cpu_topology(unsigned int cpu, int level) return find_acpi_cpu_topology_tag(cpu, level, 0); } -/** - * find_acpi_cpu_cache_topology() - Determine a unique cache topology value - * @cpu: Kernel logical CPU number - * @level: The cache level for which we would like a unique ID - * - * Determine a unique ID for each unified cache in the system - * - * Return: -ENOENT if the PPTT doesn't exist, or the CPU cannot be found. - * Otherwise returns a value which represents a unique topological feature. - */ -int find_acpi_cpu_cache_topology(unsigned int cpu, int level) -{ - struct acpi_table_header *table; - struct acpi_pptt_cache *found_cache; - acpi_status status; - u32 acpi_cpu_id = get_acpi_id_for_cpu(cpu); - struct acpi_pptt_processor *cpu_node = NULL; - int ret = -1; - - status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); - if (ACPI_FAILURE(status)) { - acpi_pptt_warn_missing(); - return -ENOENT; - } - - found_cache = acpi_find_cache_node(table, acpi_cpu_id, - CACHE_TYPE_UNIFIED, - level, - &cpu_node); - if (found_cache) - ret = ACPI_PTR_DIFF(cpu_node, table); - - acpi_put_table(table); - - return ret; -} - /** * find_acpi_cpu_topology_package() - Determine a unique CPU package value * @cpu: Kernel logical CPU number @@ -766,50 +734,38 @@ int find_acpi_cpu_topology_package(unsigned int cpu) int find_acpi_cpu_topology_cluster(unsigned int cpu) { struct acpi_table_header *table; - acpi_status status; struct acpi_pptt_processor *cpu_node, *cluster_node; u32 acpi_cpu_id; int retval; int is_thread; - status = acpi_get_table(ACPI_SIG_PPTT, 0, &table); - if (ACPI_FAILURE(status)) { - acpi_pptt_warn_missing(); + table = acpi_get_pptt(); + if (!table) return -ENOENT; - } acpi_cpu_id = get_acpi_id_for_cpu(cpu); cpu_node = acpi_find_processor_node(table, acpi_cpu_id); - if (cpu_node == NULL || !cpu_node->parent) { - retval = -ENOENT; - goto put_table; - } + if (!cpu_node || !cpu_node->parent) + return -ENOENT; is_thread = cpu_node->flags & ACPI_PPTT_ACPI_PROCESSOR_IS_THREAD; cluster_node = fetch_pptt_node(table, cpu_node->parent); - if (cluster_node == NULL) { - retval = -ENOENT; - goto put_table; - } + if (!cluster_node) + return -ENOENT; + if (is_thread) { - if (!cluster_node->parent) { - retval = -ENOENT; - goto put_table; - } + if (!cluster_node->parent) + return -ENOENT; + cluster_node = fetch_pptt_node(table, cluster_node->parent); - if (cluster_node == NULL) { - retval = -ENOENT; - goto put_table; - } + if (!cluster_node) + return -ENOENT; } if (cluster_node->flags & ACPI_PPTT_ACPI_PROCESSOR_ID_VALID) retval = cluster_node->acpi_processor_id; else retval = ACPI_PTR_DIFF(cluster_node, table); -put_table: - acpi_put_table(table); - return retval; } diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c index 0cb20324da16..32b0e0b930c1 100644 --- a/drivers/amba/bus.c +++ b/drivers/amba/bus.c @@ -130,11 +130,100 @@ static struct attribute *amba_dev_attrs[] = { }; ATTRIBUTE_GROUPS(amba_dev); +static int amba_read_periphid(struct amba_device *dev) +{ + struct reset_control *rstc; + u32 size, pid, cid; + void __iomem *tmp; + int i, ret; + + ret = dev_pm_domain_attach(&dev->dev, true); + if (ret) { + dev_dbg(&dev->dev, "can't get PM domain: %d\n", ret); + goto err_out; + } + + ret = amba_get_enable_pclk(dev); + if (ret) { + dev_dbg(&dev->dev, "can't get pclk: %d\n", ret); + goto err_pm; + } + + /* + * Find reset control(s) of the amba bus and de-assert them. + */ + rstc = of_reset_control_array_get_optional_shared(dev->dev.of_node); + if (IS_ERR(rstc)) { + ret = PTR_ERR(rstc); + if (ret != -EPROBE_DEFER) + dev_err(&dev->dev, "can't get reset: %d\n", ret); + goto err_clk; + } + reset_control_deassert(rstc); + reset_control_put(rstc); + + size = resource_size(&dev->res); + tmp = ioremap(dev->res.start, size); + if (!tmp) { + ret = -ENOMEM; + goto err_clk; + } + + /* + * Read pid and cid based on size of resource + * they are located at end of region + */ + for (pid = 0, i = 0; i < 4; i++) + pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); + for (cid = 0, i = 0; i < 4; i++) + cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); + + if (cid == CORESIGHT_CID) { + /* set the base to the start of the last 4k block */ + void __iomem *csbase = tmp + size - 4096; + + dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET); + dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; + } + + if (cid == AMBA_CID || cid == CORESIGHT_CID) { + dev->periphid = pid; + dev->cid = cid; + } + + if (!dev->periphid) + ret = -ENODEV; + + iounmap(tmp); + +err_clk: + amba_put_disable_pclk(dev); +err_pm: + dev_pm_domain_detach(&dev->dev, true); +err_out: + return ret; +} + static int amba_match(struct device *dev, struct device_driver *drv) { struct amba_device *pcdev = to_amba_device(dev); struct amba_driver *pcdrv = to_amba_driver(drv); + if (!pcdev->periphid) { + int ret = amba_read_periphid(pcdev); + + /* + * Returning any error other than -EPROBE_DEFER from bus match + * can cause driver registration failure. So, if there's a + * permanent failure in reading pid and cid, simply map it to + * -EPROBE_DEFER. + */ + if (ret) + return -EPROBE_DEFER; + dev_set_uevent_suppress(dev, false); + kobject_uevent(&dev->kobj, KOBJ_ADD); + } + /* When driver_override is set, only bind to the matching driver */ if (pcdev->driver_override) return !strcmp(pcdev->driver_override, drv->name); @@ -368,6 +457,42 @@ static int __init amba_init(void) postcore_initcall(amba_init); +static int amba_proxy_probe(struct amba_device *adev, + const struct amba_id *id) +{ + WARN(1, "Stub driver should never match any device.\n"); + return -ENODEV; +} + +static const struct amba_id amba_stub_drv_ids[] = { + { 0, 0 }, +}; + +static struct amba_driver amba_proxy_drv = { + .drv = { + .name = "amba-proxy", + }, + .probe = amba_proxy_probe, + .id_table = amba_stub_drv_ids, +}; + +static int __init amba_stub_drv_init(void) +{ + if (!IS_ENABLED(CONFIG_MODULES)) + return 0; + + /* + * The amba_match() function will get called only if there is at least + * one amba driver registered. If all amba drivers are modules and are + * only loaded based on uevents, then we'll hit a chicken-and-egg + * situation where amba_match() is waiting on drivers and drivers are + * waiting on amba_match(). So, register a stub driver to make sure + * amba_match() is called even if no amba driver has been registered. + */ + return amba_driver_register(&amba_proxy_drv); +} +late_initcall_sync(amba_stub_drv_init); + /** * amba_driver_register - register an AMBA device driver * @drv: amba device driver structure @@ -410,156 +535,6 @@ static void amba_device_release(struct device *dev) kfree(d); } -static int amba_read_periphid(struct amba_device *dev) -{ - struct reset_control *rstc; - u32 size, pid, cid; - void __iomem *tmp; - int i, ret; - - ret = dev_pm_domain_attach(&dev->dev, true); - if (ret) - goto err_out; - - ret = amba_get_enable_pclk(dev); - if (ret) - goto err_pm; - - /* - * Find reset control(s) of the amba bus and de-assert them. - */ - rstc = of_reset_control_array_get_optional_shared(dev->dev.of_node); - if (IS_ERR(rstc)) { - ret = PTR_ERR(rstc); - if (ret != -EPROBE_DEFER) - dev_err(&dev->dev, "can't get reset: %d\n", ret); - goto err_clk; - } - reset_control_deassert(rstc); - reset_control_put(rstc); - - size = resource_size(&dev->res); - tmp = ioremap(dev->res.start, size); - if (!tmp) { - ret = -ENOMEM; - goto err_clk; - } - - /* - * Read pid and cid based on size of resource - * they are located at end of region - */ - for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(tmp + size - 0x20 + 4 * i) & 255) << (i * 8); - for (cid = 0, i = 0; i < 4; i++) - cid |= (readl(tmp + size - 0x10 + 4 * i) & 255) << (i * 8); - - if (cid == CORESIGHT_CID) { - /* set the base to the start of the last 4k block */ - void __iomem *csbase = tmp + size - 4096; - - dev->uci.devarch = readl(csbase + UCI_REG_DEVARCH_OFFSET); - dev->uci.devtype = readl(csbase + UCI_REG_DEVTYPE_OFFSET) & 0xff; - } - - if (cid == AMBA_CID || cid == CORESIGHT_CID) { - dev->periphid = pid; - dev->cid = cid; - } - - if (!dev->periphid) - ret = -ENODEV; - - iounmap(tmp); - -err_clk: - amba_put_disable_pclk(dev); -err_pm: - dev_pm_domain_detach(&dev->dev, true); -err_out: - return ret; -} - -static int amba_device_try_add(struct amba_device *dev, struct resource *parent) -{ - int ret; - - ret = request_resource(parent, &dev->res); - if (ret) - goto err_out; - - /* Hard-coded primecell ID instead of plug-n-play */ - if (dev->periphid != 0) - goto skip_probe; - - ret = amba_read_periphid(dev); - if (ret) - goto err_release; - -skip_probe: - ret = device_add(&dev->dev); -err_release: - if (ret) - release_resource(&dev->res); -err_out: - return ret; -} - -/* - * Registration of AMBA device require reading its pid and cid registers. - * To do this, the device must be turned on (if it is a part of power domain) - * and have clocks enabled. However in some cases those resources might not be - * yet available. Returning EPROBE_DEFER is not a solution in such case, - * because callers don't handle this special error code. Instead such devices - * are added to the special list and their registration is retried from - * periodic worker, until all resources are available and registration succeeds. - */ -struct deferred_device { - struct amba_device *dev; - struct resource *parent; - struct list_head node; -}; - -static LIST_HEAD(deferred_devices); -static DEFINE_MUTEX(deferred_devices_lock); - -static void amba_deferred_retry_func(struct work_struct *dummy); -static DECLARE_DELAYED_WORK(deferred_retry_work, amba_deferred_retry_func); - -#define DEFERRED_DEVICE_TIMEOUT (msecs_to_jiffies(5 * 1000)) - -static int amba_deferred_retry(void) -{ - struct deferred_device *ddev, *tmp; - - mutex_lock(&deferred_devices_lock); - - list_for_each_entry_safe(ddev, tmp, &deferred_devices, node) { - int ret = amba_device_try_add(ddev->dev, ddev->parent); - - if (ret == -EPROBE_DEFER) - continue; - - list_del_init(&ddev->node); - amba_device_put(ddev->dev); - kfree(ddev); - } - - mutex_unlock(&deferred_devices_lock); - - return 0; -} -late_initcall(amba_deferred_retry); - -static void amba_deferred_retry_func(struct work_struct *dummy) -{ - amba_deferred_retry(); - - if (!list_empty(&deferred_devices)) - schedule_delayed_work(&deferred_retry_work, - DEFERRED_DEVICE_TIMEOUT); -} - /** * amba_device_add - add a previously allocated AMBA device structure * @dev: AMBA device allocated by amba_device_alloc @@ -571,28 +546,30 @@ static void amba_deferred_retry_func(struct work_struct *dummy) */ int amba_device_add(struct amba_device *dev, struct resource *parent) { - int ret = amba_device_try_add(dev, parent); + int ret; - if (ret == -EPROBE_DEFER) { - struct deferred_device *ddev; + ret = request_resource(parent, &dev->res); + if (ret) + return ret; - ddev = kmalloc(sizeof(*ddev), GFP_KERNEL); - if (!ddev) - return -ENOMEM; - - ddev->dev = dev; - ddev->parent = parent; - ret = 0; - - mutex_lock(&deferred_devices_lock); - - if (list_empty(&deferred_devices)) - schedule_delayed_work(&deferred_retry_work, - DEFERRED_DEVICE_TIMEOUT); - list_add_tail(&ddev->node, &deferred_devices); - - mutex_unlock(&deferred_devices_lock); + /* If primecell ID isn't hard-coded, figure it out */ + if (!dev->periphid) { + /* + * AMBA device uevents require reading its pid and cid + * registers. To do this, the device must be on, clocked and + * out of reset. However in some cases those resources might + * not yet be available. If that's the case, we suppress the + * generation of uevents until we can read the pid and cid + * registers. See also amba_match(). + */ + if (amba_read_periphid(dev)) + dev_set_uevent_suppress(&dev->dev, true); } + + ret = device_add(&dev->dev); + if (ret) + release_resource(&dev->res); + return ret; } EXPORT_SYMBOL_GPL(amba_device_add); diff --git a/drivers/base/arch_topology.c b/drivers/base/arch_topology.c index 579c851a2bd7..0424b59b695e 100644 --- a/drivers/base/arch_topology.c +++ b/drivers/base/arch_topology.c @@ -7,6 +7,7 @@ */ #include +#include #include #include #include @@ -496,7 +497,7 @@ static int __init get_cpu_for_node(struct device_node *node) } static int __init parse_core(struct device_node *core, int package_id, - int core_id) + int cluster_id, int core_id) { char name[20]; bool leaf = true; @@ -512,6 +513,7 @@ static int __init parse_core(struct device_node *core, int package_id, cpu = get_cpu_for_node(t); if (cpu >= 0) { cpu_topology[cpu].package_id = package_id; + cpu_topology[cpu].cluster_id = cluster_id; cpu_topology[cpu].core_id = core_id; cpu_topology[cpu].thread_id = i; } else if (cpu != -ENODEV) { @@ -533,6 +535,7 @@ static int __init parse_core(struct device_node *core, int package_id, } cpu_topology[cpu].package_id = package_id; + cpu_topology[cpu].cluster_id = cluster_id; cpu_topology[cpu].core_id = core_id; } else if (leaf && cpu != -ENODEV) { pr_err("%pOF: Can't get CPU for leaf core\n", core); @@ -542,13 +545,13 @@ static int __init parse_core(struct device_node *core, int package_id, return 0; } -static int __init parse_cluster(struct device_node *cluster, int depth) +static int __init parse_cluster(struct device_node *cluster, int package_id, + int cluster_id, int depth) { char name[20]; bool leaf = true; bool has_cores = false; struct device_node *c; - static int package_id __initdata; int core_id = 0; int i, ret; @@ -563,7 +566,9 @@ static int __init parse_cluster(struct device_node *cluster, int depth) c = of_get_child_by_name(cluster, name); if (c) { leaf = false; - ret = parse_cluster(c, depth + 1); + ret = parse_cluster(c, package_id, i, depth + 1); + if (depth > 0) + pr_warn("Topology for clusters of clusters not yet supported\n"); of_node_put(c); if (ret != 0) return ret; @@ -587,7 +592,8 @@ static int __init parse_cluster(struct device_node *cluster, int depth) } if (leaf) { - ret = parse_core(c, package_id, core_id++); + ret = parse_core(c, package_id, cluster_id, + core_id++); } else { pr_err("%pOF: Non-leaf cluster with core %s\n", cluster, name); @@ -604,12 +610,35 @@ static int __init parse_cluster(struct device_node *cluster, int depth) if (leaf && !has_cores) pr_warn("%pOF: empty cluster\n", cluster); - if (leaf) - package_id++; - return 0; } +static int __init parse_socket(struct device_node *socket) +{ + char name[20]; + struct device_node *c; + bool has_socket = false; + int package_id = 0, ret; + + do { + snprintf(name, sizeof(name), "socket%d", package_id); + c = of_get_child_by_name(socket, name); + if (c) { + has_socket = true; + ret = parse_cluster(c, package_id, -1, 0); + of_node_put(c); + if (ret != 0) + return ret; + } + package_id++; + } while (c); + + if (!has_socket) + ret = parse_cluster(socket, 0, -1, 0); + + return ret; +} + static int __init parse_dt_topology(void) { struct device_node *cn, *map; @@ -630,7 +659,7 @@ static int __init parse_dt_topology(void) if (!map) goto out; - ret = parse_cluster(map, 0); + ret = parse_socket(map); if (ret != 0) goto out_map; @@ -641,8 +670,10 @@ static int __init parse_dt_topology(void) * only mark cores described in the DT as possible. */ for_each_possible_cpu(cpu) - if (cpu_topology[cpu].package_id == -1) + if (cpu_topology[cpu].package_id < 0) { ret = -EINVAL; + break; + } out_map: of_node_put(map); @@ -667,7 +698,8 @@ const struct cpumask *cpu_coregroup_mask(int cpu) /* not numa in package, lets use the package siblings */ core_mask = &cpu_topology[cpu].core_sibling; } - if (cpu_topology[cpu].llc_id != -1) { + + if (last_level_cache_is_valid(cpu)) { if (cpumask_subset(&cpu_topology[cpu].llc_sibling, core_mask)) core_mask = &cpu_topology[cpu].llc_sibling; } @@ -686,19 +718,31 @@ const struct cpumask *cpu_coregroup_mask(int cpu) const struct cpumask *cpu_clustergroup_mask(int cpu) { + /* + * Forbid cpu_clustergroup_mask() to span more or the same CPUs as + * cpu_coregroup_mask(). + */ + if (cpumask_subset(cpu_coregroup_mask(cpu), + &cpu_topology[cpu].cluster_sibling)) + return get_cpu_mask(cpu); + return &cpu_topology[cpu].cluster_sibling; } void update_siblings_masks(unsigned int cpuid) { struct cpu_topology *cpu_topo, *cpuid_topo = &cpu_topology[cpuid]; - int cpu; + int cpu, ret; + + ret = detect_cache_attributes(cpuid); + if (ret) + pr_info("Early cacheinfo failed, ret = %d\n", ret); /* update core and thread sibling masks */ for_each_online_cpu(cpu) { cpu_topo = &cpu_topology[cpu]; - if (cpu_topo->llc_id != -1 && cpuid_topo->llc_id == cpu_topo->llc_id) { + if (last_level_cache_is_shared(cpu, cpuid)) { cpumask_set_cpu(cpu, &cpuid_topo->llc_sibling); cpumask_set_cpu(cpuid, &cpu_topo->llc_sibling); } @@ -706,15 +750,17 @@ void update_siblings_masks(unsigned int cpuid) if (cpuid_topo->package_id != cpu_topo->package_id) continue; - if (cpuid_topo->cluster_id == cpu_topo->cluster_id && - cpuid_topo->cluster_id != -1) { + cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); + cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); + + if (cpuid_topo->cluster_id != cpu_topo->cluster_id) + continue; + + if (cpuid_topo->cluster_id >= 0) { cpumask_set_cpu(cpu, &cpuid_topo->cluster_sibling); cpumask_set_cpu(cpuid, &cpu_topo->cluster_sibling); } - cpumask_set_cpu(cpuid, &cpu_topo->core_sibling); - cpumask_set_cpu(cpu, &cpuid_topo->core_sibling); - if (cpuid_topo->core_id != cpu_topo->core_id) continue; @@ -750,7 +796,6 @@ void __init reset_cpu_topology(void) cpu_topo->core_id = -1; cpu_topo->cluster_id = -1; cpu_topo->package_id = -1; - cpu_topo->llc_id = -1; clear_cpu_topology(cpu); } @@ -780,15 +825,20 @@ __weak int __init parse_acpi_topology(void) #if defined(CONFIG_ARM64) || defined(CONFIG_RISCV) void __init init_cpu_topology(void) { - reset_cpu_topology(); + int ret; - /* - * Discard anything that was parsed if we hit an error so we - * don't use partial information. - */ - if (parse_acpi_topology()) - reset_cpu_topology(); - else if (of_have_populated_dt() && parse_dt_topology()) + reset_cpu_topology(); + ret = parse_acpi_topology(); + if (!ret) + ret = of_have_populated_dt() && parse_dt_topology(); + + if (ret) { + /* + * Discard anything that was parsed if we hit an error so we + * don't use partial information. + */ reset_cpu_topology(); + return; + } } #endif diff --git a/drivers/base/base.h b/drivers/base/base.h index ab71403d102f..b3a43a164dcd 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -160,6 +160,7 @@ extern int devres_release_all(struct device *dev); extern void device_block_probing(void); extern void device_unblock_probing(void); extern void deferred_probe_extend_timeout(void); +extern void driver_deferred_probe_trigger(void); /* /sys/devices directory */ extern struct kset *devices_kset; diff --git a/drivers/base/cacheinfo.c b/drivers/base/cacheinfo.c index dad296229161..4b5cd08c5a65 100644 --- a/drivers/base/cacheinfo.c +++ b/drivers/base/cacheinfo.c @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include #include #include @@ -25,19 +25,60 @@ static DEFINE_PER_CPU(struct cpu_cacheinfo, ci_cpu_cacheinfo); #define ci_cacheinfo(cpu) (&per_cpu(ci_cpu_cacheinfo, cpu)) #define cache_leaves(cpu) (ci_cacheinfo(cpu)->num_leaves) #define per_cpu_cacheinfo(cpu) (ci_cacheinfo(cpu)->info_list) +#define per_cpu_cacheinfo_idx(cpu, idx) \ + (per_cpu_cacheinfo(cpu) + (idx)) struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu) { return ci_cacheinfo(cpu); } -#ifdef CONFIG_OF static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, struct cacheinfo *sib_leaf) { + /* + * For non DT/ACPI systems, assume unique level 1 caches, + * system-wide shared caches for all other levels. This will be used + * only if arch specific code has not populated shared_cpu_map + */ + if (!(IS_ENABLED(CONFIG_OF) || IS_ENABLED(CONFIG_ACPI))) + return !(this_leaf->level == 1); + + if ((sib_leaf->attributes & CACHE_ID) && + (this_leaf->attributes & CACHE_ID)) + return sib_leaf->id == this_leaf->id; + return sib_leaf->fw_token == this_leaf->fw_token; } +bool last_level_cache_is_valid(unsigned int cpu) +{ + struct cacheinfo *llc; + + if (!cache_leaves(cpu)) + return false; + + llc = per_cpu_cacheinfo_idx(cpu, cache_leaves(cpu) - 1); + + return (llc->attributes & CACHE_ID) || !!llc->fw_token; + +} + +bool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y) +{ + struct cacheinfo *llc_x, *llc_y; + + if (!last_level_cache_is_valid(cpu_x) || + !last_level_cache_is_valid(cpu_y)) + return false; + + llc_x = per_cpu_cacheinfo_idx(cpu_x, cache_leaves(cpu_x) - 1); + llc_y = per_cpu_cacheinfo_idx(cpu_y, cache_leaves(cpu_y) - 1); + + return cache_leaves_are_shared(llc_x, llc_y); +} + +#ifdef CONFIG_OF /* OF properties to query for a given cache type */ struct cache_type_info { const char *size_prop; @@ -157,27 +198,16 @@ static int cache_setup_of_node(unsigned int cpu) { struct device_node *np; struct cacheinfo *this_leaf; - struct device *cpu_dev = get_cpu_device(cpu); - struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); unsigned int index = 0; - /* skip if fw_token is already populated */ - if (this_cpu_ci->info_list->fw_token) { - return 0; - } - - if (!cpu_dev) { - pr_err("No cpu device for CPU %d\n", cpu); - return -ENODEV; - } - np = cpu_dev->of_node; + np = of_cpu_device_node_get(cpu); if (!np) { pr_err("Failed to find cpu%d device node\n", cpu); return -ENOENT; } while (index < cache_leaves(cpu)) { - this_leaf = this_cpu_ci->info_list + index; + this_leaf = per_cpu_cacheinfo_idx(cpu, index); if (this_leaf->level != 1) np = of_find_next_cache_node(np); else @@ -196,16 +226,6 @@ static int cache_setup_of_node(unsigned int cpu) } #else static inline int cache_setup_of_node(unsigned int cpu) { return 0; } -static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, - struct cacheinfo *sib_leaf) -{ - /* - * For non-DT/ACPI systems, assume unique level 1 caches, system-wide - * shared caches for all other levels. This will be used only if - * arch specific code has not populated shared_cpu_map - */ - return !(this_leaf->level == 1); -} #endif int __weak cache_setup_acpi(unsigned int cpu) @@ -215,6 +235,18 @@ int __weak cache_setup_acpi(unsigned int cpu) unsigned int coherency_max_size; +static int cache_setup_properties(unsigned int cpu) +{ + int ret = 0; + + if (of_have_populated_dt()) + ret = cache_setup_of_node(cpu); + else if (!acpi_disabled) + ret = cache_setup_acpi(cpu); + + return ret; +} + static int cache_shared_cpu_map_setup(unsigned int cpu) { struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); @@ -225,21 +257,21 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) if (this_cpu_ci->cpu_map_populated) return 0; - if (of_have_populated_dt()) - ret = cache_setup_of_node(cpu); - else if (!acpi_disabled) - ret = cache_setup_acpi(cpu); - - if (ret) - return ret; + /* + * skip setting up cache properties if LLC is valid, just need + * to update the shared cpu_map if the cache attributes were + * populated early before all the cpus are brought online + */ + if (!last_level_cache_is_valid(cpu)) { + ret = cache_setup_properties(cpu); + if (ret) + return ret; + } for (index = 0; index < cache_leaves(cpu); index++) { unsigned int i; - this_leaf = this_cpu_ci->info_list + index; - /* skip if shared_cpu_map is already populated */ - if (!cpumask_empty(&this_leaf->shared_cpu_map)) - continue; + this_leaf = per_cpu_cacheinfo_idx(cpu, index); cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map); for_each_online_cpu(i) { @@ -247,7 +279,8 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) if (i == cpu || !sib_cpu_ci->info_list) continue;/* skip if itself or no cacheinfo */ - sib_leaf = sib_cpu_ci->info_list + index; + + sib_leaf = per_cpu_cacheinfo_idx(i, index); if (cache_leaves_are_shared(this_leaf, sib_leaf)) { cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); cpumask_set_cpu(i, &this_leaf->shared_cpu_map); @@ -263,23 +296,19 @@ static int cache_shared_cpu_map_setup(unsigned int cpu) static void cache_shared_cpu_map_remove(unsigned int cpu) { - struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); struct cacheinfo *this_leaf, *sib_leaf; unsigned int sibling, index; for (index = 0; index < cache_leaves(cpu); index++) { - this_leaf = this_cpu_ci->info_list + index; + this_leaf = per_cpu_cacheinfo_idx(cpu, index); for_each_cpu(sibling, &this_leaf->shared_cpu_map) { - struct cpu_cacheinfo *sib_cpu_ci; + struct cpu_cacheinfo *sib_cpu_ci = + get_cpu_cacheinfo(sibling); - if (sibling == cpu) /* skip itself */ - continue; + if (sibling == cpu || !sib_cpu_ci->info_list) + continue;/* skip if itself or no cacheinfo */ - sib_cpu_ci = get_cpu_cacheinfo(sibling); - if (!sib_cpu_ci->info_list) - continue; - - sib_leaf = sib_cpu_ci->info_list + index; + sib_leaf = per_cpu_cacheinfo_idx(sibling, index); cpumask_clear_cpu(cpu, &sib_leaf->shared_cpu_map); cpumask_clear_cpu(sibling, &this_leaf->shared_cpu_map); } @@ -310,17 +339,28 @@ int __weak populate_cache_leaves(unsigned int cpu) return -ENOENT; } -static int detect_cache_attributes(unsigned int cpu) +int detect_cache_attributes(unsigned int cpu) { int ret; + /* Since early detection of the cacheinfo is allowed via this + * function and this also gets called as CPU hotplug callbacks via + * cacheinfo_cpu_online, the initialisation can be skipped and only + * CPU maps can be updated as the CPU online status would be update + * if called via cacheinfo_cpu_online path. + */ + if (per_cpu_cacheinfo(cpu)) + goto update_cpu_map; + if (init_cache_level(cpu) || !cache_leaves(cpu)) return -ENOENT; per_cpu_cacheinfo(cpu) = kcalloc(cache_leaves(cpu), - sizeof(struct cacheinfo), GFP_KERNEL); - if (per_cpu_cacheinfo(cpu) == NULL) + sizeof(struct cacheinfo), GFP_ATOMIC); + if (per_cpu_cacheinfo(cpu) == NULL) { + cache_leaves(cpu) = 0; return -ENOMEM; + } /* * populate_cache_leaves() may completely setup the cache leaves and @@ -329,6 +369,8 @@ static int detect_cache_attributes(unsigned int cpu) ret = populate_cache_leaves(cpu); if (ret) goto free_ci; + +update_cpu_map: /* * For systems using DT for cache hierarchy, fw_token * and shared_cpu_map will be set up here only if they are @@ -614,7 +656,6 @@ static int cache_add_dev(unsigned int cpu) int rc; struct device *ci_dev, *parent; struct cacheinfo *this_leaf; - struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); const struct attribute_group **cache_groups; rc = cpu_cache_sysfs_init(cpu); @@ -623,7 +664,7 @@ static int cache_add_dev(unsigned int cpu) parent = per_cpu_cache_dev(cpu); for (i = 0; i < cache_leaves(cpu); i++) { - this_leaf = this_cpu_ci->info_list + i; + this_leaf = per_cpu_cacheinfo_idx(cpu, i); if (this_leaf->disable_sysfs) continue; if (this_leaf->type == CACHE_TYPE_NOCACHE) diff --git a/drivers/base/core.c b/drivers/base/core.c index bf6b5078f6ed..753e7cca0f40 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -54,6 +54,7 @@ static unsigned int defer_sync_state_count = 1; static DEFINE_MUTEX(fwnode_link_lock); static bool fw_devlink_is_permissive(void); static bool fw_devlink_drv_reg_done; +static bool fw_devlink_best_effort; /** * fwnode_link_add - Create a link between two fwnode_handles. @@ -976,6 +977,12 @@ static void device_links_missing_supplier(struct device *dev) } } +static bool dev_is_best_effort(struct device *dev) +{ + return (fw_devlink_best_effort && dev->can_match) || + (dev->fwnode && (dev->fwnode->flags & FWNODE_FLAG_BEST_EFFORT)); +} + /** * device_links_check_suppliers - Check presence of supplier drivers. * @dev: Consumer device. @@ -995,7 +1002,7 @@ static void device_links_missing_supplier(struct device *dev) int device_links_check_suppliers(struct device *dev) { struct device_link *link; - int ret = 0; + int ret = 0, fwnode_ret = 0; struct fwnode_handle *sup_fw; /* @@ -1008,12 +1015,17 @@ int device_links_check_suppliers(struct device *dev) sup_fw = list_first_entry(&dev->fwnode->suppliers, struct fwnode_link, c_hook)->supplier; - dev_err_probe(dev, -EPROBE_DEFER, "wait for supplier %pfwP\n", - sup_fw); - mutex_unlock(&fwnode_link_lock); - return -EPROBE_DEFER; + if (!dev_is_best_effort(dev)) { + fwnode_ret = -EPROBE_DEFER; + dev_err_probe(dev, -EPROBE_DEFER, + "wait for supplier %pfwP\n", sup_fw); + } else { + fwnode_ret = -EAGAIN; + } } mutex_unlock(&fwnode_link_lock); + if (fwnode_ret == -EPROBE_DEFER) + return fwnode_ret; device_links_write_lock(); @@ -1023,6 +1035,14 @@ int device_links_check_suppliers(struct device *dev) if (link->status != DL_STATE_AVAILABLE && !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) { + + if (dev_is_best_effort(dev) && + link->flags & DL_FLAG_INFERRED && + !link->supplier->can_match) { + ret = -EAGAIN; + continue; + } + device_links_missing_supplier(dev); dev_err_probe(dev, -EPROBE_DEFER, "supplier %s not ready\n", @@ -1035,7 +1055,8 @@ int device_links_check_suppliers(struct device *dev) dev->links.status = DL_DEV_PROBING; device_links_write_unlock(); - return ret; + + return ret ? ret : fwnode_ret; } /** @@ -1300,6 +1321,18 @@ void device_links_driver_bound(struct device *dev) * save to drop the managed link completely. */ device_link_drop_managed(link); + } else if (dev_is_best_effort(dev) && + link->flags & DL_FLAG_INFERRED && + link->status != DL_STATE_CONSUMER_PROBE && + !link->supplier->can_match) { + /* + * When dev_is_best_effort() is true, we ignore device + * links to suppliers that don't have a driver. If the + * consumer device still managed to probe, there's no + * point in maintaining a device link in a weird state + * (consumer probed before supplier). So delete it. + */ + device_link_drop_managed(link); } else { WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); WRITE_ONCE(link->status, DL_STATE_ACTIVE); @@ -1666,6 +1699,62 @@ void fw_devlink_drivers_done(void) device_links_write_unlock(); } +/** + * wait_for_init_devices_probe - Try to probe any device needed for init + * + * Some devices might need to be probed and bound successfully before the kernel + * boot sequence can finish and move on to init/userspace. For example, a + * network interface might need to be bound to be able to mount a NFS rootfs. + * + * With fw_devlink=on by default, some of these devices might be blocked from + * probing because they are waiting on a optional supplier that doesn't have a + * driver. While fw_devlink will eventually identify such devices and unblock + * the probing automatically, it might be too late by the time it unblocks the + * probing of devices. For example, the IP4 autoconfig might timeout before + * fw_devlink unblocks probing of the network interface. + * + * This function is available to temporarily try and probe all devices that have + * a driver even if some of their suppliers haven't been added or don't have + * drivers. + * + * The drivers can then decide which of the suppliers are optional vs mandatory + * and probe the device if possible. By the time this function returns, all such + * "best effort" probes are guaranteed to be completed. If a device successfully + * probes in this mode, we delete all fw_devlink discovered dependencies of that + * device where the supplier hasn't yet probed successfully because they have to + * be optional dependencies. + * + * Any devices that didn't successfully probe go back to being treated as if + * this function was never called. + * + * This also means that some devices that aren't needed for init and could have + * waited for their optional supplier to probe (when the supplier's module is + * loaded later on) would end up probing prematurely with limited functionality. + * So call this function only when boot would fail without it. + */ +void __init wait_for_init_devices_probe(void) +{ + if (!fw_devlink_flags || fw_devlink_is_permissive()) + return; + + /* + * Wait for all ongoing probes to finish so that the "best effort" is + * only applied to devices that can't probe otherwise. + */ + wait_for_device_probe(); + + pr_info("Trying to probe devices needed for running init ...\n"); + fw_devlink_best_effort = true; + driver_deferred_probe_trigger(); + + /* + * Wait for all "best effort" probes to finish before going back to + * normal enforcement. + */ + wait_for_device_probe(); + fw_devlink_best_effort = false; +} + static void fw_devlink_unblock_consumers(struct device *dev) { struct device_link *link; @@ -3843,6 +3932,26 @@ struct device *device_find_child_by_name(struct device *parent, } EXPORT_SYMBOL_GPL(device_find_child_by_name); +static int match_any(struct device *dev, void *unused) +{ + return 1; +} + +/** + * device_find_any_child - device iterator for locating a child device, if any. + * @parent: parent struct device + * + * This is similar to the device_find_child() function above, but it + * returns a reference to a child device, if any. + * + * NOTE: you will need to drop the reference with put_device() after use. + */ +struct device *device_find_any_child(struct device *parent) +{ + return device_find_child(parent, NULL, match_any); +} +EXPORT_SYMBOL_GPL(device_find_any_child); + int __init devices_init(void) { devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); diff --git a/drivers/base/dd.c b/drivers/base/dd.c index 11b0fb6414d3..70f79fc71539 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -172,7 +172,7 @@ static bool driver_deferred_probe_enable; * changes in the midst of a probe, then deferred processing should be triggered * again. */ -static void driver_deferred_probe_trigger(void) +void driver_deferred_probe_trigger(void) { if (!driver_deferred_probe_enable) return; @@ -256,7 +256,12 @@ static int deferred_devs_show(struct seq_file *s, void *data) } DEFINE_SHOW_ATTRIBUTE(deferred_devs); +#ifdef CONFIG_MODULES +int driver_deferred_probe_timeout = 10; +#else int driver_deferred_probe_timeout; +#endif + EXPORT_SYMBOL_GPL(driver_deferred_probe_timeout); static int __init deferred_probe_timeout_setup(char *str) @@ -269,42 +274,12 @@ static int __init deferred_probe_timeout_setup(char *str) } __setup("deferred_probe_timeout=", deferred_probe_timeout_setup); -/** - * driver_deferred_probe_check_state() - Check deferred probe state - * @dev: device to check - * - * Return: - * * -ENODEV if initcalls have completed and modules are disabled. - * * -ETIMEDOUT if the deferred probe timeout was set and has expired - * and modules are enabled. - * * -EPROBE_DEFER in other cases. - * - * Drivers or subsystems can opt-in to calling this function instead of directly - * returning -EPROBE_DEFER. - */ -int driver_deferred_probe_check_state(struct device *dev) -{ - if (!IS_ENABLED(CONFIG_MODULES) && initcalls_done) { - dev_warn(dev, "ignoring dependency for device, assuming no driver\n"); - return -ENODEV; - } - - if (!driver_deferred_probe_timeout && initcalls_done) { - dev_warn(dev, "deferred probe timeout, ignoring dependency\n"); - return -ETIMEDOUT; - } - - return -EPROBE_DEFER; -} -EXPORT_SYMBOL_GPL(driver_deferred_probe_check_state); - static void deferred_probe_timeout_work_func(struct work_struct *work) { struct device_private *p; fw_devlink_drivers_done(); - driver_deferred_probe_timeout = 0; driver_deferred_probe_trigger(); flush_work(&deferred_probe_work); @@ -580,7 +555,7 @@ static int really_probe(struct device *dev, struct device_driver *drv) { bool test_remove = IS_ENABLED(CONFIG_DEBUG_TEST_DRIVER_REMOVE) && !drv->suppress_bind_attrs; - int ret; + int ret, link_ret; if (defer_all_probes) { /* @@ -592,9 +567,9 @@ static int really_probe(struct device *dev, struct device_driver *drv) return -EPROBE_DEFER; } - ret = device_links_check_suppliers(dev); - if (ret) - return ret; + link_ret = device_links_check_suppliers(dev); + if (link_ret == -EPROBE_DEFER) + return link_ret; pr_debug("bus: '%s': %s: probing driver %s with device %s\n", drv->bus->name, __func__, drv->name, dev_name(dev)); @@ -633,6 +608,15 @@ re_probe: ret = call_driver_probe(dev, drv); if (ret) { + /* + * If fw_devlink_best_effort is active (denoted by -EAGAIN), the + * device might actually probe properly once some of its missing + * suppliers have probed. So, treat this as if the driver + * returned -EPROBE_DEFER. + */ + if (link_ret == -EAGAIN) + ret = -EPROBE_DEFER; + /* * Return probe errors as positive values so that the callers * can distinguish them from other errors. @@ -1115,6 +1099,7 @@ static void __driver_attach_async_helper(void *_dev, async_cookie_t cookie) static int __driver_attach(struct device *dev, void *data) { struct device_driver *drv = data; + bool async = false; int ret; /* @@ -1153,9 +1138,11 @@ static int __driver_attach(struct device *dev, void *data) if (!dev->driver && !dev->p->async_driver) { get_device(dev); dev->p->async_driver = drv; - async_schedule_dev(__driver_attach_async_helper, dev); + async = true; } device_unlock(dev); + if (async) + async_schedule_dev(__driver_attach_async_helper, dev); return 0; } diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index 8a3ddbae3b70..e4bffeabf344 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -482,6 +482,7 @@ int __init devtmpfs_init(void) if (err) { printk(KERN_ERR "devtmpfs: unable to create devtmpfs %i\n", err); unregister_filesystem(&dev_fs_type); + thread = NULL; return err; } diff --git a/drivers/base/firmware_loader/main.c b/drivers/base/firmware_loader/main.c index ac3f34e80194..7c3590fd97c2 100644 --- a/drivers/base/firmware_loader/main.c +++ b/drivers/base/firmware_loader/main.c @@ -435,11 +435,11 @@ static int fw_decompress_xz_pages(struct device *dev, struct fw_priv *fw_priv, /* decompress onto the new allocated page */ page = fw_priv->pages[fw_priv->nr_pages - 1]; - xz_buf.out = kmap(page); + xz_buf.out = kmap_local_page(page); xz_buf.out_pos = 0; xz_buf.out_size = PAGE_SIZE; xz_ret = xz_dec_run(xz_dec, &xz_buf); - kunmap(page); + kunmap_local(xz_buf.out); fw_priv->size += xz_buf.out_pos; /* partial decompression means either end or error */ if (xz_buf.out_pos != PAGE_SIZE) diff --git a/drivers/base/firmware_loader/sysfs.c b/drivers/base/firmware_loader/sysfs.c index 5b0b85b70b6f..77bad32c481a 100644 --- a/drivers/base/firmware_loader/sysfs.c +++ b/drivers/base/firmware_loader/sysfs.c @@ -242,19 +242,17 @@ static void firmware_rw(struct fw_priv *fw_priv, char *buffer, loff_t offset, size_t count, bool read) { while (count) { - void *page_data; int page_nr = offset >> PAGE_SHIFT; int page_ofs = offset & (PAGE_SIZE - 1); int page_cnt = min_t(size_t, PAGE_SIZE - page_ofs, count); - page_data = kmap(fw_priv->pages[page_nr]); - if (read) - memcpy(buffer, page_data + page_ofs, page_cnt); + memcpy_from_page(buffer, fw_priv->pages[page_nr], + page_ofs, page_cnt); else - memcpy(page_data + page_ofs, buffer, page_cnt); + memcpy_to_page(fw_priv->pages[page_nr], page_ofs, + buffer, page_cnt); - kunmap(fw_priv->pages[page_nr]); buffer += page_cnt; offset += page_cnt; count -= page_cnt; diff --git a/drivers/base/node.c b/drivers/base/node.c index 0ac6376ef7a1..eb0f43784c2b 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -45,7 +45,7 @@ static inline ssize_t cpumap_read(struct file *file, struct kobject *kobj, return n; } -static BIN_ATTR_RO(cpumap, 0); +static BIN_ATTR_RO(cpumap, CPUMAP_FILE_MAX_BYTES); static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj, struct bin_attribute *attr, char *buf, @@ -66,7 +66,7 @@ static inline ssize_t cpulist_read(struct file *file, struct kobject *kobj, return n; } -static BIN_ATTR_RO(cpulist, 0); +static BIN_ATTR_RO(cpulist, CPULIST_FILE_MAX_BYTES); /** * struct node_access_nodes - Access class device to hold user visible diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 55a10e6d4e2a..5a2e0232862e 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2733,7 +2733,7 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, mutex_unlock(&gpd_list_lock); dev_dbg(dev, "%s() failed to find PM domain: %ld\n", __func__, PTR_ERR(pd)); - return driver_deferred_probe_check_state(base_dev); + return -ENODEV; } dev_dbg(dev, "adding to PM domain %s\n", pd->name); diff --git a/drivers/base/topology.c b/drivers/base/topology.c index ac6ad9ab67f9..89f98be5c5b9 100644 --- a/drivers/base/topology.c +++ b/drivers/base/topology.c @@ -62,47 +62,47 @@ define_id_show_func(ppin, "0x%llx"); static DEVICE_ATTR_ADMIN_RO(ppin); define_siblings_read_func(thread_siblings, sibling_cpumask); -static BIN_ATTR_RO(thread_siblings, 0); -static BIN_ATTR_RO(thread_siblings_list, 0); +static BIN_ATTR_RO(thread_siblings, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(thread_siblings_list, CPULIST_FILE_MAX_BYTES); define_siblings_read_func(core_cpus, sibling_cpumask); -static BIN_ATTR_RO(core_cpus, 0); -static BIN_ATTR_RO(core_cpus_list, 0); +static BIN_ATTR_RO(core_cpus, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(core_cpus_list, CPULIST_FILE_MAX_BYTES); define_siblings_read_func(core_siblings, core_cpumask); -static BIN_ATTR_RO(core_siblings, 0); -static BIN_ATTR_RO(core_siblings_list, 0); +static BIN_ATTR_RO(core_siblings, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(core_siblings_list, CPULIST_FILE_MAX_BYTES); #ifdef TOPOLOGY_CLUSTER_SYSFS define_siblings_read_func(cluster_cpus, cluster_cpumask); -static BIN_ATTR_RO(cluster_cpus, 0); -static BIN_ATTR_RO(cluster_cpus_list, 0); +static BIN_ATTR_RO(cluster_cpus, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(cluster_cpus_list, CPULIST_FILE_MAX_BYTES); #endif #ifdef TOPOLOGY_DIE_SYSFS define_siblings_read_func(die_cpus, die_cpumask); -static BIN_ATTR_RO(die_cpus, 0); -static BIN_ATTR_RO(die_cpus_list, 0); +static BIN_ATTR_RO(die_cpus, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(die_cpus_list, CPULIST_FILE_MAX_BYTES); #endif define_siblings_read_func(package_cpus, core_cpumask); -static BIN_ATTR_RO(package_cpus, 0); -static BIN_ATTR_RO(package_cpus_list, 0); +static BIN_ATTR_RO(package_cpus, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(package_cpus_list, CPULIST_FILE_MAX_BYTES); #ifdef TOPOLOGY_BOOK_SYSFS define_id_show_func(book_id, "%d"); static DEVICE_ATTR_RO(book_id); define_siblings_read_func(book_siblings, book_cpumask); -static BIN_ATTR_RO(book_siblings, 0); -static BIN_ATTR_RO(book_siblings_list, 0); +static BIN_ATTR_RO(book_siblings, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(book_siblings_list, CPULIST_FILE_MAX_BYTES); #endif #ifdef TOPOLOGY_DRAWER_SYSFS define_id_show_func(drawer_id, "%d"); static DEVICE_ATTR_RO(drawer_id); define_siblings_read_func(drawer_siblings, drawer_cpumask); -static BIN_ATTR_RO(drawer_siblings, 0); -static BIN_ATTR_RO(drawer_siblings_list, 0); +static BIN_ATTR_RO(drawer_siblings, CPUMAP_FILE_MAX_BYTES); +static BIN_ATTR_RO(drawer_siblings_list, CPULIST_FILE_MAX_BYTES); #endif static struct bin_attribute *bin_attrs[] = { diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index e19fcab016ba..db1b4b202646 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -248,15 +248,6 @@ config BLK_DEV_NBD If unsure, say N. -config BLK_DEV_SX8 - tristate "Promise SATA SX8 support" - depends on PCI - help - Saying Y or M here will enable support for the - Promise SATA SX8 controllers. - - Use devices /dev/sx8/$N and /dev/sx8/$Np$M. - config BLK_DEV_RAM tristate "RAM block device support" help diff --git a/drivers/block/Makefile b/drivers/block/Makefile index be631352567e..101612cba303 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -26,8 +26,6 @@ obj-$(CONFIG_SUNVDC) += sunvdc.o obj-$(CONFIG_BLK_DEV_NBD) += nbd.o obj-$(CONFIG_VIRTIO_BLK) += virtio_blk.o -obj-$(CONFIG_BLK_DEV_SX8) += sx8.o - obj-$(CONFIG_XEN_BLKDEV_FRONTEND) += xen-blkfront.o obj-$(CONFIG_XEN_BLKDEV_BACKEND) += xen-blkback/ obj-$(CONFIG_BLK_DEV_DRBD) += drbd/ diff --git a/drivers/block/drbd/drbd_bitmap.c b/drivers/block/drbd/drbd_bitmap.c index 603f6828dd79..7d9db33363de 100644 --- a/drivers/block/drbd/drbd_bitmap.c +++ b/drivers/block/drbd/drbd_bitmap.c @@ -974,25 +974,58 @@ static void drbd_bm_endio(struct bio *bio) } } +/* For the layout, see comment above drbd_md_set_sector_offsets(). */ +static inline sector_t drbd_md_last_bitmap_sector(struct drbd_backing_dev *bdev) +{ + switch (bdev->md.meta_dev_idx) { + case DRBD_MD_INDEX_INTERNAL: + case DRBD_MD_INDEX_FLEX_INT: + return bdev->md.md_offset + bdev->md.al_offset -1; + case DRBD_MD_INDEX_FLEX_EXT: + default: + return bdev->md.md_offset + bdev->md.md_size_sect -1; + } +} + static void bm_page_io_async(struct drbd_bm_aio_ctx *ctx, int page_nr) __must_hold(local) { struct drbd_device *device = ctx->device; enum req_op op = ctx->flags & BM_AIO_READ ? REQ_OP_READ : REQ_OP_WRITE; - struct bio *bio = bio_alloc_bioset(device->ldev->md_bdev, 1, op, - GFP_NOIO, &drbd_md_io_bio_set); struct drbd_bitmap *b = device->bitmap; + struct bio *bio; struct page *page; + sector_t last_bm_sect; + sector_t first_bm_sect; + sector_t on_disk_sector; unsigned int len; - sector_t on_disk_sector = - device->ldev->md.md_offset + device->ldev->md.bm_offset; - on_disk_sector += ((sector_t)page_nr) << (PAGE_SHIFT-9); + first_bm_sect = device->ldev->md.md_offset + device->ldev->md.bm_offset; + on_disk_sector = first_bm_sect + (((sector_t)page_nr) << (PAGE_SHIFT-SECTOR_SHIFT)); /* this might happen with very small * flexible external meta data device, * or with PAGE_SIZE > 4k */ - len = min_t(unsigned int, PAGE_SIZE, - (drbd_md_last_sector(device->ldev) - on_disk_sector + 1)<<9); + last_bm_sect = drbd_md_last_bitmap_sector(device->ldev); + if (first_bm_sect <= on_disk_sector && last_bm_sect >= on_disk_sector) { + sector_t len_sect = last_bm_sect - on_disk_sector + 1; + if (len_sect < PAGE_SIZE/SECTOR_SIZE) + len = (unsigned int)len_sect*SECTOR_SIZE; + else + len = PAGE_SIZE; + } else { + if (__ratelimit(&drbd_ratelimit_state)) { + drbd_err(device, "Invalid offset during on-disk bitmap access: " + "page idx %u, sector %llu\n", page_nr, on_disk_sector); + } + ctx->error = -EIO; + bm_set_page_io_err(b->bm_pages[page_nr]); + if (atomic_dec_and_test(&ctx->in_flight)) { + ctx->done = 1; + wake_up(&device->misc_wait); + kref_put(&ctx->kref, &drbd_bm_aio_ctx_destroy); + } + return; + } /* serialize IO on this page */ bm_page_lock_io(device, page_nr); @@ -1007,6 +1040,8 @@ static void bm_page_io_async(struct drbd_bm_aio_ctx *ctx, int page_nr) __must_ho bm_store_page_idx(page, page_nr); } else page = b->bm_pages[page_nr]; + bio = bio_alloc_bioset(device->ldev->md_bdev, 1, op, GFP_NOIO, + &drbd_md_io_bio_set); bio->bi_iter.bi_sector = on_disk_sector; /* bio_add_page of a single page to an empty bio will always succeed, * according to api. Do we want to assert that? */ diff --git a/drivers/block/drbd/drbd_req.c b/drivers/block/drbd/drbd_req.c index 6d8dd14458c6..8f7f144e54f3 100644 --- a/drivers/block/drbd/drbd_req.c +++ b/drivers/block/drbd/drbd_req.c @@ -1608,7 +1608,7 @@ void drbd_submit_bio(struct bio *bio) { struct drbd_device *device = bio->bi_bdev->bd_disk->private_data; - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); /* * what we "blindly" assume: diff --git a/drivers/block/nbd.c b/drivers/block/nbd.c index f5d098a148cb..2a709daefbc4 100644 --- a/drivers/block/nbd.c +++ b/drivers/block/nbd.c @@ -11,6 +11,8 @@ * (part of code stolen from loop.c) */ +#define pr_fmt(fmt) "nbd: " fmt + #include #include @@ -1950,7 +1952,7 @@ again: test_bit(NBD_DISCONNECT_REQUESTED, &nbd->flags)) || !refcount_inc_not_zero(&nbd->refs)) { mutex_unlock(&nbd_index_mutex); - pr_err("nbd: device at index %d is going down\n", + pr_err("device at index %d is going down\n", index); return -EINVAL; } @@ -1961,7 +1963,7 @@ again: if (!nbd) { nbd = nbd_dev_add(index, 2); if (IS_ERR(nbd)) { - pr_err("nbd: failed to add new device\n"); + pr_err("failed to add new device\n"); return PTR_ERR(nbd); } } diff --git a/drivers/block/null_blk/main.c b/drivers/block/null_blk/main.c index 8b224ede2e33..c451c477978f 100644 --- a/drivers/block/null_blk/main.c +++ b/drivers/block/null_blk/main.c @@ -201,6 +201,22 @@ static bool g_use_per_node_hctx; module_param_named(use_per_node_hctx, g_use_per_node_hctx, bool, 0444); MODULE_PARM_DESC(use_per_node_hctx, "Use per-node allocation for hardware context queues. Default: false"); +static bool g_memory_backed; +module_param_named(memory_backed, g_memory_backed, bool, 0444); +MODULE_PARM_DESC(memory_backed, "Create a memory-backed block device. Default: false"); + +static bool g_discard; +module_param_named(discard, g_discard, bool, 0444); +MODULE_PARM_DESC(discard, "Support discard operations (requires memory-backed null_blk device). Default: false"); + +static unsigned long g_cache_size; +module_param_named(cache_size, g_cache_size, ulong, 0444); +MODULE_PARM_DESC(mbps, "Cache size in MiB for memory-backed device. Default: 0 (none)"); + +static unsigned int g_mbps; +module_param_named(mbps, g_mbps, uint, 0444); +MODULE_PARM_DESC(mbps, "Limit maximum bandwidth (in MiB/s). Default: 0 (no limit)"); + static bool g_zoned; module_param_named(zoned, g_zoned, bool, S_IRUGO); MODULE_PARM_DESC(zoned, "Make device as a host-managed zoned block device. Default: false"); @@ -409,6 +425,8 @@ NULLB_DEVICE_ATTR(zone_nr_conv, uint, NULL); NULLB_DEVICE_ATTR(zone_max_open, uint, NULL); NULLB_DEVICE_ATTR(zone_max_active, uint, NULL); NULLB_DEVICE_ATTR(virt_boundary, bool, NULL); +NULLB_DEVICE_ATTR(no_sched, bool, NULL); +NULLB_DEVICE_ATTR(shared_tag_bitmap, bool, NULL); static ssize_t nullb_device_power_show(struct config_item *item, char *page) { @@ -532,6 +550,8 @@ static struct configfs_attribute *nullb_device_attrs[] = { &nullb_device_attr_zone_max_open, &nullb_device_attr_zone_max_active, &nullb_device_attr_virt_boundary, + &nullb_device_attr_no_sched, + &nullb_device_attr_shared_tag_bitmap, NULL, }; @@ -588,7 +608,13 @@ nullb_group_drop_item(struct config_group *group, struct config_item *item) static ssize_t memb_group_features_show(struct config_item *item, char *page) { return snprintf(page, PAGE_SIZE, - "memory_backed,discard,bandwidth,cache,badblocks,zoned,zone_size,zone_capacity,zone_nr_conv,zone_max_open,zone_max_active,blocksize,max_sectors,virt_boundary\n"); + "badblocks,blocking,blocksize,cache_size," + "completion_nsec,discard,home_node,hw_queue_depth," + "irqmode,max_sectors,mbps,memory_backed,no_sched," + "poll_queues,power,queue_mode,shared_tag_bitmap,size," + "submit_queues,use_per_node_hctx,virt_boundary,zoned," + "zone_capacity,zone_max_active,zone_max_open," + "zone_nr_conv,zone_size\n"); } CONFIGFS_ATTR_RO(memb_group_, features); @@ -650,6 +676,10 @@ static struct nullb_device *null_alloc_dev(void) dev->irqmode = g_irqmode; dev->hw_queue_depth = g_hw_queue_depth; dev->blocking = g_blocking; + dev->memory_backed = g_memory_backed; + dev->discard = g_discard; + dev->cache_size = g_cache_size; + dev->mbps = g_mbps; dev->use_per_node_hctx = g_use_per_node_hctx; dev->zoned = g_zoned; dev->zone_size = g_zone_size; @@ -658,6 +688,8 @@ static struct nullb_device *null_alloc_dev(void) dev->zone_max_open = g_zone_max_open; dev->zone_max_active = g_zone_max_active; dev->virt_boundary = g_virt_boundary; + dev->no_sched = g_no_sched; + dev->shared_tag_bitmap = g_shared_tag_bitmap; return dev; } @@ -1655,7 +1687,7 @@ static blk_status_t null_queue_rq(struct blk_mq_hw_ctx *hctx, static void cleanup_queue(struct nullb_queue *nq) { - kfree(nq->tag_map); + bitmap_free(nq->tag_map); kfree(nq->cmds); } @@ -1782,14 +1814,13 @@ static const struct block_device_operations null_rq_ops = { static int setup_commands(struct nullb_queue *nq) { struct nullb_cmd *cmd; - int i, tag_size; + int i; nq->cmds = kcalloc(nq->queue_depth, sizeof(*cmd), GFP_KERNEL); if (!nq->cmds) return -ENOMEM; - tag_size = ALIGN(nq->queue_depth, BITS_PER_LONG) / BITS_PER_LONG; - nq->tag_map = kcalloc(tag_size, sizeof(unsigned long), GFP_KERNEL); + nq->tag_map = bitmap_zalloc(nq->queue_depth, GFP_KERNEL); if (!nq->tag_map) { kfree(nq->cmds); return -ENOMEM; @@ -1866,31 +1897,48 @@ static int null_gendisk_register(struct nullb *nullb) static int null_init_tag_set(struct nullb *nullb, struct blk_mq_tag_set *set) { + unsigned int flags = BLK_MQ_F_SHOULD_MERGE; + int hw_queues, numa_node; + unsigned int queue_depth; int poll_queues; - set->ops = &null_mq_ops; - set->nr_hw_queues = nullb ? nullb->dev->submit_queues : - g_submit_queues; - poll_queues = nullb ? nullb->dev->poll_queues : g_poll_queues; - if (poll_queues) - set->nr_hw_queues += poll_queues; - set->queue_depth = nullb ? nullb->dev->hw_queue_depth : - g_hw_queue_depth; - set->numa_node = nullb ? nullb->dev->home_node : g_home_node; - set->cmd_size = sizeof(struct nullb_cmd); - set->flags = BLK_MQ_F_SHOULD_MERGE; - if (g_no_sched) - set->flags |= BLK_MQ_F_NO_SCHED; - if (g_shared_tag_bitmap) - set->flags |= BLK_MQ_F_TAG_HCTX_SHARED; - set->driver_data = nullb; - if (poll_queues) - set->nr_maps = 3; - else - set->nr_maps = 1; + if (nullb) { + hw_queues = nullb->dev->submit_queues; + poll_queues = nullb->dev->poll_queues; + queue_depth = nullb->dev->hw_queue_depth; + numa_node = nullb->dev->home_node; + if (nullb->dev->no_sched) + flags |= BLK_MQ_F_NO_SCHED; + if (nullb->dev->shared_tag_bitmap) + flags |= BLK_MQ_F_TAG_HCTX_SHARED; + if (nullb->dev->blocking) + flags |= BLK_MQ_F_BLOCKING; + } else { + hw_queues = g_submit_queues; + poll_queues = g_poll_queues; + queue_depth = g_hw_queue_depth; + numa_node = g_home_node; + if (g_no_sched) + flags |= BLK_MQ_F_NO_SCHED; + if (g_shared_tag_bitmap) + flags |= BLK_MQ_F_TAG_HCTX_SHARED; + if (g_blocking) + flags |= BLK_MQ_F_BLOCKING; + } - if ((nullb && nullb->dev->blocking) || g_blocking) - set->flags |= BLK_MQ_F_BLOCKING; + set->ops = &null_mq_ops; + set->cmd_size = sizeof(struct nullb_cmd); + set->flags = flags; + set->driver_data = nullb; + set->nr_hw_queues = hw_queues; + set->queue_depth = queue_depth; + set->numa_node = numa_node; + if (poll_queues) { + set->nr_hw_queues += poll_queues; + set->nr_maps = 3; + } else { + set->nr_maps = 1; + } return blk_mq_alloc_tag_set(set); } @@ -2042,8 +2090,13 @@ static int null_add_dev(struct nullb_device *dev) blk_queue_flag_clear(QUEUE_FLAG_ADD_RANDOM, nullb->q); mutex_lock(&lock); - nullb->index = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL); - dev->index = nullb->index; + rv = ida_simple_get(&nullb_indexes, 0, 0, GFP_KERNEL); + if (rv < 0) { + mutex_unlock(&lock); + goto out_cleanup_zone; + } + nullb->index = rv; + dev->index = rv; mutex_unlock(&lock); blk_queue_logical_block_size(nullb->q, dev->blocksize); @@ -2069,7 +2122,7 @@ static int null_add_dev(struct nullb_device *dev) rv = null_gendisk_register(nullb); if (rv) - goto out_cleanup_zone; + goto out_ida_free; mutex_lock(&lock); list_add_tail(&nullb->list, &nullb_list); @@ -2078,6 +2131,9 @@ static int null_add_dev(struct nullb_device *dev) pr_info("disk %s created\n", nullb->disk_name); return 0; + +out_ida_free: + ida_free(&nullb_indexes, nullb->index); out_cleanup_zone: null_free_zoned_dev(dev); out_cleanup_disk: diff --git a/drivers/block/null_blk/null_blk.h b/drivers/block/null_blk/null_blk.h index 6fbf0a1b2622..94ff68052b1e 100644 --- a/drivers/block/null_blk/null_blk.h +++ b/drivers/block/null_blk/null_blk.h @@ -113,6 +113,8 @@ struct nullb_device { bool discard; /* if support discard */ bool zoned; /* if device is zoned */ bool virt_boundary; /* virtual boundary on/off for the device */ + bool no_sched; /* no IO scheduler for the device */ + bool shared_tag_bitmap; /* use hostwide shared tags */ }; struct nullb { diff --git a/drivers/block/pktcdvd.c b/drivers/block/pktcdvd.c index 01a15dbd9cde..4cea3b08087e 100644 --- a/drivers/block/pktcdvd.c +++ b/drivers/block/pktcdvd.c @@ -2399,7 +2399,7 @@ static void pkt_submit_bio(struct bio *bio) struct pktcdvd_device *pd = bio->bi_bdev->bd_disk->queue->queuedata; struct bio *split; - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); pkt_dbg(2, pd, "start = %6llx stop = %6llx\n", (unsigned long long)bio->bi_iter.bi_sector, diff --git a/drivers/block/ps3vram.c b/drivers/block/ps3vram.c index d1e0fefec90b..e1d080f680ed 100644 --- a/drivers/block/ps3vram.c +++ b/drivers/block/ps3vram.c @@ -586,7 +586,7 @@ static void ps3vram_submit_bio(struct bio *bio) dev_dbg(&dev->core, "%s\n", __func__); - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); spin_lock_irq(&priv->lock); busy = !bio_list_empty(&priv->list); diff --git a/drivers/block/rnbd/rnbd-clt-sysfs.c b/drivers/block/rnbd/rnbd-clt-sysfs.c index 2be5d87a3ca6..e7c7d9a68168 100644 --- a/drivers/block/rnbd/rnbd-clt-sysfs.c +++ b/drivers/block/rnbd/rnbd-clt-sysfs.c @@ -376,7 +376,7 @@ static ssize_t rnbd_clt_resize_dev_store(struct kobject *kobj, if (ret) return ret; - ret = rnbd_clt_resize_disk(dev, (size_t)sectors); + ret = rnbd_clt_resize_disk(dev, sectors); if (ret) return ret; diff --git a/drivers/block/rnbd/rnbd-clt.c b/drivers/block/rnbd/rnbd-clt.c index b8d9e2824d9c..04da33a22ef4 100644 --- a/drivers/block/rnbd/rnbd-clt.c +++ b/drivers/block/rnbd/rnbd-clt.c @@ -68,39 +68,18 @@ static inline bool rnbd_clt_get_dev(struct rnbd_clt_dev *dev) return refcount_inc_not_zero(&dev->refcount); } -static int rnbd_clt_set_dev_attr(struct rnbd_clt_dev *dev, - const struct rnbd_msg_open_rsp *rsp) +static void rnbd_clt_change_capacity(struct rnbd_clt_dev *dev, + sector_t new_nsectors) { - struct rnbd_clt_session *sess = dev->sess; + if (get_capacity(dev->gd) == new_nsectors) + return; - if (!rsp->logical_block_size) - return -EINVAL; - - dev->device_id = le32_to_cpu(rsp->device_id); - dev->nsectors = le64_to_cpu(rsp->nsectors); - dev->logical_block_size = le16_to_cpu(rsp->logical_block_size); - dev->physical_block_size = le16_to_cpu(rsp->physical_block_size); - dev->max_discard_sectors = le32_to_cpu(rsp->max_discard_sectors); - dev->discard_granularity = le32_to_cpu(rsp->discard_granularity); - dev->discard_alignment = le32_to_cpu(rsp->discard_alignment); - dev->secure_discard = le16_to_cpu(rsp->secure_discard); - dev->wc = !!(rsp->cache_policy & RNBD_WRITEBACK); - dev->fua = !!(rsp->cache_policy & RNBD_FUA); - - dev->max_hw_sectors = sess->max_io_size / SECTOR_SIZE; - dev->max_segments = sess->max_segments; - - return 0; -} - -static int rnbd_clt_change_capacity(struct rnbd_clt_dev *dev, - size_t new_nsectors) -{ - rnbd_clt_info(dev, "Device size changed from %zu to %zu sectors\n", - dev->nsectors, new_nsectors); - dev->nsectors = new_nsectors; - set_capacity_and_notify(dev->gd, dev->nsectors); - return 0; + /* + * If the size changed, we need to revalidate it + */ + rnbd_clt_info(dev, "Device size changed from %llu to %llu sectors\n", + get_capacity(dev->gd), new_nsectors); + set_capacity_and_notify(dev->gd, new_nsectors); } static int process_msg_open_rsp(struct rnbd_clt_dev *dev, @@ -119,19 +98,16 @@ static int process_msg_open_rsp(struct rnbd_clt_dev *dev, if (dev->dev_state == DEV_STATE_MAPPED_DISCONNECTED) { u64 nsectors = le64_to_cpu(rsp->nsectors); - /* - * If the device was remapped and the size changed in the - * meantime we need to revalidate it - */ - if (dev->nsectors != nsectors) - rnbd_clt_change_capacity(dev, nsectors); + rnbd_clt_change_capacity(dev, nsectors); gd_kobj = &disk_to_dev(dev->gd)->kobj; kobject_uevent(gd_kobj, KOBJ_ONLINE); rnbd_clt_info(dev, "Device online, device remapped successfully\n"); } - err = rnbd_clt_set_dev_attr(dev, rsp); - if (err) + if (!rsp->logical_block_size) { + err = -EINVAL; goto out; + } + dev->device_id = le32_to_cpu(rsp->device_id); dev->dev_state = DEV_STATE_MAPPED; out: @@ -140,7 +116,7 @@ out: return err; } -int rnbd_clt_resize_disk(struct rnbd_clt_dev *dev, size_t newsize) +int rnbd_clt_resize_disk(struct rnbd_clt_dev *dev, sector_t newsize) { int ret = 0; @@ -150,7 +126,7 @@ int rnbd_clt_resize_disk(struct rnbd_clt_dev *dev, size_t newsize) ret = -ENOENT; goto out; } - ret = rnbd_clt_change_capacity(dev, newsize); + rnbd_clt_change_capacity(dev, newsize); out: mutex_unlock(&dev->lock); @@ -507,6 +483,11 @@ static void msg_open_conf(struct work_struct *work) struct rnbd_msg_open_rsp *rsp = iu->buf; struct rnbd_clt_dev *dev = iu->dev; int errno = iu->errno; + bool from_map = false; + + /* INIT state is only triggered from rnbd_clt_map_device */ + if (dev->dev_state == DEV_STATE_INIT) + from_map = true; if (errno) { rnbd_clt_err(dev, @@ -523,7 +504,9 @@ static void msg_open_conf(struct work_struct *work) send_msg_close(dev, device_id, RTRS_PERMIT_NOWAIT); } } - kfree(rsp); + /* We free rsp in rnbd_clt_map_device for map scenario */ + if (!from_map) + kfree(rsp); wake_up_iu_comp(iu, errno); rnbd_put_iu(dev->sess, iu); rnbd_clt_put_dev(dev); @@ -942,7 +925,7 @@ static int rnbd_client_open(struct block_device *block_device, fmode_t mode) { struct rnbd_clt_dev *dev = block_device->bd_disk->private_data; - if (dev->read_only && (mode & FMODE_WRITE)) + if (get_disk_ro(dev->gd) && (mode & FMODE_WRITE)) return -EPERM; if (dev->dev_state == DEV_STATE_UNMAPPED || @@ -963,10 +946,10 @@ static int rnbd_client_getgeo(struct block_device *block_device, struct hd_geometry *geo) { u64 size; - struct rnbd_clt_dev *dev; + struct rnbd_clt_dev *dev = block_device->bd_disk->private_data; + struct queue_limits *limit = &dev->queue->limits; - dev = block_device->bd_disk->private_data; - size = dev->size * (dev->logical_block_size / SECTOR_SIZE); + size = dev->size * (limit->logical_block_size / SECTOR_SIZE); geo->cylinders = size >> 6; /* size/64 */ geo->heads = 4; geo->sectors = 16; @@ -1350,11 +1333,15 @@ static void rnbd_init_mq_hw_queues(struct rnbd_clt_dev *dev) } } -static void setup_request_queue(struct rnbd_clt_dev *dev) +static void setup_request_queue(struct rnbd_clt_dev *dev, + struct rnbd_msg_open_rsp *rsp) { - blk_queue_logical_block_size(dev->queue, dev->logical_block_size); - blk_queue_physical_block_size(dev->queue, dev->physical_block_size); - blk_queue_max_hw_sectors(dev->queue, dev->max_hw_sectors); + blk_queue_logical_block_size(dev->queue, + le16_to_cpu(rsp->logical_block_size)); + blk_queue_physical_block_size(dev->queue, + le16_to_cpu(rsp->physical_block_size)); + blk_queue_max_hw_sectors(dev->queue, + dev->sess->max_io_size / SECTOR_SIZE); /* * we don't support discards to "discontiguous" segments @@ -1362,21 +1349,27 @@ static void setup_request_queue(struct rnbd_clt_dev *dev) */ blk_queue_max_discard_segments(dev->queue, 1); - blk_queue_max_discard_sectors(dev->queue, dev->max_discard_sectors); - dev->queue->limits.discard_granularity = dev->discard_granularity; - dev->queue->limits.discard_alignment = dev->discard_alignment; - if (dev->secure_discard) + blk_queue_max_discard_sectors(dev->queue, + le32_to_cpu(rsp->max_discard_sectors)); + dev->queue->limits.discard_granularity = + le32_to_cpu(rsp->discard_granularity); + dev->queue->limits.discard_alignment = + le32_to_cpu(rsp->discard_alignment); + if (le16_to_cpu(rsp->secure_discard)) blk_queue_max_secure_erase_sectors(dev->queue, - dev->max_discard_sectors); + le32_to_cpu(rsp->max_discard_sectors)); blk_queue_flag_set(QUEUE_FLAG_SAME_COMP, dev->queue); blk_queue_flag_set(QUEUE_FLAG_SAME_FORCE, dev->queue); - blk_queue_max_segments(dev->queue, dev->max_segments); + blk_queue_max_segments(dev->queue, dev->sess->max_segments); blk_queue_io_opt(dev->queue, dev->sess->max_io_size); blk_queue_virt_boundary(dev->queue, SZ_4K - 1); - blk_queue_write_cache(dev->queue, dev->wc, dev->fua); + blk_queue_write_cache(dev->queue, + !!(rsp->cache_policy & RNBD_WRITEBACK), + !!(rsp->cache_policy & RNBD_FUA)); } -static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev, int idx) +static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev, + struct rnbd_msg_open_rsp *rsp, int idx) { int err; @@ -1388,19 +1381,15 @@ static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev, int idx) dev->gd->private_data = dev; snprintf(dev->gd->disk_name, sizeof(dev->gd->disk_name), "rnbd%d", idx); - pr_debug("disk_name=%s, capacity=%zu\n", + pr_debug("disk_name=%s, capacity=%llu\n", dev->gd->disk_name, - dev->nsectors * (dev->logical_block_size / SECTOR_SIZE) - ); + le64_to_cpu(rsp->nsectors) * + (le16_to_cpu(rsp->logical_block_size) / SECTOR_SIZE)); - set_capacity(dev->gd, dev->nsectors); + set_capacity(dev->gd, le64_to_cpu(rsp->nsectors)); - if (dev->access_mode == RNBD_ACCESS_RO) { - dev->read_only = true; + if (dev->access_mode == RNBD_ACCESS_RO) set_disk_ro(dev->gd, true); - } else { - dev->read_only = false; - } /* * Network device does not need rotational @@ -1413,11 +1402,13 @@ static int rnbd_clt_setup_gen_disk(struct rnbd_clt_dev *dev, int idx) return err; } -static int rnbd_client_setup_device(struct rnbd_clt_dev *dev) +static int rnbd_client_setup_device(struct rnbd_clt_dev *dev, + struct rnbd_msg_open_rsp *rsp) { int idx = dev->clt_device_id; - dev->size = dev->nsectors * dev->logical_block_size; + dev->size = le64_to_cpu(rsp->nsectors) * + le16_to_cpu(rsp->logical_block_size); dev->gd = blk_mq_alloc_disk(&dev->sess->tag_set, dev); if (IS_ERR(dev->gd)) @@ -1425,8 +1416,8 @@ static int rnbd_client_setup_device(struct rnbd_clt_dev *dev) dev->queue = dev->gd->queue; rnbd_init_mq_hw_queues(dev); - setup_request_queue(dev); - return rnbd_clt_setup_gen_disk(dev, idx); + setup_request_queue(dev, rsp); + return rnbd_clt_setup_gen_disk(dev, rsp, idx); } static struct rnbd_clt_dev *init_dev(struct rnbd_clt_session *sess, @@ -1562,7 +1553,14 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname, { struct rnbd_clt_session *sess; struct rnbd_clt_dev *dev; - int ret; + int ret, errno; + struct rnbd_msg_open_rsp *rsp; + struct rnbd_msg_open msg; + struct rnbd_iu *iu; + struct kvec vec = { + .iov_base = &msg, + .iov_len = sizeof(msg) + }; if (exists_devpath(pathname, sessname)) return ERR_PTR(-EEXIST); @@ -1582,17 +1580,47 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname, ret = -EEXIST; goto put_dev; } - ret = send_msg_open(dev, RTRS_PERMIT_WAIT); + + rsp = kzalloc(sizeof(*rsp), GFP_KERNEL); + if (!rsp) { + ret = -ENOMEM; + goto del_dev; + } + + iu = rnbd_get_iu(sess, RTRS_ADMIN_CON, RTRS_PERMIT_WAIT); + if (!iu) { + ret = -ENOMEM; + kfree(rsp); + goto del_dev; + } + iu->buf = rsp; + iu->dev = dev; + sg_init_one(iu->sgt.sgl, rsp, sizeof(*rsp)); + + msg.hdr.type = cpu_to_le16(RNBD_MSG_OPEN); + msg.access_mode = dev->access_mode; + strscpy(msg.dev_name, dev->pathname, sizeof(msg.dev_name)); + + WARN_ON(!rnbd_clt_get_dev(dev)); + ret = send_usr_msg(sess->rtrs, READ, iu, + &vec, sizeof(*rsp), iu->sgt.sgl, 1, + msg_open_conf, &errno, RTRS_PERMIT_WAIT); + if (ret) { + rnbd_clt_put_dev(dev); + rnbd_put_iu(sess, iu); + } else { + ret = errno; + } if (ret) { rnbd_clt_err(dev, "map_device: failed, can't open remote device, err: %d\n", ret); - goto del_dev; + goto put_iu; } mutex_lock(&dev->lock); pr_debug("Opened remote device: session=%s, path='%s'\n", sess->sessname, pathname); - ret = rnbd_client_setup_device(dev); + ret = rnbd_client_setup_device(dev, rsp); if (ret) { rnbd_clt_err(dev, "map_device: Failed to configure device, err: %d\n", @@ -1602,21 +1630,30 @@ struct rnbd_clt_dev *rnbd_clt_map_device(const char *sessname, } rnbd_clt_info(dev, - "map_device: Device mapped as %s (nsectors: %zu, logical_block_size: %d, physical_block_size: %d, max_discard_sectors: %d, discard_granularity: %d, discard_alignment: %d, secure_discard: %d, max_segments: %d, max_hw_sectors: %d, wc: %d, fua: %d)\n", - dev->gd->disk_name, dev->nsectors, - dev->logical_block_size, dev->physical_block_size, - dev->max_discard_sectors, - dev->discard_granularity, dev->discard_alignment, - dev->secure_discard, dev->max_segments, - dev->max_hw_sectors, dev->wc, dev->fua); + "map_device: Device mapped as %s (nsectors: %llu, logical_block_size: %d, physical_block_size: %d, max_discard_sectors: %d, discard_granularity: %d, discard_alignment: %d, secure_discard: %d, max_segments: %d, max_hw_sectors: %d, wc: %d, fua: %d)\n", + dev->gd->disk_name, le64_to_cpu(rsp->nsectors), + le16_to_cpu(rsp->logical_block_size), + le16_to_cpu(rsp->physical_block_size), + le32_to_cpu(rsp->max_discard_sectors), + le32_to_cpu(rsp->discard_granularity), + le32_to_cpu(rsp->discard_alignment), + le16_to_cpu(rsp->secure_discard), + sess->max_segments, sess->max_io_size / SECTOR_SIZE, + !!(rsp->cache_policy & RNBD_WRITEBACK), + !!(rsp->cache_policy & RNBD_FUA)); mutex_unlock(&dev->lock); + kfree(rsp); + rnbd_put_iu(sess, iu); rnbd_clt_put_sess(sess); return dev; send_close: send_msg_close(dev, dev->device_id, RTRS_PERMIT_WAIT); +put_iu: + kfree(rsp); + rnbd_put_iu(sess, iu); del_dev: delete_dev(dev); put_dev: diff --git a/drivers/block/rnbd/rnbd-clt.h b/drivers/block/rnbd/rnbd-clt.h index 2e2e8c4a85c1..a48e040abe63 100644 --- a/drivers/block/rnbd/rnbd-clt.h +++ b/drivers/block/rnbd/rnbd-clt.h @@ -106,6 +106,7 @@ struct rnbd_queue { }; struct rnbd_clt_dev { + struct kobject kobj; struct rnbd_clt_session *sess; struct request_queue *queue; struct rnbd_queue *hw_queues; @@ -114,27 +115,14 @@ struct rnbd_clt_dev { u32 clt_device_id; struct mutex lock; enum rnbd_clt_dev_state dev_state; + refcount_t refcount; char *pathname; enum rnbd_access_mode access_mode; u32 nr_poll_queues; - bool read_only; - bool wc; - bool fua; - u32 max_hw_sectors; - u32 max_discard_sectors; - u32 discard_granularity; - u32 discard_alignment; - u16 secure_discard; - u16 physical_block_size; - u16 logical_block_size; - u16 max_segments; - size_t nsectors; u64 size; /* device size in bytes */ struct list_head list; struct gendisk *gd; - struct kobject kobj; char *blk_symlink_name; - refcount_t refcount; struct work_struct unmap_on_rmmod_work; }; @@ -150,7 +138,7 @@ int rnbd_clt_unmap_device(struct rnbd_clt_dev *dev, bool force, const struct attribute *sysfs_self); int rnbd_clt_remap_device(struct rnbd_clt_dev *dev); -int rnbd_clt_resize_disk(struct rnbd_clt_dev *dev, size_t newsize); +int rnbd_clt_resize_disk(struct rnbd_clt_dev *dev, sector_t newsize); /* rnbd-clt-sysfs.c */ diff --git a/drivers/block/rnbd/rnbd-srv.c b/drivers/block/rnbd/rnbd-srv.c index 0713014bf423..5e08da277ddf 100644 --- a/drivers/block/rnbd/rnbd-srv.c +++ b/drivers/block/rnbd/rnbd-srv.c @@ -224,7 +224,6 @@ void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev, bool keep_id) wait_for_completion(&dc); /* wait for inflights to drop to zero */ rnbd_dev_close(sess_dev->rnbd_dev); - list_del(&sess_dev->sess_list); mutex_lock(&sess_dev->dev->lock); list_del(&sess_dev->dev_list); if (sess_dev->open_flags & FMODE_WRITE) @@ -239,14 +238,14 @@ void rnbd_destroy_sess_dev(struct rnbd_srv_sess_dev *sess_dev, bool keep_id) static void destroy_sess(struct rnbd_srv_session *srv_sess) { - struct rnbd_srv_sess_dev *sess_dev, *tmp; + struct rnbd_srv_sess_dev *sess_dev; + unsigned long index; - if (list_empty(&srv_sess->sess_dev_list)) + if (xa_empty(&srv_sess->index_idr)) goto out; mutex_lock(&srv_sess->lock); - list_for_each_entry_safe(sess_dev, tmp, &srv_sess->sess_dev_list, - sess_list) + xa_for_each(&srv_sess->index_idr, index, sess_dev) rnbd_srv_destroy_dev_session_sysfs(sess_dev); mutex_unlock(&srv_sess->lock); @@ -281,7 +280,6 @@ static int create_sess(struct rtrs_srv_sess *rtrs) srv_sess->queue_depth = rtrs_srv_get_queue_depth(rtrs); xa_init_flags(&srv_sess->index_idr, XA_FLAGS_ALLOC); - INIT_LIST_HEAD(&srv_sess->sess_dev_list); mutex_init(&srv_sess->lock); mutex_lock(&sess_lock); list_add(&srv_sess->list, &sess_list); @@ -323,10 +321,11 @@ void rnbd_srv_sess_dev_force_close(struct rnbd_srv_sess_dev *sess_dev, { struct rnbd_srv_session *sess = sess_dev->sess; - sess_dev->keep_id = true; /* It is already started to close by client's close message. */ if (!mutex_trylock(&sess->lock)) return; + + sess_dev->keep_id = true; /* first remove sysfs itself to avoid deadlock */ sysfs_remove_file_self(&sess_dev->kobj, &attr->attr); rnbd_srv_destroy_dev_session_sysfs(sess_dev); @@ -666,11 +665,12 @@ static struct rnbd_srv_sess_dev * find_srv_sess_dev(struct rnbd_srv_session *srv_sess, const char *dev_name) { struct rnbd_srv_sess_dev *sess_dev; + unsigned long index; - if (list_empty(&srv_sess->sess_dev_list)) + if (xa_empty(&srv_sess->index_idr)) return NULL; - list_for_each_entry(sess_dev, &srv_sess->sess_dev_list, sess_list) + xa_for_each(&srv_sess->index_idr, index, sess_dev) if (!strcmp(sess_dev->pathname, dev_name)) return sess_dev; @@ -780,8 +780,6 @@ static int process_msg_open(struct rnbd_srv_session *srv_sess, list_add(&srv_sess_dev->dev_list, &srv_dev->sess_dev_list); mutex_unlock(&srv_dev->lock); - list_add(&srv_sess_dev->sess_list, &srv_sess->sess_dev_list); - rnbd_srv_info(srv_sess_dev, "Opened device '%s'\n", srv_dev->id); kfree(full_path); diff --git a/drivers/block/rnbd/rnbd-srv.h b/drivers/block/rnbd/rnbd-srv.h index 6926f9069dc4..081bceaf4ae9 100644 --- a/drivers/block/rnbd/rnbd-srv.h +++ b/drivers/block/rnbd/rnbd-srv.h @@ -25,8 +25,6 @@ struct rnbd_srv_session { int queue_depth; struct xarray index_idr; - /* List of struct rnbd_srv_sess_dev */ - struct list_head sess_dev_list; struct mutex lock; u8 ver; }; @@ -48,8 +46,6 @@ struct rnbd_srv_dev { struct rnbd_srv_sess_dev { /* Entry inside rnbd_srv_dev struct */ struct list_head dev_list; - /* Entry inside rnbd_srv_session struct */ - struct list_head sess_list; struct rnbd_dev *rnbd_dev; struct rnbd_srv_session *sess; struct rnbd_srv_dev *dev; diff --git a/drivers/block/sx8.c b/drivers/block/sx8.c deleted file mode 100644 index 0e1a484cab0b..000000000000 --- a/drivers/block/sx8.c +++ /dev/null @@ -1,1582 +0,0 @@ -/* - * sx8.c: Driver for Promise SATA SX8 looks-like-I2O hardware - * - * Copyright 2004-2005 Red Hat, Inc. - * - * Author/maintainer: Jeff Garzik - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#if 0 -#define CARM_DEBUG -#define CARM_VERBOSE_DEBUG -#else -#undef CARM_DEBUG -#undef CARM_VERBOSE_DEBUG -#endif -#undef CARM_NDEBUG - -#define DRV_NAME "sx8" -#define DRV_VERSION "1.0" -#define PFX DRV_NAME ": " - -MODULE_AUTHOR("Jeff Garzik"); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Promise SATA SX8 block driver"); -MODULE_VERSION(DRV_VERSION); - -/* - * SX8 hardware has a single message queue for all ATA ports. - * When this driver was written, the hardware (firmware?) would - * corrupt data eventually, if more than one request was outstanding. - * As one can imagine, having 8 ports bottlenecking on a single - * command hurts performance. - * - * Based on user reports, later versions of the hardware (firmware?) - * seem to be able to survive with more than one command queued. - * - * Therefore, we default to the safe option -- 1 command -- but - * allow the user to increase this. - * - * SX8 should be able to support up to ~60 queued commands (CARM_MAX_REQ), - * but problems seem to occur when you exceed ~30, even on newer hardware. - */ -static int max_queue = 1; -module_param(max_queue, int, 0444); -MODULE_PARM_DESC(max_queue, "Maximum number of queued commands. (min==1, max==30, safe==1)"); - - -#define NEXT_RESP(idx) ((idx + 1) % RMSG_Q_LEN) - -/* 0xf is just arbitrary, non-zero noise; this is sorta like poisoning */ -#define TAG_ENCODE(tag) (((tag) << 16) | 0xf) -#define TAG_DECODE(tag) (((tag) >> 16) & 0x1f) -#define TAG_VALID(tag) ((((tag) & 0xf) == 0xf) && (TAG_DECODE(tag) < 32)) - -/* note: prints function name for you */ -#ifdef CARM_DEBUG -#define DPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args) -#ifdef CARM_VERBOSE_DEBUG -#define VPRINTK(fmt, args...) printk(KERN_ERR "%s: " fmt, __func__, ## args) -#else -#define VPRINTK(fmt, args...) -#endif /* CARM_VERBOSE_DEBUG */ -#else -#define DPRINTK(fmt, args...) -#define VPRINTK(fmt, args...) -#endif /* CARM_DEBUG */ - -#ifdef CARM_NDEBUG -#define assert(expr) -#else -#define assert(expr) \ - if(unlikely(!(expr))) { \ - printk(KERN_ERR "Assertion failed! %s,%s,%s,line=%d\n", \ - #expr, __FILE__, __func__, __LINE__); \ - } -#endif - -/* defines only for the constants which don't work well as enums */ -struct carm_host; - -enum { - /* adapter-wide limits */ - CARM_MAX_PORTS = 8, - CARM_SHM_SIZE = (4096 << 7), - CARM_MINORS_PER_MAJOR = 256 / CARM_MAX_PORTS, - CARM_MAX_WAIT_Q = CARM_MAX_PORTS + 1, - - /* command message queue limits */ - CARM_MAX_REQ = 64, /* max command msgs per host */ - CARM_MSG_LOW_WATER = (CARM_MAX_REQ / 4), /* refill mark */ - - /* S/G limits, host-wide and per-request */ - CARM_MAX_REQ_SG = 32, /* max s/g entries per request */ - CARM_MAX_HOST_SG = 600, /* max s/g entries per host */ - CARM_SG_LOW_WATER = (CARM_MAX_HOST_SG / 4), /* re-fill mark */ - - /* hardware registers */ - CARM_IHQP = 0x1c, - CARM_INT_STAT = 0x10, /* interrupt status */ - CARM_INT_MASK = 0x14, /* interrupt mask */ - CARM_HMUC = 0x18, /* host message unit control */ - RBUF_ADDR_LO = 0x20, /* response msg DMA buf low 32 bits */ - RBUF_ADDR_HI = 0x24, /* response msg DMA buf high 32 bits */ - RBUF_BYTE_SZ = 0x28, - CARM_RESP_IDX = 0x2c, - CARM_CMS0 = 0x30, /* command message size reg 0 */ - CARM_LMUC = 0x48, - CARM_HMPHA = 0x6c, - CARM_INITC = 0xb5, - - /* bits in CARM_INT_{STAT,MASK} */ - INT_RESERVED = 0xfffffff0, - INT_WATCHDOG = (1 << 3), /* watchdog timer */ - INT_Q_OVERFLOW = (1 << 2), /* cmd msg q overflow */ - INT_Q_AVAILABLE = (1 << 1), /* cmd msg q has free space */ - INT_RESPONSE = (1 << 0), /* response msg available */ - INT_ACK_MASK = INT_WATCHDOG | INT_Q_OVERFLOW, - INT_DEF_MASK = INT_RESERVED | INT_Q_OVERFLOW | - INT_RESPONSE, - - /* command messages, and related register bits */ - CARM_HAVE_RESP = 0x01, - CARM_MSG_READ = 1, - CARM_MSG_WRITE = 2, - CARM_MSG_VERIFY = 3, - CARM_MSG_GET_CAPACITY = 4, - CARM_MSG_FLUSH = 5, - CARM_MSG_IOCTL = 6, - CARM_MSG_ARRAY = 8, - CARM_MSG_MISC = 9, - CARM_CME = (1 << 2), - CARM_RME = (1 << 1), - CARM_WZBC = (1 << 0), - CARM_RMI = (1 << 0), - CARM_Q_FULL = (1 << 3), - CARM_MSG_SIZE = 288, - CARM_Q_LEN = 48, - - /* CARM_MSG_IOCTL messages */ - CARM_IOC_SCAN_CHAN = 5, /* scan channels for devices */ - CARM_IOC_GET_TCQ = 13, /* get tcq/ncq depth */ - CARM_IOC_SET_TCQ = 14, /* set tcq/ncq depth */ - - IOC_SCAN_CHAN_NODEV = 0x1f, - IOC_SCAN_CHAN_OFFSET = 0x40, - - /* CARM_MSG_ARRAY messages */ - CARM_ARRAY_INFO = 0, - - ARRAY_NO_EXIST = (1 << 31), - - /* response messages */ - RMSG_SZ = 8, /* sizeof(struct carm_response) */ - RMSG_Q_LEN = 48, /* resp. msg list length */ - RMSG_OK = 1, /* bit indicating msg was successful */ - /* length of entire resp. msg buffer */ - RBUF_LEN = RMSG_SZ * RMSG_Q_LEN, - - PDC_SHM_SIZE = (4096 << 7), /* length of entire h/w buffer */ - - /* CARM_MSG_MISC messages */ - MISC_GET_FW_VER = 2, - MISC_ALLOC_MEM = 3, - MISC_SET_TIME = 5, - - /* MISC_GET_FW_VER feature bits */ - FW_VER_4PORT = (1 << 2), /* 1=4 ports, 0=8 ports */ - FW_VER_NON_RAID = (1 << 1), /* 1=non-RAID firmware, 0=RAID */ - FW_VER_ZCR = (1 << 0), /* zero channel RAID (whatever that is) */ - - /* carm_host flags */ - FL_NON_RAID = FW_VER_NON_RAID, - FL_4PORT = FW_VER_4PORT, - FL_FW_VER_MASK = (FW_VER_NON_RAID | FW_VER_4PORT), - FL_DYN_MAJOR = (1 << 17), -}; - -enum { - CARM_SG_BOUNDARY = 0xffffUL, /* s/g segment boundary */ -}; - -enum scatter_gather_types { - SGT_32BIT = 0, - SGT_64BIT = 1, -}; - -enum host_states { - HST_INVALID, /* invalid state; never used */ - HST_ALLOC_BUF, /* setting up master SHM area */ - HST_ERROR, /* we never leave here */ - HST_PORT_SCAN, /* start dev scan */ - HST_DEV_SCAN_START, /* start per-device probe */ - HST_DEV_SCAN, /* continue per-device probe */ - HST_DEV_ACTIVATE, /* activate devices we found */ - HST_PROBE_FINISHED, /* probe is complete */ - HST_PROBE_START, /* initiate probe */ - HST_SYNC_TIME, /* tell firmware what time it is */ - HST_GET_FW_VER, /* get firmware version, adapter port cnt */ -}; - -#ifdef CARM_DEBUG -static const char *state_name[] = { - "HST_INVALID", - "HST_ALLOC_BUF", - "HST_ERROR", - "HST_PORT_SCAN", - "HST_DEV_SCAN_START", - "HST_DEV_SCAN", - "HST_DEV_ACTIVATE", - "HST_PROBE_FINISHED", - "HST_PROBE_START", - "HST_SYNC_TIME", - "HST_GET_FW_VER", -}; -#endif - -struct carm_port { - unsigned int port_no; - struct gendisk *disk; - struct carm_host *host; - - /* attached device characteristics */ - u64 capacity; - char name[41]; - u16 dev_geom_head; - u16 dev_geom_sect; - u16 dev_geom_cyl; -}; - -struct carm_request { - int n_elem; - unsigned int msg_type; - unsigned int msg_subtype; - unsigned int msg_bucket; - struct scatterlist sg[CARM_MAX_REQ_SG]; -}; - -struct carm_host { - unsigned long flags; - void __iomem *mmio; - void *shm; - dma_addr_t shm_dma; - - int major; - int id; - char name[32]; - - spinlock_t lock; - struct pci_dev *pdev; - unsigned int state; - u32 fw_ver; - - struct blk_mq_tag_set tag_set; - struct request_queue *oob_q; - unsigned int n_oob; - - unsigned int hw_sg_used; - - unsigned int resp_idx; - - unsigned int wait_q_prod; - unsigned int wait_q_cons; - struct request_queue *wait_q[CARM_MAX_WAIT_Q]; - - void *msg_base; - dma_addr_t msg_dma; - - int cur_scan_dev; - unsigned long dev_active; - unsigned long dev_present; - struct carm_port port[CARM_MAX_PORTS]; - - struct work_struct fsm_task; - - int probe_err; - struct completion probe_comp; -}; - -struct carm_response { - __le32 ret_handle; - __le32 status; -} __attribute__((packed)); - -struct carm_msg_sg { - __le32 start; - __le32 len; -} __attribute__((packed)); - -struct carm_msg_rw { - u8 type; - u8 id; - u8 sg_count; - u8 sg_type; - __le32 handle; - __le32 lba; - __le16 lba_count; - __le16 lba_high; - struct carm_msg_sg sg[32]; -} __attribute__((packed)); - -struct carm_msg_allocbuf { - u8 type; - u8 subtype; - u8 n_sg; - u8 sg_type; - __le32 handle; - __le32 addr; - __le32 len; - __le32 evt_pool; - __le32 n_evt; - __le32 rbuf_pool; - __le32 n_rbuf; - __le32 msg_pool; - __le32 n_msg; - struct carm_msg_sg sg[8]; -} __attribute__((packed)); - -struct carm_msg_ioctl { - u8 type; - u8 subtype; - u8 array_id; - u8 reserved1; - __le32 handle; - __le32 data_addr; - u32 reserved2; -} __attribute__((packed)); - -struct carm_msg_sync_time { - u8 type; - u8 subtype; - u16 reserved1; - __le32 handle; - u32 reserved2; - __le32 timestamp; -} __attribute__((packed)); - -struct carm_msg_get_fw_ver { - u8 type; - u8 subtype; - u16 reserved1; - __le32 handle; - __le32 data_addr; - u32 reserved2; -} __attribute__((packed)); - -struct carm_fw_ver { - __le32 version; - u8 features; - u8 reserved1; - u16 reserved2; -} __attribute__((packed)); - -struct carm_array_info { - __le32 size; - - __le16 size_hi; - __le16 stripe_size; - - __le32 mode; - - __le16 stripe_blk_sz; - __le16 reserved1; - - __le16 cyl; - __le16 head; - - __le16 sect; - u8 array_id; - u8 reserved2; - - char name[40]; - - __le32 array_status; - - /* device list continues beyond this point? */ -} __attribute__((packed)); - -static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent); -static void carm_remove_one (struct pci_dev *pdev); -static int carm_bdev_getgeo(struct block_device *bdev, struct hd_geometry *geo); - -static const struct pci_device_id carm_pci_tbl[] = { - { PCI_VENDOR_ID_PROMISE, 0x8000, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - { PCI_VENDOR_ID_PROMISE, 0x8002, PCI_ANY_ID, PCI_ANY_ID, 0, 0, }, - { } /* terminate list */ -}; -MODULE_DEVICE_TABLE(pci, carm_pci_tbl); - -static struct pci_driver carm_driver = { - .name = DRV_NAME, - .id_table = carm_pci_tbl, - .probe = carm_init_one, - .remove = carm_remove_one, -}; - -static const struct block_device_operations carm_bd_ops = { - .owner = THIS_MODULE, - .getgeo = carm_bdev_getgeo, -}; - -static unsigned int carm_host_id; -static unsigned long carm_major_alloc; - - - -static int carm_bdev_getgeo(struct block_device *bdev, struct hd_geometry *geo) -{ - struct carm_port *port = bdev->bd_disk->private_data; - - geo->heads = (u8) port->dev_geom_head; - geo->sectors = (u8) port->dev_geom_sect; - geo->cylinders = port->dev_geom_cyl; - return 0; -} - -static const u32 msg_sizes[] = { 32, 64, 128, CARM_MSG_SIZE }; - -static inline int carm_lookup_bucket(u32 msg_size) -{ - int i; - - for (i = 0; i < ARRAY_SIZE(msg_sizes); i++) - if (msg_size <= msg_sizes[i]) - return i; - - return -ENOENT; -} - -static void carm_init_buckets(void __iomem *mmio) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(msg_sizes); i++) - writel(msg_sizes[i], mmio + CARM_CMS0 + (4 * i)); -} - -static inline void *carm_ref_msg(struct carm_host *host, - unsigned int msg_idx) -{ - return host->msg_base + (msg_idx * CARM_MSG_SIZE); -} - -static inline dma_addr_t carm_ref_msg_dma(struct carm_host *host, - unsigned int msg_idx) -{ - return host->msg_dma + (msg_idx * CARM_MSG_SIZE); -} - -static int carm_send_msg(struct carm_host *host, - struct carm_request *crq, unsigned tag) -{ - void __iomem *mmio = host->mmio; - u32 msg = (u32) carm_ref_msg_dma(host, tag); - u32 cm_bucket = crq->msg_bucket; - u32 tmp; - int rc = 0; - - VPRINTK("ENTER\n"); - - tmp = readl(mmio + CARM_HMUC); - if (tmp & CARM_Q_FULL) { -#if 0 - tmp = readl(mmio + CARM_INT_MASK); - tmp |= INT_Q_AVAILABLE; - writel(tmp, mmio + CARM_INT_MASK); - readl(mmio + CARM_INT_MASK); /* flush */ -#endif - DPRINTK("host msg queue full\n"); - rc = -EBUSY; - } else { - writel(msg | (cm_bucket << 1), mmio + CARM_IHQP); - readl(mmio + CARM_IHQP); /* flush */ - } - - return rc; -} - -static int carm_array_info (struct carm_host *host, unsigned int array_idx) -{ - struct carm_msg_ioctl *ioc; - u32 msg_data; - dma_addr_t msg_dma; - struct carm_request *crq; - struct request *rq; - int rc; - - rq = blk_mq_alloc_request(host->oob_q, REQ_OP_DRV_OUT, 0); - if (IS_ERR(rq)) { - rc = -ENOMEM; - goto err_out; - } - crq = blk_mq_rq_to_pdu(rq); - - ioc = carm_ref_msg(host, rq->tag); - msg_dma = carm_ref_msg_dma(host, rq->tag); - msg_data = (u32) (msg_dma + sizeof(struct carm_array_info)); - - crq->msg_type = CARM_MSG_ARRAY; - crq->msg_subtype = CARM_ARRAY_INFO; - rc = carm_lookup_bucket(sizeof(struct carm_msg_ioctl) + - sizeof(struct carm_array_info)); - BUG_ON(rc < 0); - crq->msg_bucket = (u32) rc; - - memset(ioc, 0, sizeof(*ioc)); - ioc->type = CARM_MSG_ARRAY; - ioc->subtype = CARM_ARRAY_INFO; - ioc->array_id = (u8) array_idx; - ioc->handle = cpu_to_le32(TAG_ENCODE(rq->tag)); - ioc->data_addr = cpu_to_le32(msg_data); - - spin_lock_irq(&host->lock); - assert(host->state == HST_DEV_SCAN_START || - host->state == HST_DEV_SCAN); - spin_unlock_irq(&host->lock); - - DPRINTK("blk_execute_rq_nowait, tag == %u\n", rq->tag); - blk_execute_rq_nowait(rq, true); - - return 0; - -err_out: - spin_lock_irq(&host->lock); - host->state = HST_ERROR; - spin_unlock_irq(&host->lock); - return rc; -} - -typedef unsigned int (*carm_sspc_t)(struct carm_host *, unsigned int, void *); - -static int carm_send_special (struct carm_host *host, carm_sspc_t func) -{ - struct request *rq; - struct carm_request *crq; - struct carm_msg_ioctl *ioc; - void *mem; - unsigned int msg_size; - int rc; - - rq = blk_mq_alloc_request(host->oob_q, REQ_OP_DRV_OUT, 0); - if (IS_ERR(rq)) - return -ENOMEM; - crq = blk_mq_rq_to_pdu(rq); - - mem = carm_ref_msg(host, rq->tag); - - msg_size = func(host, rq->tag, mem); - - ioc = mem; - crq->msg_type = ioc->type; - crq->msg_subtype = ioc->subtype; - rc = carm_lookup_bucket(msg_size); - BUG_ON(rc < 0); - crq->msg_bucket = (u32) rc; - - DPRINTK("blk_execute_rq_nowait, tag == %u\n", rq->tag); - blk_execute_rq_nowait(rq, true); - - return 0; -} - -static unsigned int carm_fill_sync_time(struct carm_host *host, - unsigned int idx, void *mem) -{ - struct carm_msg_sync_time *st = mem; - - time64_t tv = ktime_get_real_seconds(); - - memset(st, 0, sizeof(*st)); - st->type = CARM_MSG_MISC; - st->subtype = MISC_SET_TIME; - st->handle = cpu_to_le32(TAG_ENCODE(idx)); - st->timestamp = cpu_to_le32(tv); - - return sizeof(struct carm_msg_sync_time); -} - -static unsigned int carm_fill_alloc_buf(struct carm_host *host, - unsigned int idx, void *mem) -{ - struct carm_msg_allocbuf *ab = mem; - - memset(ab, 0, sizeof(*ab)); - ab->type = CARM_MSG_MISC; - ab->subtype = MISC_ALLOC_MEM; - ab->handle = cpu_to_le32(TAG_ENCODE(idx)); - ab->n_sg = 1; - ab->sg_type = SGT_32BIT; - ab->addr = cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1)); - ab->len = cpu_to_le32(PDC_SHM_SIZE >> 1); - ab->evt_pool = cpu_to_le32(host->shm_dma + (16 * 1024)); - ab->n_evt = cpu_to_le32(1024); - ab->rbuf_pool = cpu_to_le32(host->shm_dma); - ab->n_rbuf = cpu_to_le32(RMSG_Q_LEN); - ab->msg_pool = cpu_to_le32(host->shm_dma + RBUF_LEN); - ab->n_msg = cpu_to_le32(CARM_Q_LEN); - ab->sg[0].start = cpu_to_le32(host->shm_dma + (PDC_SHM_SIZE >> 1)); - ab->sg[0].len = cpu_to_le32(65536); - - return sizeof(struct carm_msg_allocbuf); -} - -static unsigned int carm_fill_scan_channels(struct carm_host *host, - unsigned int idx, void *mem) -{ - struct carm_msg_ioctl *ioc = mem; - u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + - IOC_SCAN_CHAN_OFFSET); - - memset(ioc, 0, sizeof(*ioc)); - ioc->type = CARM_MSG_IOCTL; - ioc->subtype = CARM_IOC_SCAN_CHAN; - ioc->handle = cpu_to_le32(TAG_ENCODE(idx)); - ioc->data_addr = cpu_to_le32(msg_data); - - /* fill output data area with "no device" default values */ - mem += IOC_SCAN_CHAN_OFFSET; - memset(mem, IOC_SCAN_CHAN_NODEV, CARM_MAX_PORTS); - - return IOC_SCAN_CHAN_OFFSET + CARM_MAX_PORTS; -} - -static unsigned int carm_fill_get_fw_ver(struct carm_host *host, - unsigned int idx, void *mem) -{ - struct carm_msg_get_fw_ver *ioc = mem; - u32 msg_data = (u32) (carm_ref_msg_dma(host, idx) + sizeof(*ioc)); - - memset(ioc, 0, sizeof(*ioc)); - ioc->type = CARM_MSG_MISC; - ioc->subtype = MISC_GET_FW_VER; - ioc->handle = cpu_to_le32(TAG_ENCODE(idx)); - ioc->data_addr = cpu_to_le32(msg_data); - - return sizeof(struct carm_msg_get_fw_ver) + - sizeof(struct carm_fw_ver); -} - -static inline void carm_push_q (struct carm_host *host, struct request_queue *q) -{ - unsigned int idx = host->wait_q_prod % CARM_MAX_WAIT_Q; - - blk_mq_stop_hw_queues(q); - VPRINTK("STOPPED QUEUE %p\n", q); - - host->wait_q[idx] = q; - host->wait_q_prod++; - BUG_ON(host->wait_q_prod == host->wait_q_cons); /* overrun */ -} - -static inline struct request_queue *carm_pop_q(struct carm_host *host) -{ - unsigned int idx; - - if (host->wait_q_prod == host->wait_q_cons) - return NULL; - - idx = host->wait_q_cons % CARM_MAX_WAIT_Q; - host->wait_q_cons++; - - return host->wait_q[idx]; -} - -static inline void carm_round_robin(struct carm_host *host) -{ - struct request_queue *q = carm_pop_q(host); - if (q) { - blk_mq_start_hw_queues(q); - VPRINTK("STARTED QUEUE %p\n", q); - } -} - -static inline enum dma_data_direction carm_rq_dir(struct request *rq) -{ - return op_is_write(req_op(rq)) ? DMA_TO_DEVICE : DMA_FROM_DEVICE; -} - -static blk_status_t carm_queue_rq(struct blk_mq_hw_ctx *hctx, - const struct blk_mq_queue_data *bd) -{ - struct request_queue *q = hctx->queue; - struct request *rq = bd->rq; - struct carm_port *port = q->queuedata; - struct carm_host *host = port->host; - struct carm_request *crq = blk_mq_rq_to_pdu(rq); - struct carm_msg_rw *msg; - struct scatterlist *sg; - int i, n_elem = 0, rc; - unsigned int msg_size; - u32 tmp; - - crq->n_elem = 0; - sg_init_table(crq->sg, CARM_MAX_REQ_SG); - - blk_mq_start_request(rq); - - spin_lock_irq(&host->lock); - if (req_op(rq) == REQ_OP_DRV_OUT) - goto send_msg; - - /* get scatterlist from block layer */ - sg = &crq->sg[0]; - n_elem = blk_rq_map_sg(q, rq, sg); - if (n_elem <= 0) - goto out_ioerr; - - /* map scatterlist to PCI bus addresses */ - n_elem = dma_map_sg(&host->pdev->dev, sg, n_elem, carm_rq_dir(rq)); - if (n_elem <= 0) - goto out_ioerr; - - /* obey global hardware limit on S/G entries */ - if (host->hw_sg_used >= CARM_MAX_HOST_SG - n_elem) - goto out_resource; - - crq->n_elem = n_elem; - host->hw_sg_used += n_elem; - - /* - * build read/write message - */ - - VPRINTK("build msg\n"); - msg = (struct carm_msg_rw *) carm_ref_msg(host, rq->tag); - - if (rq_data_dir(rq) == WRITE) { - msg->type = CARM_MSG_WRITE; - crq->msg_type = CARM_MSG_WRITE; - } else { - msg->type = CARM_MSG_READ; - crq->msg_type = CARM_MSG_READ; - } - - msg->id = port->port_no; - msg->sg_count = n_elem; - msg->sg_type = SGT_32BIT; - msg->handle = cpu_to_le32(TAG_ENCODE(rq->tag)); - msg->lba = cpu_to_le32(blk_rq_pos(rq) & 0xffffffff); - tmp = (blk_rq_pos(rq) >> 16) >> 16; - msg->lba_high = cpu_to_le16( (u16) tmp ); - msg->lba_count = cpu_to_le16(blk_rq_sectors(rq)); - - msg_size = sizeof(struct carm_msg_rw) - sizeof(msg->sg); - for (i = 0; i < n_elem; i++) { - struct carm_msg_sg *carm_sg = &msg->sg[i]; - carm_sg->start = cpu_to_le32(sg_dma_address(&crq->sg[i])); - carm_sg->len = cpu_to_le32(sg_dma_len(&crq->sg[i])); - msg_size += sizeof(struct carm_msg_sg); - } - - rc = carm_lookup_bucket(msg_size); - BUG_ON(rc < 0); - crq->msg_bucket = (u32) rc; -send_msg: - /* - * queue read/write message to hardware - */ - VPRINTK("send msg, tag == %u\n", rq->tag); - rc = carm_send_msg(host, crq, rq->tag); - if (rc) { - host->hw_sg_used -= n_elem; - goto out_resource; - } - - spin_unlock_irq(&host->lock); - return BLK_STS_OK; -out_resource: - dma_unmap_sg(&host->pdev->dev, &crq->sg[0], n_elem, carm_rq_dir(rq)); - carm_push_q(host, q); - spin_unlock_irq(&host->lock); - return BLK_STS_DEV_RESOURCE; -out_ioerr: - carm_round_robin(host); - spin_unlock_irq(&host->lock); - return BLK_STS_IOERR; -} - -static void carm_handle_array_info(struct carm_host *host, - struct carm_request *crq, u8 *mem, - blk_status_t error) -{ - struct carm_port *port; - u8 *msg_data = mem + sizeof(struct carm_array_info); - struct carm_array_info *desc = (struct carm_array_info *) msg_data; - u64 lo, hi; - int cur_port; - size_t slen; - - DPRINTK("ENTER\n"); - - if (error) - goto out; - if (le32_to_cpu(desc->array_status) & ARRAY_NO_EXIST) - goto out; - - cur_port = host->cur_scan_dev; - - /* should never occur */ - if ((cur_port < 0) || (cur_port >= CARM_MAX_PORTS)) { - printk(KERN_ERR PFX "BUG: cur_scan_dev==%d, array_id==%d\n", - cur_port, (int) desc->array_id); - goto out; - } - - port = &host->port[cur_port]; - - lo = (u64) le32_to_cpu(desc->size); - hi = (u64) le16_to_cpu(desc->size_hi); - - port->capacity = lo | (hi << 32); - port->dev_geom_head = le16_to_cpu(desc->head); - port->dev_geom_sect = le16_to_cpu(desc->sect); - port->dev_geom_cyl = le16_to_cpu(desc->cyl); - - host->dev_active |= (1 << cur_port); - - strncpy(port->name, desc->name, sizeof(port->name)); - port->name[sizeof(port->name) - 1] = 0; - slen = strlen(port->name); - while (slen && (port->name[slen - 1] == ' ')) { - port->name[slen - 1] = 0; - slen--; - } - - printk(KERN_INFO DRV_NAME "(%s): port %u device %Lu sectors\n", - pci_name(host->pdev), port->port_no, - (unsigned long long) port->capacity); - printk(KERN_INFO DRV_NAME "(%s): port %u device \"%s\"\n", - pci_name(host->pdev), port->port_no, port->name); - -out: - assert(host->state == HST_DEV_SCAN); - schedule_work(&host->fsm_task); -} - -static void carm_handle_scan_chan(struct carm_host *host, - struct carm_request *crq, u8 *mem, - blk_status_t error) -{ - u8 *msg_data = mem + IOC_SCAN_CHAN_OFFSET; - unsigned int i, dev_count = 0; - int new_state = HST_DEV_SCAN_START; - - DPRINTK("ENTER\n"); - - if (error) { - new_state = HST_ERROR; - goto out; - } - - /* TODO: scan and support non-disk devices */ - for (i = 0; i < 8; i++) - if (msg_data[i] == 0) { /* direct-access device (disk) */ - host->dev_present |= (1 << i); - dev_count++; - } - - printk(KERN_INFO DRV_NAME "(%s): found %u interesting devices\n", - pci_name(host->pdev), dev_count); - -out: - assert(host->state == HST_PORT_SCAN); - host->state = new_state; - schedule_work(&host->fsm_task); -} - -static void carm_handle_generic(struct carm_host *host, - struct carm_request *crq, blk_status_t error, - int cur_state, int next_state) -{ - DPRINTK("ENTER\n"); - - assert(host->state == cur_state); - if (error) - host->state = HST_ERROR; - else - host->state = next_state; - schedule_work(&host->fsm_task); -} - -static inline void carm_handle_resp(struct carm_host *host, - __le32 ret_handle_le, u32 status) -{ - u32 handle = le32_to_cpu(ret_handle_le); - unsigned int msg_idx; - struct request *rq; - struct carm_request *crq; - blk_status_t error = (status == RMSG_OK) ? 0 : BLK_STS_IOERR; - u8 *mem; - - VPRINTK("ENTER, handle == 0x%x\n", handle); - - if (unlikely(!TAG_VALID(handle))) { - printk(KERN_ERR DRV_NAME "(%s): BUG: invalid tag 0x%x\n", - pci_name(host->pdev), handle); - return; - } - - msg_idx = TAG_DECODE(handle); - VPRINTK("tag == %u\n", msg_idx); - - rq = blk_mq_tag_to_rq(host->tag_set.tags[0], msg_idx); - crq = blk_mq_rq_to_pdu(rq); - - /* fast path */ - if (likely(crq->msg_type == CARM_MSG_READ || - crq->msg_type == CARM_MSG_WRITE)) { - dma_unmap_sg(&host->pdev->dev, &crq->sg[0], crq->n_elem, - carm_rq_dir(rq)); - goto done; - } - - mem = carm_ref_msg(host, msg_idx); - - switch (crq->msg_type) { - case CARM_MSG_IOCTL: { - switch (crq->msg_subtype) { - case CARM_IOC_SCAN_CHAN: - carm_handle_scan_chan(host, crq, mem, error); - goto done; - default: - /* unknown / invalid response */ - goto err_out; - } - break; - } - - case CARM_MSG_MISC: { - switch (crq->msg_subtype) { - case MISC_ALLOC_MEM: - carm_handle_generic(host, crq, error, - HST_ALLOC_BUF, HST_SYNC_TIME); - goto done; - case MISC_SET_TIME: - carm_handle_generic(host, crq, error, - HST_SYNC_TIME, HST_GET_FW_VER); - goto done; - case MISC_GET_FW_VER: { - struct carm_fw_ver *ver = (struct carm_fw_ver *) - (mem + sizeof(struct carm_msg_get_fw_ver)); - if (!error) { - host->fw_ver = le32_to_cpu(ver->version); - host->flags |= (ver->features & FL_FW_VER_MASK); - } - carm_handle_generic(host, crq, error, - HST_GET_FW_VER, HST_PORT_SCAN); - goto done; - } - default: - /* unknown / invalid response */ - goto err_out; - } - break; - } - - case CARM_MSG_ARRAY: { - switch (crq->msg_subtype) { - case CARM_ARRAY_INFO: - carm_handle_array_info(host, crq, mem, error); - break; - default: - /* unknown / invalid response */ - goto err_out; - } - break; - } - - default: - /* unknown / invalid response */ - goto err_out; - } - - return; - -err_out: - printk(KERN_WARNING DRV_NAME "(%s): BUG: unhandled message type %d/%d\n", - pci_name(host->pdev), crq->msg_type, crq->msg_subtype); - error = BLK_STS_IOERR; -done: - host->hw_sg_used -= crq->n_elem; - blk_mq_end_request(blk_mq_rq_from_pdu(crq), error); - - if (host->hw_sg_used <= CARM_SG_LOW_WATER) - carm_round_robin(host); -} - -static inline void carm_handle_responses(struct carm_host *host) -{ - void __iomem *mmio = host->mmio; - struct carm_response *resp = (struct carm_response *) host->shm; - unsigned int work = 0; - unsigned int idx = host->resp_idx % RMSG_Q_LEN; - - while (1) { - u32 status = le32_to_cpu(resp[idx].status); - - if (status == 0xffffffff) { - VPRINTK("ending response on index %u\n", idx); - writel(idx << 3, mmio + CARM_RESP_IDX); - break; - } - - /* response to a message we sent */ - else if ((status & (1 << 31)) == 0) { - VPRINTK("handling msg response on index %u\n", idx); - carm_handle_resp(host, resp[idx].ret_handle, status); - resp[idx].status = cpu_to_le32(0xffffffff); - } - - /* asynchronous events the hardware throws our way */ - else if ((status & 0xff000000) == (1 << 31)) { - u8 *evt_type_ptr = (u8 *) &resp[idx]; - u8 evt_type = *evt_type_ptr; - printk(KERN_WARNING DRV_NAME "(%s): unhandled event type %d\n", - pci_name(host->pdev), (int) evt_type); - resp[idx].status = cpu_to_le32(0xffffffff); - } - - idx = NEXT_RESP(idx); - work++; - } - - VPRINTK("EXIT, work==%u\n", work); - host->resp_idx += work; -} - -static irqreturn_t carm_interrupt(int irq, void *__host) -{ - struct carm_host *host = __host; - void __iomem *mmio; - u32 mask; - int handled = 0; - unsigned long flags; - - if (!host) { - VPRINTK("no host\n"); - return IRQ_NONE; - } - - spin_lock_irqsave(&host->lock, flags); - - mmio = host->mmio; - - /* reading should also clear interrupts */ - mask = readl(mmio + CARM_INT_STAT); - - if (mask == 0 || mask == 0xffffffff) { - VPRINTK("no work, mask == 0x%x\n", mask); - goto out; - } - - if (mask & INT_ACK_MASK) - writel(mask, mmio + CARM_INT_STAT); - - if (unlikely(host->state == HST_INVALID)) { - VPRINTK("not initialized yet, mask = 0x%x\n", mask); - goto out; - } - - if (mask & CARM_HAVE_RESP) { - handled = 1; - carm_handle_responses(host); - } - -out: - spin_unlock_irqrestore(&host->lock, flags); - VPRINTK("EXIT\n"); - return IRQ_RETVAL(handled); -} - -static void carm_fsm_task (struct work_struct *work) -{ - struct carm_host *host = - container_of(work, struct carm_host, fsm_task); - unsigned long flags; - unsigned int state; - int rc, i, next_dev; - int reschedule = 0; - int new_state = HST_INVALID; - - spin_lock_irqsave(&host->lock, flags); - state = host->state; - spin_unlock_irqrestore(&host->lock, flags); - - DPRINTK("ENTER, state == %s\n", state_name[state]); - - switch (state) { - case HST_PROBE_START: - new_state = HST_ALLOC_BUF; - reschedule = 1; - break; - - case HST_ALLOC_BUF: - rc = carm_send_special(host, carm_fill_alloc_buf); - if (rc) { - new_state = HST_ERROR; - reschedule = 1; - } - break; - - case HST_SYNC_TIME: - rc = carm_send_special(host, carm_fill_sync_time); - if (rc) { - new_state = HST_ERROR; - reschedule = 1; - } - break; - - case HST_GET_FW_VER: - rc = carm_send_special(host, carm_fill_get_fw_ver); - if (rc) { - new_state = HST_ERROR; - reschedule = 1; - } - break; - - case HST_PORT_SCAN: - rc = carm_send_special(host, carm_fill_scan_channels); - if (rc) { - new_state = HST_ERROR; - reschedule = 1; - } - break; - - case HST_DEV_SCAN_START: - host->cur_scan_dev = -1; - new_state = HST_DEV_SCAN; - reschedule = 1; - break; - - case HST_DEV_SCAN: - next_dev = -1; - for (i = host->cur_scan_dev + 1; i < CARM_MAX_PORTS; i++) - if (host->dev_present & (1 << i)) { - next_dev = i; - break; - } - - if (next_dev >= 0) { - host->cur_scan_dev = next_dev; - rc = carm_array_info(host, next_dev); - if (rc) { - new_state = HST_ERROR; - reschedule = 1; - } - } else { - new_state = HST_DEV_ACTIVATE; - reschedule = 1; - } - break; - - case HST_DEV_ACTIVATE: { - int activated = 0; - for (i = 0; i < CARM_MAX_PORTS; i++) - if (host->dev_active & (1 << i)) { - struct carm_port *port = &host->port[i]; - struct gendisk *disk = port->disk; - - set_capacity(disk, port->capacity); - host->probe_err = add_disk(disk); - if (!host->probe_err) - activated++; - else - break; - } - - printk(KERN_INFO DRV_NAME "(%s): %d ports activated\n", - pci_name(host->pdev), activated); - - new_state = HST_PROBE_FINISHED; - reschedule = 1; - break; - } - case HST_PROBE_FINISHED: - complete(&host->probe_comp); - break; - case HST_ERROR: - /* FIXME: TODO */ - break; - - default: - /* should never occur */ - printk(KERN_ERR PFX "BUG: unknown state %d\n", state); - assert(0); - break; - } - - if (new_state != HST_INVALID) { - spin_lock_irqsave(&host->lock, flags); - host->state = new_state; - spin_unlock_irqrestore(&host->lock, flags); - } - if (reschedule) - schedule_work(&host->fsm_task); -} - -static int carm_init_wait(void __iomem *mmio, u32 bits, unsigned int test_bit) -{ - unsigned int i; - - for (i = 0; i < 50000; i++) { - u32 tmp = readl(mmio + CARM_LMUC); - udelay(100); - - if (test_bit) { - if ((tmp & bits) == bits) - return 0; - } else { - if ((tmp & bits) == 0) - return 0; - } - - cond_resched(); - } - - printk(KERN_ERR PFX "carm_init_wait timeout, bits == 0x%x, test_bit == %s\n", - bits, test_bit ? "yes" : "no"); - return -EBUSY; -} - -static void carm_init_responses(struct carm_host *host) -{ - void __iomem *mmio = host->mmio; - unsigned int i; - struct carm_response *resp = (struct carm_response *) host->shm; - - for (i = 0; i < RMSG_Q_LEN; i++) - resp[i].status = cpu_to_le32(0xffffffff); - - writel(0, mmio + CARM_RESP_IDX); -} - -static int carm_init_host(struct carm_host *host) -{ - void __iomem *mmio = host->mmio; - u32 tmp; - u8 tmp8; - int rc; - - DPRINTK("ENTER\n"); - - writel(0, mmio + CARM_INT_MASK); - - tmp8 = readb(mmio + CARM_INITC); - if (tmp8 & 0x01) { - tmp8 &= ~0x01; - writeb(tmp8, mmio + CARM_INITC); - readb(mmio + CARM_INITC); /* flush */ - - DPRINTK("snooze...\n"); - msleep(5000); - } - - tmp = readl(mmio + CARM_HMUC); - if (tmp & CARM_CME) { - DPRINTK("CME bit present, waiting\n"); - rc = carm_init_wait(mmio, CARM_CME, 1); - if (rc) { - DPRINTK("EXIT, carm_init_wait 1 failed\n"); - return rc; - } - } - if (tmp & CARM_RME) { - DPRINTK("RME bit present, waiting\n"); - rc = carm_init_wait(mmio, CARM_RME, 1); - if (rc) { - DPRINTK("EXIT, carm_init_wait 2 failed\n"); - return rc; - } - } - - tmp &= ~(CARM_RME | CARM_CME); - writel(tmp, mmio + CARM_HMUC); - readl(mmio + CARM_HMUC); /* flush */ - - rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 0); - if (rc) { - DPRINTK("EXIT, carm_init_wait 3 failed\n"); - return rc; - } - - carm_init_buckets(mmio); - - writel(host->shm_dma & 0xffffffff, mmio + RBUF_ADDR_LO); - writel((host->shm_dma >> 16) >> 16, mmio + RBUF_ADDR_HI); - writel(RBUF_LEN, mmio + RBUF_BYTE_SZ); - - tmp = readl(mmio + CARM_HMUC); - tmp |= (CARM_RME | CARM_CME | CARM_WZBC); - writel(tmp, mmio + CARM_HMUC); - readl(mmio + CARM_HMUC); /* flush */ - - rc = carm_init_wait(mmio, CARM_RME | CARM_CME, 1); - if (rc) { - DPRINTK("EXIT, carm_init_wait 4 failed\n"); - return rc; - } - - writel(0, mmio + CARM_HMPHA); - writel(INT_DEF_MASK, mmio + CARM_INT_MASK); - - carm_init_responses(host); - - /* start initialization, probing state machine */ - spin_lock_irq(&host->lock); - assert(host->state == HST_INVALID); - host->state = HST_PROBE_START; - spin_unlock_irq(&host->lock); - schedule_work(&host->fsm_task); - - DPRINTK("EXIT\n"); - return 0; -} - -static const struct blk_mq_ops carm_mq_ops = { - .queue_rq = carm_queue_rq, -}; - -static int carm_init_disk(struct carm_host *host, unsigned int port_no) -{ - struct carm_port *port = &host->port[port_no]; - struct gendisk *disk; - - port->host = host; - port->port_no = port_no; - - disk = blk_mq_alloc_disk(&host->tag_set, port); - if (IS_ERR(disk)) - return PTR_ERR(disk); - - port->disk = disk; - sprintf(disk->disk_name, DRV_NAME "/%u", - (unsigned int)host->id * CARM_MAX_PORTS + port_no); - disk->major = host->major; - disk->first_minor = port_no * CARM_MINORS_PER_MAJOR; - disk->minors = CARM_MINORS_PER_MAJOR; - disk->fops = &carm_bd_ops; - disk->private_data = port; - - blk_queue_max_segments(disk->queue, CARM_MAX_REQ_SG); - blk_queue_segment_boundary(disk->queue, CARM_SG_BOUNDARY); - return 0; -} - -static void carm_free_disk(struct carm_host *host, unsigned int port_no) -{ - struct carm_port *port = &host->port[port_no]; - struct gendisk *disk = port->disk; - - if (!disk) - return; - - if (host->state > HST_DEV_ACTIVATE) - del_gendisk(disk); - put_disk(disk); -} - -static int carm_init_shm(struct carm_host *host) -{ - host->shm = dma_alloc_coherent(&host->pdev->dev, CARM_SHM_SIZE, - &host->shm_dma, GFP_KERNEL); - if (!host->shm) - return -ENOMEM; - - host->msg_base = host->shm + RBUF_LEN; - host->msg_dma = host->shm_dma + RBUF_LEN; - - memset(host->shm, 0xff, RBUF_LEN); - memset(host->msg_base, 0, PDC_SHM_SIZE - RBUF_LEN); - - return 0; -} - -static int carm_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) -{ - struct carm_host *host; - int rc; - struct request_queue *q; - unsigned int i; - - printk_once(KERN_DEBUG DRV_NAME " version " DRV_VERSION "\n"); - - rc = pci_enable_device(pdev); - if (rc) - return rc; - - rc = pci_request_regions(pdev, DRV_NAME); - if (rc) - goto err_out; - - rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)); - if (rc) { - printk(KERN_ERR DRV_NAME "(%s): DMA mask failure\n", - pci_name(pdev)); - goto err_out_regions; - } - - host = kzalloc(sizeof(*host), GFP_KERNEL); - if (!host) { - rc = -ENOMEM; - goto err_out_regions; - } - - host->pdev = pdev; - spin_lock_init(&host->lock); - INIT_WORK(&host->fsm_task, carm_fsm_task); - init_completion(&host->probe_comp); - - host->mmio = ioremap(pci_resource_start(pdev, 0), - pci_resource_len(pdev, 0)); - if (!host->mmio) { - printk(KERN_ERR DRV_NAME "(%s): MMIO alloc failure\n", - pci_name(pdev)); - rc = -ENOMEM; - goto err_out_kfree; - } - - rc = carm_init_shm(host); - if (rc) { - printk(KERN_ERR DRV_NAME "(%s): DMA SHM alloc failure\n", - pci_name(pdev)); - goto err_out_iounmap; - } - - memset(&host->tag_set, 0, sizeof(host->tag_set)); - host->tag_set.ops = &carm_mq_ops; - host->tag_set.cmd_size = sizeof(struct carm_request); - host->tag_set.nr_hw_queues = 1; - host->tag_set.nr_maps = 1; - host->tag_set.queue_depth = max_queue; - host->tag_set.numa_node = NUMA_NO_NODE; - host->tag_set.flags = BLK_MQ_F_SHOULD_MERGE; - - rc = blk_mq_alloc_tag_set(&host->tag_set); - if (rc) - goto err_out_dma_free; - - q = blk_mq_init_queue(&host->tag_set); - if (IS_ERR(q)) { - rc = PTR_ERR(q); - blk_mq_free_tag_set(&host->tag_set); - goto err_out_dma_free; - } - - host->oob_q = q; - q->queuedata = host; - - /* - * Figure out which major to use: 160, 161, or dynamic - */ - if (!test_and_set_bit(0, &carm_major_alloc)) - host->major = 160; - else if (!test_and_set_bit(1, &carm_major_alloc)) - host->major = 161; - else - host->flags |= FL_DYN_MAJOR; - - host->id = carm_host_id; - sprintf(host->name, DRV_NAME "%d", carm_host_id); - - rc = register_blkdev(host->major, host->name); - if (rc < 0) - goto err_out_free_majors; - if (host->flags & FL_DYN_MAJOR) - host->major = rc; - - for (i = 0; i < CARM_MAX_PORTS; i++) { - rc = carm_init_disk(host, i); - if (rc) - goto err_out_blkdev_disks; - } - - pci_set_master(pdev); - - rc = request_irq(pdev->irq, carm_interrupt, IRQF_SHARED, DRV_NAME, host); - if (rc) { - printk(KERN_ERR DRV_NAME "(%s): irq alloc failure\n", - pci_name(pdev)); - goto err_out_blkdev_disks; - } - - rc = carm_init_host(host); - if (rc) - goto err_out_free_irq; - - DPRINTK("waiting for probe_comp\n"); - host->probe_err = -ENODEV; - wait_for_completion(&host->probe_comp); - if (host->probe_err) { - rc = host->probe_err; - goto err_out_free_irq; - } - - printk(KERN_INFO "%s: pci %s, ports %d, io %llx, irq %u, major %d\n", - host->name, pci_name(pdev), (int) CARM_MAX_PORTS, - (unsigned long long)pci_resource_start(pdev, 0), - pdev->irq, host->major); - - carm_host_id++; - pci_set_drvdata(pdev, host); - return 0; - -err_out_free_irq: - free_irq(pdev->irq, host); -err_out_blkdev_disks: - for (i = 0; i < CARM_MAX_PORTS; i++) - carm_free_disk(host, i); - unregister_blkdev(host->major, host->name); -err_out_free_majors: - if (host->major == 160) - clear_bit(0, &carm_major_alloc); - else if (host->major == 161) - clear_bit(1, &carm_major_alloc); - blk_mq_destroy_queue(host->oob_q); - blk_mq_free_tag_set(&host->tag_set); -err_out_dma_free: - dma_free_coherent(&pdev->dev, CARM_SHM_SIZE, host->shm, host->shm_dma); -err_out_iounmap: - iounmap(host->mmio); -err_out_kfree: - kfree(host); -err_out_regions: - pci_release_regions(pdev); -err_out: - pci_disable_device(pdev); - return rc; -} - -static void carm_remove_one (struct pci_dev *pdev) -{ - struct carm_host *host = pci_get_drvdata(pdev); - unsigned int i; - - if (!host) { - printk(KERN_ERR PFX "BUG: no host data for PCI(%s)\n", - pci_name(pdev)); - return; - } - - free_irq(pdev->irq, host); - for (i = 0; i < CARM_MAX_PORTS; i++) - carm_free_disk(host, i); - unregister_blkdev(host->major, host->name); - if (host->major == 160) - clear_bit(0, &carm_major_alloc); - else if (host->major == 161) - clear_bit(1, &carm_major_alloc); - blk_mq_destroy_queue(host->oob_q); - blk_mq_free_tag_set(&host->tag_set); - dma_free_coherent(&pdev->dev, CARM_SHM_SIZE, host->shm, host->shm_dma); - iounmap(host->mmio); - kfree(host); - pci_release_regions(pdev); - pci_disable_device(pdev); -} - -module_pci_driver(carm_driver); diff --git a/drivers/block/ublk_drv.c b/drivers/block/ublk_drv.c index 3f1906965ac8..2b7d1db5c4a7 100644 --- a/drivers/block/ublk_drv.c +++ b/drivers/block/ublk_drv.c @@ -47,7 +47,12 @@ #define UBLK_MINORS (1U << MINORBITS) /* All UBLK_F_* have to be included into UBLK_F_ALL */ -#define UBLK_F_ALL (UBLK_F_SUPPORT_ZERO_COPY | UBLK_F_URING_CMD_COMP_IN_TASK) +#define UBLK_F_ALL (UBLK_F_SUPPORT_ZERO_COPY \ + | UBLK_F_URING_CMD_COMP_IN_TASK \ + | UBLK_F_NEED_GET_DATA) + +/* All UBLK_PARAM_TYPE_* should be included here */ +#define UBLK_PARAM_TYPE_ALL (UBLK_PARAM_TYPE_BASIC | UBLK_PARAM_TYPE_DISCARD) struct ublk_rq_data { struct callback_head work; @@ -86,6 +91,15 @@ struct ublk_uring_cmd_pdu { */ #define UBLK_IO_FLAG_ABORTED 0x04 +/* + * UBLK_IO_FLAG_NEED_GET_DATA is set because IO command requires + * get data buffer address from ublksrv. + * + * Then, bio data could be copied into this data buffer for a WRITE request + * after the IO command is issued again and UBLK_IO_FLAG_NEED_GET_DATA is unset. + */ +#define UBLK_IO_FLAG_NEED_GET_DATA 0x08 + struct ublk_io { /* userspace buffer address from io cmd */ __u64 addr; @@ -119,7 +133,6 @@ struct ublk_device { char *__queues; unsigned short queue_size; - unsigned short bs_shift; struct ublksrv_ctrl_dev_info dev_info; struct blk_mq_tag_set tag_set; @@ -137,6 +150,8 @@ struct ublk_device { spinlock_t mm_lock; struct mm_struct *mm; + struct ublk_params params; + struct completion completion; unsigned int nr_queues_ready; atomic_t nr_aborted_queues; @@ -149,6 +164,12 @@ struct ublk_device { struct work_struct stop_work; }; +/* header of ublk_params */ +struct ublk_params_header { + __u32 len; + __u32 types; +}; + static dev_t ublk_chr_devt; static struct class *ublk_chr_class; @@ -160,6 +181,90 @@ static DEFINE_MUTEX(ublk_ctl_mutex); static struct miscdevice ublk_misc; +static void ublk_dev_param_basic_apply(struct ublk_device *ub) +{ + struct request_queue *q = ub->ub_disk->queue; + const struct ublk_param_basic *p = &ub->params.basic; + + blk_queue_logical_block_size(q, 1 << p->logical_bs_shift); + blk_queue_physical_block_size(q, 1 << p->physical_bs_shift); + blk_queue_io_min(q, 1 << p->io_min_shift); + blk_queue_io_opt(q, 1 << p->io_opt_shift); + + blk_queue_write_cache(q, p->attrs & UBLK_ATTR_VOLATILE_CACHE, + p->attrs & UBLK_ATTR_FUA); + if (p->attrs & UBLK_ATTR_ROTATIONAL) + blk_queue_flag_clear(QUEUE_FLAG_NONROT, q); + else + blk_queue_flag_set(QUEUE_FLAG_NONROT, q); + + blk_queue_max_hw_sectors(q, p->max_sectors); + blk_queue_chunk_sectors(q, p->chunk_sectors); + blk_queue_virt_boundary(q, p->virt_boundary_mask); + + if (p->attrs & UBLK_ATTR_READ_ONLY) + set_disk_ro(ub->ub_disk, true); + + set_capacity(ub->ub_disk, p->dev_sectors); +} + +static void ublk_dev_param_discard_apply(struct ublk_device *ub) +{ + struct request_queue *q = ub->ub_disk->queue; + const struct ublk_param_discard *p = &ub->params.discard; + + q->limits.discard_alignment = p->discard_alignment; + q->limits.discard_granularity = p->discard_granularity; + blk_queue_max_discard_sectors(q, p->max_discard_sectors); + blk_queue_max_write_zeroes_sectors(q, + p->max_write_zeroes_sectors); + blk_queue_max_discard_segments(q, p->max_discard_segments); +} + +static int ublk_validate_params(const struct ublk_device *ub) +{ + /* basic param is the only one which must be set */ + if (ub->params.types & UBLK_PARAM_TYPE_BASIC) { + const struct ublk_param_basic *p = &ub->params.basic; + + if (p->logical_bs_shift > PAGE_SHIFT) + return -EINVAL; + + if (p->logical_bs_shift > p->physical_bs_shift) + return -EINVAL; + + if (p->max_sectors > (ub->dev_info.max_io_buf_bytes >> 9)) + return -EINVAL; + } else + return -EINVAL; + + if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) { + const struct ublk_param_discard *p = &ub->params.discard; + + /* So far, only support single segment discard */ + if (p->max_discard_sectors && p->max_discard_segments != 1) + return -EINVAL; + + if (!p->discard_granularity) + return -EINVAL; + } + + return 0; +} + +static int ublk_apply_params(struct ublk_device *ub) +{ + if (!(ub->params.types & UBLK_PARAM_TYPE_BASIC)) + return -EINVAL; + + ublk_dev_param_basic_apply(ub); + + if (ub->params.types & UBLK_PARAM_TYPE_DISCARD) + ublk_dev_param_discard_apply(ub); + + return 0; +} + static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq) { if (IS_BUILTIN(CONFIG_BLK_DEV_UBLK) && @@ -168,6 +273,13 @@ static inline bool ublk_can_use_task_work(const struct ublk_queue *ubq) return false; } +static inline bool ublk_need_get_data(const struct ublk_queue *ubq) +{ + if (ubq->flags & UBLK_F_NEED_GET_DATA) + return true; + return false; +} + static struct ublk_device *ublk_get_device(struct ublk_device *ub) { if (kobject_get_unless_zero(&ub->cdev_dev.kobj)) @@ -509,6 +621,21 @@ static void __ublk_fail_req(struct ublk_io *io, struct request *req) } } +static void ubq_complete_io_cmd(struct ublk_io *io, int res) +{ + /* mark this cmd owned by ublksrv */ + io->flags |= UBLK_IO_FLAG_OWNED_BY_SRV; + + /* + * clear ACTIVE since we are done with this sqe/cmd slot + * We can only accept io cmd in case of being not active. + */ + io->flags &= ~UBLK_IO_FLAG_ACTIVE; + + /* tell ublksrv one io request is coming */ + io_uring_cmd_done(io->cmd, res, 0); +} + #define UBLK_REQUEUE_DELAY_MS 3 static inline void __ublk_rq_task_work(struct request *req) @@ -531,6 +658,30 @@ static inline void __ublk_rq_task_work(struct request *req) return; } + if (ublk_need_get_data(ubq) && + (req_op(req) == REQ_OP_WRITE || + req_op(req) == REQ_OP_FLUSH)) { + /* + * We have not handled UBLK_IO_NEED_GET_DATA command yet, + * so immepdately pass UBLK_IO_RES_NEED_GET_DATA to ublksrv + * and notify it. + */ + if (!(io->flags & UBLK_IO_FLAG_NEED_GET_DATA)) { + io->flags |= UBLK_IO_FLAG_NEED_GET_DATA; + pr_devel("%s: need get data. op %d, qid %d tag %d io_flags %x\n", + __func__, io->cmd->cmd_op, ubq->q_id, + req->tag, io->flags); + ubq_complete_io_cmd(io, UBLK_IO_RES_NEED_GET_DATA); + return; + } + /* + * We have handled UBLK_IO_NEED_GET_DATA command, + * so clear UBLK_IO_FLAG_NEED_GET_DATA now and just + * do the copy work. + */ + io->flags &= ~UBLK_IO_FLAG_NEED_GET_DATA; + } + mapped_bytes = ublk_map_io(ubq, req, io); /* partially mapped, update io descriptor */ @@ -553,17 +704,7 @@ static inline void __ublk_rq_task_work(struct request *req) mapped_bytes >> 9; } - /* mark this cmd owned by ublksrv */ - io->flags |= UBLK_IO_FLAG_OWNED_BY_SRV; - - /* - * clear ACTIVE since we are done with this sqe/cmd slot - * We can only accept io cmd in case of being not active. - */ - io->flags &= ~UBLK_IO_FLAG_ACTIVE; - - /* tell ublksrv one io request is coming */ - io_uring_cmd_done(io->cmd, UBLK_IO_RES_OK, 0); + ubq_complete_io_cmd(io, UBLK_IO_RES_OK); } static void ublk_rq_task_work_cb(struct io_uring_cmd *cmd) @@ -788,16 +929,27 @@ static void ublk_daemon_monitor_work(struct work_struct *work) UBLK_DAEMON_MONITOR_PERIOD); } +static inline bool ublk_queue_ready(struct ublk_queue *ubq) +{ + return ubq->nr_io_ready == ubq->q_depth; +} + static void ublk_cancel_queue(struct ublk_queue *ubq) { int i; + if (!ublk_queue_ready(ubq)) + return; + for (i = 0; i < ubq->q_depth; i++) { struct ublk_io *io = &ubq->ios[i]; if (io->flags & UBLK_IO_FLAG_ACTIVE) io_uring_cmd_done(io->cmd, UBLK_IO_RES_ABORT, 0); } + + /* all io commands are canceled */ + ubq->nr_io_ready = 0; } /* Cancel all pending commands, must be called after del_gendisk() returns */ @@ -818,19 +970,14 @@ static void ublk_stop_dev(struct ublk_device *ub) del_gendisk(ub->ub_disk); ub->dev_info.state = UBLK_S_DEV_DEAD; ub->dev_info.ublksrv_pid = -1; - ublk_cancel_dev(ub); put_disk(ub->ub_disk); ub->ub_disk = NULL; unlock: + ublk_cancel_dev(ub); mutex_unlock(&ub->mutex); cancel_delayed_work_sync(&ub->monitor_work); } -static inline bool ublk_queue_ready(struct ublk_queue *ubq) -{ - return ubq->nr_io_ready == ubq->q_depth; -} - /* device can only be started after all IOs are ready */ static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq) { @@ -846,6 +993,25 @@ static void ublk_mark_io_ready(struct ublk_device *ub, struct ublk_queue *ubq) mutex_unlock(&ub->mutex); } +static void ublk_handle_need_get_data(struct ublk_device *ub, int q_id, + int tag, struct io_uring_cmd *cmd) +{ + struct ublk_queue *ubq = ublk_get_queue(ub, q_id); + struct request *req = blk_mq_tag_to_rq(ub->tag_set.tags[q_id], tag); + + if (ublk_can_use_task_work(ubq)) { + struct ublk_rq_data *data = blk_mq_rq_to_pdu(req); + + /* should not fail since we call it just in ubq->ubq_daemon */ + task_work_add(ubq->ubq_daemon, &data->work, TWA_SIGNAL_NO_IPI); + } else { + struct ublk_uring_cmd_pdu *pdu = ublk_get_uring_cmd_pdu(cmd); + + pdu->req = req; + io_uring_cmd_complete_in_task(cmd, ublk_rq_task_work_cb); + } +} + static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) { struct ublksrv_io_cmd *ub_cmd = (struct ublksrv_io_cmd *)cmd->cmd; @@ -884,6 +1050,14 @@ static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) goto out; } + /* + * ensure that the user issues UBLK_IO_NEED_GET_DATA + * iff the driver have set the UBLK_IO_FLAG_NEED_GET_DATA. + */ + if ((!!(io->flags & UBLK_IO_FLAG_NEED_GET_DATA)) + ^ (cmd_op == UBLK_IO_NEED_GET_DATA)) + goto out; + switch (cmd_op) { case UBLK_IO_FETCH_REQ: /* UBLK_IO_FETCH_REQ is only allowed before queue is setup */ @@ -917,6 +1091,14 @@ static int ublk_ch_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) io->cmd = cmd; ublk_commit_completion(ub, ub_cmd); break; + case UBLK_IO_NEED_GET_DATA: + if (!(io->flags & UBLK_IO_FLAG_OWNED_BY_SRV)) + goto out; + io->addr = ub_cmd->addr; + io->cmd = cmd; + io->flags |= UBLK_IO_FLAG_ACTIVE; + ublk_handle_need_get_data(ub, ub_cmd->q_id, ub_cmd->tag, cmd); + break; default: goto out; } @@ -1083,13 +1265,13 @@ static void ublk_stop_work_fn(struct work_struct *work) ublk_stop_dev(ub); } -/* align maximum I/O size to PAGE_SIZE */ +/* align max io buffer size with PAGE_SIZE */ static void ublk_align_max_io_size(struct ublk_device *ub) { - unsigned int max_rq_bytes = ub->dev_info.rq_max_blocks << ub->bs_shift; + unsigned int max_io_bytes = ub->dev_info.max_io_buf_bytes; - ub->dev_info.rq_max_blocks = - round_down(max_rq_bytes, PAGE_SIZE) >> ub->bs_shift; + ub->dev_info.max_io_buf_bytes = + round_down(max_io_bytes, PAGE_SIZE); } static int ublk_add_tag_set(struct ublk_device *ub) @@ -1132,7 +1314,6 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd) { struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd; int ublksrv_pid = (int)header->data[0]; - unsigned long dev_blocks = header->data[1]; struct ublk_device *ub; struct gendisk *disk; int ret = -EINVAL; @@ -1155,10 +1336,6 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd) goto out_unlock; } - /* We may get disk size updated */ - if (dev_blocks) - ub->dev_info.dev_blocks = dev_blocks; - disk = blk_mq_alloc_disk(&ub->tag_set, ub); if (IS_ERR(disk)) { ret = PTR_ERR(disk); @@ -1168,27 +1345,28 @@ static int ublk_ctrl_start_dev(struct io_uring_cmd *cmd) disk->fops = &ub_fops; disk->private_data = ub; - blk_queue_logical_block_size(disk->queue, ub->dev_info.block_size); - blk_queue_physical_block_size(disk->queue, ub->dev_info.block_size); - blk_queue_io_min(disk->queue, ub->dev_info.block_size); - blk_queue_max_hw_sectors(disk->queue, - ub->dev_info.rq_max_blocks << (ub->bs_shift - 9)); - disk->queue->limits.discard_granularity = PAGE_SIZE; - blk_queue_max_discard_sectors(disk->queue, UINT_MAX >> 9); - blk_queue_max_write_zeroes_sectors(disk->queue, UINT_MAX >> 9); - - set_capacity(disk, ub->dev_info.dev_blocks << (ub->bs_shift - 9)); - ub->dev_info.ublksrv_pid = ublksrv_pid; ub->ub_disk = disk; + + ret = ublk_apply_params(ub); + if (ret) + goto out_put_disk; + get_device(&ub->cdev_dev); ret = add_disk(disk); if (ret) { - put_disk(disk); - goto out_unlock; + /* + * Has to drop the reference since ->free_disk won't be + * called in case of add_disk failure. + */ + ublk_put_device(ub); + goto out_put_disk; } set_bit(UB_STATE_USED, &ub->state); ub->dev_info.state = UBLK_S_DEV_LIVE; +out_put_disk: + if (ret) + put_disk(disk); out_unlock: mutex_unlock(&ub->mutex); ublk_put_device(ub); @@ -1250,9 +1428,8 @@ static inline void ublk_dump_dev_info(struct ublksrv_ctrl_dev_info *info) { pr_devel("%s: dev id %d flags %llx\n", __func__, info->dev_id, info->flags); - pr_devel("\t nr_hw_queues %d queue_depth %d block size %d dev_capacity %lld\n", - info->nr_hw_queues, info->queue_depth, - info->block_size, info->dev_blocks); + pr_devel("\t nr_hw_queues %d queue_depth %d\n", + info->nr_hw_queues, info->queue_depth); } static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd) @@ -1312,7 +1489,6 @@ static int ublk_ctrl_add_dev(struct io_uring_cmd *cmd) /* We are not ready to support zero copy */ ub->dev_info.flags &= ~UBLK_F_SUPPORT_ZERO_COPY; - ub->bs_shift = ilog2(ub->dev_info.block_size); ub->dev_info.nr_hw_queues = min_t(unsigned int, ub->dev_info.nr_hw_queues, nr_cpu_ids); ublk_align_max_io_size(ub); @@ -1436,6 +1612,82 @@ static int ublk_ctrl_get_dev_info(struct io_uring_cmd *cmd) return ret; } +static int ublk_ctrl_get_params(struct io_uring_cmd *cmd) +{ + struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd; + void __user *argp = (void __user *)(unsigned long)header->addr; + struct ublk_params_header ph; + struct ublk_device *ub; + int ret; + + if (header->len <= sizeof(ph) || !header->addr) + return -EINVAL; + + if (copy_from_user(&ph, argp, sizeof(ph))) + return -EFAULT; + + if (ph.len > header->len || !ph.len) + return -EINVAL; + + if (ph.len > sizeof(struct ublk_params)) + ph.len = sizeof(struct ublk_params); + + ub = ublk_get_device_from_id(header->dev_id); + if (!ub) + return -EINVAL; + + mutex_lock(&ub->mutex); + if (copy_to_user(argp, &ub->params, ph.len)) + ret = -EFAULT; + else + ret = 0; + mutex_unlock(&ub->mutex); + + ublk_put_device(ub); + return ret; +} + +static int ublk_ctrl_set_params(struct io_uring_cmd *cmd) +{ + struct ublksrv_ctrl_cmd *header = (struct ublksrv_ctrl_cmd *)cmd->cmd; + void __user *argp = (void __user *)(unsigned long)header->addr; + struct ublk_params_header ph; + struct ublk_device *ub; + int ret = -EFAULT; + + if (header->len <= sizeof(ph) || !header->addr) + return -EINVAL; + + if (copy_from_user(&ph, argp, sizeof(ph))) + return -EFAULT; + + if (ph.len > header->len || !ph.len || !ph.types) + return -EINVAL; + + if (ph.len > sizeof(struct ublk_params)) + ph.len = sizeof(struct ublk_params); + + ub = ublk_get_device_from_id(header->dev_id); + if (!ub) + return -EINVAL; + + /* parameters can only be changed when device isn't live */ + mutex_lock(&ub->mutex); + if (ub->dev_info.state == UBLK_S_DEV_LIVE) { + ret = -EACCES; + } else if (copy_from_user(&ub->params, argp, ph.len)) { + ret = -EFAULT; + } else { + /* clear all we don't support yet */ + ub->params.types &= UBLK_PARAM_TYPE_ALL; + ret = ublk_validate_params(ub); + } + mutex_unlock(&ub->mutex); + ublk_put_device(ub); + + return ret; +} + static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd, unsigned int issue_flags) { @@ -1471,6 +1723,12 @@ static int ublk_ctrl_uring_cmd(struct io_uring_cmd *cmd, case UBLK_CMD_GET_QUEUE_AFFINITY: ret = ublk_ctrl_get_queue_affinity(cmd); break; + case UBLK_CMD_GET_PARAMS: + ret = ublk_ctrl_get_params(cmd); + break; + case UBLK_CMD_SET_PARAMS: + ret = ublk_ctrl_set_params(cmd); + break; default: break; } diff --git a/drivers/bluetooth/btmrvl_debugfs.c b/drivers/bluetooth/btmrvl_debugfs.c index db35b917aecf..32329a2e526f 100644 --- a/drivers/bluetooth/btmrvl_debugfs.c +++ b/drivers/bluetooth/btmrvl_debugfs.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Marvell Bluetooth driver: debugfs related functions * * Copyright (C) 2009, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. **/ #include diff --git a/drivers/bluetooth/btmrvl_drv.h b/drivers/bluetooth/btmrvl_drv.h index fb7729779166..d7df05c56b28 100644 --- a/drivers/bluetooth/btmrvl_drv.h +++ b/drivers/bluetooth/btmrvl_drv.h @@ -1,22 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Marvell Bluetooth driver: global definitions & declarations * * Copyright (C) 2009, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - * */ #include diff --git a/drivers/bluetooth/btmrvl_main.c b/drivers/bluetooth/btmrvl_main.c index 181338f60530..9658b33c824a 100644 --- a/drivers/bluetooth/btmrvl_main.c +++ b/drivers/bluetooth/btmrvl_main.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Marvell Bluetooth driver * * Copyright (C) 2009, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. **/ #include diff --git a/drivers/bluetooth/btmrvl_sdio.c b/drivers/bluetooth/btmrvl_sdio.c index b8ef66f89fc1..ba057ebfda5c 100644 --- a/drivers/bluetooth/btmrvl_sdio.c +++ b/drivers/bluetooth/btmrvl_sdio.c @@ -1,21 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Marvell BT-over-SDIO driver: SDIO interface related functions. * * Copyright (C) 2009, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. **/ #include diff --git a/drivers/bluetooth/btmrvl_sdio.h b/drivers/bluetooth/btmrvl_sdio.h index 3a522d23ee6e..72dd3b7d82aa 100644 --- a/drivers/bluetooth/btmrvl_sdio.h +++ b/drivers/bluetooth/btmrvl_sdio.h @@ -1,22 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /** * Marvell BT-over-SDIO driver: SDIO interface related definitions * * Copyright (C) 2009, Marvell International Ltd. - * - * This software file (the "File") is distributed by Marvell International - * Ltd. under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - * **/ #define SDIO_HEADER_LEN 4 diff --git a/drivers/bus/mvebu-mbus.c b/drivers/bus/mvebu-mbus.c index 9711e4c827a3..5dc2669432ba 100644 --- a/drivers/bus/mvebu-mbus.c +++ b/drivers/bus/mvebu-mbus.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Address map functions for Marvell EBU SoCs (Kirkwood, Armada * 370/XP, Dove, Orion5x and MV78xx0) * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The Marvell EBU SoCs have a configurable physical address space: * the physical address at which certain devices (PCIe, NOR, NAND, * etc.) sit can be configured. The configuration takes place through diff --git a/drivers/bus/omap_l3_noc.c b/drivers/bus/omap_l3_noc.c index dcfb32ee5cb6..eb1ba6319fda 100644 --- a/drivers/bus/omap_l3_noc.c +++ b/drivers/bus/omap_l3_noc.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP L3 Interconnect error handling driver * * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar * Sricharan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/bus/omap_l3_noc.h b/drivers/bus/omap_l3_noc.h index 73431f81da28..bb3eebd3465d 100644 --- a/drivers/bus/omap_l3_noc.h +++ b/drivers/bus/omap_l3_noc.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * OMAP L3 Interconnect error handling driver header * * Copyright (C) 2011-2015 Texas Instruments Incorporated - http://www.ti.com/ * Santosh Shilimkar * sricharan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __OMAP_L3_NOC_H #define __OMAP_L3_NOC_H diff --git a/drivers/bus/sunxi-rsb.c b/drivers/bus/sunxi-rsb.c index 60b082fe2ed0..4cd2e127946e 100644 --- a/drivers/bus/sunxi-rsb.c +++ b/drivers/bus/sunxi-rsb.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * RSB (Reduced Serial Bus) driver. * * Author: Chen-Yu Tsai * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. - * * The RSB controller looks like an SMBus controller which only supports * byte and word data transfers. But, it differs from standard SMBus * protocol on several aspects: @@ -31,7 +28,6 @@ * This document is officially released by Allwinner. * * This driver is based on i2c-sun6i-p2wi.c, the P2WI bus driver. - * */ #include diff --git a/drivers/bus/tegra-gmi.c b/drivers/bus/tegra-gmi.c index 35b59f92fa66..662266719682 100644 --- a/drivers/bus/tegra-gmi.c +++ b/drivers/bus/tegra-gmi.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for NVIDIA Generic Memory Interface * * Copyright (C) 2016 Host Mobility AB. All rights reserved. - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/bus/ts-nbus.c b/drivers/bus/ts-nbus.c index 9989ce904a37..38c886dc2ed6 100644 --- a/drivers/bus/ts-nbus.c +++ b/drivers/bus/ts-nbus.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NBUS driver for TS-4600 based boards * * Copyright (c) 2016 - Savoir-faire Linux * Author: Sebastien Bourdelin * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * This driver implements a GPIOs bit-banged bus, called the NBUS by Technologic * Systems. It is used to communicate with the peripherals in the FPGA on the * TS-4600 SoM. diff --git a/drivers/char/hw_random/iproc-rng200.c b/drivers/char/hw_random/iproc-rng200.c index a43743887db1..06bc060534d8 100644 --- a/drivers/char/hw_random/iproc-rng200.c +++ b/drivers/char/hw_random/iproc-rng200.c @@ -1,14 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Broadcom Corporation * -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License as -* published by the Free Software Foundation version 2. -* -* This program is distributed "as is" WITHOUT ANY WARRANTY of any -* kind, whether express or implied; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. */ /* * DESCRIPTION: The Broadcom iProc RNG200 Driver diff --git a/drivers/clk/axs10x/i2s_pll_clock.c b/drivers/clk/axs10x/i2s_pll_clock.c index e9da0e69bf6c..e1fda6ad5cd5 100644 --- a/drivers/clk/axs10x/i2s_pll_clock.c +++ b/drivers/clk/axs10x/i2s_pll_clock.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Synopsys AXS10X SDP I2S PLL clock driver * * Copyright (C) 2016 Synopsys - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/axs10x/pll_clock.c b/drivers/clk/axs10x/pll_clock.c index 500345d99adb..90fb0e6ff573 100644 --- a/drivers/clk/axs10x/pll_clock.c +++ b/drivers/clk/axs10x/pll_clock.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Synopsys AXS10X SDP Generic PLL clock driver * * Copyright (C) 2017 Synopsys - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/bcm/clk-bcm21664.c b/drivers/clk/bcm/clk-bcm21664.c index eeae4cad2281..520c3aeb4ea9 100644 --- a/drivers/clk/bcm/clk-bcm21664.c +++ b/drivers/clk/bcm/clk-bcm21664.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Broadcom Corporation * Copyright 2014 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "clk-kona.h" diff --git a/drivers/clk/bcm/clk-bcm281xx.c b/drivers/clk/bcm/clk-bcm281xx.c index 502a487d62c5..823d5dfa31b8 100644 --- a/drivers/clk/bcm/clk-bcm281xx.c +++ b/drivers/clk/bcm/clk-bcm281xx.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "clk-kona.h" diff --git a/drivers/clk/bcm/clk-bcm63xx.c b/drivers/clk/bcm/clk-bcm63xx.c index fbc17ae5ff2b..c8383834fb39 100644 --- a/drivers/clk/bcm/clk-bcm63xx.c +++ b/drivers/clk/bcm/clk-bcm63xx.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015 Broadcom Corporation #include #include #include diff --git a/drivers/clk/bcm/clk-cygnus.c b/drivers/clk/bcm/clk-cygnus.c index b8d073e4855f..43b04fc4c493 100644 --- a/drivers/clk/bcm/clk-cygnus.c +++ b/drivers/clk/bcm/clk-cygnus.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/clk/bcm/clk-hr2.c b/drivers/clk/bcm/clk-hr2.c index f7c5b7379475..9f6318f3752b 100644 --- a/drivers/clk/bcm/clk-hr2.c +++ b/drivers/clk/bcm/clk-hr2.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2017 Broadcom #include #include diff --git a/drivers/clk/bcm/clk-iproc-armpll.c b/drivers/clk/bcm/clk-iproc-armpll.c index d7d628214b85..9e86c0c10b57 100644 --- a/drivers/clk/bcm/clk-iproc-armpll.c +++ b/drivers/clk/bcm/clk-iproc-armpll.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/clk/bcm/clk-iproc-asiu.c b/drivers/clk/bcm/clk-iproc-asiu.c index e062dd4992ea..dcacf55c55ae 100644 --- a/drivers/clk/bcm/clk-iproc-asiu.c +++ b/drivers/clk/bcm/clk-iproc-asiu.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/clk/bcm/clk-iproc-pll.c b/drivers/clk/bcm/clk-iproc-pll.c index 33da30f99c79..1a098db12062 100644 --- a/drivers/clk/bcm/clk-iproc-pll.c +++ b/drivers/clk/bcm/clk-iproc-pll.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/clk/bcm/clk-iproc.h b/drivers/clk/bcm/clk-iproc.h index d7e5b94bed45..0151d6ae1661 100644 --- a/drivers/clk/bcm/clk-iproc.h +++ b/drivers/clk/bcm/clk-iproc.h @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2014 Broadcom Corporation */ #ifndef _CLK_IPROC_H #define _CLK_IPROC_H diff --git a/drivers/clk/bcm/clk-kona-setup.c b/drivers/clk/bcm/clk-kona-setup.c index 5dd65164c8b1..338558f6fbae 100644 --- a/drivers/clk/bcm/clk-kona-setup.c +++ b/drivers/clk/bcm/clk-kona-setup.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/bcm/clk-kona.c b/drivers/clk/bcm/clk-kona.c index cc3b1e1bc087..ec5749e301ba 100644 --- a/drivers/clk/bcm/clk-kona.c +++ b/drivers/clk/bcm/clk-kona.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include "clk-kona.h" diff --git a/drivers/clk/bcm/clk-kona.h b/drivers/clk/bcm/clk-kona.h index f4b39bb5558a..e09655024ac2 100644 --- a/drivers/clk/bcm/clk-kona.h +++ b/drivers/clk/bcm/clk-kona.h @@ -1,15 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _CLK_KONA_H diff --git a/drivers/clk/bcm/clk-ns2.c b/drivers/clk/bcm/clk-ns2.c index adc14145861a..065f4290aaad 100644 --- a/drivers/clk/bcm/clk-ns2.c +++ b/drivers/clk/bcm/clk-ns2.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015 Broadcom Corporation #include #include diff --git a/drivers/clk/bcm/clk-nsp.c b/drivers/clk/bcm/clk-nsp.c index cf66f640a47d..c24c9adbc6f3 100644 --- a/drivers/clk/bcm/clk-nsp.c +++ b/drivers/clk/bcm/clk-nsp.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015 Broadcom Corporation #include #include diff --git a/drivers/clk/clk-devres.c b/drivers/clk/clk-devres.c index f9d5b7334341..4fb4fd4b06bd 100644 --- a/drivers/clk/clk-devres.c +++ b/drivers/clk/clk-devres.c @@ -4,42 +4,101 @@ #include #include +struct devm_clk_state { + struct clk *clk; + void (*exit)(struct clk *clk); +}; + static void devm_clk_release(struct device *dev, void *res) { - clk_put(*(struct clk **)res); + struct devm_clk_state *state = res; + + if (state->exit) + state->exit(state->clk); + + clk_put(state->clk); +} + +static struct clk *__devm_clk_get(struct device *dev, const char *id, + struct clk *(*get)(struct device *dev, const char *id), + int (*init)(struct clk *clk), + void (*exit)(struct clk *clk)) +{ + struct devm_clk_state *state; + struct clk *clk; + int ret; + + state = devres_alloc(devm_clk_release, sizeof(*state), GFP_KERNEL); + if (!state) + return ERR_PTR(-ENOMEM); + + clk = get(dev, id); + if (IS_ERR(clk)) { + ret = PTR_ERR(clk); + goto err_clk_get; + } + + if (init) { + ret = init(clk); + if (ret) + goto err_clk_init; + } + + state->clk = clk; + state->exit = exit; + + devres_add(dev, state); + + return clk; + +err_clk_init: + + clk_put(clk); +err_clk_get: + + devres_free(state); + return ERR_PTR(ret); } struct clk *devm_clk_get(struct device *dev, const char *id) { - struct clk **ptr, *clk; - - ptr = devres_alloc(devm_clk_release, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); - - clk = clk_get(dev, id); - if (!IS_ERR(clk)) { - *ptr = clk; - devres_add(dev, ptr); - } else { - devres_free(ptr); - } - - return clk; + return __devm_clk_get(dev, id, clk_get, NULL, NULL); } EXPORT_SYMBOL(devm_clk_get); +struct clk *devm_clk_get_prepared(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get, clk_prepare, clk_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_prepared); + +struct clk *devm_clk_get_enabled(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get, + clk_prepare_enable, clk_disable_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_enabled); + struct clk *devm_clk_get_optional(struct device *dev, const char *id) { - struct clk *clk = devm_clk_get(dev, id); - - if (clk == ERR_PTR(-ENOENT)) - return NULL; - - return clk; + return __devm_clk_get(dev, id, clk_get_optional, NULL, NULL); } EXPORT_SYMBOL(devm_clk_get_optional); +struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get_optional, + clk_prepare, clk_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_optional_prepared); + +struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id) +{ + return __devm_clk_get(dev, id, clk_get_optional, + clk_prepare_enable, clk_disable_unprepare); +} +EXPORT_SYMBOL_GPL(devm_clk_get_optional_enabled); + struct clk_bulk_devres { struct clk_bulk_data *clks; int num_clks; diff --git a/drivers/clk/clk-fixed-factor.c b/drivers/clk/clk-fixed-factor.c index 54942d758ee6..f734e34735a9 100644 --- a/drivers/clk/clk-fixed-factor.c +++ b/drivers/clk/clk-fixed-factor.c @@ -78,7 +78,8 @@ static void devm_clk_hw_register_fixed_factor_release(struct device *dev, void * static struct clk_hw * __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np, - const char *name, const char *parent_name, int index, + const char *name, const char *parent_name, + const struct clk_hw *parent_hw, int index, unsigned long flags, unsigned int mult, unsigned int div, bool devm) { @@ -110,6 +111,8 @@ __clk_hw_register_fixed_factor(struct device *dev, struct device_node *np, init.flags = flags; if (parent_name) init.parent_names = &parent_name; + else if (parent_hw) + init.parent_hws = &parent_hw; else init.parent_data = &pdata; init.num_parents = 1; @@ -148,16 +151,48 @@ struct clk_hw *devm_clk_hw_register_fixed_factor_index(struct device *dev, const char *name, unsigned int index, unsigned long flags, unsigned int mult, unsigned int div) { - return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, index, + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, NULL, index, flags, mult, div, true); } EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_index); +/** + * devm_clk_hw_register_fixed_factor_parent_hw - Register a fixed factor clock with + * pointer to parent clock + * @dev: device that is registering this clock + * @name: name of this clock + * @parent_hw: pointer to parent clk + * @flags: fixed factor flags + * @mult: multiplier + * @div: divider + * + * Return: Pointer to fixed factor clk_hw structure that was registered or + * an error pointer. + */ +struct clk_hw *devm_clk_hw_register_fixed_factor_parent_hw(struct device *dev, + const char *name, const struct clk_hw *parent_hw, + unsigned long flags, unsigned int mult, unsigned int div) +{ + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, parent_hw, + -1, flags, mult, div, true); +} +EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor_parent_hw); + +struct clk_hw *clk_hw_register_fixed_factor_parent_hw(struct device *dev, + const char *name, const struct clk_hw *parent_hw, + unsigned long flags, unsigned int mult, unsigned int div) +{ + return __clk_hw_register_fixed_factor(dev, NULL, name, NULL, + parent_hw, -1, flags, mult, div, + false); +} +EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor_parent_hw); + struct clk_hw *clk_hw_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { - return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1, + return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL, -1, flags, mult, div, false); } EXPORT_SYMBOL_GPL(clk_hw_register_fixed_factor); @@ -204,22 +239,16 @@ struct clk_hw *devm_clk_hw_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div) { - return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, -1, + return __clk_hw_register_fixed_factor(dev, NULL, name, parent_name, NULL, -1, flags, mult, div, true); } EXPORT_SYMBOL_GPL(devm_clk_hw_register_fixed_factor); #ifdef CONFIG_OF -static const struct of_device_id set_rate_parent_matches[] = { - { .compatible = "allwinner,sun4i-a10-pll3-2x-clk" }, - { /* Sentinel */ }, -}; - static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node) { struct clk_hw *hw; const char *clk_name = node->name; - unsigned long flags = 0; u32 div, mult; int ret; @@ -237,11 +266,8 @@ static struct clk_hw *_of_fixed_factor_clk_setup(struct device_node *node) of_property_read_string(node, "clock-output-names", &clk_name); - if (of_match_node(set_rate_parent_matches, node)) - flags |= CLK_SET_RATE_PARENT; - - hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, 0, - flags, mult, div, false); + hw = __clk_hw_register_fixed_factor(NULL, node, clk_name, NULL, NULL, 0, + 0, mult, div, false); if (IS_ERR(hw)) { /* * Clear OF_POPULATED flag so that clock registration can be diff --git a/drivers/clk/clk-hsdk-pll.c b/drivers/clk/clk-hsdk-pll.c index b4f8852201cb..60007b508590 100644 --- a/drivers/clk/clk-hsdk-pll.c +++ b/drivers/clk/clk-hsdk-pll.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Synopsys HSDK SDP Generic PLL clock driver * * Copyright (C) 2017 Synopsys - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/clk-moxart.c b/drivers/clk/clk-moxart.c index 58428d0043fd..3786a0153ad1 100644 --- a/drivers/clk/clk-moxart.c +++ b/drivers/clk/clk-moxart.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MOXA ART SoCs clock driver. * * Copyright (C) 2013 Jonas Jensen * * Jonas Jensen - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 777f56b210dc..8dcee98cf743 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -4344,54 +4344,6 @@ int devm_clk_hw_register(struct device *dev, struct clk_hw *hw) } EXPORT_SYMBOL_GPL(devm_clk_hw_register); -static int devm_clk_match(struct device *dev, void *res, void *data) -{ - struct clk *c = res; - if (WARN_ON(!c)) - return 0; - return c == data; -} - -static int devm_clk_hw_match(struct device *dev, void *res, void *data) -{ - struct clk_hw *hw = res; - - if (WARN_ON(!hw)) - return 0; - return hw == data; -} - -/** - * devm_clk_unregister - resource managed clk_unregister() - * @dev: device that is unregistering the clock data - * @clk: clock to unregister - * - * Deallocate a clock allocated with devm_clk_register(). Normally - * this function will not need to be called and the resource management - * code will ensure that the resource is freed. - */ -void devm_clk_unregister(struct device *dev, struct clk *clk) -{ - WARN_ON(devres_release(dev, devm_clk_unregister_cb, devm_clk_match, clk)); -} -EXPORT_SYMBOL_GPL(devm_clk_unregister); - -/** - * devm_clk_hw_unregister - resource managed clk_hw_unregister() - * @dev: device that is unregistering the hardware-specific clock data - * @hw: link to hardware-specific clock data - * - * Unregister a clk_hw registered with devm_clk_hw_register(). Normally - * this function will not need to be called and the resource management - * code will ensure that the resource is freed. - */ -void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw) -{ - WARN_ON(devres_release(dev, devm_clk_hw_unregister_cb, devm_clk_hw_match, - hw)); -} -EXPORT_SYMBOL_GPL(devm_clk_hw_unregister); - static void devm_clk_release(struct device *dev, void *res) { clk_put(*(struct clk **)res); diff --git a/drivers/clk/imx/clk-fracn-gppll.c b/drivers/clk/imx/clk-fracn-gppll.c index 71c102d950ab..a2aaa14fc1ae 100644 --- a/drivers/clk/imx/clk-fracn-gppll.c +++ b/drivers/clk/imx/clk-fracn-gppll.c @@ -64,10 +64,13 @@ struct clk_fracn_gppll { * Fout = Fvco / (rdiv * odiv) */ static const struct imx_fracn_gppll_rate_table fracn_tbl[] = { - PLL_FRACN_GP(650000000U, 81, 0, 0, 0, 3), - PLL_FRACN_GP(594000000U, 198, 0, 0, 0, 8), - PLL_FRACN_GP(560000000U, 70, 0, 0, 0, 3), - PLL_FRACN_GP(400000000U, 50, 0, 0, 0, 3), + PLL_FRACN_GP(650000000U, 81, 0, 1, 0, 3), + PLL_FRACN_GP(594000000U, 198, 0, 1, 0, 8), + PLL_FRACN_GP(560000000U, 70, 0, 1, 0, 3), + PLL_FRACN_GP(498000000U, 83, 0, 1, 0, 4), + PLL_FRACN_GP(484000000U, 121, 0, 1, 0, 6), + PLL_FRACN_GP(445333333U, 167, 0, 1, 0, 9), + PLL_FRACN_GP(400000000U, 50, 0, 1, 0, 3), PLL_FRACN_GP(393216000U, 81, 92, 100, 0, 5) }; @@ -131,18 +134,7 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon mfi = FIELD_GET(PLL_MFI_MASK, pll_div); rdiv = FIELD_GET(PLL_RDIV_MASK, pll_div); - rdiv = rdiv + 1; odiv = FIELD_GET(PLL_ODIV_MASK, pll_div); - switch (odiv) { - case 0: - odiv = 2; - break; - case 1: - odiv = 3; - break; - default: - break; - } /* * Sometimes, the recalculated rate has deviation due to @@ -160,6 +152,20 @@ static unsigned long clk_fracn_gppll_recalc_rate(struct clk_hw *hw, unsigned lon if (rate) return (unsigned long)rate; + if (!rdiv) + rdiv = rdiv + 1; + + switch (odiv) { + case 0: + odiv = 2; + break; + case 1: + odiv = 3; + break; + default: + break; + } + /* Fvco = Fref * (MFI + MFN / MFD) */ fvco = fvco * mfi * mfd + fvco * mfn; do_div(fvco, mfd * rdiv * odiv); diff --git a/drivers/clk/imx/clk-imx93.c b/drivers/clk/imx/clk-imx93.c index edcc87661d1f..f5c9fa40491c 100644 --- a/drivers/clk/imx/clk-imx93.c +++ b/drivers/clk/imx/clk-imx93.c @@ -150,7 +150,7 @@ static const struct imx93_clk_ccgr { { IMX93_CLK_A55_GATE, "a55", "a55_root", 0x8000, }, /* M33 critical clk for system run */ { IMX93_CLK_CM33_GATE, "cm33", "m33_root", 0x8040, CLK_IS_CRITICAL }, - { IMX93_CLK_ADC1_GATE, "adc1", "osc_24m", 0x82c0, }, + { IMX93_CLK_ADC1_GATE, "adc1", "adc_root", 0x82c0, }, { IMX93_CLK_WDOG1_GATE, "wdog1", "osc_24m", 0x8300, }, { IMX93_CLK_WDOG2_GATE, "wdog2", "osc_24m", 0x8340, }, { IMX93_CLK_WDOG3_GATE, "wdog3", "osc_24m", 0x8380, }, @@ -160,7 +160,7 @@ static const struct imx93_clk_ccgr { { IMX93_CLK_SEMA2_GATE, "sema2", "bus_wakeup_root", 0x8480, }, { IMX93_CLK_MU_A_GATE, "mu_a", "bus_aon_root", 0x84c0, }, { IMX93_CLK_MU_B_GATE, "mu_b", "bus_aon_root", 0x8500, }, - { IMX93_CLK_EDMA1_GATE, "edma1", "wakeup_axi_root", 0x8540, }, + { IMX93_CLK_EDMA1_GATE, "edma1", "m33_root", 0x8540, }, { IMX93_CLK_EDMA2_GATE, "edma2", "wakeup_axi_root", 0x8580, }, { IMX93_CLK_FLEXSPI1_GATE, "flexspi", "flexspi_root", 0x8640, }, { IMX93_CLK_GPIO1_GATE, "gpio1", "m33_root", 0x8880, }, @@ -219,7 +219,7 @@ static const struct imx93_clk_ccgr { { IMX93_CLK_LCDIF_GATE, "lcdif", "media_apb_root", 0x9640, }, { IMX93_CLK_PXP_GATE, "pxp", "media_apb_root", 0x9680, }, { IMX93_CLK_ISI_GATE, "isi", "media_apb_root", 0x96c0, }, - { IMX93_CLK_NIC_MEDIA_GATE, "nic_media", "media_apb_root", 0x9700, }, + { IMX93_CLK_NIC_MEDIA_GATE, "nic_media", "media_axi_root", 0x9700, }, { IMX93_CLK_USB_CONTROLLER_GATE, "usb_controller", "hsio_root", 0x9a00, }, { IMX93_CLK_USB_TEST_60M_GATE, "usb_test_60m", "hsio_usb_test_60m_root", 0x9a40, }, { IMX93_CLK_HSIO_TROUT_24M_GATE, "hsio_trout_24m", "osc_24m", 0x9a80, }, diff --git a/drivers/clk/keystone/sci-clk.c b/drivers/clk/keystone/sci-clk.c index 7e1b136e71ae..d4b4e74e22da 100644 --- a/drivers/clk/keystone/sci-clk.c +++ b/drivers/clk/keystone/sci-clk.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SCI Clock driver for keystone based devices * * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/clk/mediatek/clk-mt2701-eth.c b/drivers/clk/mediatek/clk-mt2701-eth.c index 47c2289f3d1d..edf1e2ed2b59 100644 --- a/drivers/clk/mediatek/clk-mt2701-eth.c +++ b/drivers/clk/mediatek/clk-mt2701-eth.c @@ -36,6 +36,14 @@ static const struct mtk_gate eth_clks[] = { GATE_ETH(CLK_ETHSYS_CRYPTO, "crypto_clk", "ethif_sel", 29), }; +static u16 rst_ofs[] = { 0x34, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static const struct of_device_id of_match_clk_mt2701_eth[] = { { .compatible = "mediatek,mt2701-ethsys", }, {} @@ -58,7 +66,7 @@ static int clk_mt2701_eth_probe(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt2701-g3d.c b/drivers/clk/mediatek/clk-mt2701-g3d.c index 79929ed37f83..1458109d99d9 100644 --- a/drivers/clk/mediatek/clk-mt2701-g3d.c +++ b/drivers/clk/mediatek/clk-mt2701-g3d.c @@ -35,6 +35,14 @@ static const struct mtk_gate g3d_clks[] = { GATE_G3D(CLK_G3DSYS_CORE, "g3d_core", "mfg_sel", 0), }; +static u16 rst_ofs[] = { 0xc, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static int clk_mt2701_g3dsys_init(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -52,7 +60,7 @@ static int clk_mt2701_g3dsys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0xc); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt2701-hif.c b/drivers/clk/mediatek/clk-mt2701-hif.c index 1aa36cb93ad0..434cbbe8c037 100644 --- a/drivers/clk/mediatek/clk-mt2701-hif.c +++ b/drivers/clk/mediatek/clk-mt2701-hif.c @@ -33,6 +33,14 @@ static const struct mtk_gate hif_clks[] = { GATE_HIF(CLK_HIFSYS_PCIE2, "pcie2_clk", "ethpll_500m_ck", 26), }; +static u16 rst_ofs[] = { 0x34, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static const struct of_device_id of_match_clk_mt2701_hif[] = { { .compatible = "mediatek,mt2701-hifsys", }, {} @@ -57,7 +65,7 @@ static int clk_mt2701_hif_probe(struct platform_device *pdev) return r; } - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return 0; } diff --git a/drivers/clk/mediatek/clk-mt2701.c b/drivers/clk/mediatek/clk-mt2701.c index 04ba356db2d7..9b442af37e67 100644 --- a/drivers/clk/mediatek/clk-mt2701.c +++ b/drivers/clk/mediatek/clk-mt2701.c @@ -735,6 +735,24 @@ static const struct mtk_fixed_factor infra_fixed_divs[] = { FACTOR(CLK_INFRA_CLK_13M, "clk13m", "clk26m", 1, 2), }; +static u16 infrasys_rst_ofs[] = { 0x30, 0x34, }; +static u16 pericfg_rst_ofs[] = { 0x0, 0x4, }; + +static const struct mtk_clk_rst_desc clk_rst_desc[] = { + /* infrasys */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = infrasys_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs), + }, + /* pericfg */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = pericfg_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs), + }, +}; + static struct clk_hw_onecell_data *infra_clk_data; static void __init mtk_infrasys_init_early(struct device_node *node) @@ -787,7 +805,7 @@ static int mtk_infrasys_init(struct platform_device *pdev) if (r) return r; - mtk_register_reset_controller(node, 2, 0x30); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[0]); return 0; } @@ -910,7 +928,7 @@ static int mtk_pericfg_init(struct platform_device *pdev) if (r) return r; - mtk_register_reset_controller(node, 2, 0x0); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[1]); return 0; } diff --git a/drivers/clk/mediatek/clk-mt2712.c b/drivers/clk/mediatek/clk-mt2712.c index 410b059727ea..56980dd6c2ea 100644 --- a/drivers/clk/mediatek/clk-mt2712.c +++ b/drivers/clk/mediatek/clk-mt2712.c @@ -1258,6 +1258,24 @@ static const struct mtk_pll_data plls[] = { 0, 31, 0x0300, 4, 0, 0, 0, 0x0304, 0), }; +static u16 infrasys_rst_ofs[] = { 0x30, 0x34, }; +static u16 pericfg_rst_ofs[] = { 0x0, 0x4, }; + +static const struct mtk_clk_rst_desc clk_rst_desc[] = { + /* infra */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = infrasys_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs), + }, + /* peri */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = pericfg_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs), + }, +}; + static int clk_mt2712_apmixed_probe(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -1361,7 +1379,7 @@ static int clk_mt2712_infra_probe(struct platform_device *pdev) pr_err("%s(): could not register clock provider: %d\n", __func__, r); - mtk_register_reset_controller(node, 2, 0x30); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[0]); return r; } @@ -1383,7 +1401,7 @@ static int clk_mt2712_peri_probe(struct platform_device *pdev) pr_err("%s(): could not register clock provider: %d\n", __func__, r); - mtk_register_reset_controller(node, 2, 0); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[1]); return r; } diff --git a/drivers/clk/mediatek/clk-mt7622-eth.c b/drivers/clk/mediatek/clk-mt7622-eth.c index b12d48705496..43de0477d5d9 100644 --- a/drivers/clk/mediatek/clk-mt7622-eth.c +++ b/drivers/clk/mediatek/clk-mt7622-eth.c @@ -65,6 +65,14 @@ static const struct mtk_gate sgmii_clks[] = { "ssusb_cdr_fb", 5), }; +static u16 rst_ofs[] = { 0x34, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static int clk_mt7622_ethsys_init(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -82,7 +90,7 @@ static int clk_mt7622_ethsys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt7622-hif.c b/drivers/clk/mediatek/clk-mt7622-hif.c index 58728e35e80a..67e96231dd25 100644 --- a/drivers/clk/mediatek/clk-mt7622-hif.c +++ b/drivers/clk/mediatek/clk-mt7622-hif.c @@ -76,6 +76,14 @@ static const struct mtk_gate pcie_clks[] = { GATE_PCIE(CLK_SATA_PM_EN, "sata_pm_en", "univpll2_d4", 30), }; +static u16 rst_ofs[] = { 0x34, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static int clk_mt7622_ssusbsys_init(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -93,7 +101,7 @@ static int clk_mt7622_ssusbsys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } @@ -115,7 +123,7 @@ static int clk_mt7622_pciesys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt7622.c b/drivers/clk/mediatek/clk-mt7622.c index e4a5e5230861..3b55f8641fae 100644 --- a/drivers/clk/mediatek/clk-mt7622.c +++ b/drivers/clk/mediatek/clk-mt7622.c @@ -610,6 +610,24 @@ static struct mtk_composite peri_muxes[] = { MUX(CLK_PERIBUS_SEL, "peribus_ck_sel", peribus_ck_parents, 0x05C, 0, 1), }; +static u16 infrasys_rst_ofs[] = { 0x30, }; +static u16 pericfg_rst_ofs[] = { 0x0, 0x4, }; + +static const struct mtk_clk_rst_desc clk_rst_desc[] = { + /* infrasys */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = infrasys_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs), + }, + /* pericfg */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = pericfg_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs), + }, +}; + static int mtk_topckgen_init(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -663,7 +681,7 @@ static int mtk_infrasys_init(struct platform_device *pdev) if (r) return r; - mtk_register_reset_controller(node, 1, 0x30); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[0]); return 0; } @@ -714,7 +732,7 @@ static int mtk_pericfg_init(struct platform_device *pdev) clk_prepare_enable(clk_data->hws[CLK_PERI_UART0_PD]->clk); - mtk_register_reset_controller(node, 2, 0x0); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc[1]); return 0; } diff --git a/drivers/clk/mediatek/clk-mt7629-eth.c b/drivers/clk/mediatek/clk-mt7629-eth.c index c49fd732c9b2..282dd6559465 100644 --- a/drivers/clk/mediatek/clk-mt7629-eth.c +++ b/drivers/clk/mediatek/clk-mt7629-eth.c @@ -76,6 +76,14 @@ static const struct mtk_gate sgmii_clks[2][4] = { } }; +static u16 rst_ofs[] = { 0x34, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static int clk_mt7629_ethsys_init(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -92,7 +100,7 @@ static int clk_mt7629_ethsys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt7629-hif.c b/drivers/clk/mediatek/clk-mt7629-hif.c index acaa97fda331..0c8b9e139789 100644 --- a/drivers/clk/mediatek/clk-mt7629-hif.c +++ b/drivers/clk/mediatek/clk-mt7629-hif.c @@ -71,6 +71,14 @@ static const struct mtk_gate pcie_clks[] = { GATE_PCIE(CLK_PCIE_P0_PIPE_EN, "pcie_p0_pipe_en", "pcie0_pipe_en", 23), }; +static u16 rst_ofs[] = { 0x34, }; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = rst_ofs, + .rst_bank_nr = ARRAY_SIZE(rst_ofs), +}; + static int clk_mt7629_ssusbsys_init(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -88,7 +96,7 @@ static int clk_mt7629_ssusbsys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } @@ -110,7 +118,7 @@ static int clk_mt7629_pciesys_init(struct platform_device *pdev) "could not register clock provider: %s: %d\n", pdev->name, r); - mtk_register_reset_controller(node, 1, 0x34); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt8135.c b/drivers/clk/mediatek/clk-mt8135.c index 9ef524b44862..b68888a034c4 100644 --- a/drivers/clk/mediatek/clk-mt8135.c +++ b/drivers/clk/mediatek/clk-mt8135.c @@ -514,6 +514,24 @@ static const struct mtk_composite peri_clks[] __initconst = { MUX(CLK_PERI_UART3_SEL, "uart3_ck_sel", uart_ck_sel_parents, 0x40c, 3, 1), }; +static u16 infrasys_rst_ofs[] = { 0x30, 0x34, }; +static u16 pericfg_rst_ofs[] = { 0x0, 0x4, }; + +static const struct mtk_clk_rst_desc clk_rst_desc[] = { + /* infrasys */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = infrasys_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs), + }, + /* pericfg */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = pericfg_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs), + } +}; + static void __init mtk_topckgen_init(struct device_node *node) { struct clk_hw_onecell_data *clk_data; @@ -559,7 +577,7 @@ static void __init mtk_infrasys_init(struct device_node *node) pr_err("%s(): could not register clock provider: %d\n", __func__, r); - mtk_register_reset_controller(node, 2, 0x30); + mtk_register_reset_controller(node, &clk_rst_desc[0]); } CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8135-infracfg", mtk_infrasys_init); @@ -587,7 +605,7 @@ static void __init mtk_pericfg_init(struct device_node *node) pr_err("%s(): could not register clock provider: %d\n", __func__, r); - mtk_register_reset_controller(node, 2, 0); + mtk_register_reset_controller(node, &clk_rst_desc[1]); } CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8135-pericfg", mtk_pericfg_init); diff --git a/drivers/clk/mediatek/clk-mt8173.c b/drivers/clk/mediatek/clk-mt8173.c index 0929db330852..b8529ee7199d 100644 --- a/drivers/clk/mediatek/clk-mt8173.c +++ b/drivers/clk/mediatek/clk-mt8173.c @@ -819,6 +819,24 @@ static const struct mtk_gate venclt_clks[] __initconst = { GATE_VENCLT(CLK_VENCLT_CKE1, "venclt_cke1", "venclt_sel", 4), }; +static u16 infrasys_rst_ofs[] = { 0x30, 0x34, }; +static u16 pericfg_rst_ofs[] = { 0x0, 0x4, }; + +static const struct mtk_clk_rst_desc clk_rst_desc[] = { + /* infrasys */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = infrasys_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infrasys_rst_ofs), + }, + /* pericfg */ + { + .version = MTK_RST_SIMPLE, + .rst_bank_ofs = pericfg_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(pericfg_rst_ofs), + } +}; + static struct clk_hw_onecell_data *mt8173_top_clk_data __initdata; static struct clk_hw_onecell_data *mt8173_pll_clk_data __initdata; @@ -882,7 +900,7 @@ static void __init mtk_infrasys_init(struct device_node *node) pr_err("%s(): could not register clock provider: %d\n", __func__, r); - mtk_register_reset_controller(node, 2, 0x30); + mtk_register_reset_controller(node, &clk_rst_desc[0]); } CLK_OF_DECLARE(mtk_infrasys, "mediatek,mt8173-infracfg", mtk_infrasys_init); @@ -910,7 +928,7 @@ static void __init mtk_pericfg_init(struct device_node *node) pr_err("%s(): could not register clock provider: %d\n", __func__, r); - mtk_register_reset_controller(node, 2, 0); + mtk_register_reset_controller(node, &clk_rst_desc[1]); } CLK_OF_DECLARE(mtk_pericfg, "mediatek,mt8173-pericfg", mtk_pericfg_init); diff --git a/drivers/clk/mediatek/clk-mt8183.c b/drivers/clk/mediatek/clk-mt8183.c index b5c17988c337..8512101e1189 100644 --- a/drivers/clk/mediatek/clk-mt8183.c +++ b/drivers/clk/mediatek/clk-mt8183.c @@ -18,9 +18,6 @@ #include -/* Infra global controller reset set register */ -#define INFRA_RST0_SET_OFFSET 0x120 - static DEFINE_SPINLOCK(mt8183_clk_lock); static const struct mtk_fixed_clk top_fixed_clks[] = { @@ -1153,6 +1150,19 @@ static const struct mtk_pll_data plls[] = { 0, 0, 32, 8, 0x02B4, 1, 0x02BC, 0x0014, 1, 0x02B8, 0, 0x02B4), }; +static u16 infra_rst_ofs[] = { + INFRA_RST0_SET_OFFSET, + INFRA_RST1_SET_OFFSET, + INFRA_RST2_SET_OFFSET, + INFRA_RST3_SET_OFFSET, +}; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SET_CLR, + .rst_bank_ofs = infra_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infra_rst_ofs), +}; + static int clk_mt8183_apmixed_probe(struct platform_device *pdev) { struct clk_hw_onecell_data *clk_data; @@ -1240,7 +1250,7 @@ static int clk_mt8183_infra_probe(struct platform_device *pdev) return r; } - mtk_register_reset_controller_set_clr(node, 4, INFRA_RST0_SET_OFFSET); + mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); return r; } diff --git a/drivers/clk/mediatek/clk-mt8186-infra_ao.c b/drivers/clk/mediatek/clk-mt8186-infra_ao.c index 2a7adc25abaa..df2a6bd1aefa 100644 --- a/drivers/clk/mediatek/clk-mt8186-infra_ao.c +++ b/drivers/clk/mediatek/clk-mt8186-infra_ao.c @@ -6,6 +6,7 @@ #include #include #include +#include #include "clk-gate.h" #include "clk-mtk.h" @@ -191,9 +192,31 @@ static const struct mtk_gate infra_ao_clks[] = { GATE_INFRA_AO3(CLK_INFRA_AO_FLASHIF_66M, "infra_ao_flashif_66m", "top_axi", 29), }; +static u16 infra_ao_rst_ofs[] = { + INFRA_RST0_SET_OFFSET, + INFRA_RST1_SET_OFFSET, + INFRA_RST2_SET_OFFSET, + INFRA_RST3_SET_OFFSET, + INFRA_RST4_SET_OFFSET, +}; + +static u16 infra_ao_idx_map[] = { + [MT8186_INFRA_THERMAL_CTRL_RST] = 0 * RST_NR_PER_BANK + 0, + [MT8186_INFRA_PTP_CTRL_RST] = 1 * RST_NR_PER_BANK + 0, +}; + +static struct mtk_clk_rst_desc infra_ao_rst_desc = { + .version = MTK_RST_SET_CLR, + .rst_bank_ofs = infra_ao_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infra_ao_rst_ofs), + .rst_idx_map = infra_ao_idx_map, + .rst_idx_map_nr = ARRAY_SIZE(infra_ao_idx_map), +}; + static const struct mtk_clk_desc infra_ao_desc = { .clks = infra_ao_clks, .num_clks = ARRAY_SIZE(infra_ao_clks), + .rst_desc = &infra_ao_rst_desc, }; static const struct of_device_id of_match_clk_mt8186_infra_ao[] = { diff --git a/drivers/clk/mediatek/clk-mt8192-msdc.c b/drivers/clk/mediatek/clk-mt8192-msdc.c index 87c3b79b79cf..635f7a0b629a 100644 --- a/drivers/clk/mediatek/clk-mt8192-msdc.c +++ b/drivers/clk/mediatek/clk-mt8192-msdc.c @@ -12,28 +12,15 @@ #include -static const struct mtk_gate_regs msdc_cg_regs = { - .set_ofs = 0xb4, - .clr_ofs = 0xb4, - .sta_ofs = 0xb4, -}; - static const struct mtk_gate_regs msdc_top_cg_regs = { .set_ofs = 0x0, .clr_ofs = 0x0, .sta_ofs = 0x0, }; -#define GATE_MSDC(_id, _name, _parent, _shift) \ - GATE_MTK(_id, _name, _parent, &msdc_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv) - #define GATE_MSDC_TOP(_id, _name, _parent, _shift) \ GATE_MTK(_id, _name, _parent, &msdc_top_cg_regs, _shift, &mtk_clk_gate_ops_no_setclr_inv) -static const struct mtk_gate msdc_clks[] = { - GATE_MSDC(CLK_MSDC_AXI_WRAP, "msdc_axi_wrap", "axi_sel", 22), -}; - static const struct mtk_gate msdc_top_clks[] = { GATE_MSDC_TOP(CLK_MSDC_TOP_AES_0P, "msdc_top_aes_0p", "aes_msdcfde_sel", 0), GATE_MSDC_TOP(CLK_MSDC_TOP_SRC_0P, "msdc_top_src_0p", "infra_msdc0_src", 1), @@ -52,11 +39,6 @@ static const struct mtk_gate msdc_top_clks[] = { GATE_MSDC_TOP(CLK_MSDC_TOP_AHB2AXI_BRG_AXI, "msdc_top_ahb2axi_brg_axi", "axi_sel", 14), }; -static const struct mtk_clk_desc msdc_desc = { - .clks = msdc_clks, - .num_clks = ARRAY_SIZE(msdc_clks), -}; - static const struct mtk_clk_desc msdc_top_desc = { .clks = msdc_top_clks, .num_clks = ARRAY_SIZE(msdc_top_clks), @@ -64,9 +46,6 @@ static const struct mtk_clk_desc msdc_top_desc = { static const struct of_device_id of_match_clk_mt8192_msdc[] = { { - .compatible = "mediatek,mt8192-msdc", - .data = &msdc_desc, - }, { .compatible = "mediatek,mt8192-msdc_top", .data = &msdc_top_desc, }, { diff --git a/drivers/clk/mediatek/clk-mt8192.c b/drivers/clk/mediatek/clk-mt8192.c index dda211b7a745..ebbd2798d9a3 100644 --- a/drivers/clk/mediatek/clk-mt8192.c +++ b/drivers/clk/mediatek/clk-mt8192.c @@ -18,6 +18,7 @@ #include "clk-pll.h" #include +#include static DEFINE_SPINLOCK(mt8192_clk_lock); @@ -1114,6 +1115,30 @@ static const struct mtk_gate top_clks[] = { GATE_TOP(CLK_TOP_SSUSB_PHY_REF, "ssusb_phy_ref", "clk26m", 25), }; +static u16 infra_ao_rst_ofs[] = { + INFRA_RST0_SET_OFFSET, + INFRA_RST1_SET_OFFSET, + INFRA_RST2_SET_OFFSET, + INFRA_RST3_SET_OFFSET, + INFRA_RST4_SET_OFFSET, +}; + +static u16 infra_ao_idx_map[] = { + [MT8192_INFRA_RST0_THERM_CTRL_SWRST] = 0 * RST_NR_PER_BANK + 0, + [MT8192_INFRA_RST2_PEXTP_PHY_SWRST] = 2 * RST_NR_PER_BANK + 15, + [MT8192_INFRA_RST3_THERM_CTRL_PTP_SWRST] = 3 * RST_NR_PER_BANK + 5, + [MT8192_INFRA_RST4_PCIE_TOP_SWRST] = 4 * RST_NR_PER_BANK + 1, + [MT8192_INFRA_RST4_THERM_CTRL_MCU_SWRST] = 4 * RST_NR_PER_BANK + 12, +}; + +static const struct mtk_clk_rst_desc clk_rst_desc = { + .version = MTK_RST_SET_CLR, + .rst_bank_ofs = infra_ao_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infra_ao_rst_ofs), + .rst_idx_map = infra_ao_idx_map, + .rst_idx_map_nr = ARRAY_SIZE(infra_ao_idx_map), +}; + #define MT8192_PLL_FMAX (3800UL * MHZ) #define MT8192_PLL_FMIN (1500UL * MHZ) #define MT8192_INTEGER_BITS 8 @@ -1240,6 +1265,10 @@ static int clk_mt8192_infra_probe(struct platform_device *pdev) if (r) goto free_clk_data; + r = mtk_register_reset_controller_with_dev(&pdev->dev, &clk_rst_desc); + if (r) + goto free_clk_data; + r = of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data); if (r) goto free_clk_data; diff --git a/drivers/clk/mediatek/clk-mt8195-infra_ao.c b/drivers/clk/mediatek/clk-mt8195-infra_ao.c index 8ebe3b9415c4..97657f255618 100644 --- a/drivers/clk/mediatek/clk-mt8195-infra_ao.c +++ b/drivers/clk/mediatek/clk-mt8195-infra_ao.c @@ -7,6 +7,7 @@ #include "clk-mtk.h" #include +#include #include #include @@ -182,9 +183,32 @@ static const struct mtk_gate infra_ao_clks[] = { GATE_INFRA_AO4(CLK_INFRA_AO_PERI_UFS_MEM_SUB, "infra_ao_peri_ufs_mem_sub", "mem_466m", 31), }; +static u16 infra_ao_rst_ofs[] = { + INFRA_RST0_SET_OFFSET, + INFRA_RST1_SET_OFFSET, + INFRA_RST2_SET_OFFSET, + INFRA_RST3_SET_OFFSET, + INFRA_RST4_SET_OFFSET, +}; + +static u16 infra_ao_idx_map[] = { + [MT8195_INFRA_RST0_THERM_CTRL_SWRST] = 0 * RST_NR_PER_BANK + 0, + [MT8195_INFRA_RST3_THERM_CTRL_PTP_SWRST] = 3 * RST_NR_PER_BANK + 5, + [MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST] = 4 * RST_NR_PER_BANK + 10, +}; + +static struct mtk_clk_rst_desc infra_ao_rst_desc = { + .version = MTK_RST_SET_CLR, + .rst_bank_ofs = infra_ao_rst_ofs, + .rst_bank_nr = ARRAY_SIZE(infra_ao_rst_ofs), + .rst_idx_map = infra_ao_idx_map, + .rst_idx_map_nr = ARRAY_SIZE(infra_ao_idx_map), +}; + static const struct mtk_clk_desc infra_ao_desc = { .clks = infra_ao_clks, .num_clks = ARRAY_SIZE(infra_ao_clks), + .rst_desc = &infra_ao_rst_desc, }; static const struct of_device_id of_match_clk_mt8195_infra_ao[] = { diff --git a/drivers/clk/mediatek/clk-mtk.c b/drivers/clk/mediatek/clk-mtk.c index b9188000ab3c..05a188c62119 100644 --- a/drivers/clk/mediatek/clk-mtk.c +++ b/drivers/clk/mediatek/clk-mtk.c @@ -444,6 +444,13 @@ int mtk_clk_simple_probe(struct platform_device *pdev) platform_set_drvdata(pdev, clk_data); + if (mcd->rst_desc) { + r = mtk_register_reset_controller_with_dev(&pdev->dev, + mcd->rst_desc); + if (r) + goto unregister_clks; + } + return r; unregister_clks: diff --git a/drivers/clk/mediatek/clk-mtk.h b/drivers/clk/mediatek/clk-mtk.h index adb1304d35d4..1b95c484d5aa 100644 --- a/drivers/clk/mediatek/clk-mtk.h +++ b/drivers/clk/mediatek/clk-mtk.h @@ -13,6 +13,8 @@ #include #include +#include "reset.h" + #define MAX_MUX_GATE_BIT 31 #define INVALID_MUX_GATE_BIT (MAX_MUX_GATE_BIT + 1) @@ -187,15 +189,10 @@ void mtk_free_clk_data(struct clk_hw_onecell_data *clk_data); struct clk_hw *mtk_clk_register_ref2usb_tx(const char *name, const char *parent_name, void __iomem *reg); -void mtk_register_reset_controller(struct device_node *np, - unsigned int num_regs, int regofs); - -void mtk_register_reset_controller_set_clr(struct device_node *np, - unsigned int num_regs, int regofs); - struct mtk_clk_desc { const struct mtk_gate *clks; size_t num_clks; + const struct mtk_clk_rst_desc *rst_desc; }; int mtk_clk_simple_probe(struct platform_device *pdev); diff --git a/drivers/clk/mediatek/reset.c b/drivers/clk/mediatek/reset.c index bcec4b89f449..179505549a7c 100644 --- a/drivers/clk/mediatek/reset.c +++ b/drivers/clk/mediatek/reset.c @@ -8,55 +8,39 @@ #include #include #include -#include #include -#include "clk-mtk.h" +#include "reset.h" -struct mtk_reset { - struct regmap *regmap; - int regofs; - struct reset_controller_dev rcdev; -}; - -static int mtk_reset_assert_set_clr(struct reset_controller_dev *rcdev, - unsigned long id) +static inline struct mtk_clk_rst_data *to_mtk_clk_rst_data(struct reset_controller_dev *rcdev) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - unsigned int reg = data->regofs + ((id / 32) << 4); - - return regmap_write(data->regmap, reg, 1); + return container_of(rcdev, struct mtk_clk_rst_data, rcdev); } -static int mtk_reset_deassert_set_clr(struct reset_controller_dev *rcdev, - unsigned long id) +static int mtk_reset_update(struct reset_controller_dev *rcdev, + unsigned long id, bool deassert) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - unsigned int reg = data->regofs + ((id / 32) << 4) + 0x4; + struct mtk_clk_rst_data *data = to_mtk_clk_rst_data(rcdev); + unsigned int val = deassert ? 0 : ~0; - return regmap_write(data->regmap, reg, 1); + return regmap_update_bits(data->regmap, + data->desc->rst_bank_ofs[id / RST_NR_PER_BANK], + BIT(id % RST_NR_PER_BANK), val); } static int mtk_reset_assert(struct reset_controller_dev *rcdev, - unsigned long id) + unsigned long id) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - - return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), - BIT(id % 32), ~0); + return mtk_reset_update(rcdev, id, false); } static int mtk_reset_deassert(struct reset_controller_dev *rcdev, - unsigned long id) + unsigned long id) { - struct mtk_reset *data = container_of(rcdev, struct mtk_reset, rcdev); - - return regmap_update_bits(data->regmap, data->regofs + ((id / 32) << 2), - BIT(id % 32), 0); + return mtk_reset_update(rcdev, id, true); } -static int mtk_reset(struct reset_controller_dev *rcdev, - unsigned long id) +static int mtk_reset(struct reset_controller_dev *rcdev, unsigned long id) { int ret; @@ -67,8 +51,32 @@ static int mtk_reset(struct reset_controller_dev *rcdev, return mtk_reset_deassert(rcdev, id); } +static int mtk_reset_update_set_clr(struct reset_controller_dev *rcdev, + unsigned long id, bool deassert) +{ + struct mtk_clk_rst_data *data = to_mtk_clk_rst_data(rcdev); + unsigned int deassert_ofs = deassert ? 0x4 : 0; + + return regmap_write(data->regmap, + data->desc->rst_bank_ofs[id / RST_NR_PER_BANK] + + deassert_ofs, + BIT(id % RST_NR_PER_BANK)); +} + +static int mtk_reset_assert_set_clr(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return mtk_reset_update_set_clr(rcdev, id, false); +} + +static int mtk_reset_deassert_set_clr(struct reset_controller_dev *rcdev, + unsigned long id) +{ + return mtk_reset_update_set_clr(rcdev, id, true); +} + static int mtk_reset_set_clr(struct reset_controller_dev *rcdev, - unsigned long id) + unsigned long id) { int ret; @@ -90,51 +98,135 @@ static const struct reset_control_ops mtk_reset_ops_set_clr = { .reset = mtk_reset_set_clr, }; -static void mtk_register_reset_controller_common(struct device_node *np, - unsigned int num_regs, int regofs, - const struct reset_control_ops *reset_ops) +static int reset_xlate(struct reset_controller_dev *rcdev, + const struct of_phandle_args *reset_spec) +{ + struct mtk_clk_rst_data *data = to_mtk_clk_rst_data(rcdev); + + if (reset_spec->args[0] >= rcdev->nr_resets || + reset_spec->args[0] >= data->desc->rst_idx_map_nr) + return -EINVAL; + + return data->desc->rst_idx_map[reset_spec->args[0]]; +} + +int mtk_register_reset_controller(struct device_node *np, + const struct mtk_clk_rst_desc *desc) { - struct mtk_reset *data; - int ret; struct regmap *regmap; + const struct reset_control_ops *rcops = NULL; + struct mtk_clk_rst_data *data; + int ret; + + if (!desc) { + pr_err("mtk clock reset desc is NULL\n"); + return -EINVAL; + } + + switch (desc->version) { + case MTK_RST_SIMPLE: + rcops = &mtk_reset_ops; + break; + case MTK_RST_SET_CLR: + rcops = &mtk_reset_ops_set_clr; + break; + default: + pr_err("Unknown reset version %d\n", desc->version); + return -EINVAL; + } regmap = device_node_to_regmap(np); if (IS_ERR(regmap)) { pr_err("Cannot find regmap for %pOF: %pe\n", np, regmap); - return; + return -EINVAL; } data = kzalloc(sizeof(*data), GFP_KERNEL); if (!data) - return; + return -ENOMEM; + data->desc = desc; data->regmap = regmap; - data->regofs = regofs; data->rcdev.owner = THIS_MODULE; - data->rcdev.nr_resets = num_regs * 32; - data->rcdev.ops = reset_ops; + data->rcdev.ops = rcops; data->rcdev.of_node = np; + if (data->desc->rst_idx_map_nr > 0) { + data->rcdev.of_reset_n_cells = 1; + data->rcdev.nr_resets = desc->rst_idx_map_nr; + data->rcdev.of_xlate = reset_xlate; + } else { + data->rcdev.nr_resets = desc->rst_bank_nr * RST_NR_PER_BANK; + } + ret = reset_controller_register(&data->rcdev); if (ret) { pr_err("could not register reset controller: %d\n", ret); kfree(data); - return; + return ret; } + + return 0; } -void mtk_register_reset_controller(struct device_node *np, - unsigned int num_regs, int regofs) +int mtk_register_reset_controller_with_dev(struct device *dev, + const struct mtk_clk_rst_desc *desc) { - mtk_register_reset_controller_common(np, num_regs, regofs, - &mtk_reset_ops); -} + struct device_node *np = dev->of_node; + struct regmap *regmap; + const struct reset_control_ops *rcops = NULL; + struct mtk_clk_rst_data *data; + int ret; -void mtk_register_reset_controller_set_clr(struct device_node *np, - unsigned int num_regs, int regofs) -{ - mtk_register_reset_controller_common(np, num_regs, regofs, - &mtk_reset_ops_set_clr); + if (!desc) { + dev_err(dev, "mtk clock reset desc is NULL\n"); + return -EINVAL; + } + + switch (desc->version) { + case MTK_RST_SIMPLE: + rcops = &mtk_reset_ops; + break; + case MTK_RST_SET_CLR: + rcops = &mtk_reset_ops_set_clr; + break; + default: + dev_err(dev, "Unknown reset version %d\n", desc->version); + return -EINVAL; + } + + regmap = device_node_to_regmap(np); + if (IS_ERR(regmap)) { + dev_err(dev, "Cannot find regmap %pe\n", regmap); + return -EINVAL; + } + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->desc = desc; + data->regmap = regmap; + data->rcdev.owner = THIS_MODULE; + data->rcdev.ops = rcops; + data->rcdev.of_node = np; + data->rcdev.dev = dev; + + if (data->desc->rst_idx_map_nr > 0) { + data->rcdev.of_reset_n_cells = 1; + data->rcdev.nr_resets = desc->rst_idx_map_nr; + data->rcdev.of_xlate = reset_xlate; + } else { + data->rcdev.nr_resets = desc->rst_bank_nr * RST_NR_PER_BANK; + } + + ret = devm_reset_controller_register(dev, &data->rcdev); + if (ret) { + dev_err(dev, "could not register reset controller: %d\n", ret); + return ret; + } + + return 0; } MODULE_LICENSE("GPL"); diff --git a/drivers/clk/mediatek/reset.h b/drivers/clk/mediatek/reset.h new file mode 100644 index 000000000000..6a58a3d59165 --- /dev/null +++ b/drivers/clk/mediatek/reset.h @@ -0,0 +1,82 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2022 MediaTek Inc. + */ + +#ifndef __DRV_CLK_MTK_RESET_H +#define __DRV_CLK_MTK_RESET_H + +#include +#include + +#define RST_NR_PER_BANK 32 + +/* Infra global controller reset set register */ +#define INFRA_RST0_SET_OFFSET 0x120 +#define INFRA_RST1_SET_OFFSET 0x130 +#define INFRA_RST2_SET_OFFSET 0x140 +#define INFRA_RST3_SET_OFFSET 0x150 +#define INFRA_RST4_SET_OFFSET 0x730 + +/** + * enum mtk_reset_version - Version of MediaTek clock reset controller. + * @MTK_RST_SIMPLE: Use the same registers for bit set and clear. + * @MTK_RST_SET_CLR: Use separate registers for bit set and clear. + * @MTK_RST_MAX: Total quantity of version for MediaTek clock reset controller. + */ +enum mtk_reset_version { + MTK_RST_SIMPLE = 0, + MTK_RST_SET_CLR, + MTK_RST_MAX, +}; + +/** + * struct mtk_clk_rst_desc - Description of MediaTek clock reset. + * @version: Reset version which is defined in enum mtk_reset_version. + * @rst_bank_ofs: Pointer to an array containing base offsets of the reset register. + * @rst_bank_nr: Quantity of reset bank. + * @rst_idx_map:Pointer to an array containing ids if input argument is index. + * This array is not necessary if our input argument does not mean index. + * @rst_idx_map_nr: Quantity of reset index map. + */ +struct mtk_clk_rst_desc { + enum mtk_reset_version version; + u16 *rst_bank_ofs; + u32 rst_bank_nr; + u16 *rst_idx_map; + u32 rst_idx_map_nr; +}; + +/** + * struct mtk_clk_rst_data - Data of MediaTek clock reset controller. + * @regmap: Pointer to base address of reset register address. + * @rcdev: Reset controller device. + * @desc: Pointer to description of the reset controller. + */ +struct mtk_clk_rst_data { + struct regmap *regmap; + struct reset_controller_dev rcdev; + const struct mtk_clk_rst_desc *desc; +}; + +/** + * mtk_register_reset_controller - Register MediaTek clock reset controller + * @np: Pointer to device node. + * @desc: Constant pointer to description of clock reset. + * + * Return: 0 on success and errorno otherwise. + */ +int mtk_register_reset_controller(struct device_node *np, + const struct mtk_clk_rst_desc *desc); + +/** + * mtk_register_reset_controller - Register mediatek clock reset controller with device + * @np: Pointer to device. + * @desc: Constant pointer to description of clock reset. + * + * Return: 0 on success and errorno otherwise. + */ +int mtk_register_reset_controller_with_dev(struct device *dev, + const struct mtk_clk_rst_desc *desc); + +#endif /* __DRV_CLK_MTK_RESET_H */ diff --git a/drivers/clk/meson/axg-audio.c b/drivers/clk/meson/axg-audio.c index bfe36bd41339..5016682e47c8 100644 --- a/drivers/clk/meson/axg-audio.c +++ b/drivers/clk/meson/axg-audio.c @@ -1657,35 +1657,6 @@ static struct clk_regmap *const sm1_clk_regmaps[] = { &sm1_sysclk_b_en, }; -static int devm_clk_get_enable(struct device *dev, char *id) -{ - struct clk *clk; - int ret; - - clk = devm_clk_get(dev, id); - if (IS_ERR(clk)) { - ret = PTR_ERR(clk); - dev_err_probe(dev, ret, "failed to get %s", id); - return ret; - } - - ret = clk_prepare_enable(clk); - if (ret) { - dev_err(dev, "failed to enable %s", id); - return ret; - } - - ret = devm_add_action_or_reset(dev, - (void(*)(void *))clk_disable_unprepare, - clk); - if (ret) { - dev_err(dev, "failed to add reset action on %s", id); - return ret; - } - - return 0; -} - struct axg_audio_reset_data { struct reset_controller_dev rstc; struct regmap *map; @@ -1787,6 +1758,7 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) struct regmap *map; void __iomem *regs; struct clk_hw *hw; + struct clk *clk; int ret, i; data = of_device_get_match_data(dev); @@ -1804,9 +1776,9 @@ static int axg_audio_clkc_probe(struct platform_device *pdev) } /* Get the mandatory peripheral clock */ - ret = devm_clk_get_enable(dev, "pclk"); - if (ret) - return ret; + clk = devm_clk_get_enabled(dev, "pclk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); ret = device_reset(dev); if (ret) { diff --git a/drivers/clk/mmp/clk-apbc.c b/drivers/clk/mmp/clk-apbc.c index fb294ada0b03..23c43a46604e 100644 --- a/drivers/clk/mmp/clk-apbc.c +++ b/drivers/clk/mmp/clk-apbc.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp APB clock operation source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-apmu.c b/drivers/clk/mmp/clk-apmu.c index b7ce8f52026e..9313428b083a 100644 --- a/drivers/clk/mmp/clk-apmu.c +++ b/drivers/clk/mmp/clk-apmu.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp AXI peripharal clock operation source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-frac.c b/drivers/clk/mmp/clk-frac.c index 48f592bd633d..1b90867b60c4 100644 --- a/drivers/clk/mmp/clk-frac.c +++ b/drivers/clk/mmp/clk-frac.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp factor clock operation source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-gate.c b/drivers/clk/mmp/clk-gate.c index 1755916ddef2..350eeb3e9e25 100644 --- a/drivers/clk/mmp/clk-gate.c +++ b/drivers/clk/mmp/clk-gate.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp gate clock operation source file * * Copyright (C) 2014 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-mix.c b/drivers/clk/mmp/clk-mix.c index 7a351ec65564..454d131f475e 100644 --- a/drivers/clk/mmp/clk-mix.c +++ b/drivers/clk/mmp/clk-mix.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp mix(div and mux) clock operation source file * * Copyright (C) 2014 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-mmp2.c b/drivers/clk/mmp/clk-mmp2.c index 7460031714da..aabacfa10158 100644 --- a/drivers/clk/mmp/clk-mmp2.c +++ b/drivers/clk/mmp/clk-mmp2.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp2 clock framework source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-of-mmp2.c b/drivers/clk/mmp/clk-of-mmp2.c index 50a780274ba0..bcf60f43aa13 100644 --- a/drivers/clk/mmp/clk-of-mmp2.c +++ b/drivers/clk/mmp/clk-of-mmp2.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * mmp2 clock framework source file * * Copyright (C) 2012 Marvell * Chao Xie * Copyright (C) 2020 Lubomir Rintel - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-of-pxa168.c b/drivers/clk/mmp/clk-of-pxa168.c index f110c02e83cb..48dfb18b490e 100644 --- a/drivers/clk/mmp/clk-of-pxa168.c +++ b/drivers/clk/mmp/clk-of-pxa168.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * pxa168 clock framework source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-of-pxa1928.c b/drivers/clk/mmp/clk-of-pxa1928.c index 998fc4207b0e..2508a0d795f8 100644 --- a/drivers/clk/mmp/clk-of-pxa1928.c +++ b/drivers/clk/mmp/clk-of-pxa1928.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * pxa1928 clock framework source file * @@ -7,10 +8,6 @@ * Based on drivers/clk/mmp/clk-of-mmp2.c: * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include #include diff --git a/drivers/clk/mmp/clk-of-pxa910.c b/drivers/clk/mmp/clk-of-pxa910.c index 1dcabe95cb67..4d15bac987eb 100644 --- a/drivers/clk/mmp/clk-of-pxa910.c +++ b/drivers/clk/mmp/clk-of-pxa910.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * pxa910 clock framework source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-pxa168.c b/drivers/clk/mmp/clk-pxa168.c index b351039cac09..8a9b8fb3a465 100644 --- a/drivers/clk/mmp/clk-pxa168.c +++ b/drivers/clk/mmp/clk-pxa168.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * pxa168 clock framework source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/mmp/clk-pxa910.c b/drivers/clk/mmp/clk-pxa910.c index f254ceff3ea7..9fcd76316d7e 100644 --- a/drivers/clk/mmp/clk-pxa910.c +++ b/drivers/clk/mmp/clk-pxa910.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * pxa910 clock framework source file * * Copyright (C) 2012 Marvell * Chao Xie - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/nxp/clk-lpc18xx-ccu.c b/drivers/clk/nxp/clk-lpc18xx-ccu.c index f2e171a01fb4..ddb28b38f549 100644 --- a/drivers/clk/nxp/clk-lpc18xx-ccu.c +++ b/drivers/clk/nxp/clk-lpc18xx-ccu.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Clk driver for NXP LPC18xx/LPC43xx Clock Control Unit (CCU) * * Copyright (C) 2015 Joachim Eastwood - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/nxp/clk-lpc18xx-cgu.c b/drivers/clk/nxp/clk-lpc18xx-cgu.c index c23ac463ab0f..f253ef1996b1 100644 --- a/drivers/clk/nxp/clk-lpc18xx-cgu.c +++ b/drivers/clk/nxp/clk-lpc18xx-cgu.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Clk driver for NXP LPC18xx/LPC43xx Clock Generation Unit (CGU) * * Copyright (C) 2015 Joachim Eastwood - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/nxp/clk-lpc18xx-creg.c b/drivers/clk/nxp/clk-lpc18xx-creg.c index c6e802e7e6ec..3d3982e9c661 100644 --- a/drivers/clk/nxp/clk-lpc18xx-creg.c +++ b/drivers/clk/nxp/clk-lpc18xx-creg.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Clk driver for NXP LPC18xx/43xx Configuration Registers (CREG) * * Copyright (C) 2015 Joachim Eastwood - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index bc4dcf356d82..1cf1ef70e347 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -166,6 +166,7 @@ config IPQ_LCC_806X config IPQ_GCC_8074 tristate "IPQ8074 Global Clock Controller" + select QCOM_GDSC help Support for global clock controller on ipq8074 devices. Say Y if you want to use peripheral devices such as UART, SPI, @@ -608,6 +609,13 @@ config SM_CAMCC_8250 Support for the camera clock controller on SM8250 devices. Say Y if you want to support camera devices and camera functionality. +config SM_CAMCC_8450 + tristate "SM8450 Camera Clock Controller" + select SM_GCC_8450 + help + Support for the camera clock controller on SM8450 devices. + Say Y if you want to support camera devices and camera functionality. + config SM_DISPCC_6125 tristate "SM6125 Display Clock Controller" depends on SM_GCC_6125 @@ -618,11 +626,11 @@ config SM_DISPCC_6125 splash screen config SM_DISPCC_8250 - tristate "SM8150 and SM8250 Display Clock Controller" - depends on SM_GCC_8150 || SM_GCC_8250 + tristate "SM8150/SM8250/SM8350 Display Clock Controller" + depends on SM_GCC_8150 || SM_GCC_8250 || SM_GCC_8350 help Support for the display clock controller on Qualcomm Technologies, Inc - SM8150 and SM8250 devices. + SM8150/SM8250/SM8350 devices. Say Y if you want to support display devices and functionality such as splash screen. @@ -712,6 +720,14 @@ config SM_GPUCC_8250 Say Y if you want to support graphics controller devices and functionality such as 3D graphics. +config SM_GPUCC_8350 + tristate "SM8350 Graphics Clock Controller" + select SM_GCC_8350 + help + Support for the graphics clock controller on SM8350 devices. + Say Y if you want to support graphics controller devices and + functionality such as 3D graphics. + config SM_VIDEOCC_8150 tristate "SM8150 Video Clock Controller" select SM_GCC_8150 diff --git a/drivers/clk/qcom/Makefile b/drivers/clk/qcom/Makefile index 08594230c1c1..fbcf04073f07 100644 --- a/drivers/clk/qcom/Makefile +++ b/drivers/clk/qcom/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_SDM_VIDEOCC_845) += videocc-sdm845.o obj-$(CONFIG_SDX_GCC_55) += gcc-sdx55.o obj-$(CONFIG_SDX_GCC_65) += gcc-sdx65.o obj-$(CONFIG_SM_CAMCC_8250) += camcc-sm8250.o +obj-$(CONFIG_SM_CAMCC_8450) += camcc-sm8450.o obj-$(CONFIG_SM_DISPCC_6125) += dispcc-sm6125.o obj-$(CONFIG_SM_DISPCC_6350) += dispcc-sm6350.o obj-$(CONFIG_SM_DISPCC_8250) += dispcc-sm8250.o @@ -102,6 +103,7 @@ obj-$(CONFIG_SM_GCC_8450) += gcc-sm8450.o obj-$(CONFIG_SM_GPUCC_6350) += gpucc-sm6350.o obj-$(CONFIG_SM_GPUCC_8150) += gpucc-sm8150.o obj-$(CONFIG_SM_GPUCC_8250) += gpucc-sm8250.o +obj-$(CONFIG_SM_GPUCC_8350) += gpucc-sm8350.o obj-$(CONFIG_SM_VIDEOCC_8150) += videocc-sm8150.o obj-$(CONFIG_SM_VIDEOCC_8250) += videocc-sm8250.o obj-$(CONFIG_SPMI_PMIC_CLKDIV) += clk-spmi-pmic-div.o diff --git a/drivers/clk/qcom/camcc-sdm845.c b/drivers/clk/qcom/camcc-sdm845.c index be3f95326965..27d44188a7ab 100644 --- a/drivers/clk/qcom/camcc-sdm845.c +++ b/drivers/clk/qcom/camcc-sdm845.c @@ -1534,6 +1534,8 @@ static struct clk_branch cam_cc_sys_tmr_clk = { }, }; +static struct gdsc titan_top_gdsc; + static struct gdsc bps_gdsc = { .gdscr = 0x6004, .pd = { @@ -1567,6 +1569,7 @@ static struct gdsc ife_0_gdsc = { .name = "ife_0_gdsc", }, .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, .pwrsts = PWRSTS_OFF_ON, }; @@ -1576,6 +1579,7 @@ static struct gdsc ife_1_gdsc = { .name = "ife_1_gdsc", }, .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, .pwrsts = PWRSTS_OFF_ON, }; diff --git a/drivers/clk/qcom/camcc-sm8250.c b/drivers/clk/qcom/camcc-sm8250.c index 439eaafdcc86..9b32c56a5bc5 100644 --- a/drivers/clk/qcom/camcc-sm8250.c +++ b/drivers/clk/qcom/camcc-sm8250.c @@ -2205,6 +2205,8 @@ static struct clk_branch cam_cc_sleep_clk = { }, }; +static struct gdsc titan_top_gdsc; + static struct gdsc bps_gdsc = { .gdscr = 0x7004, .pd = { @@ -2238,6 +2240,7 @@ static struct gdsc ife_0_gdsc = { .name = "ife_0_gdsc", }, .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, .pwrsts = PWRSTS_OFF_ON, }; @@ -2247,6 +2250,7 @@ static struct gdsc ife_1_gdsc = { .name = "ife_1_gdsc", }, .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, .pwrsts = PWRSTS_OFF_ON, }; @@ -2440,17 +2444,7 @@ static struct platform_driver cam_cc_sm8250_driver = { }, }; -static int __init cam_cc_sm8250_init(void) -{ - return platform_driver_register(&cam_cc_sm8250_driver); -} -subsys_initcall(cam_cc_sm8250_init); - -static void __exit cam_cc_sm8250_exit(void) -{ - platform_driver_unregister(&cam_cc_sm8250_driver); -} -module_exit(cam_cc_sm8250_exit); +module_platform_driver(cam_cc_sm8250_driver); MODULE_DESCRIPTION("QTI CAMCC SM8250 Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/camcc-sm8450.c b/drivers/clk/qcom/camcc-sm8450.c new file mode 100644 index 000000000000..e3c09471dadf --- /dev/null +++ b/drivers/clk/qcom/camcc-sm8450.c @@ -0,0 +1,2856 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved. + */ + +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap-divider.h" +#include "clk-regmap-mux.h" +#include "clk-regmap.h" +#include "common.h" +#include "gdsc.h" +#include "reset.h" + +enum { + DT_IFACE, + DT_BI_TCXO, + DT_BI_TCXO_AO, + DT_SLEEP_CLK +}; + +enum { + P_BI_TCXO, + P_CAM_CC_PLL0_OUT_EVEN, + P_CAM_CC_PLL0_OUT_MAIN, + P_CAM_CC_PLL0_OUT_ODD, + P_CAM_CC_PLL1_OUT_EVEN, + P_CAM_CC_PLL2_OUT_EVEN, + P_CAM_CC_PLL2_OUT_MAIN, + P_CAM_CC_PLL3_OUT_EVEN, + P_CAM_CC_PLL4_OUT_EVEN, + P_CAM_CC_PLL5_OUT_EVEN, + P_CAM_CC_PLL6_OUT_EVEN, + P_CAM_CC_PLL7_OUT_EVEN, + P_CAM_CC_PLL8_OUT_EVEN, + P_SLEEP_CLK, +}; + +static const struct pll_vco lucid_evo_vco[] = { + { 249600000, 2000000000, 0 }, +}; + +static const struct pll_vco rivian_evo_vco[] = { + { 864000000, 1056000000, 0 }, +}; + +static const struct clk_parent_data pll_parent_data_tcxo = { .index = DT_BI_TCXO }; + +static const struct alpha_pll_config cam_cc_pll0_config = { + .l = 0x3e, + .alpha = 0x8000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00008400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll0 = { + .offset = 0x0, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_even = { + .offset = 0x0, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll0_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll0_out_odd[] = { + { 0x2, 3 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll0_out_odd = { + .offset = 0x0, + .post_div_shift = 14, + .post_div_table = post_div_table_cam_cc_pll0_out_odd, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll0_out_odd), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll0_out_odd", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll0.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll1_config = { + .l = 0x25, + .alpha = 0xeaaa, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll1 = { + .offset = 0x1000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll1_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll1_out_even = { + .offset = 0x1000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll1_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll1_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll1_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll1.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll2_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x90008820, + .config_ctl_hi_val = 0x00890263, + .config_ctl_hi1_val = 0x00000217, +}; + +static struct clk_alpha_pll cam_cc_pll2 = { + .offset = 0x2000, + .vco_table = rivian_evo_vco, + .num_vco = ARRAY_SIZE(rivian_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_RIVIAN_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll2", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_rivian_evo_ops, + }, + }, +}; + +static const struct alpha_pll_config cam_cc_pll3_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll3 = { + .offset = 0x3000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll3_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll3_out_even = { + .offset = 0x3000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll3_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll3_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll3_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll3.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll4_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll4 = { + .offset = 0x4000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll4", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll4_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll4_out_even = { + .offset = 0x4000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll4_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll4_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll4_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll4.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll5_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll5 = { + .offset = 0x5000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll5", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll5_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll5_out_even = { + .offset = 0x5000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll5_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll5_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll5_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll5.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll6_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll6 = { + .offset = 0x6000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll6_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll6_out_even = { + .offset = 0x6000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll6_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll6_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll6_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll6.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll7_config = { + .l = 0x2d, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll7 = { + .offset = 0x7000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll7", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll7_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll7_out_even = { + .offset = 0x7000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll7_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll7_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll7_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll7.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct alpha_pll_config cam_cc_pll8_config = { + .l = 0x32, + .alpha = 0x0, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00182261, + .config_ctl_hi1_val = 0x32aa299c, + .user_ctl_val = 0x00000400, + .user_ctl_hi_val = 0x00000805, +}; + +static struct clk_alpha_pll cam_cc_pll8 = { + .offset = 0x8000, + .vco_table = lucid_evo_vco, + .num_vco = ARRAY_SIZE(lucid_evo_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr = { + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll8", + .parent_data = &pll_parent_data_tcxo, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_evo_ops, + }, + }, +}; + +static const struct clk_div_table post_div_table_cam_cc_pll8_out_even[] = { + { 0x1, 2 }, + { } +}; + +static struct clk_alpha_pll_postdiv cam_cc_pll8_out_even = { + .offset = 0x8000, + .post_div_shift = 10, + .post_div_table = post_div_table_cam_cc_pll8_out_even, + .num_post_div = ARRAY_SIZE(post_div_table_cam_cc_pll8_out_even), + .width = 4, + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID_EVO], + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_pll8_out_even", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_pll8.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_alpha_pll_postdiv_lucid_evo_ops, + }, +}; + +static const struct parent_map cam_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL0_OUT_MAIN, 1 }, + { P_CAM_CC_PLL0_OUT_EVEN, 2 }, + { P_CAM_CC_PLL0_OUT_ODD, 3 }, + { P_CAM_CC_PLL8_OUT_EVEN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_0[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll0.clkr.hw }, + { .hw = &cam_cc_pll0_out_even.clkr.hw }, + { .hw = &cam_cc_pll0_out_odd.clkr.hw }, + { .hw = &cam_cc_pll8_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL2_OUT_EVEN, 3 }, + { P_CAM_CC_PLL2_OUT_MAIN, 5 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_1[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll2.clkr.hw }, + { .hw = &cam_cc_pll2.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_2[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL3_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_2[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll3_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_3[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL4_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_3[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll4_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_4[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL5_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_4[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll5_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_5[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL1_OUT_EVEN, 4 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_5[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll1_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_6[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL6_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_6[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll6_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_7[] = { + { P_BI_TCXO, 0 }, + { P_CAM_CC_PLL7_OUT_EVEN, 6 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_7[] = { + { .index = DT_BI_TCXO }, + { .hw = &cam_cc_pll7_out_even.clkr.hw }, +}; + +static const struct parent_map cam_cc_parent_map_8[] = { + { P_SLEEP_CLK, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_8[] = { + { .index = DT_SLEEP_CLK }, +}; + +static const struct parent_map cam_cc_parent_map_9[] = { + { P_BI_TCXO, 0 }, +}; + +static const struct clk_parent_data cam_cc_parent_data_9_ao[] = { + { .index = DT_BI_TCXO_AO, .name = "bi_tcxo_ao" }, +}; + +static const struct freq_tbl ftbl_cam_cc_bps_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_ODD, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL8_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_bps_clk_src = { + .cmd_rcgr = 0x10050, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_camnoc_axi_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_EVEN, 2, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_camnoc_axi_clk_src = { + .cmd_rcgr = 0x13194, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_camnoc_axi_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cci_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(37500000, P_CAM_CC_PLL0_OUT_EVEN, 16, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cci_0_clk_src = { + .cmd_rcgr = 0x1312c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_cci_1_clk_src = { + .cmd_rcgr = 0x13148, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cci_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_cphy_rx_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_cphy_rx_clk_src = { + .cmd_rcgr = 0x1104c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_cphy_rx_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cphy_rx_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csi0phytimer_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csi0phytimer_clk_src = { + .cmd_rcgr = 0x150e0, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi1phytimer_clk_src = { + .cmd_rcgr = 0x15104, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi2phytimer_clk_src = { + .cmd_rcgr = 0x15124, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi3phytimer_clk_src = { + .cmd_rcgr = 0x1514c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi4phytimer_clk_src = { + .cmd_rcgr = 0x1516c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi4phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_csi5phytimer_clk_src = { + .cmd_rcgr = 0x1518c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csi0phytimer_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi5phytimer_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_csid_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + F(480000000, P_CAM_CC_PLL0_OUT_MAIN, 2.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_csid_clk_src = { + .cmd_rcgr = 0x13174, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_csid_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_fast_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(100000000, P_CAM_CC_PLL0_OUT_EVEN, 6, 0, 0), + F(200000000, P_CAM_CC_PLL0_OUT_EVEN, 3, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_MAIN, 3, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_fast_ahb_clk_src = { + .cmd_rcgr = 0x10018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_fast_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_fast_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_icp_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL8_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_icp_clk_src = { + .cmd_rcgr = 0x13108, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_icp_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(432000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL3_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_0_clk_src = { + .cmd_rcgr = 0x11018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_2, + .freq_tbl = ftbl_cam_cc_ife_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk_src", + .parent_data = cam_cc_parent_data_2, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_2), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_1_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(432000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL4_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_1_clk_src = { + .cmd_rcgr = 0x12018, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_3, + .freq_tbl = ftbl_cam_cc_ife_1_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_clk_src", + .parent_data = cam_cc_parent_data_3, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_3), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_2_clk_src[] = { + F(432000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL5_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_2_clk_src = { + .cmd_rcgr = 0x12064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_4, + .freq_tbl = ftbl_cam_cc_ife_2_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_clk_src", + .parent_data = cam_cc_parent_data_4, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_4), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ife_lite_clk_src[] = { + F(400000000, P_CAM_CC_PLL0_OUT_ODD, 1, 0, 0), + F(480000000, P_CAM_CC_PLL8_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ife_lite_clk_src = { + .cmd_rcgr = 0x13000, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_ife_lite_csid_clk_src = { + .cmd_rcgr = 0x13024, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_ife_lite_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_ipe_nps_clk_src[] = { + F(364000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(500000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(600000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + F(700000000, P_CAM_CC_PLL1_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_ipe_nps_clk_src = { + .cmd_rcgr = 0x1008c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_5, + .freq_tbl = ftbl_cam_cc_ipe_nps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_clk_src", + .parent_data = cam_cc_parent_data_5, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_5), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_jpeg_clk_src = { + .cmd_rcgr = 0x130dc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_bps_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_mclk0_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(24000000, P_CAM_CC_PLL2_OUT_MAIN, 10, 1, 4), + F(68571429, P_CAM_CC_PLL2_OUT_MAIN, 14, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_mclk0_clk_src = { + .cmd_rcgr = 0x15000, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk1_clk_src = { + .cmd_rcgr = 0x1501c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk2_clk_src = { + .cmd_rcgr = 0x15038, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk3_clk_src = { + .cmd_rcgr = 0x15054, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk4_clk_src = { + .cmd_rcgr = 0x15070, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk5_clk_src = { + .cmd_rcgr = 0x1508c, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk6_clk_src = { + .cmd_rcgr = 0x150a8, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_rcg2 cam_cc_mclk7_clk_src = { + .cmd_rcgr = 0x150c4, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_1, + .freq_tbl = ftbl_cam_cc_mclk0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk_src", + .parent_data = cam_cc_parent_data_1, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_qdss_debug_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(75000000, P_CAM_CC_PLL0_OUT_EVEN, 8, 0, 0), + F(150000000, P_CAM_CC_PLL0_OUT_EVEN, 4, 0, 0), + F(300000000, P_CAM_CC_PLL0_OUT_MAIN, 4, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_qdss_debug_clk_src = { + .cmd_rcgr = 0x131bc, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_qdss_debug_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_sfe_0_clk_src[] = { + F(432000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL6_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_sfe_0_clk_src = { + .cmd_rcgr = 0x13064, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_6, + .freq_tbl = ftbl_cam_cc_sfe_0_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sfe_0_clk_src", + .parent_data = cam_cc_parent_data_6, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_6), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_sfe_1_clk_src[] = { + F(432000000, P_CAM_CC_PLL7_OUT_EVEN, 1, 0, 0), + F(594000000, P_CAM_CC_PLL7_OUT_EVEN, 1, 0, 0), + F(675000000, P_CAM_CC_PLL7_OUT_EVEN, 1, 0, 0), + F(727000000, P_CAM_CC_PLL7_OUT_EVEN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_sfe_1_clk_src = { + .cmd_rcgr = 0x130ac, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_7, + .freq_tbl = ftbl_cam_cc_sfe_1_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sfe_1_clk_src", + .parent_data = cam_cc_parent_data_7, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_7), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_sleep_clk_src[] = { + F(32000, P_SLEEP_CLK, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_sleep_clk_src = { + .cmd_rcgr = 0x13210, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_8, + .freq_tbl = ftbl_cam_cc_sleep_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sleep_clk_src", + .parent_data = cam_cc_parent_data_8, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_8), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_slow_ahb_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(80000000, P_CAM_CC_PLL0_OUT_EVEN, 7.5, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_slow_ahb_clk_src = { + .cmd_rcgr = 0x10034, + .mnd_width = 8, + .hid_width = 5, + .parent_map = cam_cc_parent_map_0, + .freq_tbl = ftbl_cam_cc_slow_ahb_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_slow_ahb_clk_src", + .parent_data = cam_cc_parent_data_0, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_cam_cc_xo_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + { } +}; + +static struct clk_rcg2 cam_cc_xo_clk_src = { + .cmd_rcgr = 0x131f4, + .mnd_width = 0, + .hid_width = 5, + .parent_map = cam_cc_parent_map_9, + .freq_tbl = ftbl_cam_cc_xo_clk_src, + .clkr.hw.init = &(const struct clk_init_data) { + .name = "cam_cc_xo_clk_src", + .parent_data = cam_cc_parent_data_9_ao, + .num_parents = ARRAY_SIZE(cam_cc_parent_data_9_ao), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_branch cam_cc_gdsc_clk = { + .halt_reg = 0x1320c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1320c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_gdsc_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_IS_CRITICAL | CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_ahb_clk = { + .halt_reg = 0x1004c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1004c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_clk = { + .halt_reg = 0x10068, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10068, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_bps_fast_ahb_clk = { + .halt_reg = 0x10030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_bps_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_axi_clk = { + .halt_reg = 0x131ac, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x131ac, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_axi_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_camnoc_axi_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_camnoc_dcd_xo_clk = { + .halt_reg = 0x131b4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x131b4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_camnoc_dcd_xo_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_0_clk = { + .halt_reg = 0x13144, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13144, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cci_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cci_1_clk = { + .halt_reg = 0x13160, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13160, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cci_1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cci_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_core_ahb_clk = { + .halt_reg = 0x131f0, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x131f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_core_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ahb_clk = { + .halt_reg = 0x13164, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13164, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_bps_clk = { + .halt_reg = 0x10070, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x10070, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_bps_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_bps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_fast_ahb_clk = { + .halt_reg = 0x1316c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1316c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_0_clk = { + .halt_reg = 0x11038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_1_clk = { + .halt_reg = 0x12038, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12038, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_2_clk = { + .halt_reg = 0x12084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_2_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ife_lite_clk = { + .halt_reg = 0x13020, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13020, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ife_lite_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_ipe_nps_clk = { + .halt_reg = 0x100ac, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100ac, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_ipe_nps_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_sbi_clk = { + .halt_reg = 0x100ec, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100ec, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_sbi_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_sfe_0_clk = { + .halt_reg = 0x13084, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13084, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_sfe_0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_sfe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_cpas_sfe_1_clk = { + .halt_reg = 0x130cc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x130cc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_cpas_sfe_1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_sfe_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi0phytimer_clk = { + .halt_reg = 0x150f8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150f8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi0phytimer_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csi0phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi1phytimer_clk = { + .halt_reg = 0x1511c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1511c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi1phytimer_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csi1phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi2phytimer_clk = { + .halt_reg = 0x1513c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1513c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi2phytimer_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csi2phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi3phytimer_clk = { + .halt_reg = 0x15164, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15164, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi3phytimer_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csi3phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi4phytimer_clk = { + .halt_reg = 0x15184, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15184, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi4phytimer_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csi4phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csi5phytimer_clk = { + .halt_reg = 0x151a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x151a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csi5phytimer_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csi5phytimer_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csid_clk = { + .halt_reg = 0x1318c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1318c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csid_csiphy_rx_clk = { + .halt_reg = 0x15100, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15100, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csid_csiphy_rx_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy0_clk = { + .halt_reg = 0x150fc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150fc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy1_clk = { + .halt_reg = 0x15120, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15120, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy2_clk = { + .halt_reg = 0x15140, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15140, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy2_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy3_clk = { + .halt_reg = 0x15168, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15168, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy3_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy4_clk = { + .halt_reg = 0x15188, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15188, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy4_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_csiphy5_clk = { + .halt_reg = 0x151a8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x151a8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_csiphy5_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_ahb_clk = { + .halt_reg = 0x13128, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13128, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_icp_clk = { + .halt_reg = 0x13120, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13120, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_icp_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_icp_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_clk = { + .halt_reg = 0x11030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_dsp_clk = { + .halt_reg = 0x1103c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1103c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_dsp_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_0_fast_ahb_clk = { + .halt_reg = 0x11048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x11048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_0_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_clk = { + .halt_reg = 0x12030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12030, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_dsp_clk = { + .halt_reg = 0x1203c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1203c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_dsp_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_1_fast_ahb_clk = { + .halt_reg = 0x12048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_1_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_clk = { + .halt_reg = 0x1207c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1207c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_dsp_clk = { + .halt_reg = 0x12088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_dsp_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_2_fast_ahb_clk = { + .halt_reg = 0x12094, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x12094, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_2_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_ahb_clk = { + .halt_reg = 0x13048, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13048, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_clk = { + .halt_reg = 0x13018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_lite_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_cphy_rx_clk = { + .halt_reg = 0x13044, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13044, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_cphy_rx_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_cphy_rx_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ife_lite_csid_clk = { + .halt_reg = 0x1303c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1303c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ife_lite_csid_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_lite_csid_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_ahb_clk = { + .halt_reg = 0x100c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_clk = { + .halt_reg = 0x100a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_nps_fast_ahb_clk = { + .halt_reg = 0x100c4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100c4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_nps_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_pps_clk = { + .halt_reg = 0x100b0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100b0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_pps_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ipe_nps_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_ipe_pps_fast_ahb_clk = { + .halt_reg = 0x100c8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100c8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_ipe_pps_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_jpeg_clk = { + .halt_reg = 0x130f4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x130f4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_jpeg_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_jpeg_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk0_clk = { + .halt_reg = 0x15018, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15018, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk1_clk = { + .halt_reg = 0x15034, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15034, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk2_clk = { + .halt_reg = 0x15050, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15050, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk2_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk2_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk3_clk = { + .halt_reg = 0x1506c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1506c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk3_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk3_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk4_clk = { + .halt_reg = 0x15088, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x15088, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk4_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk4_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk5_clk = { + .halt_reg = 0x150a4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150a4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk5_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk5_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk6_clk = { + .halt_reg = 0x150c0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150c0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk6_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk6_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_mclk7_clk = { + .halt_reg = 0x150dc, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x150dc, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_mclk7_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_mclk7_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_clk = { + .halt_reg = 0x131d4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x131d4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_qdss_debug_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_qdss_debug_xo_clk = { + .halt_reg = 0x131d8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x131d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_qdss_debug_xo_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_xo_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sbi_ahb_clk = { + .halt_reg = 0x100f0, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100f0, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sbi_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_slow_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sbi_clk = { + .halt_reg = 0x100e4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x100e4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sbi_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_ife_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sfe_0_clk = { + .halt_reg = 0x1307c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1307c, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sfe_0_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_sfe_0_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sfe_0_fast_ahb_clk = { + .halt_reg = 0x13090, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13090, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sfe_0_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sfe_1_clk = { + .halt_reg = 0x130c4, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x130c4, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sfe_1_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_sfe_1_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sfe_1_fast_ahb_clk = { + .halt_reg = 0x130d8, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x130d8, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sfe_1_fast_ahb_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_fast_ahb_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch cam_cc_sleep_clk = { + .halt_reg = 0x13228, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x13228, + .enable_mask = BIT(0), + .hw.init = &(const struct clk_init_data) { + .name = "cam_cc_sleep_clk", + .parent_data = &(const struct clk_parent_data) { + .hw = &cam_cc_sleep_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_regmap *cam_cc_sm8450_clocks[] = { + [CAM_CC_BPS_AHB_CLK] = &cam_cc_bps_ahb_clk.clkr, + [CAM_CC_BPS_CLK] = &cam_cc_bps_clk.clkr, + [CAM_CC_BPS_CLK_SRC] = &cam_cc_bps_clk_src.clkr, + [CAM_CC_BPS_FAST_AHB_CLK] = &cam_cc_bps_fast_ahb_clk.clkr, + [CAM_CC_CAMNOC_AXI_CLK] = &cam_cc_camnoc_axi_clk.clkr, + [CAM_CC_CAMNOC_AXI_CLK_SRC] = &cam_cc_camnoc_axi_clk_src.clkr, + [CAM_CC_CAMNOC_DCD_XO_CLK] = &cam_cc_camnoc_dcd_xo_clk.clkr, + [CAM_CC_CCI_0_CLK] = &cam_cc_cci_0_clk.clkr, + [CAM_CC_CCI_0_CLK_SRC] = &cam_cc_cci_0_clk_src.clkr, + [CAM_CC_CCI_1_CLK] = &cam_cc_cci_1_clk.clkr, + [CAM_CC_CCI_1_CLK_SRC] = &cam_cc_cci_1_clk_src.clkr, + [CAM_CC_CORE_AHB_CLK] = &cam_cc_core_ahb_clk.clkr, + [CAM_CC_CPAS_AHB_CLK] = &cam_cc_cpas_ahb_clk.clkr, + [CAM_CC_CPAS_BPS_CLK] = &cam_cc_cpas_bps_clk.clkr, + [CAM_CC_CPAS_FAST_AHB_CLK] = &cam_cc_cpas_fast_ahb_clk.clkr, + [CAM_CC_CPAS_IFE_0_CLK] = &cam_cc_cpas_ife_0_clk.clkr, + [CAM_CC_CPAS_IFE_1_CLK] = &cam_cc_cpas_ife_1_clk.clkr, + [CAM_CC_CPAS_IFE_2_CLK] = &cam_cc_cpas_ife_2_clk.clkr, + [CAM_CC_CPAS_IFE_LITE_CLK] = &cam_cc_cpas_ife_lite_clk.clkr, + [CAM_CC_CPAS_IPE_NPS_CLK] = &cam_cc_cpas_ipe_nps_clk.clkr, + [CAM_CC_CPAS_SBI_CLK] = &cam_cc_cpas_sbi_clk.clkr, + [CAM_CC_CPAS_SFE_0_CLK] = &cam_cc_cpas_sfe_0_clk.clkr, + [CAM_CC_CPAS_SFE_1_CLK] = &cam_cc_cpas_sfe_1_clk.clkr, + [CAM_CC_CPHY_RX_CLK_SRC] = &cam_cc_cphy_rx_clk_src.clkr, + [CAM_CC_CSI0PHYTIMER_CLK] = &cam_cc_csi0phytimer_clk.clkr, + [CAM_CC_CSI0PHYTIMER_CLK_SRC] = &cam_cc_csi0phytimer_clk_src.clkr, + [CAM_CC_CSI1PHYTIMER_CLK] = &cam_cc_csi1phytimer_clk.clkr, + [CAM_CC_CSI1PHYTIMER_CLK_SRC] = &cam_cc_csi1phytimer_clk_src.clkr, + [CAM_CC_CSI2PHYTIMER_CLK] = &cam_cc_csi2phytimer_clk.clkr, + [CAM_CC_CSI2PHYTIMER_CLK_SRC] = &cam_cc_csi2phytimer_clk_src.clkr, + [CAM_CC_CSI3PHYTIMER_CLK] = &cam_cc_csi3phytimer_clk.clkr, + [CAM_CC_CSI3PHYTIMER_CLK_SRC] = &cam_cc_csi3phytimer_clk_src.clkr, + [CAM_CC_CSI4PHYTIMER_CLK] = &cam_cc_csi4phytimer_clk.clkr, + [CAM_CC_CSI4PHYTIMER_CLK_SRC] = &cam_cc_csi4phytimer_clk_src.clkr, + [CAM_CC_CSI5PHYTIMER_CLK] = &cam_cc_csi5phytimer_clk.clkr, + [CAM_CC_CSI5PHYTIMER_CLK_SRC] = &cam_cc_csi5phytimer_clk_src.clkr, + [CAM_CC_CSID_CLK] = &cam_cc_csid_clk.clkr, + [CAM_CC_CSID_CLK_SRC] = &cam_cc_csid_clk_src.clkr, + [CAM_CC_CSID_CSIPHY_RX_CLK] = &cam_cc_csid_csiphy_rx_clk.clkr, + [CAM_CC_CSIPHY0_CLK] = &cam_cc_csiphy0_clk.clkr, + [CAM_CC_CSIPHY1_CLK] = &cam_cc_csiphy1_clk.clkr, + [CAM_CC_CSIPHY2_CLK] = &cam_cc_csiphy2_clk.clkr, + [CAM_CC_CSIPHY3_CLK] = &cam_cc_csiphy3_clk.clkr, + [CAM_CC_CSIPHY4_CLK] = &cam_cc_csiphy4_clk.clkr, + [CAM_CC_CSIPHY5_CLK] = &cam_cc_csiphy5_clk.clkr, + [CAM_CC_FAST_AHB_CLK_SRC] = &cam_cc_fast_ahb_clk_src.clkr, + [CAM_CC_GDSC_CLK] = &cam_cc_gdsc_clk.clkr, + [CAM_CC_ICP_AHB_CLK] = &cam_cc_icp_ahb_clk.clkr, + [CAM_CC_ICP_CLK] = &cam_cc_icp_clk.clkr, + [CAM_CC_ICP_CLK_SRC] = &cam_cc_icp_clk_src.clkr, + [CAM_CC_IFE_0_CLK] = &cam_cc_ife_0_clk.clkr, + [CAM_CC_IFE_0_CLK_SRC] = &cam_cc_ife_0_clk_src.clkr, + [CAM_CC_IFE_0_DSP_CLK] = &cam_cc_ife_0_dsp_clk.clkr, + [CAM_CC_IFE_0_FAST_AHB_CLK] = &cam_cc_ife_0_fast_ahb_clk.clkr, + [CAM_CC_IFE_1_CLK] = &cam_cc_ife_1_clk.clkr, + [CAM_CC_IFE_1_CLK_SRC] = &cam_cc_ife_1_clk_src.clkr, + [CAM_CC_IFE_1_DSP_CLK] = &cam_cc_ife_1_dsp_clk.clkr, + [CAM_CC_IFE_1_FAST_AHB_CLK] = &cam_cc_ife_1_fast_ahb_clk.clkr, + [CAM_CC_IFE_2_CLK] = &cam_cc_ife_2_clk.clkr, + [CAM_CC_IFE_2_CLK_SRC] = &cam_cc_ife_2_clk_src.clkr, + [CAM_CC_IFE_2_DSP_CLK] = &cam_cc_ife_2_dsp_clk.clkr, + [CAM_CC_IFE_2_FAST_AHB_CLK] = &cam_cc_ife_2_fast_ahb_clk.clkr, + [CAM_CC_IFE_LITE_AHB_CLK] = &cam_cc_ife_lite_ahb_clk.clkr, + [CAM_CC_IFE_LITE_CLK] = &cam_cc_ife_lite_clk.clkr, + [CAM_CC_IFE_LITE_CLK_SRC] = &cam_cc_ife_lite_clk_src.clkr, + [CAM_CC_IFE_LITE_CPHY_RX_CLK] = &cam_cc_ife_lite_cphy_rx_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK] = &cam_cc_ife_lite_csid_clk.clkr, + [CAM_CC_IFE_LITE_CSID_CLK_SRC] = &cam_cc_ife_lite_csid_clk_src.clkr, + [CAM_CC_IPE_NPS_AHB_CLK] = &cam_cc_ipe_nps_ahb_clk.clkr, + [CAM_CC_IPE_NPS_CLK] = &cam_cc_ipe_nps_clk.clkr, + [CAM_CC_IPE_NPS_CLK_SRC] = &cam_cc_ipe_nps_clk_src.clkr, + [CAM_CC_IPE_NPS_FAST_AHB_CLK] = &cam_cc_ipe_nps_fast_ahb_clk.clkr, + [CAM_CC_IPE_PPS_CLK] = &cam_cc_ipe_pps_clk.clkr, + [CAM_CC_IPE_PPS_FAST_AHB_CLK] = &cam_cc_ipe_pps_fast_ahb_clk.clkr, + [CAM_CC_JPEG_CLK] = &cam_cc_jpeg_clk.clkr, + [CAM_CC_JPEG_CLK_SRC] = &cam_cc_jpeg_clk_src.clkr, + [CAM_CC_MCLK0_CLK] = &cam_cc_mclk0_clk.clkr, + [CAM_CC_MCLK0_CLK_SRC] = &cam_cc_mclk0_clk_src.clkr, + [CAM_CC_MCLK1_CLK] = &cam_cc_mclk1_clk.clkr, + [CAM_CC_MCLK1_CLK_SRC] = &cam_cc_mclk1_clk_src.clkr, + [CAM_CC_MCLK2_CLK] = &cam_cc_mclk2_clk.clkr, + [CAM_CC_MCLK2_CLK_SRC] = &cam_cc_mclk2_clk_src.clkr, + [CAM_CC_MCLK3_CLK] = &cam_cc_mclk3_clk.clkr, + [CAM_CC_MCLK3_CLK_SRC] = &cam_cc_mclk3_clk_src.clkr, + [CAM_CC_MCLK4_CLK] = &cam_cc_mclk4_clk.clkr, + [CAM_CC_MCLK4_CLK_SRC] = &cam_cc_mclk4_clk_src.clkr, + [CAM_CC_MCLK5_CLK] = &cam_cc_mclk5_clk.clkr, + [CAM_CC_MCLK5_CLK_SRC] = &cam_cc_mclk5_clk_src.clkr, + [CAM_CC_MCLK6_CLK] = &cam_cc_mclk6_clk.clkr, + [CAM_CC_MCLK6_CLK_SRC] = &cam_cc_mclk6_clk_src.clkr, + [CAM_CC_MCLK7_CLK] = &cam_cc_mclk7_clk.clkr, + [CAM_CC_MCLK7_CLK_SRC] = &cam_cc_mclk7_clk_src.clkr, + [CAM_CC_PLL0] = &cam_cc_pll0.clkr, + [CAM_CC_PLL0_OUT_EVEN] = &cam_cc_pll0_out_even.clkr, + [CAM_CC_PLL0_OUT_ODD] = &cam_cc_pll0_out_odd.clkr, + [CAM_CC_PLL1] = &cam_cc_pll1.clkr, + [CAM_CC_PLL1_OUT_EVEN] = &cam_cc_pll1_out_even.clkr, + [CAM_CC_PLL2] = &cam_cc_pll2.clkr, + [CAM_CC_PLL3] = &cam_cc_pll3.clkr, + [CAM_CC_PLL3_OUT_EVEN] = &cam_cc_pll3_out_even.clkr, + [CAM_CC_PLL4] = &cam_cc_pll4.clkr, + [CAM_CC_PLL4_OUT_EVEN] = &cam_cc_pll4_out_even.clkr, + [CAM_CC_PLL5] = &cam_cc_pll5.clkr, + [CAM_CC_PLL5_OUT_EVEN] = &cam_cc_pll5_out_even.clkr, + [CAM_CC_PLL6] = &cam_cc_pll6.clkr, + [CAM_CC_PLL6_OUT_EVEN] = &cam_cc_pll6_out_even.clkr, + [CAM_CC_PLL7] = &cam_cc_pll7.clkr, + [CAM_CC_PLL7_OUT_EVEN] = &cam_cc_pll7_out_even.clkr, + [CAM_CC_PLL8] = &cam_cc_pll8.clkr, + [CAM_CC_PLL8_OUT_EVEN] = &cam_cc_pll8_out_even.clkr, + [CAM_CC_QDSS_DEBUG_CLK] = &cam_cc_qdss_debug_clk.clkr, + [CAM_CC_QDSS_DEBUG_CLK_SRC] = &cam_cc_qdss_debug_clk_src.clkr, + [CAM_CC_QDSS_DEBUG_XO_CLK] = &cam_cc_qdss_debug_xo_clk.clkr, + [CAM_CC_SBI_AHB_CLK] = &cam_cc_sbi_ahb_clk.clkr, + [CAM_CC_SBI_CLK] = &cam_cc_sbi_clk.clkr, + [CAM_CC_SFE_0_CLK] = &cam_cc_sfe_0_clk.clkr, + [CAM_CC_SFE_0_CLK_SRC] = &cam_cc_sfe_0_clk_src.clkr, + [CAM_CC_SFE_0_FAST_AHB_CLK] = &cam_cc_sfe_0_fast_ahb_clk.clkr, + [CAM_CC_SFE_1_CLK] = &cam_cc_sfe_1_clk.clkr, + [CAM_CC_SFE_1_CLK_SRC] = &cam_cc_sfe_1_clk_src.clkr, + [CAM_CC_SFE_1_FAST_AHB_CLK] = &cam_cc_sfe_1_fast_ahb_clk.clkr, + [CAM_CC_SLEEP_CLK] = &cam_cc_sleep_clk.clkr, + [CAM_CC_SLEEP_CLK_SRC] = &cam_cc_sleep_clk_src.clkr, + [CAM_CC_SLOW_AHB_CLK_SRC] = &cam_cc_slow_ahb_clk_src.clkr, + [CAM_CC_XO_CLK_SRC] = &cam_cc_xo_clk_src.clkr, +}; + +static const struct qcom_reset_map cam_cc_sm8450_resets[] = { + [CAM_CC_BPS_BCR] = { 0x10000 }, + [CAM_CC_ICP_BCR] = { 0x13104 }, + [CAM_CC_IFE_0_BCR] = { 0x11000 }, + [CAM_CC_IFE_1_BCR] = { 0x12000 }, + [CAM_CC_IFE_2_BCR] = { 0x1204c }, + [CAM_CC_IPE_0_BCR] = { 0x10074 }, + [CAM_CC_QDSS_DEBUG_BCR] = { 0x131b8 }, + [CAM_CC_SBI_BCR] = { 0x100cc }, + [CAM_CC_SFE_0_BCR] = { 0x1304c }, + [CAM_CC_SFE_1_BCR] = { 0x13094 }, +}; + +static const struct regmap_config cam_cc_sm8450_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x1601c, + .fast_io = true, +}; + +static struct gdsc titan_top_gdsc; + +static struct gdsc bps_gdsc = { + .gdscr = 0x10004, + .pd = { + .name = "bps_gdsc", + }, + .flags = HW_CTRL | POLL_CFG_GDSCR, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc ipe_0_gdsc = { + .gdscr = 0x10078, + .pd = { + .name = "ipe_0_gdsc", + }, + .flags = HW_CTRL | POLL_CFG_GDSCR, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc sbi_gdsc = { + .gdscr = 0x100d0, + .pd = { + .name = "sbi_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc ife_0_gdsc = { + .gdscr = 0x11004, + .pd = { + .name = "ife_0_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc ife_1_gdsc = { + .gdscr = 0x12004, + .pd = { + .name = "ife_1_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc ife_2_gdsc = { + .gdscr = 0x12050, + .pd = { + .name = "ife_2_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc sfe_0_gdsc = { + .gdscr = 0x13050, + .pd = { + .name = "sfe_0_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc sfe_1_gdsc = { + .gdscr = 0x13098, + .pd = { + .name = "sfe_1_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .parent = &titan_top_gdsc.pd, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc titan_top_gdsc = { + .gdscr = 0x131dc, + .pd = { + .name = "titan_top_gdsc", + }, + .flags = POLL_CFG_GDSCR, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc *cam_cc_sm8450_gdscs[] = { + [BPS_GDSC] = &bps_gdsc, + [IPE_0_GDSC] = &ipe_0_gdsc, + [SBI_GDSC] = &sbi_gdsc, + [IFE_0_GDSC] = &ife_0_gdsc, + [IFE_1_GDSC] = &ife_1_gdsc, + [IFE_2_GDSC] = &ife_2_gdsc, + [SFE_0_GDSC] = &sfe_0_gdsc, + [SFE_1_GDSC] = &sfe_1_gdsc, + [TITAN_TOP_GDSC] = &titan_top_gdsc, +}; + +static const struct qcom_cc_desc cam_cc_sm8450_desc = { + .config = &cam_cc_sm8450_regmap_config, + .clks = cam_cc_sm8450_clocks, + .num_clks = ARRAY_SIZE(cam_cc_sm8450_clocks), + .resets = cam_cc_sm8450_resets, + .num_resets = ARRAY_SIZE(cam_cc_sm8450_resets), + .gdscs = cam_cc_sm8450_gdscs, + .num_gdscs = ARRAY_SIZE(cam_cc_sm8450_gdscs), +}; + +static const struct of_device_id cam_cc_sm8450_match_table[] = { + { .compatible = "qcom,sm8450-camcc" }, + { } +}; +MODULE_DEVICE_TABLE(of, cam_cc_sm8450_match_table); + +static int cam_cc_sm8450_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &cam_cc_sm8450_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + clk_lucid_evo_pll_configure(&cam_cc_pll0, regmap, &cam_cc_pll0_config); + clk_lucid_evo_pll_configure(&cam_cc_pll1, regmap, &cam_cc_pll1_config); + clk_rivian_evo_pll_configure(&cam_cc_pll2, regmap, &cam_cc_pll2_config); + clk_lucid_evo_pll_configure(&cam_cc_pll3, regmap, &cam_cc_pll3_config); + clk_lucid_evo_pll_configure(&cam_cc_pll4, regmap, &cam_cc_pll4_config); + clk_lucid_evo_pll_configure(&cam_cc_pll5, regmap, &cam_cc_pll5_config); + clk_lucid_evo_pll_configure(&cam_cc_pll6, regmap, &cam_cc_pll6_config); + clk_lucid_evo_pll_configure(&cam_cc_pll7, regmap, &cam_cc_pll7_config); + clk_lucid_evo_pll_configure(&cam_cc_pll8, regmap, &cam_cc_pll8_config); + + return qcom_cc_really_probe(pdev, &cam_cc_sm8450_desc, regmap); +} + +static struct platform_driver cam_cc_sm8450_driver = { + .probe = cam_cc_sm8450_probe, + .driver = { + .name = "camcc-sm8450", + .of_match_table = cam_cc_sm8450_match_table, + }, +}; + +module_platform_driver(cam_cc_sm8450_driver); + +MODULE_DESCRIPTION("QCOM CAMCC SM8450 Driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/clk/qcom/clk-alpha-pll.c b/drivers/clk/qcom/clk-alpha-pll.c index 4406cf609aae..b42684703fbb 100644 --- a/drivers/clk/qcom/clk-alpha-pll.c +++ b/drivers/clk/qcom/clk-alpha-pll.c @@ -154,6 +154,18 @@ const u8 clk_alpha_pll_regs[][PLL_OFF_MAX_REGS] = { [PLL_OFF_TEST_CTL_U] = 0x30, [PLL_OFF_TEST_CTL_U1] = 0x34, }, + [CLK_ALPHA_PLL_TYPE_RIVIAN_EVO] = { + [PLL_OFF_OPMODE] = 0x04, + [PLL_OFF_STATUS] = 0x0c, + [PLL_OFF_L_VAL] = 0x10, + [PLL_OFF_USER_CTL] = 0x14, + [PLL_OFF_USER_CTL_U] = 0x18, + [PLL_OFF_CONFIG_CTL] = 0x1c, + [PLL_OFF_CONFIG_CTL_U] = 0x20, + [PLL_OFF_CONFIG_CTL_U1] = 0x24, + [PLL_OFF_TEST_CTL] = 0x28, + [PLL_OFF_TEST_CTL_U] = 0x2c, + }, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); @@ -191,8 +203,10 @@ EXPORT_SYMBOL_GPL(clk_alpha_pll_regs); #define LUCID_5LPE_ENABLE_VOTE_RUN BIT(21) /* LUCID EVO PLL specific settings and offsets */ +#define LUCID_EVO_PCAL_NOT_DONE BIT(8) #define LUCID_EVO_ENABLE_VOTE_RUN BIT(25) #define LUCID_EVO_PLL_L_VAL_MASK GENMASK(15, 0) +#define LUCID_EVO_PLL_CAL_L_VAL_SHIFT 16 /* ZONDA PLL specific */ #define ZONDA_PLL_OUT_MASK 0xf @@ -1439,7 +1453,7 @@ const struct clk_ops clk_alpha_pll_postdiv_fabia_ops = { EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_fabia_ops); /** - * clk_lucid_pll_configure - configure the lucid pll + * clk_trion_pll_configure - configure the trion pll * * @pll: clk alpha pll * @regmap: register map @@ -1823,7 +1837,7 @@ const struct clk_ops clk_alpha_pll_lucid_5lpe_ops = { .round_rate = clk_alpha_pll_round_rate, .set_rate = alpha_pll_lucid_5lpe_set_rate, }; -EXPORT_SYMBOL(clk_alpha_pll_lucid_5lpe_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_lucid_5lpe_ops); const struct clk_ops clk_alpha_pll_fixed_lucid_5lpe_ops = { .enable = alpha_pll_lucid_5lpe_enable, @@ -1832,14 +1846,14 @@ const struct clk_ops clk_alpha_pll_fixed_lucid_5lpe_ops = { .recalc_rate = clk_trion_pll_recalc_rate, .round_rate = clk_alpha_pll_round_rate, }; -EXPORT_SYMBOL(clk_alpha_pll_fixed_lucid_5lpe_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_fixed_lucid_5lpe_ops); const struct clk_ops clk_alpha_pll_postdiv_lucid_5lpe_ops = { .recalc_rate = clk_alpha_pll_postdiv_fabia_recalc_rate, .round_rate = clk_alpha_pll_postdiv_fabia_round_rate, .set_rate = clk_lucid_5lpe_pll_postdiv_set_rate, }; -EXPORT_SYMBOL(clk_alpha_pll_postdiv_lucid_5lpe_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_lucid_5lpe_ops); void clk_zonda_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config) @@ -1992,7 +2006,33 @@ const struct clk_ops clk_alpha_pll_zonda_ops = { .round_rate = clk_alpha_pll_round_rate, .set_rate = clk_zonda_pll_set_rate, }; -EXPORT_SYMBOL(clk_alpha_pll_zonda_ops); +EXPORT_SYMBOL_GPL(clk_alpha_pll_zonda_ops); + +void clk_lucid_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config) +{ + u32 lval = config->l; + + lval |= TRION_PLL_CAL_VAL << LUCID_EVO_PLL_CAL_L_VAL_SHIFT; + clk_alpha_pll_write_config(regmap, PLL_L_VAL(pll), lval); + clk_alpha_pll_write_config(regmap, PLL_ALPHA_VAL(pll), config->alpha); + clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); + clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL_U(pll), config->config_ctl_hi_val); + clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL_U1(pll), config->config_ctl_hi1_val); + clk_alpha_pll_write_config(regmap, PLL_USER_CTL(pll), config->user_ctl_val); + clk_alpha_pll_write_config(regmap, PLL_USER_CTL_U(pll), config->user_ctl_hi_val); + clk_alpha_pll_write_config(regmap, PLL_TEST_CTL(pll), config->test_ctl_val); + clk_alpha_pll_write_config(regmap, PLL_TEST_CTL_U(pll), config->test_ctl_hi_val); + clk_alpha_pll_write_config(regmap, PLL_TEST_CTL_U1(pll), config->test_ctl_hi1_val); + + /* Disable PLL output */ + regmap_update_bits(regmap, PLL_MODE(pll), PLL_OUTCTRL, 0); + + /* Set operation mode to STANDBY and de-assert the reset */ + regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); + regmap_update_bits(regmap, PLL_MODE(pll), PLL_RESET_N, PLL_RESET_N); +} +EXPORT_SYMBOL_GPL(clk_lucid_evo_pll_configure); static int alpha_pll_lucid_evo_enable(struct clk_hw *hw) { @@ -2079,6 +2119,31 @@ static void alpha_pll_lucid_evo_disable(struct clk_hw *hw) regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); } +static int alpha_pll_lucid_evo_prepare(struct clk_hw *hw) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + struct clk_hw *p; + u32 val = 0; + int ret; + + /* Return early if calibration is not needed. */ + regmap_read(pll->clkr.regmap, PLL_MODE(pll), &val); + if (!(val & LUCID_EVO_PCAL_NOT_DONE)) + return 0; + + p = clk_hw_get_parent(hw); + if (!p) + return -EINVAL; + + ret = alpha_pll_lucid_evo_enable(hw); + if (ret) + return ret; + + alpha_pll_lucid_evo_disable(hw); + + return 0; +} + static unsigned long alpha_pll_lucid_evo_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -2114,3 +2179,72 @@ const struct clk_ops clk_alpha_pll_postdiv_lucid_evo_ops = { .set_rate = clk_lucid_evo_pll_postdiv_set_rate, }; EXPORT_SYMBOL_GPL(clk_alpha_pll_postdiv_lucid_evo_ops); + +const struct clk_ops clk_alpha_pll_lucid_evo_ops = { + .prepare = alpha_pll_lucid_evo_prepare, + .enable = alpha_pll_lucid_evo_enable, + .disable = alpha_pll_lucid_evo_disable, + .is_enabled = clk_trion_pll_is_enabled, + .recalc_rate = alpha_pll_lucid_evo_recalc_rate, + .round_rate = clk_alpha_pll_round_rate, + .set_rate = alpha_pll_lucid_5lpe_set_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_lucid_evo_ops); + +void clk_rivian_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config) +{ + clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL(pll), config->config_ctl_val); + clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL_U(pll), config->config_ctl_hi_val); + clk_alpha_pll_write_config(regmap, PLL_CONFIG_CTL_U1(pll), config->config_ctl_hi1_val); + clk_alpha_pll_write_config(regmap, PLL_TEST_CTL(pll), config->test_ctl_val); + clk_alpha_pll_write_config(regmap, PLL_TEST_CTL_U(pll), config->test_ctl_hi_val); + clk_alpha_pll_write_config(regmap, PLL_L_VAL(pll), config->l); + clk_alpha_pll_write_config(regmap, PLL_USER_CTL(pll), config->user_ctl_val); + clk_alpha_pll_write_config(regmap, PLL_USER_CTL_U(pll), config->user_ctl_hi_val); + + regmap_write(regmap, PLL_OPMODE(pll), PLL_STANDBY); + + regmap_update_bits(regmap, PLL_MODE(pll), + PLL_RESET_N | PLL_BYPASSNL | PLL_OUTCTRL, + PLL_RESET_N | PLL_BYPASSNL); +} +EXPORT_SYMBOL_GPL(clk_rivian_evo_pll_configure); + +static unsigned long clk_rivian_evo_pll_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + u32 l; + + regmap_read(pll->clkr.regmap, PLL_L_VAL(pll), &l); + + return parent_rate * l; +} + +static long clk_rivian_evo_pll_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_alpha_pll *pll = to_clk_alpha_pll(hw); + unsigned long min_freq, max_freq; + u32 l; + u64 a; + + rate = alpha_pll_round_rate(rate, *prate, &l, &a, 0); + if (!pll->vco_table || alpha_pll_find_vco(pll, rate)) + return rate; + + min_freq = pll->vco_table[0].min_freq; + max_freq = pll->vco_table[pll->num_vco - 1].max_freq; + + return clamp(rate, min_freq, max_freq); +} + +const struct clk_ops clk_alpha_pll_rivian_evo_ops = { + .enable = alpha_pll_lucid_5lpe_enable, + .disable = alpha_pll_lucid_5lpe_disable, + .is_enabled = clk_trion_pll_is_enabled, + .recalc_rate = clk_rivian_evo_pll_recalc_rate, + .round_rate = clk_rivian_evo_pll_round_rate, +}; +EXPORT_SYMBOL_GPL(clk_alpha_pll_rivian_evo_ops); diff --git a/drivers/clk/qcom/clk-alpha-pll.h b/drivers/clk/qcom/clk-alpha-pll.h index 6e9907deaf30..447efb82fe59 100644 --- a/drivers/clk/qcom/clk-alpha-pll.h +++ b/drivers/clk/qcom/clk-alpha-pll.h @@ -18,6 +18,7 @@ enum { CLK_ALPHA_PLL_TYPE_AGERA, CLK_ALPHA_PLL_TYPE_ZONDA, CLK_ALPHA_PLL_TYPE_LUCID_EVO, + CLK_ALPHA_PLL_TYPE_RIVIAN_EVO, CLK_ALPHA_PLL_TYPE_MAX, }; @@ -152,9 +153,14 @@ extern const struct clk_ops clk_alpha_pll_postdiv_lucid_5lpe_ops; extern const struct clk_ops clk_alpha_pll_zonda_ops; #define clk_alpha_pll_postdiv_zonda_ops clk_alpha_pll_postdiv_fabia_ops + +extern const struct clk_ops clk_alpha_pll_lucid_evo_ops; extern const struct clk_ops clk_alpha_pll_fixed_lucid_evo_ops; extern const struct clk_ops clk_alpha_pll_postdiv_lucid_evo_ops; +extern const struct clk_ops clk_alpha_pll_rivian_evo_ops; +#define clk_alpha_pll_postdiv_rivian_evo_ops clk_alpha_pll_postdiv_fabia_ops + void clk_alpha_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); void clk_fabia_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, @@ -168,6 +174,9 @@ void clk_agera_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, void clk_zonda_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, const struct alpha_pll_config *config); - +void clk_lucid_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config); +void clk_rivian_evo_pll_configure(struct clk_alpha_pll *pll, struct regmap *regmap, + const struct alpha_pll_config *config); #endif diff --git a/drivers/clk/qcom/clk-hfpll.c b/drivers/clk/qcom/clk-hfpll.c index e847d586a73a..7dd17c184b69 100644 --- a/drivers/clk/qcom/clk-hfpll.c +++ b/drivers/clk/qcom/clk-hfpll.c @@ -72,13 +72,16 @@ static void __clk_hfpll_enable(struct clk_hw *hw) regmap_update_bits(regmap, hd->mode_reg, PLL_RESET_N, PLL_RESET_N); /* Wait for PLL to lock. */ - if (hd->status_reg) { - do { - regmap_read(regmap, hd->status_reg, &val); - } while (!(val & BIT(hd->lock_bit))); - } else { + if (hd->status_reg) + /* + * Busy wait. Should never timeout, we add a timeout to + * prevent any sort of stall. + */ + regmap_read_poll_timeout(regmap, hd->status_reg, val, + !(val & BIT(hd->lock_bit)), 0, + 100 * USEC_PER_MSEC); + else udelay(60); - } /* Enable PLL output. */ regmap_update_bits(regmap, hd->mode_reg, PLL_OUTCTRL, PLL_OUTCTRL); diff --git a/drivers/clk/qcom/clk-krait.c b/drivers/clk/qcom/clk-krait.c index 59f1af415b58..45da736bd5f4 100644 --- a/drivers/clk/qcom/clk-krait.c +++ b/drivers/clk/qcom/clk-krait.c @@ -18,13 +18,23 @@ static DEFINE_SPINLOCK(krait_clock_reg_lock); #define LPL_SHIFT 8 +#define SECCLKAGD BIT(4) + static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) { unsigned long flags; u32 regval; spin_lock_irqsave(&krait_clock_reg_lock, flags); + regval = krait_get_l2_indirect_reg(mux->offset); + + /* apq/ipq8064 Errata: disable sec_src clock gating during switch. */ + if (mux->disable_sec_src_gating) { + regval |= SECCLKAGD; + krait_set_l2_indirect_reg(mux->offset, regval); + } + regval &= ~(mux->mask << mux->shift); regval |= (sel & mux->mask) << mux->shift; if (mux->lpl) { @@ -32,11 +42,22 @@ static void __krait_mux_set_sel(struct krait_mux_clk *mux, int sel) regval |= (sel & mux->mask) << (mux->shift + LPL_SHIFT); } krait_set_l2_indirect_reg(mux->offset, regval); - spin_unlock_irqrestore(&krait_clock_reg_lock, flags); + + /* apq/ipq8064 Errata: re-enabled sec_src clock gating. */ + if (mux->disable_sec_src_gating) { + regval &= ~SECCLKAGD; + krait_set_l2_indirect_reg(mux->offset, regval); + } /* Wait for switch to complete. */ mb(); udelay(1); + + /* + * Unlock now to make sure the mux register is not + * modified while switching to the new parent. + */ + spin_unlock_irqrestore(&krait_clock_reg_lock, flags); } static int krait_mux_set_parent(struct clk_hw *hw, u8 index) diff --git a/drivers/clk/qcom/clk-krait.h b/drivers/clk/qcom/clk-krait.h index 9120bd2f5297..f930538c539e 100644 --- a/drivers/clk/qcom/clk-krait.h +++ b/drivers/clk/qcom/clk-krait.h @@ -15,6 +15,7 @@ struct krait_mux_clk { u8 safe_sel; u8 old_index; bool reparent; + bool disable_sec_src_gating; struct clk_hw hw; struct notifier_block clk_nb; diff --git a/drivers/clk/qcom/clk-rcg2.c b/drivers/clk/qcom/clk-rcg2.c index 8e5dce09d162..28019edd2a50 100644 --- a/drivers/clk/qcom/clk-rcg2.c +++ b/drivers/clk/qcom/clk-rcg2.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -437,7 +438,7 @@ static int clk_rcg2_get_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) static int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) { struct clk_rcg2 *rcg = to_clk_rcg2(hw); - u32 notn_m, n, m, d, not2d, mask, duty_per; + u32 notn_m, n, m, d, not2d, mask, duty_per, cfg; int ret; /* Duty-cycle cannot be modified for non-MND RCGs */ @@ -448,6 +449,11 @@ static int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) regmap_read(rcg->clkr.regmap, RCG_N_OFFSET(rcg), ¬n_m); regmap_read(rcg->clkr.regmap, RCG_M_OFFSET(rcg), &m); + regmap_read(rcg->clkr.regmap, RCG_CFG_OFFSET(rcg), &cfg); + + /* Duty-cycle cannot be modified if MND divider is in bypass mode. */ + if (!(cfg & CFG_MODE_MASK)) + return -EINVAL; n = (~(notn_m) + m) & mask; @@ -456,9 +462,11 @@ static int clk_rcg2_set_duty_cycle(struct clk_hw *hw, struct clk_duty *duty) /* Calculate 2d value */ d = DIV_ROUND_CLOSEST(n * duty_per * 2, 100); - /* Check bit widths of 2d. If D is too big reduce duty cycle. */ - if (d > mask) - d = mask; + /* + * Check bit widths of 2d. If D is too big reduce duty cycle. + * Also make sure it is never zero. + */ + d = clamp_val(d, 1, mask); if ((d / 2) > (n - m)) d = (n - m) * 2; diff --git a/drivers/clk/qcom/clk-rpm.c b/drivers/clk/qcom/clk-rpm.c index a18811c38018..747c473b0b5e 100644 --- a/drivers/clk/qcom/clk-rpm.c +++ b/drivers/clk/qcom/clk-rpm.c @@ -23,6 +23,14 @@ #define QCOM_RPM_SCALING_ENABLE_ID 0x2 #define QCOM_RPM_XO_MODE_ON 0x2 +static const struct clk_parent_data gcc_pxo[] = { + { .fw_name = "pxo", .name = "pxo_board" }, +}; + +static const struct clk_parent_data gcc_cxo[] = { + { .fw_name = "cxo", .name = "cxo_board" }, +}; + #define DEFINE_CLK_RPM(_platform, _name, _active, r_id) \ static struct clk_rpm _platform##_##_active; \ static struct clk_rpm _platform##_##_name = { \ @@ -32,8 +40,8 @@ .hw.init = &(struct clk_init_data){ \ .ops = &clk_rpm_ops, \ .name = #_name, \ - .parent_names = (const char *[]){ "pxo_board" }, \ - .num_parents = 1, \ + .parent_data = gcc_pxo, \ + .num_parents = ARRAY_SIZE(gcc_pxo), \ }, \ }; \ static struct clk_rpm _platform##_##_active = { \ @@ -44,8 +52,8 @@ .hw.init = &(struct clk_init_data){ \ .ops = &clk_rpm_ops, \ .name = #_active, \ - .parent_names = (const char *[]){ "pxo_board" }, \ - .num_parents = 1, \ + .parent_data = gcc_pxo, \ + .num_parents = ARRAY_SIZE(gcc_pxo), \ }, \ } @@ -56,8 +64,8 @@ .hw.init = &(struct clk_init_data){ \ .ops = &clk_rpm_xo_ops, \ .name = #_name, \ - .parent_names = (const char *[]){ "cxo_board" }, \ - .num_parents = 1, \ + .parent_data = gcc_cxo, \ + .num_parents = ARRAY_SIZE(gcc_cxo), \ }, \ } @@ -68,8 +76,8 @@ .hw.init = &(struct clk_init_data){ \ .ops = &clk_rpm_fixed_ops, \ .name = #_name, \ - .parent_names = (const char *[]){ "pxo" }, \ - .num_parents = 1, \ + .parent_data = gcc_pxo, \ + .num_parents = ARRAY_SIZE(gcc_pxo), \ }, \ } diff --git a/drivers/clk/qcom/clk-rpmh.c b/drivers/clk/qcom/clk-rpmh.c index aed907982344..c07cab6905cb 100644 --- a/drivers/clk/qcom/clk-rpmh.c +++ b/drivers/clk/qcom/clk-rpmh.c @@ -274,6 +274,11 @@ static int clk_rpmh_bcm_send_cmd(struct clk_rpmh *c, bool enable) cmd.addr = c->res_addr; cmd.data = BCM_TCS_CMD(1, enable, 0, cmd_state); + /* + * Send only an active only state request. RPMh continues to + * use the active state when we're in sleep/wake state as long + * as the sleep/wake state has never been set. + */ ret = clk_rpmh_send(c, RPMH_ACTIVE_ONLY_STATE, &cmd, enable); if (ret) { dev_err(c->dev, "set active state of %s failed: (%d)\n", diff --git a/drivers/clk/qcom/dispcc-sm8250.c b/drivers/clk/qcom/dispcc-sm8250.c index db9379634fb2..709076f0f9d7 100644 --- a/drivers/clk/qcom/dispcc-sm8250.c +++ b/drivers/clk/qcom/dispcc-sm8250.c @@ -43,6 +43,10 @@ static struct pll_vco vco_table[] = { { 249600000, 2000000000, 0 }, }; +static struct pll_vco lucid_5lpe_vco[] = { + { 249600000, 1750000000, 0 }, +}; + static struct alpha_pll_config disp_cc_pll0_config = { .l = 0x47, .alpha = 0xE000, @@ -1134,7 +1138,6 @@ static struct gdsc mdss_gdsc = { }, .pwrsts = PWRSTS_OFF_ON, .flags = HW_CTRL, - .supply = "mmcx", }; static struct clk_regmap *disp_cc_sm8250_clocks[] = { @@ -1228,6 +1231,7 @@ static const struct of_device_id disp_cc_sm8250_match_table[] = { { .compatible = "qcom,sc8180x-dispcc" }, { .compatible = "qcom,sm8150-dispcc" }, { .compatible = "qcom,sm8250-dispcc" }, + { .compatible = "qcom,sm8350-dispcc" }, { } }; MODULE_DEVICE_TABLE(of, disp_cc_sm8250_match_table); @@ -1258,7 +1262,7 @@ static int disp_cc_sm8250_probe(struct platform_device *pdev) return PTR_ERR(regmap); } - /* note: trion == lucid, except for the prepare() op */ + /* Apply differences for SM8150 and SM8350 */ BUILD_BUG_ON(CLK_ALPHA_PLL_TYPE_TRION != CLK_ALPHA_PLL_TYPE_LUCID); if (of_device_is_compatible(pdev->dev.of_node, "qcom,sc8180x-dispcc") || of_device_is_compatible(pdev->dev.of_node, "qcom,sm8150-dispcc")) { @@ -1270,6 +1274,62 @@ static int disp_cc_sm8250_probe(struct platform_device *pdev) disp_cc_pll1_config.config_ctl_hi1_val = 0x00000024; disp_cc_pll1_config.user_ctl_hi1_val = 0x000000D0; disp_cc_pll1_init.ops = &clk_alpha_pll_trion_ops; + } else if (of_device_is_compatible(pdev->dev.of_node, "qcom,sm8350-dispcc")) { + static struct clk_rcg2 * const rcgs[] = { + &disp_cc_mdss_byte0_clk_src, + &disp_cc_mdss_byte1_clk_src, + &disp_cc_mdss_dp_aux1_clk_src, + &disp_cc_mdss_dp_aux_clk_src, + &disp_cc_mdss_dp_link1_clk_src, + &disp_cc_mdss_dp_link_clk_src, + &disp_cc_mdss_dp_pixel1_clk_src, + &disp_cc_mdss_dp_pixel2_clk_src, + &disp_cc_mdss_dp_pixel_clk_src, + &disp_cc_mdss_esc0_clk_src, + &disp_cc_mdss_mdp_clk_src, + &disp_cc_mdss_pclk0_clk_src, + &disp_cc_mdss_pclk1_clk_src, + &disp_cc_mdss_rot_clk_src, + &disp_cc_mdss_vsync_clk_src, + }; + static struct clk_regmap_div * const divs[] = { + &disp_cc_mdss_byte0_div_clk_src, + &disp_cc_mdss_byte1_div_clk_src, + &disp_cc_mdss_dp_link1_div_clk_src, + &disp_cc_mdss_dp_link_div_clk_src, + }; + unsigned int i; + static bool offset_applied; + + /* + * note: trion == lucid, except for the prepare() op + * only apply the offsets once (in case of deferred probe) + */ + if (!offset_applied) { + for (i = 0; i < ARRAY_SIZE(rcgs); i++) + rcgs[i]->cmd_rcgr -= 4; + + for (i = 0; i < ARRAY_SIZE(divs); i++) { + divs[i]->reg -= 4; + divs[i]->width = 4; + } + + disp_cc_mdss_ahb_clk.halt_reg -= 4; + disp_cc_mdss_ahb_clk.clkr.enable_reg -= 4; + + offset_applied = true; + } + + disp_cc_mdss_ahb_clk_src.cmd_rcgr = 0x22a0; + + disp_cc_pll0_config.config_ctl_hi1_val = 0x2a9a699c; + disp_cc_pll0_config.test_ctl_hi1_val = 0x01800000; + disp_cc_pll0_init.ops = &clk_alpha_pll_lucid_5lpe_ops; + disp_cc_pll0.vco_table = lucid_5lpe_vco; + disp_cc_pll1_config.config_ctl_hi1_val = 0x2a9a699c; + disp_cc_pll1_config.test_ctl_hi1_val = 0x01800000; + disp_cc_pll1_init.ops = &clk_alpha_pll_lucid_5lpe_ops; + disp_cc_pll1.vco_table = lucid_5lpe_vco; } clk_lucid_pll_configure(&disp_cc_pll0, regmap, &disp_cc_pll0_config); diff --git a/drivers/clk/qcom/gcc-ipq8074.c b/drivers/clk/qcom/gcc-ipq8074.c index 541016db3c4b..42d185fe19c8 100644 --- a/drivers/clk/qcom/gcc-ipq8074.c +++ b/drivers/clk/qcom/gcc-ipq8074.c @@ -22,6 +22,7 @@ #include "clk-alpha-pll.h" #include "clk-regmap-divider.h" #include "clk-regmap-mux.h" +#include "gdsc.h" #include "reset.h" enum { @@ -662,6 +663,7 @@ static struct clk_branch gcc_sleep_clk_src = { }, .num_parents = 1, .ops = &clk_branch2_ops, + .flags = CLK_IS_CRITICAL, }, }, }; @@ -1788,8 +1790,10 @@ static struct clk_regmap_div nss_port4_tx_div_clk_src = { static const struct freq_tbl ftbl_nss_port5_rx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(25000000, P_UNIPHY1_RX, 12.5, 0, 0), + F(25000000, P_UNIPHY0_RX, 5, 0, 0), F(78125000, P_UNIPHY1_RX, 4, 0, 0), F(125000000, P_UNIPHY1_RX, 2.5, 0, 0), + F(125000000, P_UNIPHY0_RX, 1, 0, 0), F(156250000, P_UNIPHY1_RX, 2, 0, 0), F(312500000, P_UNIPHY1_RX, 1, 0, 0), { } @@ -1828,8 +1832,10 @@ static struct clk_regmap_div nss_port5_rx_div_clk_src = { static const struct freq_tbl ftbl_nss_port5_tx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(25000000, P_UNIPHY1_TX, 12.5, 0, 0), + F(25000000, P_UNIPHY0_TX, 5, 0, 0), F(78125000, P_UNIPHY1_TX, 4, 0, 0), F(125000000, P_UNIPHY1_TX, 2.5, 0, 0), + F(125000000, P_UNIPHY0_TX, 1, 0, 0), F(156250000, P_UNIPHY1_TX, 2, 0, 0), F(312500000, P_UNIPHY1_TX, 1, 0, 0), { } @@ -1867,8 +1873,10 @@ static struct clk_regmap_div nss_port5_tx_div_clk_src = { static const struct freq_tbl ftbl_nss_port6_rx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY2_RX, 5, 0, 0), F(25000000, P_UNIPHY2_RX, 12.5, 0, 0), F(78125000, P_UNIPHY2_RX, 4, 0, 0), + F(125000000, P_UNIPHY2_RX, 1, 0, 0), F(125000000, P_UNIPHY2_RX, 2.5, 0, 0), F(156250000, P_UNIPHY2_RX, 2, 0, 0), F(312500000, P_UNIPHY2_RX, 1, 0, 0), @@ -1907,8 +1915,10 @@ static struct clk_regmap_div nss_port6_rx_div_clk_src = { static const struct freq_tbl ftbl_nss_port6_tx_clk_src[] = { F(19200000, P_XO, 1, 0, 0), + F(25000000, P_UNIPHY2_TX, 5, 0, 0), F(25000000, P_UNIPHY2_TX, 12.5, 0, 0), F(78125000, P_UNIPHY2_TX, 4, 0, 0), + F(125000000, P_UNIPHY2_TX, 1, 0, 0), F(125000000, P_UNIPHY2_TX, 2.5, 0, 0), F(156250000, P_UNIPHY2_TX, 2, 0, 0), F(312500000, P_UNIPHY2_TX, 1, 0, 0), @@ -3174,6 +3184,24 @@ static struct clk_branch gcc_nss_ptp_ref_clk = { }, }; +static struct clk_branch gcc_crypto_ppe_clk = { + .halt_reg = 0x68310, + .halt_bit = 31, + .clkr = { + .enable_reg = 0x68310, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gcc_crypto_ppe_clk", + .parent_names = (const char *[]){ + "nss_ppe_clk_src" + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + static struct clk_branch gcc_nssnoc_ce_apb_clk = { .halt_reg = 0x6830c, .clkr = { @@ -3346,6 +3374,7 @@ static struct clk_branch gcc_nssnoc_ubi1_ahb_clk = { static struct clk_branch gcc_ubi0_ahb_clk = { .halt_reg = 0x6820c, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x6820c, .enable_mask = BIT(0), @@ -3363,6 +3392,7 @@ static struct clk_branch gcc_ubi0_ahb_clk = { static struct clk_branch gcc_ubi0_axi_clk = { .halt_reg = 0x68200, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68200, .enable_mask = BIT(0), @@ -3380,6 +3410,7 @@ static struct clk_branch gcc_ubi0_axi_clk = { static struct clk_branch gcc_ubi0_nc_axi_clk = { .halt_reg = 0x68204, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68204, .enable_mask = BIT(0), @@ -3397,6 +3428,7 @@ static struct clk_branch gcc_ubi0_nc_axi_clk = { static struct clk_branch gcc_ubi0_core_clk = { .halt_reg = 0x68210, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68210, .enable_mask = BIT(0), @@ -3414,6 +3446,7 @@ static struct clk_branch gcc_ubi0_core_clk = { static struct clk_branch gcc_ubi0_mpt_clk = { .halt_reg = 0x68208, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68208, .enable_mask = BIT(0), @@ -3431,6 +3464,7 @@ static struct clk_branch gcc_ubi0_mpt_clk = { static struct clk_branch gcc_ubi1_ahb_clk = { .halt_reg = 0x6822c, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x6822c, .enable_mask = BIT(0), @@ -3448,6 +3482,7 @@ static struct clk_branch gcc_ubi1_ahb_clk = { static struct clk_branch gcc_ubi1_axi_clk = { .halt_reg = 0x68220, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68220, .enable_mask = BIT(0), @@ -3465,6 +3500,7 @@ static struct clk_branch gcc_ubi1_axi_clk = { static struct clk_branch gcc_ubi1_nc_axi_clk = { .halt_reg = 0x68224, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68224, .enable_mask = BIT(0), @@ -3482,6 +3518,7 @@ static struct clk_branch gcc_ubi1_nc_axi_clk = { static struct clk_branch gcc_ubi1_core_clk = { .halt_reg = 0x68230, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68230, .enable_mask = BIT(0), @@ -3499,6 +3536,7 @@ static struct clk_branch gcc_ubi1_core_clk = { static struct clk_branch gcc_ubi1_mpt_clk = { .halt_reg = 0x68228, + .halt_check = BRANCH_HALT_DELAY, .clkr = { .enable_reg = 0x68228, .enable_mask = BIT(0), @@ -4371,6 +4409,49 @@ static struct clk_branch gcc_pcie0_axi_s_bridge_clk = { }, }; +static struct gdsc usb0_gdsc = { + .gdscr = 0x3e078, + .pd = { + .name = "usb0_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static struct gdsc usb1_gdsc = { + .gdscr = 0x3f078, + .pd = { + .name = "usb1_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, +}; + +static const struct alpha_pll_config ubi32_pll_config = { + .l = 0x4e, + .config_ctl_val = 0x200d4aa8, + .config_ctl_hi_val = 0x3c2, + .main_output_mask = BIT(0), + .aux_output_mask = BIT(1), + .pre_div_val = 0x0, + .pre_div_mask = BIT(12), + .post_div_val = 0x0, + .post_div_mask = GENMASK(9, 8), +}; + +static const struct alpha_pll_config nss_crypto_pll_config = { + .l = 0x3e, + .alpha = 0x0, + .alpha_hi = 0x80, + .config_ctl_val = 0x4001055b, + .main_output_mask = BIT(0), + .pre_div_val = 0x0, + .pre_div_mask = GENMASK(14, 12), + .post_div_val = 0x1 << 8, + .post_div_mask = GENMASK(11, 8), + .vco_mask = GENMASK(21, 20), + .vco_val = 0x0, + .alpha_en_mask = BIT(24), +}; + static struct clk_hw *gcc_ipq8074_hws[] = { &gpll0_out_main_div2.hw, &gpll6_out_main_div2.hw, @@ -4609,6 +4690,7 @@ static struct clk_regmap *gcc_ipq8074_clks[] = { [GCC_PCIE0_RCHNG_CLK_SRC] = &pcie0_rchng_clk_src.clkr, [GCC_PCIE0_RCHNG_CLK] = &gcc_pcie0_rchng_clk.clkr, [GCC_PCIE0_AXI_S_BRIDGE_CLK] = &gcc_pcie0_axi_s_bridge_clk.clkr, + [GCC_CRYPTO_PPE_CLK] = &gcc_crypto_ppe_clk.clkr, }; static const struct qcom_reset_map gcc_ipq8074_resets[] = { @@ -4746,6 +4828,11 @@ static const struct qcom_reset_map gcc_ipq8074_resets[] = { [GCC_PCIE1_AXI_MASTER_STICKY_ARES] = { 0x76040, 6 }, }; +static struct gdsc *gcc_ipq8074_gdscs[] = { + [USB0_GDSC] = &usb0_gdsc, + [USB1_GDSC] = &usb1_gdsc, +}; + static const struct of_device_id gcc_ipq8074_match_table[] = { { .compatible = "qcom,gcc-ipq8074" }, { } @@ -4768,11 +4855,26 @@ static const struct qcom_cc_desc gcc_ipq8074_desc = { .num_resets = ARRAY_SIZE(gcc_ipq8074_resets), .clk_hws = gcc_ipq8074_hws, .num_clk_hws = ARRAY_SIZE(gcc_ipq8074_hws), + .gdscs = gcc_ipq8074_gdscs, + .num_gdscs = ARRAY_SIZE(gcc_ipq8074_gdscs), }; static int gcc_ipq8074_probe(struct platform_device *pdev) { - return qcom_cc_probe(pdev, &gcc_ipq8074_desc); + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &gcc_ipq8074_desc); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + /* SW Workaround for UBI32 Huayra PLL */ + regmap_update_bits(regmap, 0x2501c, BIT(26), BIT(26)); + + clk_alpha_pll_configure(&ubi32_pll_main, regmap, &ubi32_pll_config); + clk_alpha_pll_configure(&nss_crypto_pll_main, regmap, + &nss_crypto_pll_config); + + return qcom_cc_really_probe(pdev, &gcc_ipq8074_desc, regmap); } static struct platform_driver gcc_ipq8074_driver = { diff --git a/drivers/clk/qcom/gcc-msm8916.c b/drivers/clk/qcom/gcc-msm8916.c index 17e4a5a2a9fd..9a46794f6eb8 100644 --- a/drivers/clk/qcom/gcc-msm8916.c +++ b/drivers/clk/qcom/gcc-msm8916.c @@ -765,7 +765,20 @@ static struct clk_rcg2 cci_clk_src = { }, }; +/* + * This is a frequency table for "General Purpose" clocks. + * These clocks can be muxed to the SoC pins and may be used by + * external devices. They're often used as PWM source. + * + * See comment at ftbl_gcc_gp1_3_clk. + */ static const struct freq_tbl ftbl_gcc_camss_gp0_1_clk[] = { + F(10000, P_XO, 16, 1, 120), + F(100000, P_XO, 16, 1, 12), + F(500000, P_GPLL0, 16, 1, 100), + F(1000000, P_GPLL0, 16, 1, 50), + F(2500000, P_GPLL0, 16, 1, 20), + F(5000000, P_GPLL0, 16, 1, 10), F(100000000, P_GPLL0, 8, 0, 0), F(200000000, P_GPLL0, 4, 0, 0), { } @@ -927,7 +940,29 @@ static struct clk_rcg2 crypto_clk_src = { }, }; +/* + * This is a frequency table for "General Purpose" clocks. + * These clocks can be muxed to the SoC pins and may be used by + * external devices. They're often used as PWM source. + * + * Please note that MND divider must be enabled for duty-cycle + * control to be possible. (M != N) Also since D register is configured + * with a value multiplied by 2, and duty cycle is calculated as + * (2 * D) % 2^W + * DutyCycle = ---------------- + * 2 * (N % 2^W) + * (where W = .mnd_width) + * N must be half or less than maximum value for the register. + * Otherwise duty-cycle control would be limited. + * (e.g. for 8-bit NMD N should be less than 128) + */ static const struct freq_tbl ftbl_gcc_gp1_3_clk[] = { + F(10000, P_XO, 16, 1, 120), + F(100000, P_XO, 16, 1, 12), + F(500000, P_GPLL0, 16, 1, 100), + F(1000000, P_GPLL0, 16, 1, 50), + F(2500000, P_GPLL0, 16, 1, 20), + F(5000000, P_GPLL0, 16, 1, 10), F(19200000, P_XO, 1, 0, 0), { } }; diff --git a/drivers/clk/qcom/gcc-msm8939.c b/drivers/clk/qcom/gcc-msm8939.c index 39ebb443ae3d..8e2d9fb98ad5 100644 --- a/drivers/clk/qcom/gcc-msm8939.c +++ b/drivers/clk/qcom/gcc-msm8939.c @@ -632,7 +632,7 @@ static struct clk_rcg2 system_noc_bfdcd_clk_src = { }; static struct clk_rcg2 bimc_ddr_clk_src = { - .cmd_rcgr = 0x32004, + .cmd_rcgr = 0x32024, .hid_width = 5, .parent_map = gcc_xo_gpll0_bimc_map, .clkr.hw.init = &(struct clk_init_data){ @@ -644,6 +644,18 @@ static struct clk_rcg2 bimc_ddr_clk_src = { }, }; +static struct clk_rcg2 system_mm_noc_bfdcd_clk_src = { + .cmd_rcgr = 0x2600c, + .hid_width = 5, + .parent_map = gcc_xo_gpll0_gpll6a_map, + .clkr.hw.init = &(struct clk_init_data){ + .name = "system_mm_noc_bfdcd_clk_src", + .parent_data = gcc_xo_gpll0_gpll6a_parent_data, + .num_parents = 3, + .ops = &clk_rcg2_ops, + }, +}; + static const struct freq_tbl ftbl_gcc_camss_ahb_clk[] = { F(40000000, P_GPLL0, 10, 1, 2), F(80000000, P_GPLL0, 10, 0, 0), @@ -1002,7 +1014,8 @@ static struct clk_rcg2 blsp1_uart2_apps_clk_src = { }; static const struct freq_tbl ftbl_gcc_camss_cci_clk[] = { - F(19200000, P_XO, 1, 0, 0), + F(19200000, P_XO, 1, 0, 0), + F(37500000, P_GPLL0, 1, 3, 64), { } }; @@ -1142,6 +1155,9 @@ static struct clk_rcg2 csi1phytimer_clk_src = { static const struct freq_tbl ftbl_gcc_camss_cpp_clk[] = { F(160000000, P_GPLL0, 5, 0, 0), + F(200000000, P_GPLL0, 4, 0, 0), + F(228570000, P_GPLL0, 3.5, 0, 0), + F(266670000, P_GPLL0, 3, 0, 0), F(320000000, P_GPLL0, 2.5, 0, 0), F(465000000, P_GPLL2, 2, 0, 0), { } @@ -1290,6 +1306,8 @@ static const struct freq_tbl ftbl_gcc_mdss_mdp_clk[] = { F(50000000, P_GPLL0_AUX, 16, 0, 0), F(80000000, P_GPLL0_AUX, 10, 0, 0), F(100000000, P_GPLL0_AUX, 8, 0, 0), + F(145500000, P_GPLL0_AUX, 5.5, 0, 0), + F(153600000, P_GPLL0, 4, 0, 0), F(160000000, P_GPLL0_AUX, 5, 0, 0), F(177780000, P_GPLL0_AUX, 4.5, 0, 0), F(200000000, P_GPLL0_AUX, 4, 0, 0), @@ -1462,7 +1480,9 @@ static struct clk_rcg2 bimc_gpu_clk_src = { }; static const struct freq_tbl ftbl_gcc_usb_hs_system_clk[] = { + F(57140000, P_GPLL0, 14, 0, 0), F(80000000, P_GPLL0, 10, 0, 0), + F(100000000, P_GPLL0, 8, 0, 0), { } }; @@ -1823,9 +1843,9 @@ static struct clk_branch gcc_ultaudio_pcnoc_sway_clk = { }; static const struct freq_tbl ftbl_gcc_venus0_vcodec0_clk[] = { - F(100000000, P_GPLL0, 8, 0, 0), - F(160000000, P_GPLL0, 5, 0, 0), - F(228570000, P_GPLL0, 3.5, 0, 0), + F(133330000, P_GPLL0, 6, 0, 0), + F(200000000, P_GPLL0, 4, 0, 0), + F(266670000, P_GPLL0, 3, 0, 0), { } }; @@ -2441,7 +2461,7 @@ static struct clk_branch gcc_camss_jpeg_axi_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_camss_jpeg_axi_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -2645,7 +2665,7 @@ static struct clk_branch gcc_camss_vfe_axi_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_camss_vfe_axi_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -2801,7 +2821,7 @@ static struct clk_branch gcc_mdss_axi_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_mdss_axi_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -3193,7 +3213,7 @@ static struct clk_branch gcc_mdp_tbu_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_mdp_tbu_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -3211,7 +3231,7 @@ static struct clk_branch gcc_venus_tbu_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_venus_tbu_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -3229,7 +3249,7 @@ static struct clk_branch gcc_vfe_tbu_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_vfe_tbu_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -3247,7 +3267,7 @@ static struct clk_branch gcc_jpeg_tbu_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_jpeg_tbu_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -3484,7 +3504,7 @@ static struct clk_branch gcc_venus0_axi_clk = { .hw.init = &(struct clk_init_data){ .name = "gcc_venus0_axi_clk", .parent_data = &(const struct clk_parent_data){ - .hw = &system_noc_bfdcd_clk_src.clkr.hw, + .hw = &system_mm_noc_bfdcd_clk_src.clkr.hw, }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, @@ -3623,6 +3643,7 @@ static struct clk_regmap *gcc_msm8939_clocks[] = { [GPLL2_VOTE] = &gpll2_vote, [PCNOC_BFDCD_CLK_SRC] = &pcnoc_bfdcd_clk_src.clkr, [SYSTEM_NOC_BFDCD_CLK_SRC] = &system_noc_bfdcd_clk_src.clkr, + [SYSTEM_MM_NOC_BFDCD_CLK_SRC] = &system_mm_noc_bfdcd_clk_src.clkr, [CAMSS_AHB_CLK_SRC] = &camss_ahb_clk_src.clkr, [APSS_AHB_CLK_SRC] = &apss_ahb_clk_src.clkr, [CSI0_CLK_SRC] = &csi0_clk_src.clkr, diff --git a/drivers/clk/qcom/gcc-msm8960.c b/drivers/clk/qcom/gcc-msm8960.c index 051745ef99c8..a6e13b91e4c8 100644 --- a/drivers/clk/qcom/gcc-msm8960.c +++ b/drivers/clk/qcom/gcc-msm8960.c @@ -3641,6 +3641,9 @@ static int gcc_msm8960_probe(struct platform_device *pdev) hfpll_l2.d = &hfpll_l2_8064_data; } + if (of_get_available_child_count(pdev->dev.of_node) != 0) + return devm_of_platform_populate(&pdev->dev); + tsens = platform_device_register_data(&pdev->dev, "qcom-tsens", -1, NULL, 0); if (IS_ERR(tsens)) @@ -3655,7 +3658,8 @@ static int gcc_msm8960_remove(struct platform_device *pdev) { struct platform_device *tsens = platform_get_drvdata(pdev); - platform_device_unregister(tsens); + if (tsens) + platform_device_unregister(tsens); return 0; } diff --git a/drivers/clk/qcom/gcc-msm8994.c b/drivers/clk/qcom/gcc-msm8994.c index 6b702cdacbf2..0f52c48e89d8 100644 --- a/drivers/clk/qcom/gcc-msm8994.c +++ b/drivers/clk/qcom/gcc-msm8994.c @@ -52,7 +52,9 @@ static struct clk_alpha_pll_postdiv gpll0 = { .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data){ .name = "gpll0", - .parent_names = (const char *[]) { "gpll0_early" }, + .parent_hws = (const struct clk_hw*[]){ + &gpll0_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, }, @@ -81,7 +83,9 @@ static struct clk_alpha_pll_postdiv gpll4 = { .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_DEFAULT], .clkr.hw.init = &(struct clk_init_data){ .name = "gpll4", - .parent_names = (const char *[]) { "gpll4_early" }, + .parent_hws = (const struct clk_hw*[]){ + &gpll4_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, }, diff --git a/drivers/clk/qcom/gcc-sc8280xp.c b/drivers/clk/qcom/gcc-sc8280xp.c index 4b894442fdf5..a2f3ffcc5849 100644 --- a/drivers/clk/qcom/gcc-sc8280xp.c +++ b/drivers/clk/qcom/gcc-sc8280xp.c @@ -20,6 +20,7 @@ #include "clk-regmap.h" #include "clk-regmap-divider.h" #include "clk-regmap-mux.h" +#include "clk-regmap-phy-mux.h" #include "common.h" #include "gdsc.h" #include "reset.h" @@ -82,11 +83,6 @@ enum { P_GCC_USB4_PHY_PCIE_PIPEGMUX_CLK_SRC, P_GCC_USB4_PHY_PIPEGMUX_CLK_SRC, P_GCC_USB4_PHY_SYS_PIPEGMUX_CLK_SRC, - P_PCIE_2A_PIPE_CLK, - P_PCIE_2B_PIPE_CLK, - P_PCIE_3A_PIPE_CLK, - P_PCIE_3B_PIPE_CLK, - P_PCIE_4_PIPE_CLK, P_QUSB4PHY_1_GCC_USB4_RX0_CLK, P_QUSB4PHY_1_GCC_USB4_RX1_CLK, P_QUSB4PHY_GCC_USB4_RX0_CLK, @@ -351,56 +347,6 @@ static const struct clk_parent_data gcc_parent_data_9[] = { { .hw = &gcc_gpll0_out_even.clkr.hw }, }; -static const struct parent_map gcc_parent_map_10[] = { - { P_PCIE_2A_PIPE_CLK, 0 }, - { P_BI_TCXO, 2 }, -}; - -static const struct clk_parent_data gcc_parent_data_10[] = { - { .index = DT_PCIE_2A_PIPE_CLK }, - { .index = DT_BI_TCXO }, -}; - -static const struct parent_map gcc_parent_map_11[] = { - { P_PCIE_2B_PIPE_CLK, 0 }, - { P_BI_TCXO, 2 }, -}; - -static const struct clk_parent_data gcc_parent_data_11[] = { - { .index = DT_PCIE_2B_PIPE_CLK }, - { .index = DT_BI_TCXO }, -}; - -static const struct parent_map gcc_parent_map_12[] = { - { P_PCIE_3A_PIPE_CLK, 0 }, - { P_BI_TCXO, 2 }, -}; - -static const struct clk_parent_data gcc_parent_data_12[] = { - { .index = DT_PCIE_3A_PIPE_CLK }, - { .index = DT_BI_TCXO }, -}; - -static const struct parent_map gcc_parent_map_13[] = { - { P_PCIE_3B_PIPE_CLK, 0 }, - { P_BI_TCXO, 2 }, -}; - -static const struct clk_parent_data gcc_parent_data_13[] = { - { .index = DT_PCIE_3B_PIPE_CLK }, - { .index = DT_BI_TCXO }, -}; - -static const struct parent_map gcc_parent_map_14[] = { - { P_PCIE_4_PIPE_CLK, 0 }, - { P_BI_TCXO, 2 }, -}; - -static const struct clk_parent_data gcc_parent_data_14[] = { - { .index = DT_PCIE_4_PIPE_CLK }, - { .index = DT_BI_TCXO }, -}; - static const struct parent_map gcc_parent_map_15[] = { { P_BI_TCXO, 0 }, { P_GCC_GPLL0_OUT_MAIN, 1 }, @@ -741,77 +687,72 @@ static const struct clk_parent_data gcc_parent_data_41[] = { { .index = DT_USB4_PHY_GCC_USB4_PCIE_PIPE_CLK }, }; -static struct clk_regmap_mux gcc_pcie_2a_pipe_clk_src = { +static struct clk_regmap_phy_mux gcc_pcie_2a_pipe_clk_src = { .reg = 0x9d05c, - .shift = 0, - .width = 2, - .parent_map = gcc_parent_map_10, .clkr = { .hw.init = &(const struct clk_init_data) { .name = "gcc_pcie_2a_pipe_clk_src", - .parent_data = gcc_parent_data_10, - .num_parents = ARRAY_SIZE(gcc_parent_data_10), - .ops = &clk_regmap_mux_closest_ops, + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_2A_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, }, }, }; -static struct clk_regmap_mux gcc_pcie_2b_pipe_clk_src = { +static struct clk_regmap_phy_mux gcc_pcie_2b_pipe_clk_src = { .reg = 0x9e05c, - .shift = 0, - .width = 2, - .parent_map = gcc_parent_map_11, .clkr = { .hw.init = &(const struct clk_init_data) { .name = "gcc_pcie_2b_pipe_clk_src", - .parent_data = gcc_parent_data_11, - .num_parents = ARRAY_SIZE(gcc_parent_data_11), - .ops = &clk_regmap_mux_closest_ops, + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_2B_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, }, }, }; -static struct clk_regmap_mux gcc_pcie_3a_pipe_clk_src = { +static struct clk_regmap_phy_mux gcc_pcie_3a_pipe_clk_src = { .reg = 0xa005c, - .shift = 0, - .width = 2, - .parent_map = gcc_parent_map_12, .clkr = { .hw.init = &(const struct clk_init_data) { .name = "gcc_pcie_3a_pipe_clk_src", - .parent_data = gcc_parent_data_12, - .num_parents = ARRAY_SIZE(gcc_parent_data_12), - .ops = &clk_regmap_mux_closest_ops, + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_3A_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, }, }, }; -static struct clk_regmap_mux gcc_pcie_3b_pipe_clk_src = { +static struct clk_regmap_phy_mux gcc_pcie_3b_pipe_clk_src = { .reg = 0xa205c, - .shift = 0, - .width = 2, - .parent_map = gcc_parent_map_13, .clkr = { .hw.init = &(const struct clk_init_data) { .name = "gcc_pcie_3b_pipe_clk_src", - .parent_data = gcc_parent_data_13, - .num_parents = ARRAY_SIZE(gcc_parent_data_13), - .ops = &clk_regmap_mux_closest_ops, + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_3B_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, }, }, }; -static struct clk_regmap_mux gcc_pcie_4_pipe_clk_src = { +static struct clk_regmap_phy_mux gcc_pcie_4_pipe_clk_src = { .reg = 0x6b05c, - .shift = 0, - .width = 2, - .parent_map = gcc_parent_map_14, .clkr = { .hw.init = &(const struct clk_init_data) { .name = "gcc_pcie_4_pipe_clk_src", - .parent_data = gcc_parent_data_14, - .num_parents = ARRAY_SIZE(gcc_parent_data_14), - .ops = &clk_regmap_mux_closest_ops, + .parent_data = &(const struct clk_parent_data){ + .index = DT_PCIE_4_PIPE_CLK, + }, + .num_parents = 1, + .ops = &clk_regmap_phy_mux_ops, }, }, }; @@ -6807,58 +6748,79 @@ static struct clk_branch gcc_video_vcodec_throttle_clk = { static struct gdsc pcie_0_tunnel_gdsc = { .gdscr = 0xa4004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(0), .pd = { .name = "pcie_0_tunnel_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc pcie_1_tunnel_gdsc = { .gdscr = 0x8d004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(1), .pd = { .name = "pcie_1_tunnel_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc pcie_2a_gdsc = { .gdscr = 0x9d004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(2), .pd = { .name = "pcie_2a_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc pcie_2b_gdsc = { .gdscr = 0x9e004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(3), .pd = { .name = "pcie_2b_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc pcie_3a_gdsc = { .gdscr = 0xa0004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(4), .pd = { .name = "pcie_3a_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc pcie_3b_gdsc = { .gdscr = 0xa2004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(5), .pd = { .name = "pcie_3b_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc pcie_4_gdsc = { .gdscr = 0x6b004, + .collapse_ctrl = 0x52128, + .collapse_mask = BIT(6), .pd = { .name = "pcie_4_gdsc", }, .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, }; static struct gdsc ufs_card_gdsc = { diff --git a/drivers/clk/qcom/gcc-sm6350.c b/drivers/clk/qcom/gcc-sm6350.c index a4f7fba70393..69412400efa4 100644 --- a/drivers/clk/qcom/gcc-sm6350.c +++ b/drivers/clk/qcom/gcc-sm6350.c @@ -2558,7 +2558,7 @@ static int gcc_sm6350_probe(struct platform_device *pdev) if (ret) return ret; - return qcom_cc_really_probe(pdev, &gcc_sm6350_desc, regmap);; + return qcom_cc_really_probe(pdev, &gcc_sm6350_desc, regmap); } static struct platform_driver gcc_sm6350_driver = { diff --git a/drivers/clk/qcom/gdsc.c b/drivers/clk/qcom/gdsc.c index 44520efc6c72..d3244006c661 100644 --- a/drivers/clk/qcom/gdsc.c +++ b/drivers/clk/qcom/gdsc.c @@ -132,10 +132,29 @@ static int gdsc_poll_status(struct gdsc *sc, enum gdsc_status status) return -ETIMEDOUT; } +static int gdsc_update_collapse_bit(struct gdsc *sc, bool val) +{ + u32 reg, mask; + int ret; + + if (sc->collapse_mask) { + reg = sc->collapse_ctrl; + mask = sc->collapse_mask; + } else { + reg = sc->gdscr; + mask = SW_COLLAPSE_MASK; + } + + ret = regmap_update_bits(sc->regmap, reg, mask, val ? mask : 0); + if (ret) + return ret; + + return 0; +} + static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status) { int ret; - u32 val = (status == GDSC_ON) ? 0 : SW_COLLAPSE_MASK; if (status == GDSC_ON && sc->rsupply) { ret = regulator_enable(sc->rsupply); @@ -143,9 +162,7 @@ static int gdsc_toggle_logic(struct gdsc *sc, enum gdsc_status status) return ret; } - ret = regmap_update_bits(sc->regmap, sc->gdscr, SW_COLLAPSE_MASK, val); - if (ret) - return ret; + ret = gdsc_update_collapse_bit(sc, status == GDSC_OFF); /* If disabling votable gdscs, don't poll on status */ if ((sc->flags & VOTABLE) && status == GDSC_OFF) { @@ -420,13 +437,20 @@ static int gdsc_init(struct gdsc *sc) return ret; } + /* ...and the power-domain */ + ret = gdsc_pm_runtime_get(sc); + if (ret) { + if (sc->rsupply) + regulator_disable(sc->rsupply); + return ret; + } + /* * Votable GDSCs can be ON due to Vote from other masters. * If a Votable GDSC is ON, make sure we have a Vote. */ if (sc->flags & VOTABLE) { - ret = regmap_update_bits(sc->regmap, sc->gdscr, - SW_COLLAPSE_MASK, val); + ret = gdsc_update_collapse_bit(sc, false); if (ret) return ret; } diff --git a/drivers/clk/qcom/gdsc.h b/drivers/clk/qcom/gdsc.h index ad313d7210bd..5de48c9439b2 100644 --- a/drivers/clk/qcom/gdsc.h +++ b/drivers/clk/qcom/gdsc.h @@ -18,6 +18,8 @@ struct reset_controller_dev; * @pd: generic power domain * @regmap: regmap for MMIO accesses * @gdscr: gsdc control register + * @collapse_ctrl: APCS collapse-vote register + * @collapse_mask: APCS collapse-vote mask * @gds_hw_ctrl: gds_hw_ctrl register * @cxcs: offsets of branch registers to toggle mem/periph bits in * @cxc_count: number of @cxcs @@ -35,6 +37,8 @@ struct gdsc { struct generic_pm_domain *parent; struct regmap *regmap; unsigned int gdscr; + unsigned int collapse_ctrl; + unsigned int collapse_mask; unsigned int gds_hw_ctrl; unsigned int clamp_io_ctrl; unsigned int *cxcs; diff --git a/drivers/clk/qcom/gpucc-sm8350.c b/drivers/clk/qcom/gpucc-sm8350.c new file mode 100644 index 000000000000..5367ce654ac9 --- /dev/null +++ b/drivers/clk/qcom/gpucc-sm8350.c @@ -0,0 +1,637 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2022, Linaro Limited + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-alpha-pll.h" +#include "clk-branch.h" +#include "clk-pll.h" +#include "clk-rcg.h" +#include "clk-regmap.h" +#include "common.h" +#include "clk-regmap-mux.h" +#include "clk-regmap-divider.h" +#include "gdsc.h" +#include "reset.h" + +enum { + P_BI_TCXO, + P_GPLL0_OUT_MAIN, + P_GPLL0_OUT_MAIN_DIV, + P_GPU_CC_PLL0_OUT_MAIN, + P_GPU_CC_PLL1_OUT_MAIN, +}; + +static struct pll_vco lucid_5lpe_vco[] = { + { 249600000, 1750000000, 0 }, +}; + +static const struct alpha_pll_config gpu_cc_pll0_config = { + .l = 0x18, + .alpha = 0x6000, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002261, + .config_ctl_hi1_val = 0x2a9a699c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000000, + .test_ctl_hi1_val = 0x01800000, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x00000000, +}; + +static const struct clk_parent_data gpu_cc_parent = { + .fw_name = "bi_tcxo", +}; + +static struct clk_alpha_pll gpu_cc_pll0 = { + .offset = 0x0, + .vco_table = lucid_5lpe_vco, + .num_vco = ARRAY_SIZE(lucid_5lpe_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .hw.init = &(const struct clk_init_data){ + .name = "gpu_cc_pll0", + .parent_data = &gpu_cc_parent, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_5lpe_ops, + }, + }, +}; + +static const struct alpha_pll_config gpu_cc_pll1_config = { + .l = 0x1a, + .alpha = 0xaaa, + .config_ctl_val = 0x20485699, + .config_ctl_hi_val = 0x00002261, + .config_ctl_hi1_val = 0x2a9a699c, + .test_ctl_val = 0x00000000, + .test_ctl_hi_val = 0x00000000, + .test_ctl_hi1_val = 0x01800000, + .user_ctl_val = 0x00000000, + .user_ctl_hi_val = 0x00000805, + .user_ctl_hi1_val = 0x00000000, +}; + +static struct clk_alpha_pll gpu_cc_pll1 = { + .offset = 0x100, + .vco_table = lucid_5lpe_vco, + .num_vco = ARRAY_SIZE(lucid_5lpe_vco), + .regs = clk_alpha_pll_regs[CLK_ALPHA_PLL_TYPE_LUCID], + .clkr = { + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_pll1", + .parent_data = &gpu_cc_parent, + .num_parents = 1, + .ops = &clk_alpha_pll_lucid_5lpe_ops, + }, + }, +}; + +static const struct parent_map gpu_cc_parent_map_0[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL0_OUT_MAIN, 1 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_0[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &gpu_cc_pll0.clkr.hw }, + { .hw = &gpu_cc_pll1.clkr.hw }, + { .fw_name = "gcc_gpu_gpll0_clk_src" }, + { .fw_name = "gcc_gpu_gpll0_div_clk_src" }, +}; + +static const struct parent_map gpu_cc_parent_map_1[] = { + { P_BI_TCXO, 0 }, + { P_GPU_CC_PLL1_OUT_MAIN, 3 }, + { P_GPLL0_OUT_MAIN, 5 }, + { P_GPLL0_OUT_MAIN_DIV, 6 }, +}; + +static const struct clk_parent_data gpu_cc_parent_data_1[] = { + { .fw_name = "bi_tcxo" }, + { .hw = &gpu_cc_pll1.clkr.hw }, + { .fw_name = "gcc_gpu_gpll0_clk_src" }, + { .fw_name = "gcc_gpu_gpll0_div_clk_src" }, +}; + +static const struct freq_tbl ftbl_gpu_cc_gmu_clk_src[] = { + F(19200000, P_BI_TCXO, 1, 0, 0), + F(200000000, P_GPLL0_OUT_MAIN_DIV, 1.5, 0, 0), + F(500000000, P_GPU_CC_PLL1_OUT_MAIN, 1, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_gmu_clk_src = { + .cmd_rcgr = 0x1120, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_0, + .freq_tbl = ftbl_gpu_cc_gmu_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gmu_clk_src", + .parent_data = gpu_cc_parent_data_0, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_0), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static const struct freq_tbl ftbl_gpu_cc_hub_clk_src[] = { + F(150000000, P_GPLL0_OUT_MAIN_DIV, 2, 0, 0), + F(240000000, P_GPLL0_OUT_MAIN, 2.5, 0, 0), + F(300000000, P_GPLL0_OUT_MAIN, 2, 0, 0), + { } +}; + +static struct clk_rcg2 gpu_cc_hub_clk_src = { + .cmd_rcgr = 0x117c, + .mnd_width = 0, + .hid_width = 5, + .parent_map = gpu_cc_parent_map_1, + .freq_tbl = ftbl_gpu_cc_hub_clk_src, + .clkr.hw.init = &(struct clk_init_data){ + .name = "gpu_cc_hub_clk_src", + .parent_data = gpu_cc_parent_data_1, + .num_parents = ARRAY_SIZE(gpu_cc_parent_data_1), + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_rcg2_ops, + }, +}; + +static struct clk_regmap_div gpu_cc_hub_ahb_div_clk_src = { + .reg = 0x11c0, + .shift = 0, + .width = 4, + .clkr.hw.init = &(struct clk_init_data) { + .name = "gpu_cc_hub_ahb_div_clk_src", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_regmap_div gpu_cc_hub_cx_int_div_clk_src = { + .reg = 0x11bc, + .shift = 0, + .width = 4, + .clkr.hw.init = &(struct clk_init_data) { + .name = "gpu_cc_hub_cx_int_div_clk_src", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_regmap_div_ro_ops, + }, +}; + +static struct clk_branch gpu_cc_ahb_clk = { + .halt_reg = 0x1078, + .halt_check = BRANCH_HALT_DELAY, + .clkr = { + .enable_reg = 0x1078, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_ahb_clk", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_hub_ahb_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cb_clk = { + .halt_reg = 0x1170, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1170, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_crc_ahb_clk = { + .halt_reg = 0x107c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x107c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_crc_ahb_clk", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_hub_ahb_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_apb_clk = { + .halt_reg = 0x1088, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1088, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_apb_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_gmu_clk = { + .halt_reg = 0x1098, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1098, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_gmu_clk", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_qdss_at_clk = { + .halt_reg = 0x1080, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1080, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_qdss_at_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_qdss_trig_clk = { + .halt_reg = 0x1094, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1094, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_qdss_trig_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_qdss_tsctr_clk = { + .halt_reg = 0x1084, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1084, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_qdss_tsctr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cx_snoc_dvm_clk = { + .halt_reg = 0x108c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x108c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cx_snoc_dvm_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_aon_clk = { + .halt_reg = 0x1004, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1004, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_aon_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_cxo_clk = { + .halt_reg = 0x109c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x109c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_cxo_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_freq_measure_clk = { + .halt_reg = 0x120c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x120c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_freq_measure_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_gmu_clk = { + .halt_reg = 0x1064, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1064, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_gmu_clk", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_gmu_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_qdss_tsctr_clk = { + .halt_reg = 0x105c, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x105c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_qdss_tsctr_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_gx_vsense_clk = { + .halt_reg = 0x1058, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1058, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_gx_vsense_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hlos1_vote_gpu_smmu_clk = { + .halt_reg = 0x5000, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x5000, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_hlos1_vote_gpu_smmu_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hub_aon_clk = { + .halt_reg = 0x1178, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1178, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_hub_aon_clk", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_hub_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_hub_cx_int_clk = { + .halt_reg = 0x1204, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x1204, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_hub_cx_int_clk", + .parent_hws = (const struct clk_hw*[]){ + &gpu_cc_hub_cx_int_div_clk_src.clkr.hw, + }, + .num_parents = 1, + .flags = CLK_SET_RATE_PARENT, + .ops = &clk_branch2_aon_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_mnd1x_0_gfx3d_clk = { + .halt_reg = 0x802c, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x802c, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_mnd1x_0_gfx3d_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_mnd1x_1_gfx3d_clk = { + .halt_reg = 0x8030, + .halt_check = BRANCH_HALT, + .clkr = { + .enable_reg = 0x8030, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_mnd1x_1_gfx3d_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct clk_branch gpu_cc_sleep_clk = { + .halt_reg = 0x1090, + .halt_check = BRANCH_HALT_VOTED, + .clkr = { + .enable_reg = 0x1090, + .enable_mask = BIT(0), + .hw.init = &(struct clk_init_data){ + .name = "gpu_cc_sleep_clk", + .ops = &clk_branch2_ops, + }, + }, +}; + +static struct gdsc gpu_cx_gdsc = { + .gdscr = 0x106c, + .gds_hw_ctrl = 0x1540, + .pd = { + .name = "gpu_cx_gdsc", + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = VOTABLE, +}; + +static struct gdsc gpu_gx_gdsc = { + .gdscr = 0x100c, + .clamp_io_ctrl = 0x1508, + .pd = { + .name = "gpu_gx_gdsc", + .power_on = gdsc_gx_do_nothing_enable, + }, + .pwrsts = PWRSTS_OFF_ON, + .flags = CLAMP_IO | AON_RESET | POLL_CFG_GDSCR, +}; + +static struct clk_regmap *gpu_cc_sm8350_clocks[] = { + [GPU_CC_AHB_CLK] = &gpu_cc_ahb_clk.clkr, + [GPU_CC_CB_CLK] = &gpu_cc_cb_clk.clkr, + [GPU_CC_CRC_AHB_CLK] = &gpu_cc_crc_ahb_clk.clkr, + [GPU_CC_CX_APB_CLK] = &gpu_cc_cx_apb_clk.clkr, + [GPU_CC_CX_GMU_CLK] = &gpu_cc_cx_gmu_clk.clkr, + [GPU_CC_CX_QDSS_AT_CLK] = &gpu_cc_cx_qdss_at_clk.clkr, + [GPU_CC_CX_QDSS_TRIG_CLK] = &gpu_cc_cx_qdss_trig_clk.clkr, + [GPU_CC_CX_QDSS_TSCTR_CLK] = &gpu_cc_cx_qdss_tsctr_clk.clkr, + [GPU_CC_CX_SNOC_DVM_CLK] = &gpu_cc_cx_snoc_dvm_clk.clkr, + [GPU_CC_CXO_AON_CLK] = &gpu_cc_cxo_aon_clk.clkr, + [GPU_CC_CXO_CLK] = &gpu_cc_cxo_clk.clkr, + [GPU_CC_FREQ_MEASURE_CLK] = &gpu_cc_freq_measure_clk.clkr, + [GPU_CC_GMU_CLK_SRC] = &gpu_cc_gmu_clk_src.clkr, + [GPU_CC_GX_GMU_CLK] = &gpu_cc_gx_gmu_clk.clkr, + [GPU_CC_GX_QDSS_TSCTR_CLK] = &gpu_cc_gx_qdss_tsctr_clk.clkr, + [GPU_CC_GX_VSENSE_CLK] = &gpu_cc_gx_vsense_clk.clkr, + [GPU_CC_HLOS1_VOTE_GPU_SMMU_CLK] = &gpu_cc_hlos1_vote_gpu_smmu_clk.clkr, + [GPU_CC_HUB_AHB_DIV_CLK_SRC] = &gpu_cc_hub_ahb_div_clk_src.clkr, + [GPU_CC_HUB_AON_CLK] = &gpu_cc_hub_aon_clk.clkr, + [GPU_CC_HUB_CLK_SRC] = &gpu_cc_hub_clk_src.clkr, + [GPU_CC_HUB_CX_INT_CLK] = &gpu_cc_hub_cx_int_clk.clkr, + [GPU_CC_HUB_CX_INT_DIV_CLK_SRC] = &gpu_cc_hub_cx_int_div_clk_src.clkr, + [GPU_CC_MND1X_0_GFX3D_CLK] = &gpu_cc_mnd1x_0_gfx3d_clk.clkr, + [GPU_CC_MND1X_1_GFX3D_CLK] = &gpu_cc_mnd1x_1_gfx3d_clk.clkr, + [GPU_CC_PLL0] = &gpu_cc_pll0.clkr, + [GPU_CC_PLL1] = &gpu_cc_pll1.clkr, + [GPU_CC_SLEEP_CLK] = &gpu_cc_sleep_clk.clkr, +}; + +static const struct qcom_reset_map gpu_cc_sm8350_resets[] = { + [GPUCC_GPU_CC_ACD_BCR] = { 0x1160 }, + [GPUCC_GPU_CC_CB_BCR] = { 0x116c }, + [GPUCC_GPU_CC_CX_BCR] = { 0x1068 }, + [GPUCC_GPU_CC_FAST_HUB_BCR] = { 0x1174 }, + [GPUCC_GPU_CC_GFX3D_AON_BCR] = { 0x10a0 }, + [GPUCC_GPU_CC_GMU_BCR] = { 0x111c }, + [GPUCC_GPU_CC_GX_BCR] = { 0x1008 }, + [GPUCC_GPU_CC_XO_BCR] = { 0x1000 }, +}; + +static struct gdsc *gpu_cc_sm8350_gdscs[] = { + [GPU_CX_GDSC] = &gpu_cx_gdsc, + [GPU_GX_GDSC] = &gpu_gx_gdsc, +}; + +static const struct regmap_config gpu_cc_sm8350_regmap_config = { + .reg_bits = 32, + .reg_stride = 4, + .val_bits = 32, + .max_register = 0x8030, + .fast_io = true, +}; + +static const struct qcom_cc_desc gpu_cc_sm8350_desc = { + .config = &gpu_cc_sm8350_regmap_config, + .clks = gpu_cc_sm8350_clocks, + .num_clks = ARRAY_SIZE(gpu_cc_sm8350_clocks), + .resets = gpu_cc_sm8350_resets, + .num_resets = ARRAY_SIZE(gpu_cc_sm8350_resets), + .gdscs = gpu_cc_sm8350_gdscs, + .num_gdscs = ARRAY_SIZE(gpu_cc_sm8350_gdscs), +}; + +static int gpu_cc_sm8350_probe(struct platform_device *pdev) +{ + struct regmap *regmap; + + regmap = qcom_cc_map(pdev, &gpu_cc_sm8350_desc); + if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "Failed to map gpu cc registers\n"); + return PTR_ERR(regmap); + } + + clk_lucid_pll_configure(&gpu_cc_pll0, regmap, &gpu_cc_pll0_config); + clk_lucid_pll_configure(&gpu_cc_pll1, regmap, &gpu_cc_pll1_config); + + return qcom_cc_really_probe(pdev, &gpu_cc_sm8350_desc, regmap); +} + +static const struct of_device_id gpu_cc_sm8350_match_table[] = { + { .compatible = "qcom,sm8350-gpucc" }, + { } +}; +MODULE_DEVICE_TABLE(of, gpu_cc_sm8350_match_table); + +static struct platform_driver gpu_cc_sm8350_driver = { + .probe = gpu_cc_sm8350_probe, + .driver = { + .name = "sm8350-gpucc", + .of_match_table = gpu_cc_sm8350_match_table, + }, +}; + +static int __init gpu_cc_sm8350_init(void) +{ + return platform_driver_register(&gpu_cc_sm8350_driver); +} +subsys_initcall(gpu_cc_sm8350_init); + +static void __exit gpu_cc_sm8350_exit(void) +{ + platform_driver_unregister(&gpu_cc_sm8350_driver); +} +module_exit(gpu_cc_sm8350_exit); + +MODULE_DESCRIPTION("QTI GPU_CC SM8350 Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/clk/qcom/krait-cc.c b/drivers/clk/qcom/krait-cc.c index 4d4b657d33c3..cfd961d5cc45 100644 --- a/drivers/clk/qcom/krait-cc.c +++ b/drivers/clk/qcom/krait-cc.c @@ -139,6 +139,14 @@ krait_add_sec_mux(struct device *dev, int id, const char *s, mux->hw.init = &init; mux->safe_sel = 0; + /* Checking for qcom,krait-cc-v1 or qcom,krait-cc-v2 is not + * enough to limit this to apq/ipq8064. Directly check machine + * compatible to correctly handle this errata. + */ + if (of_machine_is_compatible("qcom,ipq8064") || + of_machine_is_compatible("qcom,apq8064")) + mux->disable_sec_src_gating = true; + init.name = kasprintf(GFP_KERNEL, "krait%s_sec_mux", s); if (!init.name) return -ENOMEM; diff --git a/drivers/clk/qcom/mmcc-msm8996.c b/drivers/clk/qcom/mmcc-msm8996.c index 24843e4f2599..80330dab4d81 100644 --- a/drivers/clk/qcom/mmcc-msm8996.c +++ b/drivers/clk/qcom/mmcc-msm8996.c @@ -45,194 +45,14 @@ enum { P_MMPLL4, }; -static const struct parent_map mmss_xo_hdmi_map[] = { - { P_XO, 0 }, - { P_HDMIPLL, 1 } -}; - -static const char * const mmss_xo_hdmi[] = { - "xo", - "hdmipll" -}; - -static const struct parent_map mmss_xo_dsi0pll_dsi1pll_map[] = { - { P_XO, 0 }, - { P_DSI0PLL, 1 }, - { P_DSI1PLL, 2 } -}; - -static const char * const mmss_xo_dsi0pll_dsi1pll[] = { - "xo", - "dsi0pll", - "dsi1pll" -}; - -static const struct parent_map mmss_xo_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_gpll0_gpll0_div[] = { - "xo", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_dsibyte_map[] = { - { P_XO, 0 }, - { P_DSI0PLL_BYTE, 1 }, - { P_DSI1PLL_BYTE, 2 } -}; - -static const char * const mmss_xo_dsibyte[] = { - "xo", - "dsi0pllbyte", - "dsi1pllbyte" -}; - -static const struct parent_map mmss_xo_mmpll0_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL1, 2 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "mmpll1", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL3, 3 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "mmpll3", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL5, 2 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "mmpll5", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL4, 3 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "mmpll4", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL9, 2 }, - { P_MMPLL2, 3 }, - { P_MMPLL8, 4 }, - { P_GPLL0, 5 } -}; - -static const char * const mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0[] = { - "xo", - "mmpll0", - "mmpll9", - "mmpll2", - "mmpll8", - "gpll0" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL9, 2 }, - { P_MMPLL2, 3 }, - { P_MMPLL8, 4 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "mmpll9", - "mmpll2", - "mmpll8", - "gpll0", - "gpll0_div" -}; - -static const struct parent_map mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div_map[] = { - { P_XO, 0 }, - { P_MMPLL0, 1 }, - { P_MMPLL1, 2 }, - { P_MMPLL4, 3 }, - { P_MMPLL3, 4 }, - { P_GPLL0, 5 }, - { P_GPLL0_DIV, 6 } -}; - -static const char * const mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div[] = { - "xo", - "mmpll0", - "mmpll1", - "mmpll4", - "mmpll3", - "gpll0", - "gpll0_div" -}; - static struct clk_fixed_factor gpll0_div = { .mult = 1, .div = 2, .hw.init = &(struct clk_init_data){ .name = "gpll0_div", - .parent_names = (const char *[]){ "gpll0" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "gpll0", .name = "gpll0" }, + }, .num_parents = 1, .ops = &clk_fixed_factor_ops, }, @@ -265,7 +85,9 @@ static struct clk_alpha_pll mmpll0_early = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmpll0_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -278,7 +100,9 @@ static struct clk_alpha_pll_postdiv mmpll0 = { .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll0", - .parent_names = (const char *[]){ "mmpll0_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll0_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -295,7 +119,9 @@ static struct clk_alpha_pll mmpll1_early = { .enable_mask = BIT(1), .hw.init = &(struct clk_init_data){ .name = "mmpll1_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, } @@ -308,7 +134,9 @@ static struct clk_alpha_pll_postdiv mmpll1 = { .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll1", - .parent_names = (const char *[]){ "mmpll1_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll1_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -322,7 +150,9 @@ static struct clk_alpha_pll mmpll2_early = { .num_vco = ARRAY_SIZE(mmpll_gfx_vco), .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll2_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -334,7 +164,9 @@ static struct clk_alpha_pll_postdiv mmpll2 = { .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll2", - .parent_names = (const char *[]){ "mmpll2_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll2_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -348,7 +180,9 @@ static struct clk_alpha_pll mmpll3_early = { .num_vco = ARRAY_SIZE(mmpll_p_vco), .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll3_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -360,7 +194,9 @@ static struct clk_alpha_pll_postdiv mmpll3 = { .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll3", - .parent_names = (const char *[]){ "mmpll3_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll3_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -374,7 +210,9 @@ static struct clk_alpha_pll mmpll4_early = { .num_vco = ARRAY_SIZE(mmpll_t_vco), .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll4_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -386,7 +224,9 @@ static struct clk_alpha_pll_postdiv mmpll4 = { .width = 2, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll4", - .parent_names = (const char *[]){ "mmpll4_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll4_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -400,7 +240,9 @@ static struct clk_alpha_pll mmpll5_early = { .num_vco = ARRAY_SIZE(mmpll_p_vco), .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll5_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -412,7 +254,9 @@ static struct clk_alpha_pll_postdiv mmpll5 = { .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll5", - .parent_names = (const char *[]){ "mmpll5_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll5_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -426,7 +270,9 @@ static struct clk_alpha_pll mmpll8_early = { .num_vco = ARRAY_SIZE(mmpll_gfx_vco), .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll8_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -438,7 +284,9 @@ static struct clk_alpha_pll_postdiv mmpll8 = { .width = 4, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll8", - .parent_names = (const char *[]){ "mmpll8_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll8_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, @@ -452,7 +300,9 @@ static struct clk_alpha_pll mmpll9_early = { .num_vco = ARRAY_SIZE(mmpll_t_vco), .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll9_early", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_alpha_pll_ops, }, @@ -464,13 +314,197 @@ static struct clk_alpha_pll_postdiv mmpll9 = { .width = 2, .clkr.hw.init = &(struct clk_init_data){ .name = "mmpll9", - .parent_names = (const char *[]){ "mmpll9_early" }, + .parent_hws = (const struct clk_hw*[]){ + &mmpll9_early.clkr.hw + }, .num_parents = 1, .ops = &clk_alpha_pll_postdiv_ops, .flags = CLK_SET_RATE_PARENT, }, }; +static const struct parent_map mmss_xo_hdmi_map[] = { + { P_XO, 0 }, + { P_HDMIPLL, 1 } +}; + +static const struct clk_parent_data mmss_xo_hdmi[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .fw_name = "hdmipll", .name = "hdmipll" } +}; + +static const struct parent_map mmss_xo_dsi0pll_dsi1pll_map[] = { + { P_XO, 0 }, + { P_DSI0PLL, 1 }, + { P_DSI1PLL, 2 } +}; + +static const struct clk_parent_data mmss_xo_dsi0pll_dsi1pll[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .fw_name = "dsi0pll", .name = "dsi0pll" }, + { .fw_name = "dsi1pll", .name = "dsi1pll" } +}; + +static const struct parent_map mmss_xo_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_dsibyte_map[] = { + { P_XO, 0 }, + { P_DSI0PLL_BYTE, 1 }, + { P_DSI1PLL_BYTE, 2 } +}; + +static const struct clk_parent_data mmss_xo_dsibyte[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .fw_name = "dsi0pllbyte", .name = "dsi0pllbyte" }, + { .fw_name = "dsi1pllbyte", .name = "dsi1pllbyte" } +}; + +static const struct parent_map mmss_xo_mmpll0_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL1, 2 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll1.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL3, 3 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll3.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL5, 2 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll5.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL4, 3 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll4.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL9, 2 }, + { P_MMPLL2, 3 }, + { P_MMPLL8, 4 }, + { P_GPLL0, 5 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll9.clkr.hw }, + { .hw = &mmpll2.clkr.hw }, + { .hw = &mmpll8.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL9, 2 }, + { P_MMPLL2, 3 }, + { P_MMPLL8, 4 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll9.clkr.hw }, + { .hw = &mmpll2.clkr.hw }, + { .hw = &mmpll8.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + +static const struct parent_map mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div_map[] = { + { P_XO, 0 }, + { P_MMPLL0, 1 }, + { P_MMPLL1, 2 }, + { P_MMPLL4, 3 }, + { P_MMPLL3, 4 }, + { P_GPLL0, 5 }, + { P_GPLL0_DIV, 6 } +}; + +static const struct clk_parent_data mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div[] = { + { .fw_name = "xo", .name = "xo_board" }, + { .hw = &mmpll0.clkr.hw }, + { .hw = &mmpll1.clkr.hw }, + { .hw = &mmpll4.clkr.hw }, + { .hw = &mmpll3.clkr.hw }, + { .fw_name = "gpll0", .name = "gpll0" }, + { .hw = &gpll0_div.hw } +}; + static const struct freq_tbl ftbl_ahb_clk_src[] = { F(19200000, P_XO, 1, 0, 0), F(40000000, P_GPLL0_DIV, 7.5, 0, 0), @@ -485,8 +519,8 @@ static struct clk_rcg2 ahb_clk_src = { .freq_tbl = ftbl_ahb_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "ahb_clk_src", - .parent_names = mmss_xo_mmpll0_gpll0_gpll0_div, - .num_parents = 4, + .parent_data = mmss_xo_mmpll0_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -509,8 +543,8 @@ static struct clk_rcg2 axi_clk_src = { .freq_tbl = ftbl_axi_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "axi_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -522,8 +556,8 @@ static struct clk_rcg2 maxi_clk_src = { .freq_tbl = ftbl_axi_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "maxi_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -535,8 +569,8 @@ static struct clk_rcg2_gfx3d gfx3d_clk_src = { .parent_map = mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_map, .clkr.hw.init = &(struct clk_init_data){ .name = "gfx3d_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0, - .num_parents = 6, + .parent_data = mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0), .ops = &clk_gfx3d_ops, .flags = CLK_SET_RATE_PARENT, }, @@ -560,8 +594,8 @@ static struct clk_rcg2 rbbmtimer_clk_src = { .freq_tbl = ftbl_rbbmtimer_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "rbbmtimer_clk_src", - .parent_names = mmss_xo_mmpll0_gpll0_gpll0_div, - .num_parents = 4, + .parent_data = mmss_xo_mmpll0_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -572,8 +606,8 @@ static struct clk_rcg2 isense_clk_src = { .parent_map = mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div_map, .clkr.hw.init = &(struct clk_init_data){ .name = "isense_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll9_mmpll2_mmpll8_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -591,8 +625,8 @@ static struct clk_rcg2 rbcpr_clk_src = { .freq_tbl = ftbl_rbcpr_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "rbcpr_clk_src", - .parent_names = mmss_xo_mmpll0_gpll0_gpll0_div, - .num_parents = 4, + .parent_data = mmss_xo_mmpll0_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -613,8 +647,8 @@ static struct clk_rcg2 video_core_clk_src = { .freq_tbl = ftbl_video_core_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "video_core_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -627,8 +661,8 @@ static struct clk_rcg2 video_subcore0_clk_src = { .freq_tbl = ftbl_video_core_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "video_subcore0_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -641,8 +675,8 @@ static struct clk_rcg2 video_subcore1_clk_src = { .freq_tbl = ftbl_video_core_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "video_subcore1_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -654,8 +688,8 @@ static struct clk_rcg2 pclk0_clk_src = { .parent_map = mmss_xo_dsi0pll_dsi1pll_map, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk0_clk_src", - .parent_names = mmss_xo_dsi0pll_dsi1pll, - .num_parents = 3, + .parent_data = mmss_xo_dsi0pll_dsi1pll, + .num_parents = ARRAY_SIZE(mmss_xo_dsi0pll_dsi1pll), .ops = &clk_pixel_ops, .flags = CLK_SET_RATE_PARENT, }, @@ -668,8 +702,8 @@ static struct clk_rcg2 pclk1_clk_src = { .parent_map = mmss_xo_dsi0pll_dsi1pll_map, .clkr.hw.init = &(struct clk_init_data){ .name = "pclk1_clk_src", - .parent_names = mmss_xo_dsi0pll_dsi1pll, - .num_parents = 3, + .parent_data = mmss_xo_dsi0pll_dsi1pll, + .num_parents = ARRAY_SIZE(mmss_xo_dsi0pll_dsi1pll), .ops = &clk_pixel_ops, .flags = CLK_SET_RATE_PARENT, }, @@ -695,8 +729,8 @@ static struct clk_rcg2 mdp_clk_src = { .freq_tbl = ftbl_mdp_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "mdp_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll5_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -713,8 +747,8 @@ static struct clk_rcg2 extpclk_clk_src = { .freq_tbl = extpclk_freq_tbl, .clkr.hw.init = &(struct clk_init_data){ .name = "extpclk_clk_src", - .parent_names = mmss_xo_hdmi, - .num_parents = 2, + .parent_data = mmss_xo_hdmi, + .num_parents = ARRAY_SIZE(mmss_xo_hdmi), .ops = &clk_byte_ops, .flags = CLK_SET_RATE_PARENT, }, @@ -732,8 +766,8 @@ static struct clk_rcg2 vsync_clk_src = { .freq_tbl = ftbl_mdss_vsync_clk, .clkr.hw.init = &(struct clk_init_data){ .name = "vsync_clk_src", - .parent_names = mmss_xo_gpll0_gpll0_div, - .num_parents = 3, + .parent_data = mmss_xo_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -750,8 +784,8 @@ static struct clk_rcg2 hdmi_clk_src = { .freq_tbl = ftbl_mdss_hdmi_clk, .clkr.hw.init = &(struct clk_init_data){ .name = "hdmi_clk_src", - .parent_names = mmss_xo_gpll0_gpll0_div, - .num_parents = 3, + .parent_data = mmss_xo_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -762,8 +796,8 @@ static struct clk_rcg2 byte0_clk_src = { .parent_map = mmss_xo_dsibyte_map, .clkr.hw.init = &(struct clk_init_data){ .name = "byte0_clk_src", - .parent_names = mmss_xo_dsibyte, - .num_parents = 3, + .parent_data = mmss_xo_dsibyte, + .num_parents = ARRAY_SIZE(mmss_xo_dsibyte), .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, @@ -775,8 +809,8 @@ static struct clk_rcg2 byte1_clk_src = { .parent_map = mmss_xo_dsibyte_map, .clkr.hw.init = &(struct clk_init_data){ .name = "byte1_clk_src", - .parent_names = mmss_xo_dsibyte, - .num_parents = 3, + .parent_data = mmss_xo_dsibyte, + .num_parents = ARRAY_SIZE(mmss_xo_dsibyte), .ops = &clk_byte2_ops, .flags = CLK_SET_RATE_PARENT, }, @@ -794,8 +828,8 @@ static struct clk_rcg2 esc0_clk_src = { .freq_tbl = ftbl_mdss_esc0_1_clk, .clkr.hw.init = &(struct clk_init_data){ .name = "esc0_clk_src", - .parent_names = mmss_xo_dsibyte, - .num_parents = 3, + .parent_data = mmss_xo_dsibyte, + .num_parents = ARRAY_SIZE(mmss_xo_dsibyte), .ops = &clk_rcg2_ops, }, }; @@ -807,8 +841,8 @@ static struct clk_rcg2 esc1_clk_src = { .freq_tbl = ftbl_mdss_esc0_1_clk, .clkr.hw.init = &(struct clk_init_data){ .name = "esc1_clk_src", - .parent_names = mmss_xo_dsibyte, - .num_parents = 3, + .parent_data = mmss_xo_dsibyte, + .num_parents = ARRAY_SIZE(mmss_xo_dsibyte), .ops = &clk_rcg2_ops, }, }; @@ -831,8 +865,8 @@ static struct clk_rcg2 camss_gp0_clk_src = { .freq_tbl = ftbl_camss_gp0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "camss_gp0_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -845,8 +879,8 @@ static struct clk_rcg2 camss_gp1_clk_src = { .freq_tbl = ftbl_camss_gp0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "camss_gp1_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -873,8 +907,8 @@ static struct clk_rcg2 mclk0_clk_src = { .freq_tbl = ftbl_mclk0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "mclk0_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -887,8 +921,8 @@ static struct clk_rcg2 mclk1_clk_src = { .freq_tbl = ftbl_mclk0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "mclk1_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -901,8 +935,8 @@ static struct clk_rcg2 mclk2_clk_src = { .freq_tbl = ftbl_mclk0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "mclk2_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -915,8 +949,8 @@ static struct clk_rcg2 mclk3_clk_src = { .freq_tbl = ftbl_mclk0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "mclk3_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -937,8 +971,8 @@ static struct clk_rcg2 cci_clk_src = { .freq_tbl = ftbl_cci_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "cci_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -957,8 +991,8 @@ static struct clk_rcg2 csi0phytimer_clk_src = { .freq_tbl = ftbl_csi0phytimer_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi0phytimer_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -970,8 +1004,8 @@ static struct clk_rcg2 csi1phytimer_clk_src = { .freq_tbl = ftbl_csi0phytimer_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi1phytimer_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -983,8 +1017,8 @@ static struct clk_rcg2 csi2phytimer_clk_src = { .freq_tbl = ftbl_csi0phytimer_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi2phytimer_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1004,8 +1038,8 @@ static struct clk_rcg2 csiphy0_3p_clk_src = { .freq_tbl = ftbl_csiphy0_3p_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csiphy0_3p_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1017,8 +1051,8 @@ static struct clk_rcg2 csiphy1_3p_clk_src = { .freq_tbl = ftbl_csiphy0_3p_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csiphy1_3p_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1030,8 +1064,8 @@ static struct clk_rcg2 csiphy2_3p_clk_src = { .freq_tbl = ftbl_csiphy0_3p_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csiphy2_3p_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1053,8 +1087,8 @@ static struct clk_rcg2 jpeg0_clk_src = { .freq_tbl = ftbl_jpeg0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "jpeg0_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1075,8 +1109,8 @@ static struct clk_rcg2 jpeg2_clk_src = { .freq_tbl = ftbl_jpeg2_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "jpeg2_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1088,8 +1122,8 @@ static struct clk_rcg2 jpeg_dma_clk_src = { .freq_tbl = ftbl_jpeg0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "jpeg_dma_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1111,8 +1145,8 @@ static struct clk_rcg2 vfe0_clk_src = { .freq_tbl = ftbl_vfe0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "vfe0_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1124,8 +1158,8 @@ static struct clk_rcg2 vfe1_clk_src = { .freq_tbl = ftbl_vfe0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "vfe1_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1146,8 +1180,8 @@ static struct clk_rcg2 cpp_clk_src = { .freq_tbl = ftbl_cpp_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "cpp_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1168,8 +1202,8 @@ static struct clk_rcg2 csi0_clk_src = { .freq_tbl = ftbl_csi0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi0_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1181,8 +1215,8 @@ static struct clk_rcg2 csi1_clk_src = { .freq_tbl = ftbl_csi0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi1_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1194,8 +1228,8 @@ static struct clk_rcg2 csi2_clk_src = { .freq_tbl = ftbl_csi0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi2_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1207,8 +1241,8 @@ static struct clk_rcg2 csi3_clk_src = { .freq_tbl = ftbl_csi0_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "csi3_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, - .num_parents = 7, + .parent_data = mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll1_mmpll4_mmpll3_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1227,8 +1261,8 @@ static struct clk_rcg2 fd_core_clk_src = { .freq_tbl = ftbl_fd_core_clk_src, .clkr.hw.init = &(struct clk_init_data){ .name = "fd_core_clk_src", - .parent_names = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, - .num_parents = 5, + .parent_data = mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div, + .num_parents = ARRAY_SIZE(mmss_xo_mmpll0_mmpll4_gpll0_gpll0_div), .ops = &clk_rcg2_ops, }, }; @@ -1240,7 +1274,9 @@ static struct clk_branch mmss_mmagic_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_mmagic_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1255,7 +1291,9 @@ static struct clk_branch mmss_mmagic_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_mmagic_cfg_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1270,7 +1308,9 @@ static struct clk_branch mmss_misc_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_misc_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1285,7 +1325,9 @@ static struct clk_branch mmss_misc_cxo_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_misc_cxo_clk", - .parent_names = (const char *[]){ "xo" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "xo", .name = "xo_board" }, + }, .num_parents = 1, .ops = &clk_branch2_ops, }, @@ -1299,7 +1341,9 @@ static struct clk_branch mmss_mmagic_maxi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_mmagic_maxi_clk", - .parent_names = (const char *[]){ "maxi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &maxi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1314,7 +1358,9 @@ static struct clk_branch mmagic_camss_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_camss_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1329,7 +1375,9 @@ static struct clk_branch mmagic_camss_noc_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_camss_noc_cfg_ahb_clk", - .parent_names = (const char *[]){ "gcc_mmss_noc_cfg_ahb_clk" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "gcc_mmss_noc_cfg_ahb_clk", .name = "gcc_mmss_noc_cfg_ahb_clk" }, + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1344,7 +1392,9 @@ static struct clk_branch smmu_vfe_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_vfe_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1359,7 +1409,9 @@ static struct clk_branch smmu_vfe_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_vfe_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1374,7 +1426,9 @@ static struct clk_branch smmu_cpp_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_cpp_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1389,7 +1443,9 @@ static struct clk_branch smmu_cpp_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_cpp_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1404,7 +1460,9 @@ static struct clk_branch smmu_jpeg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_jpeg_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1419,7 +1477,9 @@ static struct clk_branch smmu_jpeg_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_jpeg_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1434,7 +1494,9 @@ static struct clk_branch mmagic_mdss_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_mdss_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1449,7 +1511,9 @@ static struct clk_branch mmagic_mdss_noc_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_mdss_noc_cfg_ahb_clk", - .parent_names = (const char *[]){ "gcc_mmss_noc_cfg_ahb_clk" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "gcc_mmss_noc_cfg_ahb_clk", .name = "gcc_mmss_noc_cfg_ahb_clk" }, + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1464,7 +1528,9 @@ static struct clk_branch smmu_rot_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_rot_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1479,7 +1545,9 @@ static struct clk_branch smmu_rot_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_rot_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1494,7 +1562,9 @@ static struct clk_branch smmu_mdp_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_mdp_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1509,7 +1579,9 @@ static struct clk_branch smmu_mdp_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_mdp_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1524,7 +1596,9 @@ static struct clk_branch mmagic_video_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_video_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1539,7 +1613,9 @@ static struct clk_branch mmagic_video_noc_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_video_noc_cfg_ahb_clk", - .parent_names = (const char *[]){ "gcc_mmss_noc_cfg_ahb_clk" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "gcc_mmss_noc_cfg_ahb_clk", .name = "gcc_mmss_noc_cfg_ahb_clk" }, + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT | CLK_IS_CRITICAL, .ops = &clk_branch2_ops, @@ -1554,7 +1630,9 @@ static struct clk_branch smmu_video_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_video_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1569,7 +1647,9 @@ static struct clk_branch smmu_video_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "smmu_video_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1584,7 +1664,9 @@ static struct clk_branch mmagic_bimc_noc_cfg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmagic_bimc_noc_cfg_ahb_clk", - .parent_names = (const char *[]){ "gcc_mmss_noc_cfg_ahb_clk" }, + .parent_data = (const struct clk_parent_data[]){ + { .fw_name = "gcc_mmss_noc_cfg_ahb_clk", .name = "gcc_mmss_noc_cfg_ahb_clk" }, + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1599,7 +1681,9 @@ static struct clk_branch gpu_gx_gfx3d_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gpu_gx_gfx3d_clk", - .parent_names = (const char *[]){ "gfx3d_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &gfx3d_clk_src.rcg.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1614,7 +1698,9 @@ static struct clk_branch gpu_gx_rbbmtimer_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gpu_gx_rbbmtimer_clk", - .parent_names = (const char *[]){ "rbbmtimer_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &rbbmtimer_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1629,7 +1715,9 @@ static struct clk_branch gpu_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gpu_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1644,7 +1732,9 @@ static struct clk_branch gpu_aon_isense_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "gpu_aon_isense_clk", - .parent_names = (const char *[]){ "isense_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &isense_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1659,7 +1749,9 @@ static struct clk_branch vmem_maxi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "vmem_maxi_clk", - .parent_names = (const char *[]){ "maxi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &maxi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1674,7 +1766,9 @@ static struct clk_branch vmem_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "vmem_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1689,7 +1783,9 @@ static struct clk_branch mmss_rbcpr_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_rbcpr_clk", - .parent_names = (const char *[]){ "rbcpr_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &rbcpr_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1704,7 +1800,9 @@ static struct clk_branch mmss_rbcpr_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mmss_rbcpr_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1719,7 +1817,9 @@ static struct clk_branch video_core_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "video_core_clk", - .parent_names = (const char *[]){ "video_core_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &video_core_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1734,7 +1834,9 @@ static struct clk_branch video_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "video_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1749,7 +1851,9 @@ static struct clk_branch video_maxi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "video_maxi_clk", - .parent_names = (const char *[]){ "maxi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &maxi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1764,7 +1868,9 @@ static struct clk_branch video_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "video_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1779,7 +1885,9 @@ static struct clk_branch video_subcore0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "video_subcore0_clk", - .parent_names = (const char *[]){ "video_subcore0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &video_subcore0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1794,7 +1902,9 @@ static struct clk_branch video_subcore1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "video_subcore1_clk", - .parent_names = (const char *[]){ "video_subcore1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &video_subcore1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1809,7 +1919,9 @@ static struct clk_branch mdss_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1824,7 +1936,9 @@ static struct clk_branch mdss_hdmi_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_hdmi_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1839,7 +1953,9 @@ static struct clk_branch mdss_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1854,7 +1970,9 @@ static struct clk_branch mdss_pclk0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_pclk0_clk", - .parent_names = (const char *[]){ "pclk0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &pclk0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1869,7 +1987,9 @@ static struct clk_branch mdss_pclk1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_pclk1_clk", - .parent_names = (const char *[]){ "pclk1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &pclk1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1884,7 +2004,9 @@ static struct clk_branch mdss_mdp_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_mdp_clk", - .parent_names = (const char *[]){ "mdp_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &mdp_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1899,7 +2021,9 @@ static struct clk_branch mdss_extpclk_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_extpclk_clk", - .parent_names = (const char *[]){ "extpclk_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &extpclk_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1914,7 +2038,9 @@ static struct clk_branch mdss_vsync_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_vsync_clk", - .parent_names = (const char *[]){ "vsync_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vsync_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1929,7 +2055,9 @@ static struct clk_branch mdss_hdmi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_hdmi_clk", - .parent_names = (const char *[]){ "hdmi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &hdmi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1944,7 +2072,9 @@ static struct clk_branch mdss_byte0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_byte0_clk", - .parent_names = (const char *[]){ "byte0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &byte0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1959,7 +2089,9 @@ static struct clk_branch mdss_byte1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_byte1_clk", - .parent_names = (const char *[]){ "byte1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &byte1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1974,7 +2106,9 @@ static struct clk_branch mdss_esc0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_esc0_clk", - .parent_names = (const char *[]){ "esc0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &esc0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -1989,7 +2123,9 @@ static struct clk_branch mdss_esc1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "mdss_esc1_clk", - .parent_names = (const char *[]){ "esc1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &esc1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2004,7 +2140,9 @@ static struct clk_branch camss_top_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_top_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2019,7 +2157,9 @@ static struct clk_branch camss_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2034,7 +2174,9 @@ static struct clk_branch camss_micro_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_micro_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2049,7 +2191,9 @@ static struct clk_branch camss_gp0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_gp0_clk", - .parent_names = (const char *[]){ "camss_gp0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &camss_gp0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2064,7 +2208,9 @@ static struct clk_branch camss_gp1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_gp1_clk", - .parent_names = (const char *[]){ "camss_gp1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &camss_gp1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2079,7 +2225,9 @@ static struct clk_branch camss_mclk0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_mclk0_clk", - .parent_names = (const char *[]){ "mclk0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &mclk0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2094,7 +2242,9 @@ static struct clk_branch camss_mclk1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_mclk1_clk", - .parent_names = (const char *[]){ "mclk1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &mclk1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2109,7 +2259,9 @@ static struct clk_branch camss_mclk2_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_mclk2_clk", - .parent_names = (const char *[]){ "mclk2_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &mclk2_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2124,7 +2276,9 @@ static struct clk_branch camss_mclk3_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_mclk3_clk", - .parent_names = (const char *[]){ "mclk3_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &mclk3_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2139,7 +2293,9 @@ static struct clk_branch camss_cci_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_cci_clk", - .parent_names = (const char *[]){ "cci_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &cci_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2154,7 +2310,9 @@ static struct clk_branch camss_cci_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_cci_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2169,7 +2327,9 @@ static struct clk_branch camss_csi0phytimer_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi0phytimer_clk", - .parent_names = (const char *[]){ "csi0phytimer_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi0phytimer_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2184,7 +2344,9 @@ static struct clk_branch camss_csi1phytimer_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi1phytimer_clk", - .parent_names = (const char *[]){ "csi1phytimer_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi1phytimer_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2199,7 +2361,9 @@ static struct clk_branch camss_csi2phytimer_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi2phytimer_clk", - .parent_names = (const char *[]){ "csi2phytimer_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi2phytimer_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2214,7 +2378,9 @@ static struct clk_branch camss_csiphy0_3p_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csiphy0_3p_clk", - .parent_names = (const char *[]){ "csiphy0_3p_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csiphy0_3p_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2229,7 +2395,9 @@ static struct clk_branch camss_csiphy1_3p_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csiphy1_3p_clk", - .parent_names = (const char *[]){ "csiphy1_3p_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csiphy1_3p_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2244,7 +2412,9 @@ static struct clk_branch camss_csiphy2_3p_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csiphy2_3p_clk", - .parent_names = (const char *[]){ "csiphy2_3p_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csiphy2_3p_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2259,7 +2429,9 @@ static struct clk_branch camss_jpeg0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_jpeg0_clk", - .parent_names = (const char *[]){ "jpeg0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &jpeg0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2274,7 +2446,9 @@ static struct clk_branch camss_jpeg2_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_jpeg2_clk", - .parent_names = (const char *[]){ "jpeg2_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &jpeg2_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2289,7 +2463,9 @@ static struct clk_branch camss_jpeg_dma_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_jpeg_dma_clk", - .parent_names = (const char *[]){ "jpeg_dma_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &jpeg_dma_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2304,7 +2480,9 @@ static struct clk_branch camss_jpeg_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_jpeg_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2319,7 +2497,9 @@ static struct clk_branch camss_jpeg_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_jpeg_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2334,7 +2514,9 @@ static struct clk_branch camss_vfe_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2349,7 +2531,9 @@ static struct clk_branch camss_vfe_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2364,7 +2548,9 @@ static struct clk_branch camss_vfe0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe0_clk", - .parent_names = (const char *[]){ "vfe0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vfe0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2379,7 +2565,9 @@ static struct clk_branch camss_vfe0_stream_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe0_stream_clk", - .parent_names = (const char *[]){ "vfe0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vfe0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2394,7 +2582,9 @@ static struct clk_branch camss_vfe0_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe0_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2409,7 +2599,9 @@ static struct clk_branch camss_vfe1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe1_clk", - .parent_names = (const char *[]){ "vfe1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vfe1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2424,7 +2616,9 @@ static struct clk_branch camss_vfe1_stream_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe1_stream_clk", - .parent_names = (const char *[]){ "vfe1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vfe1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2439,7 +2633,9 @@ static struct clk_branch camss_vfe1_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_vfe1_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2454,7 +2650,9 @@ static struct clk_branch camss_csi_vfe0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi_vfe0_clk", - .parent_names = (const char *[]){ "vfe0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vfe0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2469,7 +2667,9 @@ static struct clk_branch camss_csi_vfe1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi_vfe1_clk", - .parent_names = (const char *[]){ "vfe1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &vfe1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2484,7 +2684,9 @@ static struct clk_branch camss_cpp_vbif_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_cpp_vbif_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2499,7 +2701,9 @@ static struct clk_branch camss_cpp_axi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_cpp_axi_clk", - .parent_names = (const char *[]){ "axi_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &axi_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2514,7 +2718,9 @@ static struct clk_branch camss_cpp_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_cpp_clk", - .parent_names = (const char *[]){ "cpp_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &cpp_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2529,7 +2735,9 @@ static struct clk_branch camss_cpp_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_cpp_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2544,7 +2752,9 @@ static struct clk_branch camss_csi0_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi0_clk", - .parent_names = (const char *[]){ "csi0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2559,7 +2769,9 @@ static struct clk_branch camss_csi0_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi0_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2574,7 +2786,9 @@ static struct clk_branch camss_csi0phy_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi0phy_clk", - .parent_names = (const char *[]){ "csi0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2589,7 +2803,9 @@ static struct clk_branch camss_csi0rdi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi0rdi_clk", - .parent_names = (const char *[]){ "csi0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2604,7 +2820,9 @@ static struct clk_branch camss_csi0pix_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi0pix_clk", - .parent_names = (const char *[]){ "csi0_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi0_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2619,7 +2837,9 @@ static struct clk_branch camss_csi1_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi1_clk", - .parent_names = (const char *[]){ "csi1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2634,7 +2854,9 @@ static struct clk_branch camss_csi1_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi1_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2649,7 +2871,9 @@ static struct clk_branch camss_csi1phy_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi1phy_clk", - .parent_names = (const char *[]){ "csi1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2664,7 +2888,9 @@ static struct clk_branch camss_csi1rdi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi1rdi_clk", - .parent_names = (const char *[]){ "csi1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2679,7 +2905,9 @@ static struct clk_branch camss_csi1pix_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi1pix_clk", - .parent_names = (const char *[]){ "csi1_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi1_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2694,7 +2922,9 @@ static struct clk_branch camss_csi2_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi2_clk", - .parent_names = (const char *[]){ "csi2_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi2_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2709,7 +2939,9 @@ static struct clk_branch camss_csi2_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi2_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2724,7 +2956,9 @@ static struct clk_branch camss_csi2phy_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi2phy_clk", - .parent_names = (const char *[]){ "csi2_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi2_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2739,7 +2973,9 @@ static struct clk_branch camss_csi2rdi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi2rdi_clk", - .parent_names = (const char *[]){ "csi2_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi2_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2754,7 +2990,9 @@ static struct clk_branch camss_csi2pix_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi2pix_clk", - .parent_names = (const char *[]){ "csi2_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi2_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2769,7 +3007,9 @@ static struct clk_branch camss_csi3_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi3_clk", - .parent_names = (const char *[]){ "csi3_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi3_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2784,7 +3024,9 @@ static struct clk_branch camss_csi3_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi3_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2799,7 +3041,9 @@ static struct clk_branch camss_csi3phy_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi3phy_clk", - .parent_names = (const char *[]){ "csi3_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi3_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2814,7 +3058,9 @@ static struct clk_branch camss_csi3rdi_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi3rdi_clk", - .parent_names = (const char *[]){ "csi3_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi3_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2829,7 +3075,9 @@ static struct clk_branch camss_csi3pix_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_csi3pix_clk", - .parent_names = (const char *[]){ "csi3_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &csi3_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2844,7 +3092,9 @@ static struct clk_branch camss_ispif_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "camss_ispif_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2859,7 +3109,9 @@ static struct clk_branch fd_core_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "fd_core_clk", - .parent_names = (const char *[]){ "fd_core_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &fd_core_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2874,7 +3126,9 @@ static struct clk_branch fd_core_uar_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "fd_core_uar_clk", - .parent_names = (const char *[]){ "fd_core_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &fd_core_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, @@ -2889,7 +3143,9 @@ static struct clk_branch fd_ahb_clk = { .enable_mask = BIT(0), .hw.init = &(struct clk_init_data){ .name = "fd_ahb_clk", - .parent_names = (const char *[]){ "ahb_clk_src" }, + .parent_hws = (const struct clk_hw*[]){ + &ahb_clk_src.clkr.hw + }, .num_parents = 1, .flags = CLK_SET_RATE_PARENT, .ops = &clk_branch2_ops, diff --git a/drivers/clk/qcom/videocc-sm8250.c b/drivers/clk/qcom/videocc-sm8250.c index 8617454e4a77..f28f2cb051d7 100644 --- a/drivers/clk/qcom/videocc-sm8250.c +++ b/drivers/clk/qcom/videocc-sm8250.c @@ -277,7 +277,6 @@ static struct gdsc mvs0c_gdsc = { }, .flags = 0, .pwrsts = PWRSTS_OFF_ON, - .supply = "mmcx", }; static struct gdsc mvs1c_gdsc = { @@ -287,7 +286,6 @@ static struct gdsc mvs1c_gdsc = { }, .flags = 0, .pwrsts = PWRSTS_OFF_ON, - .supply = "mmcx", }; static struct gdsc mvs0_gdsc = { @@ -297,7 +295,6 @@ static struct gdsc mvs0_gdsc = { }, .flags = HW_CTRL, .pwrsts = PWRSTS_OFF_ON, - .supply = "mmcx", }; static struct gdsc mvs1_gdsc = { @@ -307,7 +304,6 @@ static struct gdsc mvs1_gdsc = { }, .flags = HW_CTRL, .pwrsts = PWRSTS_OFF_ON, - .supply = "mmcx", }; static struct clk_regmap *video_cc_sm8250_clocks[] = { diff --git a/drivers/clk/renesas/clk-r8a73a4.c b/drivers/clk/renesas/clk-r8a73a4.c index cfed11c659d9..f45c2c45808b 100644 --- a/drivers/clk/renesas/clk-r8a73a4.c +++ b/drivers/clk/renesas/clk-r8a73a4.c @@ -18,7 +18,6 @@ struct r8a73a4_cpg { struct clk_onecell_data data; spinlock_t lock; - void __iomem *reg; }; #define CPG_CKSCR 0xc0 @@ -59,7 +58,7 @@ static const struct clk_div_table div4_div_table[] = { static struct clk * __init r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, - const char *name) + void __iomem *base, const char *name) { const struct clk_div_table *table = NULL; const char *parent_name; @@ -69,7 +68,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, if (!strcmp(name, "main")) { - u32 ckscr = readl(cpg->reg + CPG_CKSCR); + u32 ckscr = readl(base + CPG_CKSCR); switch ((ckscr >> 28) & 3) { case 0: /* extal1 */ @@ -93,14 +92,14 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, * clock implementation and we currently have no need to change * the multiplier value. */ - u32 value = readl(cpg->reg + CPG_PLL0CR); + u32 value = readl(base + CPG_PLL0CR); parent_name = "main"; mult = ((value >> 24) & 0x7f) + 1; if (value & BIT(20)) div = 2; } else if (!strcmp(name, "pll1")) { - u32 value = readl(cpg->reg + CPG_PLL1CR); + u32 value = readl(base + CPG_PLL1CR); parent_name = "main"; /* XXX: enable bit? */ @@ -123,7 +122,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, default: return ERR_PTR(-EINVAL); } - value = readl(cpg->reg + cr); + value = readl(base + cr); switch ((value >> 5) & 7) { case 0: parent_name = "main"; @@ -159,7 +158,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, shift = 0; } div *= 32; - mult = 0x20 - ((readl(cpg->reg + CPG_FRQCRC) >> shift) & 0x1f); + mult = 0x20 - ((readl(base + CPG_FRQCRC) >> shift) & 0x1f); } else { struct div4_clk *c; @@ -181,7 +180,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, mult, div); } else { return clk_register_divider_table(NULL, name, parent_name, 0, - cpg->reg + reg, shift, 4, 0, + base + reg, shift, 4, 0, table, &cpg->lock); } } @@ -189,6 +188,7 @@ r8a73a4_cpg_register_clock(struct device_node *np, struct r8a73a4_cpg *cpg, static void __init r8a73a4_cpg_clocks_init(struct device_node *np) { struct r8a73a4_cpg *cpg; + void __iomem *base; struct clk **clks; unsigned int i; int num_clks; @@ -213,8 +213,8 @@ static void __init r8a73a4_cpg_clocks_init(struct device_node *np) cpg->data.clks = clks; cpg->data.clk_num = num_clks; - cpg->reg = of_iomap(np, 0); - if (WARN_ON(cpg->reg == NULL)) + base = of_iomap(np, 0); + if (WARN_ON(base == NULL)) return; for (i = 0; i < num_clks; ++i) { @@ -224,7 +224,7 @@ static void __init r8a73a4_cpg_clocks_init(struct device_node *np) of_property_read_string_index(np, "clock-output-names", i, &name); - clk = r8a73a4_cpg_register_clock(np, cpg, name); + clk = r8a73a4_cpg_register_clock(np, cpg, base, name); if (IS_ERR(clk)) pr_err("%s: failed to register %pOFn %s clock (%ld)\n", __func__, np, name, PTR_ERR(clk)); diff --git a/drivers/clk/renesas/clk-r8a7740.c b/drivers/clk/renesas/clk-r8a7740.c index d8190f007a81..3ee3f57e4e9a 100644 --- a/drivers/clk/renesas/clk-r8a7740.c +++ b/drivers/clk/renesas/clk-r8a7740.c @@ -18,7 +18,6 @@ struct r8a7740_cpg { struct clk_onecell_data data; spinlock_t lock; - void __iomem *reg; }; #define CPG_FRQCRA 0x00 @@ -61,7 +60,7 @@ static u32 cpg_mode __initdata; static struct clk * __init r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg, - const char *name) + void __iomem *base, const char *name) { const struct clk_div_table *table = NULL; const char *parent_name; @@ -96,20 +95,20 @@ r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg, * clock implementation and we currently have no need to change * the multiplier value. */ - u32 value = readl(cpg->reg + CPG_FRQCRC); + u32 value = readl(base + CPG_FRQCRC); parent_name = "system"; mult = ((value >> 24) & 0x7f) + 1; } else if (!strcmp(name, "pllc1")) { - u32 value = readl(cpg->reg + CPG_FRQCRA); + u32 value = readl(base + CPG_FRQCRA); parent_name = "system"; mult = ((value >> 24) & 0x7f) + 1; div = 2; } else if (!strcmp(name, "pllc2")) { - u32 value = readl(cpg->reg + CPG_PLLC2CR); + u32 value = readl(base + CPG_PLLC2CR); parent_name = "system"; mult = ((value >> 24) & 0x3f) + 1; } else if (!strcmp(name, "usb24s")) { - u32 value = readl(cpg->reg + CPG_USBCKCR); + u32 value = readl(base + CPG_USBCKCR); if (value & BIT(7)) /* extal2 */ parent_name = of_clk_get_parent_name(np, 1); @@ -137,7 +136,7 @@ r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg, mult, div); } else { return clk_register_divider_table(NULL, name, parent_name, 0, - cpg->reg + reg, shift, 4, 0, + base + reg, shift, 4, 0, table, &cpg->lock); } } @@ -145,6 +144,7 @@ r8a7740_cpg_register_clock(struct device_node *np, struct r8a7740_cpg *cpg, static void __init r8a7740_cpg_clocks_init(struct device_node *np) { struct r8a7740_cpg *cpg; + void __iomem *base; struct clk **clks; unsigned int i; int num_clks; @@ -172,8 +172,8 @@ static void __init r8a7740_cpg_clocks_init(struct device_node *np) cpg->data.clks = clks; cpg->data.clk_num = num_clks; - cpg->reg = of_iomap(np, 0); - if (WARN_ON(cpg->reg == NULL)) + base = of_iomap(np, 0); + if (WARN_ON(base == NULL)) return; for (i = 0; i < num_clks; ++i) { @@ -183,7 +183,7 @@ static void __init r8a7740_cpg_clocks_init(struct device_node *np) of_property_read_string_index(np, "clock-output-names", i, &name); - clk = r8a7740_cpg_register_clock(np, cpg, name); + clk = r8a7740_cpg_register_clock(np, cpg, base, name); if (IS_ERR(clk)) pr_err("%s: failed to register %pOFn %s clock (%ld)\n", __func__, np, name, PTR_ERR(clk)); diff --git a/drivers/clk/renesas/clk-r8a7778.c b/drivers/clk/renesas/clk-r8a7778.c index 3ccc53685bdd..797556259370 100644 --- a/drivers/clk/renesas/clk-r8a7778.c +++ b/drivers/clk/renesas/clk-r8a7778.c @@ -11,12 +11,6 @@ #include #include -struct r8a7778_cpg { - struct clk_onecell_data data; - spinlock_t lock; - void __iomem *reg; -}; - /* PLL multipliers per bits 11, 12, and 18 of MODEMR */ static const struct { unsigned long plla_mult; @@ -47,8 +41,7 @@ static u32 cpg_mode_rates __initdata; static u32 cpg_mode_divs __initdata; static struct clk * __init -r8a7778_cpg_register_clock(struct device_node *np, struct r8a7778_cpg *cpg, - const char *name) +r8a7778_cpg_register_clock(struct device_node *np, const char *name) { if (!strcmp(name, "plla")) { return clk_register_fixed_factor(NULL, "plla", @@ -77,7 +70,7 @@ r8a7778_cpg_register_clock(struct device_node *np, struct r8a7778_cpg *cpg, static void __init r8a7778_cpg_clocks_init(struct device_node *np) { - struct r8a7778_cpg *cpg; + struct clk_onecell_data *data; struct clk **clks; unsigned int i; int num_clks; @@ -100,23 +93,17 @@ static void __init r8a7778_cpg_clocks_init(struct device_node *np) return; } - cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); - if (cpg == NULL || clks == NULL) { + if (data == NULL || clks == NULL) { /* We're leaking memory on purpose, there's no point in cleaning * up as the system won't boot anyway. */ return; } - spin_lock_init(&cpg->lock); - - cpg->data.clks = clks; - cpg->data.clk_num = num_clks; - - cpg->reg = of_iomap(np, 0); - if (WARN_ON(cpg->reg == NULL)) - return; + data->clks = clks; + data->clk_num = num_clks; for (i = 0; i < num_clks; ++i) { const char *name; @@ -125,15 +112,15 @@ static void __init r8a7778_cpg_clocks_init(struct device_node *np) of_property_read_string_index(np, "clock-output-names", i, &name); - clk = r8a7778_cpg_register_clock(np, cpg, name); + clk = r8a7778_cpg_register_clock(np, name); if (IS_ERR(clk)) pr_err("%s: failed to register %pOFn %s clock (%ld)\n", __func__, np, name, PTR_ERR(clk)); else - cpg->data.clks[i] = clk; + data->clks[i] = clk; } - of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + of_clk_add_provider(np, of_clk_src_onecell_get, data); cpg_mstp_add_clk_domain(np); } diff --git a/drivers/clk/renesas/clk-r8a7779.c b/drivers/clk/renesas/clk-r8a7779.c index 9f3b5522eef5..9a2fea8cf4d7 100644 --- a/drivers/clk/renesas/clk-r8a7779.c +++ b/drivers/clk/renesas/clk-r8a7779.c @@ -21,12 +21,6 @@ #define CPG_NUM_CLOCKS (R8A7779_CLK_OUT + 1) -struct r8a7779_cpg { - struct clk_onecell_data data; - spinlock_t lock; - void __iomem *reg; -}; - /* ----------------------------------------------------------------------------- * CPG Clock Data */ @@ -87,7 +81,7 @@ static const unsigned int cpg_plla_mult[4] __initconst = { 42, 48, 56, 64 }; */ static struct clk * __init -r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg, +r8a7779_cpg_register_clock(struct device_node *np, const struct cpg_clk_config *config, unsigned int plla_mult, const char *name) { @@ -119,7 +113,7 @@ r8a7779_cpg_register_clock(struct device_node *np, struct r8a7779_cpg *cpg, static void __init r8a7779_cpg_clocks_init(struct device_node *np) { const struct cpg_clk_config *config; - struct r8a7779_cpg *cpg; + struct clk_onecell_data *data; struct clk **clks; unsigned int i, plla_mult; int num_clks; @@ -134,19 +128,17 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np) return; } - cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); clks = kcalloc(CPG_NUM_CLOCKS, sizeof(*clks), GFP_KERNEL); - if (cpg == NULL || clks == NULL) { + if (data == NULL || clks == NULL) { /* We're leaking memory on purpose, there's no point in cleaning * up as the system won't boot anyway. */ return; } - spin_lock_init(&cpg->lock); - - cpg->data.clks = clks; - cpg->data.clk_num = num_clks; + data->clks = clks; + data->clk_num = num_clks; config = &cpg_clk_configs[CPG_CLK_CONFIG_INDEX(mode)]; plla_mult = cpg_plla_mult[CPG_PLLA_MULT_INDEX(mode)]; @@ -158,16 +150,15 @@ static void __init r8a7779_cpg_clocks_init(struct device_node *np) of_property_read_string_index(np, "clock-output-names", i, &name); - clk = r8a7779_cpg_register_clock(np, cpg, config, - plla_mult, name); + clk = r8a7779_cpg_register_clock(np, config, plla_mult, name); if (IS_ERR(clk)) pr_err("%s: failed to register %pOFn %s clock (%ld)\n", __func__, np, name, PTR_ERR(clk)); else - cpg->data.clks[i] = clk; + data->clks[i] = clk; } - of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + of_clk_add_provider(np, of_clk_src_onecell_get, data); cpg_mstp_add_clk_domain(np); } diff --git a/drivers/clk/renesas/clk-rz.c b/drivers/clk/renesas/clk-rz.c index 7b703f14e20b..e770f09a27ed 100644 --- a/drivers/clk/renesas/clk-rz.c +++ b/drivers/clk/renesas/clk-rz.c @@ -15,11 +15,6 @@ #include #include -struct rz_cpg { - struct clk_onecell_data data; - void __iomem *reg; -}; - #define CPG_FRQCR 0x10 #define CPG_FRQCR2 0x14 @@ -49,7 +44,8 @@ static u16 __init rz_cpg_read_mode_pins(void) } static struct clk * __init -rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *name) +rz_cpg_register_clock(struct device_node *np, void __iomem *base, + const char *name) { u32 val; unsigned mult; @@ -65,7 +61,7 @@ rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *na } /* If mapping regs failed, skip non-pll clocks. System will boot anyhow */ - if (!cpg->reg) + if (!base) return ERR_PTR(-ENXIO); /* FIXME:"i" and "g" are variable clocks with non-integer dividers (e.g. 2/3) @@ -73,9 +69,9 @@ rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *na * let them run at fixed current speed and implement the details later. */ if (strcmp(name, "i") == 0) - val = (readl(cpg->reg + CPG_FRQCR) >> 8) & 3; + val = (readl(base + CPG_FRQCR) >> 8) & 3; else if (strcmp(name, "g") == 0) - val = readl(cpg->reg + CPG_FRQCR2) & 3; + val = readl(base + CPG_FRQCR2) & 3; else return ERR_PTR(-EINVAL); @@ -85,8 +81,9 @@ rz_cpg_register_clock(struct device_node *np, struct rz_cpg *cpg, const char *na static void __init rz_cpg_clocks_init(struct device_node *np) { - struct rz_cpg *cpg; + struct clk_onecell_data *data; struct clk **clks; + void __iomem *base; unsigned i; int num_clks; @@ -94,14 +91,14 @@ static void __init rz_cpg_clocks_init(struct device_node *np) if (WARN(num_clks <= 0, "can't count CPG clocks\n")) return; - cpg = kzalloc(sizeof(*cpg), GFP_KERNEL); + data = kzalloc(sizeof(*data), GFP_KERNEL); clks = kcalloc(num_clks, sizeof(*clks), GFP_KERNEL); - BUG_ON(!cpg || !clks); + BUG_ON(!data || !clks); - cpg->data.clks = clks; - cpg->data.clk_num = num_clks; + data->clks = clks; + data->clk_num = num_clks; - cpg->reg = of_iomap(np, 0); + base = of_iomap(np, 0); for (i = 0; i < num_clks; ++i) { const char *name; @@ -109,15 +106,15 @@ static void __init rz_cpg_clocks_init(struct device_node *np) of_property_read_string_index(np, "clock-output-names", i, &name); - clk = rz_cpg_register_clock(np, cpg, name); + clk = rz_cpg_register_clock(np, base, name); if (IS_ERR(clk)) pr_err("%s: failed to register %pOFn %s clock (%ld)\n", __func__, np, name, PTR_ERR(clk)); else - cpg->data.clks[i] = clk; + data->clks[i] = clk; } - of_clk_add_provider(np, of_clk_src_onecell_get, &cpg->data); + of_clk_add_provider(np, of_clk_src_onecell_get, data); cpg_mstp_add_clk_domain(np); } diff --git a/drivers/clk/renesas/clk-sh73a0.c b/drivers/clk/renesas/clk-sh73a0.c index 4146c1d717b9..8c51090f13e1 100644 --- a/drivers/clk/renesas/clk-sh73a0.c +++ b/drivers/clk/renesas/clk-sh73a0.c @@ -18,7 +18,6 @@ struct sh73a0_cpg { struct clk_onecell_data data; spinlock_t lock; - void __iomem *reg; }; #define CPG_FRQCRA 0x00 @@ -73,7 +72,7 @@ static const struct clk_div_table z_div_table[] = { static struct clk * __init sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, - const char *name) + void __iomem *base, const char *name) { const struct clk_div_table *table = NULL; unsigned int shift, reg, width; @@ -83,12 +82,12 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, if (!strcmp(name, "main")) { /* extal1, extal1_div2, extal2, extal2_div2 */ - u32 parent_idx = (readl(cpg->reg + CPG_CKSCR) >> 28) & 3; + u32 parent_idx = (readl(base + CPG_CKSCR) >> 28) & 3; parent_name = of_clk_get_parent_name(np, parent_idx >> 1); div = (parent_idx & 1) + 1; } else if (!strncmp(name, "pll", 3)) { - void __iomem *enable_reg = cpg->reg; + void __iomem *enable_reg = base; u32 enable_bit = name[3] - '0'; parent_name = "main"; @@ -108,7 +107,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, default: return ERR_PTR(-EINVAL); } - if (readl(cpg->reg + CPG_PLLECR) & BIT(enable_bit)) { + if (readl(base + CPG_PLLECR) & BIT(enable_bit)) { mult = ((readl(enable_reg) >> 24) & 0x3f) + 1; /* handle CFG bit for PLL1 and PLL2 */ if (enable_bit == 1 || enable_bit == 2) @@ -117,7 +116,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, } } else if (!strcmp(name, "dsi0phy") || !strcmp(name, "dsi1phy")) { u32 phy_no = name[3] - '0'; - void __iomem *dsi_reg = cpg->reg + + void __iomem *dsi_reg = base + (phy_no ? CPG_DSI1PHYCR : CPG_DSI0PHYCR); parent_name = phy_no ? "dsi1pck" : "dsi0pck"; @@ -154,7 +153,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, mult, div); } else { return clk_register_divider_table(NULL, name, parent_name, 0, - cpg->reg + reg, shift, width, 0, + base + reg, shift, width, 0, table, &cpg->lock); } } @@ -162,6 +161,7 @@ sh73a0_cpg_register_clock(struct device_node *np, struct sh73a0_cpg *cpg, static void __init sh73a0_cpg_clocks_init(struct device_node *np) { struct sh73a0_cpg *cpg; + void __iomem *base; struct clk **clks; unsigned int i; int num_clks; @@ -186,14 +186,14 @@ static void __init sh73a0_cpg_clocks_init(struct device_node *np) cpg->data.clks = clks; cpg->data.clk_num = num_clks; - cpg->reg = of_iomap(np, 0); - if (WARN_ON(cpg->reg == NULL)) + base = of_iomap(np, 0); + if (WARN_ON(base == NULL)) return; /* Set SDHI clocks to a known state */ - writel(0x108, cpg->reg + CPG_SD0CKCR); - writel(0x108, cpg->reg + CPG_SD1CKCR); - writel(0x108, cpg->reg + CPG_SD2CKCR); + writel(0x108, base + CPG_SD0CKCR); + writel(0x108, base + CPG_SD1CKCR); + writel(0x108, base + CPG_SD2CKCR); for (i = 0; i < num_clks; ++i) { const char *name; @@ -202,7 +202,7 @@ static void __init sh73a0_cpg_clocks_init(struct device_node *np) of_property_read_string_index(np, "clock-output-names", i, &name); - clk = sh73a0_cpg_register_clock(np, cpg, name); + clk = sh73a0_cpg_register_clock(np, cpg, base, name); if (IS_ERR(clk)) pr_err("%s: failed to register %pOFn %s clock (%ld)\n", __func__, np, name, PTR_ERR(clk)); diff --git a/drivers/clk/renesas/r8a779f0-cpg-mssr.c b/drivers/clk/renesas/r8a779f0-cpg-mssr.c index c17ebe6b5992..cd80b6084ece 100644 --- a/drivers/clk/renesas/r8a779f0-cpg-mssr.c +++ b/drivers/clk/renesas/r8a779f0-cpg-mssr.c @@ -77,6 +77,8 @@ static const struct cpg_core_clk r8a779f0_core_clks[] __initconst = { DEF_BASE(".rpcsrc", CLK_RPCSRC, CLK_TYPE_GEN4_RPCSRC, CLK_PLL5), /* Core Clock Outputs */ + DEF_GEN4_Z("z0", R8A779F0_CLK_Z0, CLK_TYPE_GEN4_Z, CLK_PLL2, 2, 0), + DEF_GEN4_Z("z1", R8A779F0_CLK_Z1, CLK_TYPE_GEN4_Z, CLK_PLL2, 2, 8), DEF_FIXED("s0d2", R8A779F0_CLK_S0D2, CLK_S0, 2, 1), DEF_FIXED("s0d3", R8A779F0_CLK_S0D3, CLK_S0, 3, 1), DEF_FIXED("s0d4", R8A779F0_CLK_S0D4, CLK_S0, 4, 1), @@ -118,20 +120,28 @@ static const struct cpg_core_clk r8a779f0_core_clks[] __initconst = { }; static const struct mssr_mod_clk r8a779f0_mod_clks[] __initconst = { + DEF_MOD("hscif0", 514, R8A779F0_CLK_S0D3), + DEF_MOD("hscif1", 515, R8A779F0_CLK_S0D3), + DEF_MOD("hscif2", 516, R8A779F0_CLK_S0D3), + DEF_MOD("hscif3", 517, R8A779F0_CLK_S0D3), DEF_MOD("i2c0", 518, R8A779F0_CLK_S0D6_PER), DEF_MOD("i2c1", 519, R8A779F0_CLK_S0D6_PER), DEF_MOD("i2c2", 520, R8A779F0_CLK_S0D6_PER), DEF_MOD("i2c3", 521, R8A779F0_CLK_S0D6_PER), DEF_MOD("i2c4", 522, R8A779F0_CLK_S0D6_PER), DEF_MOD("i2c5", 523, R8A779F0_CLK_S0D6_PER), + DEF_MOD("pcie0", 624, R8A779F0_CLK_S0D2), + DEF_MOD("pcie1", 625, R8A779F0_CLK_S0D2), DEF_MOD("scif0", 702, R8A779F0_CLK_S0D12_PER), DEF_MOD("scif1", 703, R8A779F0_CLK_S0D12_PER), DEF_MOD("scif3", 704, R8A779F0_CLK_S0D12_PER), DEF_MOD("scif4", 705, R8A779F0_CLK_S0D12_PER), + DEF_MOD("sdhi0", 706, R8A779F0_CLK_SD0), DEF_MOD("sys-dmac0", 709, R8A779F0_CLK_S0D3_PER), DEF_MOD("sys-dmac1", 710, R8A779F0_CLK_S0D3_PER), DEF_MOD("wdt", 907, R8A779F0_CLK_R), DEF_MOD("pfc0", 915, R8A779F0_CLK_CL16M), + DEF_MOD("tsc", 919, R8A779F0_CLK_CL16M), DEF_MOD("ufs", 1514, R8A779F0_CLK_S0D4_HSC), }; diff --git a/drivers/clk/renesas/r9a06g032-clocks.c b/drivers/clk/renesas/r9a06g032-clocks.c index 35ffc462af1a..1488c9d6e639 100644 --- a/drivers/clk/renesas/r9a06g032-clocks.c +++ b/drivers/clk/renesas/r9a06g032-clocks.c @@ -51,11 +51,9 @@ struct r9a06g032_clkdesc { struct { u16 div, mul; }; - unsigned int factor; - unsigned int frequency; /* for dual gate */ struct { - uint16_t group : 1, index: 3; + uint16_t group : 1; u16 sel, g1, r1, g2, r2; } dual; }; @@ -85,10 +83,10 @@ struct r9a06g032_clkdesc { .source = 1 + R9A06G032_##_src, .name = _n, \ .reg = _reg, .div_min = _min, .div_max = _max, \ .div_table = { __VA_ARGS__ } } -#define D_UGATE(_idx, _n, _src, _g, _gi, _g1, _r1, _g2, _r2) \ +#define D_UGATE(_idx, _n, _src, _g, _g1, _r1, _g2, _r2) \ { .type = K_DUALGATE, .index = R9A06G032_##_idx, \ .source = 1 + R9A06G032_##_src, .name = _n, \ - .dual = { .group = _g, .index = _gi, \ + .dual = { .group = _g, \ .g1 = _g1, .r1 = _r1, .g2 = _g2, .r2 = _r2 }, } enum { K_GATE = 0, K_FFC, K_DIV, K_BITSEL, K_DUALGATE }; @@ -290,8 +288,8 @@ static const struct r9a06g032_clkdesc r9a06g032_clocks[] = { .name = "uart_group_012", .type = K_BITSEL, .source = 1 + R9A06G032_DIV_UART, - /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */ - .dual.sel = ((0xec / 4) << 5) | 24, + /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */ + .dual.sel = ((0x34 / 4) << 5) | 30, .dual.group = 0, }, { @@ -299,18 +297,18 @@ static const struct r9a06g032_clkdesc r9a06g032_clocks[] = { .name = "uart_group_34567", .type = K_BITSEL, .source = 1 + R9A06G032_DIV_P2_PG, - /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG0_0 */ - .dual.sel = ((0x34 / 4) << 5) | 30, + /* R9A06G032_SYSCTRL_REG_PWRCTRL_PG1_PR2 */ + .dual.sel = ((0xec / 4) << 5) | 24, .dual.group = 1, }, - D_UGATE(CLK_UART0, "clk_uart0", UART_GROUP_012, 0, 0, 0x1b2, 0x1b3, 0x1b4, 0x1b5), - D_UGATE(CLK_UART1, "clk_uart1", UART_GROUP_012, 0, 1, 0x1b6, 0x1b7, 0x1b8, 0x1b9), - D_UGATE(CLK_UART2, "clk_uart2", UART_GROUP_012, 0, 2, 0x1ba, 0x1bb, 0x1bc, 0x1bd), - D_UGATE(CLK_UART3, "clk_uart3", UART_GROUP_34567, 1, 0, 0x760, 0x761, 0x762, 0x763), - D_UGATE(CLK_UART4, "clk_uart4", UART_GROUP_34567, 1, 1, 0x764, 0x765, 0x766, 0x767), - D_UGATE(CLK_UART5, "clk_uart5", UART_GROUP_34567, 1, 2, 0x768, 0x769, 0x76a, 0x76b), - D_UGATE(CLK_UART6, "clk_uart6", UART_GROUP_34567, 1, 3, 0x76c, 0x76d, 0x76e, 0x76f), - D_UGATE(CLK_UART7, "clk_uart7", UART_GROUP_34567, 1, 4, 0x770, 0x771, 0x772, 0x773), + D_UGATE(CLK_UART0, "clk_uart0", UART_GROUP_012, 0, 0x1b2, 0x1b3, 0x1b4, 0x1b5), + D_UGATE(CLK_UART1, "clk_uart1", UART_GROUP_012, 0, 0x1b6, 0x1b7, 0x1b8, 0x1b9), + D_UGATE(CLK_UART2, "clk_uart2", UART_GROUP_012, 0, 0x1ba, 0x1bb, 0x1bc, 0x1bd), + D_UGATE(CLK_UART3, "clk_uart3", UART_GROUP_34567, 1, 0x760, 0x761, 0x762, 0x763), + D_UGATE(CLK_UART4, "clk_uart4", UART_GROUP_34567, 1, 0x764, 0x765, 0x766, 0x767), + D_UGATE(CLK_UART5, "clk_uart5", UART_GROUP_34567, 1, 0x768, 0x769, 0x76a, 0x76b), + D_UGATE(CLK_UART6, "clk_uart6", UART_GROUP_34567, 1, 0x76c, 0x76d, 0x76e, 0x76f), + D_UGATE(CLK_UART7, "clk_uart7", UART_GROUP_34567, 1, 0x770, 0x771, 0x772, 0x773), }; struct r9a06g032_priv { diff --git a/drivers/clk/renesas/r9a07g043-cpg.c b/drivers/clk/renesas/r9a07g043-cpg.c index 33c2bd8df2e5..37475465100d 100644 --- a/drivers/clk/renesas/r9a07g043-cpg.c +++ b/drivers/clk/renesas/r9a07g043-cpg.c @@ -36,9 +36,11 @@ enum clk_ids { CLK_PLL3_DIV2_4_2, CLK_SEL_PLL3_3, CLK_DIV_PLL3_C, +#ifdef CONFIG_ARM64 CLK_PLL5, CLK_PLL5_500, CLK_PLL5_250, +#endif CLK_PLL6, CLK_PLL6_250, CLK_P1_DIV2, @@ -100,9 +102,11 @@ static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = { DEF_FIXED(".pll3_533", CLK_PLL3_533, CLK_PLL3, 1, 3), DEF_MUX_RO(".sel_pll3_3", CLK_SEL_PLL3_3, SEL_PLL3_3, sel_pll3_3), DEF_DIV("divpl3c", CLK_DIV_PLL3_C, CLK_SEL_PLL3_3, DIVPL3C, dtable_1_32), +#ifdef CONFIG_ARM64 DEF_FIXED(".pll5", CLK_PLL5, CLK_EXTAL, 125, 1), DEF_FIXED(".pll5_500", CLK_PLL5_500, CLK_PLL5, 1, 6), DEF_FIXED(".pll5_250", CLK_PLL5_250, CLK_PLL5_500, 1, 2), +#endif DEF_FIXED(".pll6", CLK_PLL6, CLK_EXTAL, 125, 6), DEF_FIXED(".pll6_250", CLK_PLL6_250, CLK_PLL6, 1, 2), @@ -126,12 +130,20 @@ static const struct cpg_core_clk r9a07g043_core_clks[] __initconst = { }; static struct rzg2l_mod_clk r9a07g043_mod_clks[] = { +#ifdef CONFIG_ARM64 DEF_MOD("gic", R9A07G043_GIC600_GICCLK, R9A07G043_CLK_P1, 0x514, 0), DEF_MOD("ia55_pclk", R9A07G043_IA55_PCLK, R9A07G043_CLK_P2, 0x518, 0), DEF_MOD("ia55_clk", R9A07G043_IA55_CLK, R9A07G043_CLK_P1, 0x518, 1), +#endif +#ifdef CONFIG_RISCV + DEF_MOD("iax45_pclk", R9A07G043_IAX45_PCLK, R9A07G043_CLK_P2, + 0x518, 0), + DEF_MOD("iax45_clk", R9A07G043_IAX45_CLK, R9A07G043_CLK_P1, + 0x518, 1), +#endif DEF_MOD("dmac_aclk", R9A07G043_DMAC_ACLK, R9A07G043_CLK_P1, 0x52c, 0), DEF_MOD("dmac_pclk", R9A07G043_DMAC_PCLK, CLK_P1_DIV2, @@ -243,9 +255,14 @@ static struct rzg2l_mod_clk r9a07g043_mod_clks[] = { }; static struct rzg2l_reset r9a07g043_resets[] = { +#ifdef CONFIG_ARM64 DEF_RST(R9A07G043_GIC600_GICRESET_N, 0x814, 0), DEF_RST(R9A07G043_GIC600_DBG_GICRESET_N, 0x814, 1), DEF_RST(R9A07G043_IA55_RESETN, 0x818, 0), +#endif +#ifdef CONFIG_RISCV + DEF_RST(R9A07G043_IAX45_RESETN, 0x818, 0), +#endif DEF_RST(R9A07G043_DMAC_ARESETN, 0x82c, 0), DEF_RST(R9A07G043_DMAC_RST_ASYNC, 0x82c, 1), DEF_RST(R9A07G043_OSTM0_PRESETZ, 0x834, 0), @@ -291,8 +308,13 @@ static struct rzg2l_reset r9a07g043_resets[] = { }; static const unsigned int r9a07g043_crit_mod_clks[] __initconst = { +#ifdef CONFIG_ARM64 MOD_CLK_BASE + R9A07G043_GIC600_GICCLK, MOD_CLK_BASE + R9A07G043_IA55_CLK, +#endif +#ifdef CONFIG_RISCV + MOD_CLK_BASE + R9A07G043_IAX45_CLK, +#endif MOD_CLK_BASE + R9A07G043_DMAC_ACLK, }; @@ -310,11 +332,21 @@ const struct rzg2l_cpg_info r9a07g043_cpg_info = { /* Module Clocks */ .mod_clks = r9a07g043_mod_clks, .num_mod_clks = ARRAY_SIZE(r9a07g043_mod_clks), +#ifdef CONFIG_ARM64 .num_hw_mod_clks = R9A07G043_TSU_PCLK + 1, +#endif +#ifdef CONFIG_RISCV + .num_hw_mod_clks = R9A07G043_IAX45_PCLK + 1, +#endif /* Resets */ .resets = r9a07g043_resets, +#ifdef CONFIG_ARM64 .num_resets = R9A07G043_TSU_PRESETN + 1, /* Last reset ID + 1 */ +#endif +#ifdef CONFIG_RISCV + .num_resets = R9A07G043_IAX45_RESETN + 1, /* Last reset ID + 1 */ +#endif .has_clk_mon_regs = true, }; diff --git a/drivers/clk/renesas/r9a07g044-cpg.c b/drivers/clk/renesas/r9a07g044-cpg.c index b288897852c7..fd7c4eecd398 100644 --- a/drivers/clk/renesas/r9a07g044-cpg.c +++ b/drivers/clk/renesas/r9a07g044-cpg.c @@ -182,7 +182,7 @@ static const struct { }; static const struct { - struct rzg2l_mod_clk common[71]; + struct rzg2l_mod_clk common[76]; #ifdef CONFIG_CLK_R9A07G054 struct rzg2l_mod_clk drp[0]; #endif @@ -204,6 +204,16 @@ static const struct { 0x534, 1), DEF_MOD("ostm2_pclk", R9A07G044_OSTM2_PCLK, R9A07G044_CLK_P0, 0x534, 2), + DEF_MOD("gpt_pclk", R9A07G044_GPT_PCLK, R9A07G044_CLK_P0, + 0x540, 0), + DEF_MOD("poeg_a_clkp", R9A07G044_POEG_A_CLKP, R9A07G044_CLK_P0, + 0x544, 0), + DEF_MOD("poeg_b_clkp", R9A07G044_POEG_B_CLKP, R9A07G044_CLK_P0, + 0x544, 1), + DEF_MOD("poeg_c_clkp", R9A07G044_POEG_C_CLKP, R9A07G044_CLK_P0, + 0x544, 2), + DEF_MOD("poeg_d_clkp", R9A07G044_POEG_D_CLKP, R9A07G044_CLK_P0, + 0x544, 3), DEF_MOD("wdt0_pclk", R9A07G044_WDT0_PCLK, R9A07G044_CLK_P0, 0x548, 0), DEF_MOD("wdt0_clk", R9A07G044_WDT0_CLK, R9A07G044_OSCCLK, @@ -346,6 +356,11 @@ static struct rzg2l_reset r9a07g044_resets[] = { DEF_RST(R9A07G044_OSTM0_PRESETZ, 0x834, 0), DEF_RST(R9A07G044_OSTM1_PRESETZ, 0x834, 1), DEF_RST(R9A07G044_OSTM2_PRESETZ, 0x834, 2), + DEF_RST(R9A07G044_GPT_RST_C, 0x840, 0), + DEF_RST(R9A07G044_POEG_A_RST, 0x844, 0), + DEF_RST(R9A07G044_POEG_B_RST, 0x844, 1), + DEF_RST(R9A07G044_POEG_C_RST, 0x844, 2), + DEF_RST(R9A07G044_POEG_D_RST, 0x844, 3), DEF_RST(R9A07G044_WDT0_PRESETN, 0x848, 0), DEF_RST(R9A07G044_WDT1_PRESETN, 0x848, 1), DEF_RST(R9A07G044_WDT2_PRESETN, 0x848, 2), diff --git a/drivers/clk/renesas/r9a09g011-cpg.c b/drivers/clk/renesas/r9a09g011-cpg.c index 40693bb85b80..b21915cf6648 100644 --- a/drivers/clk/renesas/r9a09g011-cpg.c +++ b/drivers/clk/renesas/r9a09g011-cpg.c @@ -126,19 +126,24 @@ static const struct cpg_core_clk r9a09g011_core_clks[] __initconst = { }; static const struct rzg2l_mod_clk r9a09g011_mod_clks[] __initconst = { + DEF_MOD("pfc", R9A09G011_PFC_PCLK, CLK_MAIN, 0x400, 2), DEF_MOD("gic", R9A09G011_GIC_CLK, CLK_SEL_B_D2, 0x400, 5), DEF_COUPLED("eth_axi", R9A09G011_ETH0_CLK_AXI, CLK_PLL2_200, 0x40c, 8), DEF_COUPLED("eth_chi", R9A09G011_ETH0_CLK_CHI, CLK_PLL2_100, 0x40c, 8), DEF_MOD("eth_clk_gptp", R9A09G011_ETH0_GPTP_EXT, CLK_PLL2_100, 0x40c, 9), DEF_MOD("syc_cnt_clk", R9A09G011_SYC_CNT_CLK, CLK_MAIN_24, 0x41c, 12), + DEF_MOD("wdt0_pclk", R9A09G011_WDT0_PCLK, CLK_SEL_E, 0x428, 12), + DEF_MOD("wdt0_clk", R9A09G011_WDT0_CLK, CLK_MAIN, 0x428, 13), DEF_MOD("urt_pclk", R9A09G011_URT_PCLK, CLK_SEL_E, 0x438, 4), DEF_MOD("urt0_clk", R9A09G011_URT0_CLK, CLK_SEL_W0, 0x438, 5), DEF_MOD("ca53", R9A09G011_CA53_CLK, CLK_DIV_A, 0x448, 0), }; static const struct rzg2l_reset r9a09g011_resets[] = { + DEF_RST(R9A09G011_PFC_PRESETN, 0x600, 2), DEF_RST_MON(R9A09G011_ETH0_RST_HW_N, 0x608, 11, 11), DEF_RST_MON(R9A09G011_SYC_RST_N, 0x610, 9, 13), + DEF_RST_MON(R9A09G011_WDT0_PRESETN, 0x614, 12, 19), }; static const unsigned int r9a09g011_crit_mod_clks[] __initconst = { diff --git a/drivers/clk/renesas/rcar-gen4-cpg.c b/drivers/clk/renesas/rcar-gen4-cpg.c index c7ed43d6aa67..e27832e5114f 100644 --- a/drivers/clk/renesas/rcar-gen4-cpg.c +++ b/drivers/clk/renesas/rcar-gen4-cpg.c @@ -23,7 +23,7 @@ #include "rcar-gen4-cpg.h" #include "rcar-cpg-lib.h" -static const struct rcar_gen4_cpg_pll_config *cpg_pll_config __initconst; +static const struct rcar_gen4_cpg_pll_config *cpg_pll_config __initdata; static unsigned int cpg_clk_extalr __initdata; static u32 cpg_mode __initdata; diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index e2999ab2b53c..3ff6ecd61756 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -1180,7 +1180,7 @@ static int rzg2l_cpg_status(struct reset_controller_dev *rcdev, s8 monbit = info->resets[id].monbit; if (info->has_clk_mon_regs) { - return !(readl(priv->base + CLK_MRST_R(reg)) & bitmask); + return !!(readl(priv->base + CLK_MRST_R(reg)) & bitmask); } else if (monbit >= 0) { u32 monbitmask = BIT(monbit); diff --git a/drivers/clk/spear/clk-aux-synth.c b/drivers/clk/spear/clk-aux-synth.c index 906410413bc1..637938e804f8 100644 --- a/drivers/clk/spear/clk-aux-synth.c +++ b/drivers/clk/spear/clk-aux-synth.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 ST Microelectronics * Viresh Kumar * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * Auxiliary Synthesizer clock implementation */ diff --git a/drivers/clk/spear/clk-frac-synth.c b/drivers/clk/spear/clk-frac-synth.c index f5be02205ac6..2380df293a2c 100644 --- a/drivers/clk/spear/clk-frac-synth.c +++ b/drivers/clk/spear/clk-frac-synth.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 ST Microelectronics * Viresh Kumar * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * Fractional Synthesizer clock implementation */ diff --git a/drivers/clk/spear/clk-gpt-synth.c b/drivers/clk/spear/clk-gpt-synth.c index 6ed406d943ba..4ef747c2abbb 100644 --- a/drivers/clk/spear/clk-gpt-synth.c +++ b/drivers/clk/spear/clk-gpt-synth.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 ST Microelectronics * Viresh Kumar * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * General Purpose Timer Synthesizer clock implementation */ diff --git a/drivers/clk/spear/clk-vco-pll.c b/drivers/clk/spear/clk-vco-pll.c index fed194169666..348eeab0a906 100644 --- a/drivers/clk/spear/clk-vco-pll.c +++ b/drivers/clk/spear/clk-vco-pll.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 ST Microelectronics * Viresh Kumar * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * VCO-PLL clock implementation */ diff --git a/drivers/clk/spear/clk.c b/drivers/clk/spear/clk.c index 157fe099ea6a..50847cccdf58 100644 --- a/drivers/clk/spear/clk.c +++ b/drivers/clk/spear/clk.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2012 ST Microelectronics * Viresh Kumar * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * SPEAr clk - Common routines */ diff --git a/drivers/clk/spear/clk.h b/drivers/clk/spear/clk.h index af0e25f496c1..3d580d1bdadd 100644 --- a/drivers/clk/spear/clk.h +++ b/drivers/clk/spear/clk.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Clock framework definitions for SPEAr platform * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #ifndef __SPEAR_CLK_H diff --git a/drivers/clk/spear/spear1310_clock.c b/drivers/clk/spear/spear1310_clock.c index 8c8974866789..9d5959a4251a 100644 --- a/drivers/clk/spear/spear1310_clock.c +++ b/drivers/clk/spear/spear1310_clock.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear13xx/spear1310_clock.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index c0dc94355c87..8b51229d0471 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * arch/arm/mach-spear13xx/spear1340_clock.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/spear/spear3xx_clock.c b/drivers/clk/spear/spear3xx_clock.c index c403c66b6583..41717ff707f6 100644 --- a/drivers/clk/spear/spear3xx_clock.c +++ b/drivers/clk/spear/spear3xx_clock.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SPEAr3xx machines clock framework source file * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/spear/spear6xx_clock.c b/drivers/clk/spear/spear6xx_clock.c index 47810be7f15c..490701ac9e93 100644 --- a/drivers/clk/spear/spear6xx_clock.c +++ b/drivers/clk/spear/spear6xx_clock.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SPEAr6xx machines clock framework source file * * Copyright (C) 2012 ST Microelectronics * Viresh Kumar - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c index b7962e5149a5..001582ea71ba 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6-r.c @@ -143,17 +143,6 @@ static struct ccu_common *sun50i_h6_r_ccu_clks[] = { &w1_clk.common, }; -static struct ccu_common *sun50i_h616_r_ccu_clks[] = { - &r_apb1_clk.common, - &r_apb2_clk.common, - &r_apb1_twd_clk.common, - &r_apb2_i2c_clk.common, - &r_apb2_rsb_clk.common, - &r_apb1_ir_clk.common, - &r_apb1_rtc_clk.common, - &ir_clk.common, -}; - static struct clk_hw_onecell_data sun50i_h6_r_hw_clks = { .hws = { [CLK_AR100] = &ar100_clk.common.hw, @@ -219,8 +208,8 @@ static const struct sunxi_ccu_desc sun50i_h6_r_ccu_desc = { }; static const struct sunxi_ccu_desc sun50i_h616_r_ccu_desc = { - .ccu_clks = sun50i_h616_r_ccu_clks, - .num_ccu_clks = ARRAY_SIZE(sun50i_h616_r_ccu_clks), + .ccu_clks = sun50i_h6_r_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun50i_h6_r_ccu_clks), .hw_clks = &sun50i_h616_r_hw_clks, diff --git a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c index 1a5e418923f6..30056da3e0af 100644 --- a/drivers/clk/sunxi-ng/ccu-sun50i-h6.c +++ b/drivers/clk/sunxi-ng/ccu-sun50i-h6.c @@ -95,13 +95,13 @@ static struct ccu_nkmp pll_periph1_clk = { }, }; +/* For GPU PLL, using an output divider for DFS causes system to fail */ #define SUN50I_H6_PLL_GPU_REG 0x030 static struct ccu_nkmp pll_gpu_clk = { .enable = BIT(31), .lock = BIT(28), .n = _SUNXI_CCU_MULT_MIN(8, 8, 12), .m = _SUNXI_CCU_DIV(1, 1), /* input divider */ - .p = _SUNXI_CCU_DIV(0, 1), /* output divider */ .common = { .reg = 0x030, .hw.init = CLK_HW_INIT("pll-gpu", "osc24M", @@ -294,9 +294,9 @@ static SUNXI_CCU_M_WITH_MUX_GATE(deinterlace_clk, "deinterlace", static SUNXI_CCU_GATE(bus_deinterlace_clk, "bus-deinterlace", "psi-ahb1-ahb2", 0x62c, BIT(0), 0); +/* Keep GPU_CLK divider const to avoid DFS instability. */ static const char * const gpu_parents[] = { "pll-gpu" }; -static SUNXI_CCU_M_WITH_MUX_GATE(gpu_clk, "gpu", gpu_parents, 0x670, - 0, 3, /* M */ +static SUNXI_CCU_MUX_WITH_GATE(gpu_clk, "gpu", gpu_parents, 0x670, 24, 1, /* mux */ BIT(31), /* gate */ CLK_SET_RATE_PARENT); @@ -1191,6 +1191,16 @@ static int sun50i_h6_ccu_probe(struct platform_device *pdev) if (IS_ERR(reg)) return PTR_ERR(reg); + /* Force PLL_GPU output divider bits to 0 */ + val = readl(reg + SUN50I_H6_PLL_GPU_REG); + val &= ~BIT(0); + writel(val, reg + SUN50I_H6_PLL_GPU_REG); + + /* Force GPU_CLK divider bits to 0 */ + val = readl(reg + gpu_clk.common.reg); + val &= ~GENMASK(3, 0); + writel(val, reg + gpu_clk.common.reg); + /* Enable the lock bits on all PLLs */ for (i = 0; i < ARRAY_SIZE(pll_regs); i++) { val = readl(reg + pll_regs[i]); diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c index e7e3ddf4a227..2f6f02f00be2 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-de2.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-de2.c @@ -53,67 +53,28 @@ static SUNXI_CCU_M(wb_div_a83_clk, "wb-div", "pll-de", 0x0c, 8, 4, static SUNXI_CCU_M(rot_div_a83_clk, "rot-div", "pll-de", 0x0c, 0x0c, 4, CLK_SET_RATE_PARENT); -static struct ccu_common *sun8i_a83t_de2_clks[] = { +static struct ccu_common *sun8i_de2_ccu_clks[] = { &mixer0_clk.common, &mixer1_clk.common, &wb_clk.common, + &rot_clk.common, &bus_mixer0_clk.common, &bus_mixer1_clk.common, &bus_wb_clk.common, + &bus_rot_clk.common, + + &mixer0_div_clk.common, + &mixer1_div_clk.common, + &wb_div_clk.common, + &rot_div_clk.common, &mixer0_div_a83_clk.common, &mixer1_div_a83_clk.common, &wb_div_a83_clk.common, - - &bus_rot_clk.common, - &rot_clk.common, &rot_div_a83_clk.common, }; -static struct ccu_common *sun8i_h3_de2_clks[] = { - &mixer0_clk.common, - &mixer1_clk.common, - &wb_clk.common, - - &bus_mixer0_clk.common, - &bus_mixer1_clk.common, - &bus_wb_clk.common, - - &mixer0_div_clk.common, - &mixer1_div_clk.common, - &wb_div_clk.common, -}; - -static struct ccu_common *sun8i_v3s_de2_clks[] = { - &mixer0_clk.common, - &wb_clk.common, - - &bus_mixer0_clk.common, - &bus_wb_clk.common, - - &mixer0_div_clk.common, - &wb_div_clk.common, -}; - -static struct ccu_common *sun50i_a64_de2_clks[] = { - &mixer0_clk.common, - &mixer1_clk.common, - &wb_clk.common, - - &bus_mixer0_clk.common, - &bus_mixer1_clk.common, - &bus_wb_clk.common, - - &mixer0_div_clk.common, - &mixer1_div_clk.common, - &wb_div_clk.common, - - &bus_rot_clk.common, - &rot_clk.common, - &rot_div_clk.common, -}; - static struct clk_hw_onecell_data sun8i_a83t_de2_hw_clks = { .hws = { [CLK_MIXER0] = &mixer0_clk.common.hw, @@ -219,8 +180,8 @@ static struct ccu_reset_map sun50i_h5_de2_resets[] = { }; static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { - .ccu_clks = sun8i_a83t_de2_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_de2_clks), + .ccu_clks = sun8i_de2_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), .hw_clks = &sun8i_a83t_de2_hw_clks, @@ -229,8 +190,8 @@ static const struct sunxi_ccu_desc sun8i_a83t_de2_clk_desc = { }; static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = { - .ccu_clks = sun8i_h3_de2_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_h3_de2_clks), + .ccu_clks = sun8i_de2_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), .hw_clks = &sun8i_h3_de2_hw_clks, @@ -239,8 +200,8 @@ static const struct sunxi_ccu_desc sun8i_h3_de2_clk_desc = { }; static const struct sunxi_ccu_desc sun8i_r40_de2_clk_desc = { - .ccu_clks = sun50i_a64_de2_clks, - .num_ccu_clks = ARRAY_SIZE(sun50i_a64_de2_clks), + .ccu_clks = sun8i_de2_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), .hw_clks = &sun50i_a64_de2_hw_clks, @@ -249,8 +210,8 @@ static const struct sunxi_ccu_desc sun8i_r40_de2_clk_desc = { }; static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { - .ccu_clks = sun8i_v3s_de2_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_de2_clks), + .ccu_clks = sun8i_de2_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), .hw_clks = &sun8i_v3s_de2_hw_clks, @@ -259,8 +220,8 @@ static const struct sunxi_ccu_desc sun8i_v3s_de2_clk_desc = { }; static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { - .ccu_clks = sun50i_a64_de2_clks, - .num_ccu_clks = ARRAY_SIZE(sun50i_a64_de2_clks), + .ccu_clks = sun8i_de2_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), .hw_clks = &sun50i_a64_de2_hw_clks, @@ -269,8 +230,8 @@ static const struct sunxi_ccu_desc sun50i_a64_de2_clk_desc = { }; static const struct sunxi_ccu_desc sun50i_h5_de2_clk_desc = { - .ccu_clks = sun8i_h3_de2_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_h3_de2_clks), + .ccu_clks = sun8i_de2_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_de2_ccu_clks), .hw_clks = &sun8i_h3_de2_hw_clks, diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c index e058cf691aea..d3fcb983c17c 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-h3.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-h3.c @@ -562,6 +562,7 @@ static struct ccu_common *sun8i_h3_ccu_clks[] = { &bus_uart2_clk.common, &bus_uart3_clk.common, &bus_scr0_clk.common, + &bus_scr1_clk.common, &bus_ephy_clk.common, &bus_dbg_clk.common, &ths_clk.common, @@ -612,114 +613,6 @@ static struct ccu_common *sun8i_h3_ccu_clks[] = { &gpu_clk.common, }; -static struct ccu_common *sun50i_h5_ccu_clks[] = { - &pll_cpux_clk.common, - &pll_audio_base_clk.common, - &pll_video_clk.common, - &pll_ve_clk.common, - &pll_ddr_clk.common, - &pll_periph0_clk.common, - &pll_gpu_clk.common, - &pll_periph1_clk.common, - &pll_de_clk.common, - &cpux_clk.common, - &axi_clk.common, - &ahb1_clk.common, - &apb1_clk.common, - &apb2_clk.common, - &ahb2_clk.common, - &bus_ce_clk.common, - &bus_dma_clk.common, - &bus_mmc0_clk.common, - &bus_mmc1_clk.common, - &bus_mmc2_clk.common, - &bus_nand_clk.common, - &bus_dram_clk.common, - &bus_emac_clk.common, - &bus_ts_clk.common, - &bus_hstimer_clk.common, - &bus_spi0_clk.common, - &bus_spi1_clk.common, - &bus_otg_clk.common, - &bus_ehci0_clk.common, - &bus_ehci1_clk.common, - &bus_ehci2_clk.common, - &bus_ehci3_clk.common, - &bus_ohci0_clk.common, - &bus_ohci1_clk.common, - &bus_ohci2_clk.common, - &bus_ohci3_clk.common, - &bus_ve_clk.common, - &bus_tcon0_clk.common, - &bus_tcon1_clk.common, - &bus_deinterlace_clk.common, - &bus_csi_clk.common, - &bus_tve_clk.common, - &bus_hdmi_clk.common, - &bus_de_clk.common, - &bus_gpu_clk.common, - &bus_msgbox_clk.common, - &bus_spinlock_clk.common, - &bus_codec_clk.common, - &bus_spdif_clk.common, - &bus_pio_clk.common, - &bus_ths_clk.common, - &bus_i2s0_clk.common, - &bus_i2s1_clk.common, - &bus_i2s2_clk.common, - &bus_i2c0_clk.common, - &bus_i2c1_clk.common, - &bus_i2c2_clk.common, - &bus_uart0_clk.common, - &bus_uart1_clk.common, - &bus_uart2_clk.common, - &bus_uart3_clk.common, - &bus_scr0_clk.common, - &bus_scr1_clk.common, - &bus_ephy_clk.common, - &bus_dbg_clk.common, - &ths_clk.common, - &nand_clk.common, - &mmc0_clk.common, - &mmc1_clk.common, - &mmc2_clk.common, - &ts_clk.common, - &ce_clk.common, - &spi0_clk.common, - &spi1_clk.common, - &i2s0_clk.common, - &i2s1_clk.common, - &i2s2_clk.common, - &spdif_clk.common, - &usb_phy0_clk.common, - &usb_phy1_clk.common, - &usb_phy2_clk.common, - &usb_phy3_clk.common, - &usb_ohci0_clk.common, - &usb_ohci1_clk.common, - &usb_ohci2_clk.common, - &usb_ohci3_clk.common, - &dram_clk.common, - &dram_ve_clk.common, - &dram_csi_clk.common, - &dram_deinterlace_clk.common, - &dram_ts_clk.common, - &de_clk.common, - &tcon_clk.common, - &tve_clk.common, - &deinterlace_clk.common, - &csi_misc_clk.common, - &csi_sclk_clk.common, - &csi_mclk_clk.common, - &ve_clk.common, - &ac_dig_clk.common, - &avs_clk.common, - &hdmi_clk.common, - &hdmi_ddc_clk.common, - &mbus_clk.common, - &gpu_clk.common, -}; - static const struct clk_hw *clk_parent_pll_audio[] = { &pll_audio_base_clk.common.hw }; @@ -1116,8 +1009,8 @@ static const struct sunxi_ccu_desc sun8i_h3_ccu_desc = { }; static const struct sunxi_ccu_desc sun50i_h5_ccu_desc = { - .ccu_clks = sun50i_h5_ccu_clks, - .num_ccu_clks = ARRAY_SIZE(sun50i_h5_ccu_clks), + .ccu_clks = sun8i_h3_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_h3_ccu_clks), .hw_clks = &sun50i_h5_hw_clks, diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-r.c b/drivers/clk/sunxi-ng/ccu-sun8i-r.c index 5b7fab832a52..4221649b311f 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-r.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-r.c @@ -114,7 +114,7 @@ static struct ccu_mp a83t_ir_clk = { }, }; -static struct ccu_common *sun8i_a83t_r_ccu_clks[] = { +static struct ccu_common *sun8i_r_ccu_clks[] = { &ar100_clk.common, &apb0_clk.common, &apb0_pio_clk.common, @@ -124,34 +124,10 @@ static struct ccu_common *sun8i_a83t_r_ccu_clks[] = { &apb0_uart_clk.common, &apb0_i2c_clk.common, &apb0_twd_clk.common, + &ir_clk.common, &a83t_ir_clk.common, }; -static struct ccu_common *sun8i_h3_r_ccu_clks[] = { - &ar100_clk.common, - &apb0_clk.common, - &apb0_pio_clk.common, - &apb0_ir_clk.common, - &apb0_timer_clk.common, - &apb0_uart_clk.common, - &apb0_i2c_clk.common, - &apb0_twd_clk.common, - &ir_clk.common, -}; - -static struct ccu_common *sun50i_a64_r_ccu_clks[] = { - &ar100_clk.common, - &apb0_clk.common, - &apb0_pio_clk.common, - &apb0_ir_clk.common, - &apb0_timer_clk.common, - &apb0_rsb_clk.common, - &apb0_uart_clk.common, - &apb0_i2c_clk.common, - &apb0_twd_clk.common, - &ir_clk.common, -}; - static struct clk_hw_onecell_data sun8i_a83t_r_hw_clks = { .hws = { [CLK_AR100] = &ar100_clk.common.hw, @@ -226,8 +202,8 @@ static struct ccu_reset_map sun50i_a64_r_ccu_resets[] = { }; static const struct sunxi_ccu_desc sun8i_a83t_r_ccu_desc = { - .ccu_clks = sun8i_a83t_r_ccu_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_a83t_r_ccu_clks), + .ccu_clks = sun8i_r_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_r_ccu_clks), .hw_clks = &sun8i_a83t_r_hw_clks, @@ -236,8 +212,8 @@ static const struct sunxi_ccu_desc sun8i_a83t_r_ccu_desc = { }; static const struct sunxi_ccu_desc sun8i_h3_r_ccu_desc = { - .ccu_clks = sun8i_h3_r_ccu_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_h3_r_ccu_clks), + .ccu_clks = sun8i_r_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_r_ccu_clks), .hw_clks = &sun8i_h3_r_hw_clks, @@ -246,8 +222,8 @@ static const struct sunxi_ccu_desc sun8i_h3_r_ccu_desc = { }; static const struct sunxi_ccu_desc sun50i_a64_r_ccu_desc = { - .ccu_clks = sun50i_a64_r_ccu_clks, - .num_ccu_clks = ARRAY_SIZE(sun50i_a64_r_ccu_clks), + .ccu_clks = sun8i_r_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_r_ccu_clks), .hw_clks = &sun50i_a64_r_hw_clks, diff --git a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c index 87f87d6ea3ad..fbb3529f0d3e 100644 --- a/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c +++ b/drivers/clk/sunxi-ng/ccu-sun8i-v3s.c @@ -388,82 +388,6 @@ static SUNXI_CCU_M_WITH_MUX_GATE(mipi_csi_clk, "mipi-csi", mipi_csi_parents, 0x16c, 0, 3, 24, 2, BIT(31), 0); static struct ccu_common *sun8i_v3s_ccu_clks[] = { - &pll_cpu_clk.common, - &pll_audio_base_clk.common, - &pll_video_clk.common, - &pll_ve_clk.common, - &pll_ddr0_clk.common, - &pll_periph0_clk.common, - &pll_isp_clk.common, - &pll_periph1_clk.common, - &pll_ddr1_clk.common, - &cpu_clk.common, - &axi_clk.common, - &ahb1_clk.common, - &apb1_clk.common, - &apb2_clk.common, - &ahb2_clk.common, - &bus_ce_clk.common, - &bus_dma_clk.common, - &bus_mmc0_clk.common, - &bus_mmc1_clk.common, - &bus_mmc2_clk.common, - &bus_dram_clk.common, - &bus_emac_clk.common, - &bus_hstimer_clk.common, - &bus_spi0_clk.common, - &bus_otg_clk.common, - &bus_ehci0_clk.common, - &bus_ohci0_clk.common, - &bus_ve_clk.common, - &bus_tcon0_clk.common, - &bus_csi_clk.common, - &bus_de_clk.common, - &bus_codec_clk.common, - &bus_pio_clk.common, - &bus_i2c0_clk.common, - &bus_i2c1_clk.common, - &bus_uart0_clk.common, - &bus_uart1_clk.common, - &bus_uart2_clk.common, - &bus_ephy_clk.common, - &bus_dbg_clk.common, - &mmc0_clk.common, - &mmc0_sample_clk.common, - &mmc0_output_clk.common, - &mmc1_clk.common, - &mmc1_sample_clk.common, - &mmc1_output_clk.common, - &mmc2_clk.common, - &mmc2_sample_clk.common, - &mmc2_output_clk.common, - &ce_clk.common, - &spi0_clk.common, - &usb_phy0_clk.common, - &usb_ohci0_clk.common, - &dram_clk.common, - &dram_ve_clk.common, - &dram_csi_clk.common, - &dram_ohci_clk.common, - &dram_ehci_clk.common, - &de_clk.common, - &tcon_clk.common, - &csi_misc_clk.common, - &csi0_mclk_clk.common, - &csi1_sclk_clk.common, - &csi1_mclk_clk.common, - &ve_clk.common, - &ac_dig_clk.common, - &avs_clk.common, - &mbus_clk.common, - &mipi_csi_clk.common, -}; - -static const struct clk_hw *clk_parent_pll_audio[] = { - &pll_audio_base_clk.common.hw -}; - -static struct ccu_common *sun8i_v3_ccu_clks[] = { &pll_cpu_clk.common, &pll_audio_base_clk.common, &pll_video_clk.common, @@ -537,6 +461,10 @@ static struct ccu_common *sun8i_v3_ccu_clks[] = { &mipi_csi_clk.common, }; +static const struct clk_hw *clk_parent_pll_audio[] = { + &pll_audio_base_clk.common.hw +}; + /* We hardcode the divider to 1 for SDM support */ static CLK_FIXED_FACTOR_HWS(pll_audio_clk, "pll-audio", clk_parent_pll_audio, @@ -798,8 +726,8 @@ static const struct sunxi_ccu_desc sun8i_v3s_ccu_desc = { }; static const struct sunxi_ccu_desc sun8i_v3_ccu_desc = { - .ccu_clks = sun8i_v3_ccu_clks, - .num_ccu_clks = ARRAY_SIZE(sun8i_v3_ccu_clks), + .ccu_clks = sun8i_v3s_ccu_clks, + .num_ccu_clks = ARRAY_SIZE(sun8i_v3s_ccu_clks), .hw_clks = &sun8i_v3_hw_clks, diff --git a/drivers/clk/sunxi/Kconfig b/drivers/clk/sunxi/Kconfig index 3fba3d3ac9a2..1c4e543366dd 100644 --- a/drivers/clk/sunxi/Kconfig +++ b/drivers/clk/sunxi/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menuconfig CLK_SUNXI bool "Legacy clock support for Allwinner SoCs" - depends on ARCH_SUNXI || COMPILE_TEST + depends on (ARM && ARCH_SUNXI) || COMPILE_TEST default y if CLK_SUNXI @@ -19,7 +19,6 @@ config CLK_SUNXI_CLOCKS config CLK_SUNXI_PRCM_SUN6I bool "Legacy A31 PRCM driver" - select MFD_SUN6I_PRCM default y help Legacy clock driver for the A31 PRCM clocks. Those are @@ -27,7 +26,6 @@ config CLK_SUNXI_PRCM_SUN6I config CLK_SUNXI_PRCM_SUN8I bool "Legacy sun8i PRCM driver" - select MFD_SUN6I_PRCM default y help Legacy clock driver for the sun8i family PRCM clocks. diff --git a/drivers/clk/ti/adpll.c b/drivers/clk/ti/adpll.c index 962502ca7ff0..f5e7e2049241 100644 --- a/drivers/clk/ti/adpll.c +++ b/drivers/clk/ti/adpll.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #include #include diff --git a/drivers/clk/ti/apll.c b/drivers/clk/ti/apll.c index e4db6b9a55c6..dd0709c9c249 100644 --- a/drivers/clk/ti/apll.c +++ b/drivers/clk/ti/apll.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP APLL clock support * * Copyright (C) 2013 Texas Instruments, Inc. * * J Keerthy - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/autoidle.c b/drivers/clk/ti/autoidle.c index d6e5f1511ace..27e6b9cb1881 100644 --- a/drivers/clk/ti/autoidle.c +++ b/drivers/clk/ti/autoidle.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI clock autoidle support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clk-2xxx.c b/drivers/clk/ti/clk-2xxx.c index 657c4fe07a95..363c4fdbe01f 100644 --- a/drivers/clk/ti/clk-2xxx.c +++ b/drivers/clk/ti/clk-2xxx.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP2 Clock init * * Copyright (C) 2013 Texas Instruments, Inc * Tero Kristo (t-kristo@ti.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clk-33xx.c b/drivers/clk/ti/clk-33xx.c index b4d142adede4..85c50ea39e6d 100644 --- a/drivers/clk/ti/clk-33xx.c +++ b/drivers/clk/ti/clk-33xx.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * AM33XX Clock init * * Copyright (C) 2013 Texas Instruments, Inc * Tero Kristo (t-kristo@ti.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clk-3xxx.c b/drivers/clk/ti/clk-3xxx.c index 8aa5f5793835..ae943ea63c6c 100644 --- a/drivers/clk/ti/clk-3xxx.c +++ b/drivers/clk/ti/clk-3xxx.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP3 Clock init * * Copyright (C) 2013 Texas Instruments, Inc * Tero Kristo (t-kristo@ti.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clk-43xx.c b/drivers/clk/ti/clk-43xx.c index 2ff4ff3d95d5..f24f6eb2157a 100644 --- a/drivers/clk/ti/clk-43xx.c +++ b/drivers/clk/ti/clk-43xx.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * AM43XX Clock init * * Copyright (C) 2013 Texas Instruments, Inc * Tero Kristo (t-kristo@ti.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clk-44xx.c b/drivers/clk/ti/clk-44xx.c index d078e5d73ed9..868bc7af21b0 100644 --- a/drivers/clk/ti/clk-44xx.c +++ b/drivers/clk/ti/clk-44xx.c @@ -56,7 +56,7 @@ static const struct omap_clkctrl_bit_data omap4_aess_bit_data[] __initconst = { }; static const char * const omap4_func_dmic_abe_gfclk_parents[] __initconst = { - "abe_cm:clk:0018:26", + "abe-clkctrl:0018:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -76,7 +76,7 @@ static const struct omap_clkctrl_bit_data omap4_dmic_bit_data[] __initconst = { }; static const char * const omap4_func_mcasp_abe_gfclk_parents[] __initconst = { - "abe_cm:clk:0020:26", + "abe-clkctrl:0020:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -89,7 +89,7 @@ static const struct omap_clkctrl_bit_data omap4_mcasp_bit_data[] __initconst = { }; static const char * const omap4_func_mcbsp1_gfclk_parents[] __initconst = { - "abe_cm:clk:0028:26", + "abe-clkctrl:0028:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -102,7 +102,7 @@ static const struct omap_clkctrl_bit_data omap4_mcbsp1_bit_data[] __initconst = }; static const char * const omap4_func_mcbsp2_gfclk_parents[] __initconst = { - "abe_cm:clk:0030:26", + "abe-clkctrl:0030:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -115,7 +115,7 @@ static const struct omap_clkctrl_bit_data omap4_mcbsp2_bit_data[] __initconst = }; static const char * const omap4_func_mcbsp3_gfclk_parents[] __initconst = { - "abe_cm:clk:0038:26", + "abe-clkctrl:0038:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -183,18 +183,18 @@ static const struct omap_clkctrl_bit_data omap4_timer8_bit_data[] __initconst = static const struct omap_clkctrl_reg_data omap4_abe_clkctrl_regs[] __initconst = { { OMAP4_L4_ABE_CLKCTRL, NULL, 0, "ocp_abe_iclk" }, - { OMAP4_AESS_CLKCTRL, omap4_aess_bit_data, CLKF_SW_SUP, "abe_cm:clk:0008:24" }, + { OMAP4_AESS_CLKCTRL, omap4_aess_bit_data, CLKF_SW_SUP, "abe-clkctrl:0008:24" }, { OMAP4_MCPDM_CLKCTRL, NULL, CLKF_SW_SUP, "pad_clks_ck" }, - { OMAP4_DMIC_CLKCTRL, omap4_dmic_bit_data, CLKF_SW_SUP, "abe_cm:clk:0018:24" }, - { OMAP4_MCASP_CLKCTRL, omap4_mcasp_bit_data, CLKF_SW_SUP, "abe_cm:clk:0020:24" }, - { OMAP4_MCBSP1_CLKCTRL, omap4_mcbsp1_bit_data, CLKF_SW_SUP, "abe_cm:clk:0028:24" }, - { OMAP4_MCBSP2_CLKCTRL, omap4_mcbsp2_bit_data, CLKF_SW_SUP, "abe_cm:clk:0030:24" }, - { OMAP4_MCBSP3_CLKCTRL, omap4_mcbsp3_bit_data, CLKF_SW_SUP, "abe_cm:clk:0038:24" }, - { OMAP4_SLIMBUS1_CLKCTRL, omap4_slimbus1_bit_data, CLKF_SW_SUP, "abe_cm:clk:0040:8" }, - { OMAP4_TIMER5_CLKCTRL, omap4_timer5_bit_data, CLKF_SW_SUP, "abe_cm:clk:0048:24" }, - { OMAP4_TIMER6_CLKCTRL, omap4_timer6_bit_data, CLKF_SW_SUP, "abe_cm:clk:0050:24" }, - { OMAP4_TIMER7_CLKCTRL, omap4_timer7_bit_data, CLKF_SW_SUP, "abe_cm:clk:0058:24" }, - { OMAP4_TIMER8_CLKCTRL, omap4_timer8_bit_data, CLKF_SW_SUP, "abe_cm:clk:0060:24" }, + { OMAP4_DMIC_CLKCTRL, omap4_dmic_bit_data, CLKF_SW_SUP, "abe-clkctrl:0018:24" }, + { OMAP4_MCASP_CLKCTRL, omap4_mcasp_bit_data, CLKF_SW_SUP, "abe-clkctrl:0020:24" }, + { OMAP4_MCBSP1_CLKCTRL, omap4_mcbsp1_bit_data, CLKF_SW_SUP, "abe-clkctrl:0028:24" }, + { OMAP4_MCBSP2_CLKCTRL, omap4_mcbsp2_bit_data, CLKF_SW_SUP, "abe-clkctrl:0030:24" }, + { OMAP4_MCBSP3_CLKCTRL, omap4_mcbsp3_bit_data, CLKF_SW_SUP, "abe-clkctrl:0038:24" }, + { OMAP4_SLIMBUS1_CLKCTRL, omap4_slimbus1_bit_data, CLKF_SW_SUP, "abe-clkctrl:0040:8" }, + { OMAP4_TIMER5_CLKCTRL, omap4_timer5_bit_data, CLKF_SW_SUP, "abe-clkctrl:0048:24" }, + { OMAP4_TIMER6_CLKCTRL, omap4_timer6_bit_data, CLKF_SW_SUP, "abe-clkctrl:0050:24" }, + { OMAP4_TIMER7_CLKCTRL, omap4_timer7_bit_data, CLKF_SW_SUP, "abe-clkctrl:0058:24" }, + { OMAP4_TIMER8_CLKCTRL, omap4_timer8_bit_data, CLKF_SW_SUP, "abe-clkctrl:0060:24" }, { OMAP4_WD_TIMER3_CLKCTRL, NULL, CLKF_SW_SUP, "sys_32k_ck" }, { 0 }, }; @@ -287,7 +287,7 @@ static const struct omap_clkctrl_bit_data omap4_fdif_bit_data[] __initconst = { static const struct omap_clkctrl_reg_data omap4_iss_clkctrl_regs[] __initconst = { { OMAP4_ISS_CLKCTRL, omap4_iss_bit_data, CLKF_SW_SUP, "ducati_clk_mux_ck" }, - { OMAP4_FDIF_CLKCTRL, omap4_fdif_bit_data, CLKF_SW_SUP, "iss_cm:clk:0008:24" }, + { OMAP4_FDIF_CLKCTRL, omap4_fdif_bit_data, CLKF_SW_SUP, "iss-clkctrl:0008:24" }, { 0 }, }; @@ -320,7 +320,7 @@ static const struct omap_clkctrl_bit_data omap4_dss_core_bit_data[] __initconst }; static const struct omap_clkctrl_reg_data omap4_l3_dss_clkctrl_regs[] __initconst = { - { OMAP4_DSS_CORE_CLKCTRL, omap4_dss_core_bit_data, CLKF_SW_SUP, "l3_dss_cm:clk:0000:8" }, + { OMAP4_DSS_CORE_CLKCTRL, omap4_dss_core_bit_data, CLKF_SW_SUP, "l3-dss-clkctrl:0000:8" }, { 0 }, }; @@ -336,7 +336,7 @@ static const struct omap_clkctrl_bit_data omap4_gpu_bit_data[] __initconst = { }; static const struct omap_clkctrl_reg_data omap4_l3_gfx_clkctrl_regs[] __initconst = { - { OMAP4_GPU_CLKCTRL, omap4_gpu_bit_data, CLKF_SW_SUP, "l3_gfx_cm:clk:0000:24" }, + { OMAP4_GPU_CLKCTRL, omap4_gpu_bit_data, CLKF_SW_SUP, "l3-gfx-clkctrl:0000:24" }, { 0 }, }; @@ -372,12 +372,12 @@ static const struct omap_clkctrl_bit_data omap4_hsi_bit_data[] __initconst = { }; static const char * const omap4_usb_host_hs_utmi_p1_clk_parents[] __initconst = { - "l3_init_cm:clk:0038:24", + "l3-init-clkctrl:0038:24", NULL, }; static const char * const omap4_usb_host_hs_utmi_p2_clk_parents[] __initconst = { - "l3_init_cm:clk:0038:25", + "l3-init-clkctrl:0038:25", NULL, }; @@ -418,7 +418,7 @@ static const struct omap_clkctrl_bit_data omap4_usb_host_hs_bit_data[] __initcon }; static const char * const omap4_usb_otg_hs_xclk_parents[] __initconst = { - "l3_init_cm:clk:0040:24", + "l3-init-clkctrl:0040:24", NULL, }; @@ -452,14 +452,14 @@ static const struct omap_clkctrl_bit_data omap4_ocp2scp_usb_phy_bit_data[] __ini }; static const struct omap_clkctrl_reg_data omap4_l3_init_clkctrl_regs[] __initconst = { - { OMAP4_MMC1_CLKCTRL, omap4_mmc1_bit_data, CLKF_SW_SUP, "l3_init_cm:clk:0008:24" }, - { OMAP4_MMC2_CLKCTRL, omap4_mmc2_bit_data, CLKF_SW_SUP, "l3_init_cm:clk:0010:24" }, - { OMAP4_HSI_CLKCTRL, omap4_hsi_bit_data, CLKF_HW_SUP, "l3_init_cm:clk:0018:24" }, + { OMAP4_MMC1_CLKCTRL, omap4_mmc1_bit_data, CLKF_SW_SUP, "l3-init-clkctrl:0008:24" }, + { OMAP4_MMC2_CLKCTRL, omap4_mmc2_bit_data, CLKF_SW_SUP, "l3-init-clkctrl:0010:24" }, + { OMAP4_HSI_CLKCTRL, omap4_hsi_bit_data, CLKF_HW_SUP, "l3-init-clkctrl:0018:24" }, { OMAP4_USB_HOST_HS_CLKCTRL, omap4_usb_host_hs_bit_data, CLKF_SW_SUP, "init_60m_fclk" }, { OMAP4_USB_OTG_HS_CLKCTRL, omap4_usb_otg_hs_bit_data, CLKF_HW_SUP, "l3_div_ck" }, { OMAP4_USB_TLL_HS_CLKCTRL, omap4_usb_tll_hs_bit_data, CLKF_HW_SUP, "l4_div_ck" }, { OMAP4_USB_HOST_FS_CLKCTRL, NULL, CLKF_SW_SUP, "func_48mc_fclk" }, - { OMAP4_OCP2SCP_USB_PHY_CLKCTRL, omap4_ocp2scp_usb_phy_bit_data, CLKF_HW_SUP, "l3_init_cm:clk:00c0:8" }, + { OMAP4_OCP2SCP_USB_PHY_CLKCTRL, omap4_ocp2scp_usb_phy_bit_data, CLKF_HW_SUP, "l3-init-clkctrl:00c0:8" }, { 0 }, }; @@ -530,7 +530,7 @@ static const struct omap_clkctrl_bit_data omap4_gpio6_bit_data[] __initconst = { }; static const char * const omap4_per_mcbsp4_gfclk_parents[] __initconst = { - "l4_per_cm:clk:00c0:26", + "l4-per-clkctrl:00c0:26", "pad_clks_ck", NULL, }; @@ -570,12 +570,12 @@ static const struct omap_clkctrl_bit_data omap4_slimbus2_bit_data[] __initconst }; static const struct omap_clkctrl_reg_data omap4_l4_per_clkctrl_regs[] __initconst = { - { OMAP4_TIMER10_CLKCTRL, omap4_timer10_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0008:24" }, - { OMAP4_TIMER11_CLKCTRL, omap4_timer11_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0010:24" }, - { OMAP4_TIMER2_CLKCTRL, omap4_timer2_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0018:24" }, - { OMAP4_TIMER3_CLKCTRL, omap4_timer3_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0020:24" }, - { OMAP4_TIMER4_CLKCTRL, omap4_timer4_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0028:24" }, - { OMAP4_TIMER9_CLKCTRL, omap4_timer9_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0030:24" }, + { OMAP4_TIMER10_CLKCTRL, omap4_timer10_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0008:24" }, + { OMAP4_TIMER11_CLKCTRL, omap4_timer11_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0010:24" }, + { OMAP4_TIMER2_CLKCTRL, omap4_timer2_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0018:24" }, + { OMAP4_TIMER3_CLKCTRL, omap4_timer3_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0020:24" }, + { OMAP4_TIMER4_CLKCTRL, omap4_timer4_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0028:24" }, + { OMAP4_TIMER9_CLKCTRL, omap4_timer9_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0030:24" }, { OMAP4_ELM_CLKCTRL, NULL, 0, "l4_div_ck" }, { OMAP4_GPIO2_CLKCTRL, omap4_gpio2_bit_data, CLKF_HW_SUP, "l4_div_ck" }, { OMAP4_GPIO3_CLKCTRL, omap4_gpio3_bit_data, CLKF_HW_SUP, "l4_div_ck" }, @@ -588,14 +588,14 @@ static const struct omap_clkctrl_reg_data omap4_l4_per_clkctrl_regs[] __initcons { OMAP4_I2C3_CLKCTRL, NULL, CLKF_SW_SUP, "func_96m_fclk" }, { OMAP4_I2C4_CLKCTRL, NULL, CLKF_SW_SUP, "func_96m_fclk" }, { OMAP4_L4_PER_CLKCTRL, NULL, 0, "l4_div_ck" }, - { OMAP4_MCBSP4_CLKCTRL, omap4_mcbsp4_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:00c0:24" }, + { OMAP4_MCBSP4_CLKCTRL, omap4_mcbsp4_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:00c0:24" }, { OMAP4_MCSPI1_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_MCSPI2_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_MCSPI3_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_MCSPI4_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_MMC3_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_MMC4_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, - { OMAP4_SLIMBUS2_CLKCTRL, omap4_slimbus2_bit_data, CLKF_SW_SUP, "l4_per_cm:clk:0118:8" }, + { OMAP4_SLIMBUS2_CLKCTRL, omap4_slimbus2_bit_data, CLKF_SW_SUP, "l4-per-clkctrl:0118:8" }, { OMAP4_UART1_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_UART2_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, { OMAP4_UART3_CLKCTRL, NULL, CLKF_SW_SUP, "func_48m_fclk" }, @@ -630,7 +630,7 @@ static const struct omap_clkctrl_reg_data omap4_l4_wkup_clkctrl_regs[] __initcon { OMAP4_L4_WKUP_CLKCTRL, NULL, 0, "l4_wkup_clk_mux_ck" }, { OMAP4_WD_TIMER2_CLKCTRL, NULL, CLKF_SW_SUP, "sys_32k_ck" }, { OMAP4_GPIO1_CLKCTRL, omap4_gpio1_bit_data, CLKF_HW_SUP, "l4_wkup_clk_mux_ck" }, - { OMAP4_TIMER1_CLKCTRL, omap4_timer1_bit_data, CLKF_SW_SUP, "l4_wkup_cm:clk:0020:24" }, + { OMAP4_TIMER1_CLKCTRL, omap4_timer1_bit_data, CLKF_SW_SUP, "l4-wkup-clkctrl:0020:24" }, { OMAP4_COUNTER_32K_CLKCTRL, NULL, 0, "sys_32k_ck" }, { OMAP4_KBD_CLKCTRL, NULL, CLKF_SW_SUP, "sys_32k_ck" }, { 0 }, @@ -644,7 +644,7 @@ static const char * const omap4_pmd_stm_clock_mux_ck_parents[] __initconst = { }; static const char * const omap4_trace_clk_div_div_ck_parents[] __initconst = { - "emu_sys_cm:clk:0000:22", + "emu-sys-clkctrl:0000:22", NULL, }; @@ -662,7 +662,7 @@ static const struct omap_clkctrl_div_data omap4_trace_clk_div_div_ck_data __init }; static const char * const omap4_stm_clk_div_ck_parents[] __initconst = { - "emu_sys_cm:clk:0000:20", + "emu-sys-clkctrl:0000:20", NULL, }; @@ -716,73 +716,73 @@ static struct ti_dt_clk omap44xx_clks[] = { * hwmod support. Once hwmod is removed, these can be removed * also. */ - DT_CLK(NULL, "aess_fclk", "abe_cm:0008:24"), - DT_CLK(NULL, "cm2_dm10_mux", "l4_per_cm:0008:24"), - DT_CLK(NULL, "cm2_dm11_mux", "l4_per_cm:0010:24"), - DT_CLK(NULL, "cm2_dm2_mux", "l4_per_cm:0018:24"), - DT_CLK(NULL, "cm2_dm3_mux", "l4_per_cm:0020:24"), - DT_CLK(NULL, "cm2_dm4_mux", "l4_per_cm:0028:24"), - DT_CLK(NULL, "cm2_dm9_mux", "l4_per_cm:0030:24"), - DT_CLK(NULL, "dmic_sync_mux_ck", "abe_cm:0018:26"), - DT_CLK(NULL, "dmt1_clk_mux", "l4_wkup_cm:0020:24"), - DT_CLK(NULL, "dss_48mhz_clk", "l3_dss_cm:0000:9"), - DT_CLK(NULL, "dss_dss_clk", "l3_dss_cm:0000:8"), - DT_CLK(NULL, "dss_sys_clk", "l3_dss_cm:0000:10"), - DT_CLK(NULL, "dss_tv_clk", "l3_dss_cm:0000:11"), - DT_CLK(NULL, "fdif_fck", "iss_cm:0008:24"), - DT_CLK(NULL, "func_dmic_abe_gfclk", "abe_cm:0018:24"), - DT_CLK(NULL, "func_mcasp_abe_gfclk", "abe_cm:0020:24"), - DT_CLK(NULL, "func_mcbsp1_gfclk", "abe_cm:0028:24"), - DT_CLK(NULL, "func_mcbsp2_gfclk", "abe_cm:0030:24"), - DT_CLK(NULL, "func_mcbsp3_gfclk", "abe_cm:0038:24"), - DT_CLK(NULL, "gpio1_dbclk", "l4_wkup_cm:0018:8"), - DT_CLK(NULL, "gpio2_dbclk", "l4_per_cm:0040:8"), - DT_CLK(NULL, "gpio3_dbclk", "l4_per_cm:0048:8"), - DT_CLK(NULL, "gpio4_dbclk", "l4_per_cm:0050:8"), - DT_CLK(NULL, "gpio5_dbclk", "l4_per_cm:0058:8"), - DT_CLK(NULL, "gpio6_dbclk", "l4_per_cm:0060:8"), - DT_CLK(NULL, "hsi_fck", "l3_init_cm:0018:24"), - DT_CLK(NULL, "hsmmc1_fclk", "l3_init_cm:0008:24"), - DT_CLK(NULL, "hsmmc2_fclk", "l3_init_cm:0010:24"), - DT_CLK(NULL, "iss_ctrlclk", "iss_cm:0000:8"), - DT_CLK(NULL, "mcasp_sync_mux_ck", "abe_cm:0020:26"), - DT_CLK(NULL, "mcbsp1_sync_mux_ck", "abe_cm:0028:26"), - DT_CLK(NULL, "mcbsp2_sync_mux_ck", "abe_cm:0030:26"), - DT_CLK(NULL, "mcbsp3_sync_mux_ck", "abe_cm:0038:26"), - DT_CLK(NULL, "mcbsp4_sync_mux_ck", "l4_per_cm:00c0:26"), - DT_CLK(NULL, "ocp2scp_usb_phy_phy_48m", "l3_init_cm:00c0:8"), - DT_CLK(NULL, "otg_60m_gfclk", "l3_init_cm:0040:24"), - DT_CLK(NULL, "per_mcbsp4_gfclk", "l4_per_cm:00c0:24"), - DT_CLK(NULL, "pmd_stm_clock_mux_ck", "emu_sys_cm:0000:20"), - DT_CLK(NULL, "pmd_trace_clk_mux_ck", "emu_sys_cm:0000:22"), - DT_CLK(NULL, "sgx_clk_mux", "l3_gfx_cm:0000:24"), - DT_CLK(NULL, "slimbus1_fclk_0", "abe_cm:0040:8"), - DT_CLK(NULL, "slimbus1_fclk_1", "abe_cm:0040:9"), - DT_CLK(NULL, "slimbus1_fclk_2", "abe_cm:0040:10"), - DT_CLK(NULL, "slimbus1_slimbus_clk", "abe_cm:0040:11"), - DT_CLK(NULL, "slimbus2_fclk_0", "l4_per_cm:0118:8"), - DT_CLK(NULL, "slimbus2_fclk_1", "l4_per_cm:0118:9"), - DT_CLK(NULL, "slimbus2_slimbus_clk", "l4_per_cm:0118:10"), - DT_CLK(NULL, "stm_clk_div_ck", "emu_sys_cm:0000:27"), - DT_CLK(NULL, "timer5_sync_mux", "abe_cm:0048:24"), - DT_CLK(NULL, "timer6_sync_mux", "abe_cm:0050:24"), - DT_CLK(NULL, "timer7_sync_mux", "abe_cm:0058:24"), - DT_CLK(NULL, "timer8_sync_mux", "abe_cm:0060:24"), - DT_CLK(NULL, "trace_clk_div_div_ck", "emu_sys_cm:0000:24"), - DT_CLK(NULL, "usb_host_hs_func48mclk", "l3_init_cm:0038:15"), - DT_CLK(NULL, "usb_host_hs_hsic480m_p1_clk", "l3_init_cm:0038:13"), - DT_CLK(NULL, "usb_host_hs_hsic480m_p2_clk", "l3_init_cm:0038:14"), - DT_CLK(NULL, "usb_host_hs_hsic60m_p1_clk", "l3_init_cm:0038:11"), - DT_CLK(NULL, "usb_host_hs_hsic60m_p2_clk", "l3_init_cm:0038:12"), - DT_CLK(NULL, "usb_host_hs_utmi_p1_clk", "l3_init_cm:0038:8"), - DT_CLK(NULL, "usb_host_hs_utmi_p2_clk", "l3_init_cm:0038:9"), - DT_CLK(NULL, "usb_host_hs_utmi_p3_clk", "l3_init_cm:0038:10"), - DT_CLK(NULL, "usb_otg_hs_xclk", "l3_init_cm:0040:8"), - DT_CLK(NULL, "usb_tll_hs_usb_ch0_clk", "l3_init_cm:0048:8"), - DT_CLK(NULL, "usb_tll_hs_usb_ch1_clk", "l3_init_cm:0048:9"), - DT_CLK(NULL, "usb_tll_hs_usb_ch2_clk", "l3_init_cm:0048:10"), - DT_CLK(NULL, "utmi_p1_gfclk", "l3_init_cm:0038:24"), - DT_CLK(NULL, "utmi_p2_gfclk", "l3_init_cm:0038:25"), + DT_CLK(NULL, "aess_fclk", "abe-clkctrl:0008:24"), + DT_CLK(NULL, "cm2_dm10_mux", "l4-per-clkctrl:0008:24"), + DT_CLK(NULL, "cm2_dm11_mux", "l4-per-clkctrl:0010:24"), + DT_CLK(NULL, "cm2_dm2_mux", "l4-per-clkctrl:0018:24"), + DT_CLK(NULL, "cm2_dm3_mux", "l4-per-clkctrl:0020:24"), + DT_CLK(NULL, "cm2_dm4_mux", "l4-per-clkctrl:0028:24"), + DT_CLK(NULL, "cm2_dm9_mux", "l4-per-clkctrl:0030:24"), + DT_CLK(NULL, "dmic_sync_mux_ck", "abe-clkctrl:0018:26"), + DT_CLK(NULL, "dmt1_clk_mux", "l4-wkup-clkctrl:0020:24"), + DT_CLK(NULL, "dss_48mhz_clk", "l3-dss-clkctrl:0000:9"), + DT_CLK(NULL, "dss_dss_clk", "l3-dss-clkctrl:0000:8"), + DT_CLK(NULL, "dss_sys_clk", "l3-dss-clkctrl:0000:10"), + DT_CLK(NULL, "dss_tv_clk", "l3-dss-clkctrl:0000:11"), + DT_CLK(NULL, "fdif_fck", "iss-clkctrl:0008:24"), + DT_CLK(NULL, "func_dmic_abe_gfclk", "abe-clkctrl:0018:24"), + DT_CLK(NULL, "func_mcasp_abe_gfclk", "abe-clkctrl:0020:24"), + DT_CLK(NULL, "func_mcbsp1_gfclk", "abe-clkctrl:0028:24"), + DT_CLK(NULL, "func_mcbsp2_gfclk", "abe-clkctrl:0030:24"), + DT_CLK(NULL, "func_mcbsp3_gfclk", "abe-clkctrl:0038:24"), + DT_CLK(NULL, "gpio1_dbclk", "l4-wkup-clkctrl:0018:8"), + DT_CLK(NULL, "gpio2_dbclk", "l4-per-clkctrl:0040:8"), + DT_CLK(NULL, "gpio3_dbclk", "l4-per-clkctrl:0048:8"), + DT_CLK(NULL, "gpio4_dbclk", "l4-per-clkctrl:0050:8"), + DT_CLK(NULL, "gpio5_dbclk", "l4-per-clkctrl:0058:8"), + DT_CLK(NULL, "gpio6_dbclk", "l4-per-clkctrl:0060:8"), + DT_CLK(NULL, "hsi_fck", "l3-init-clkctrl:0018:24"), + DT_CLK(NULL, "hsmmc1_fclk", "l3-init-clkctrl:0008:24"), + DT_CLK(NULL, "hsmmc2_fclk", "l3-init-clkctrl:0010:24"), + DT_CLK(NULL, "iss_ctrlclk", "iss-clkctrl:0000:8"), + DT_CLK(NULL, "mcasp_sync_mux_ck", "abe-clkctrl:0020:26"), + DT_CLK(NULL, "mcbsp1_sync_mux_ck", "abe-clkctrl:0028:26"), + DT_CLK(NULL, "mcbsp2_sync_mux_ck", "abe-clkctrl:0030:26"), + DT_CLK(NULL, "mcbsp3_sync_mux_ck", "abe-clkctrl:0038:26"), + DT_CLK(NULL, "mcbsp4_sync_mux_ck", "l4-per-clkctrl:00c0:26"), + DT_CLK(NULL, "ocp2scp_usb_phy_phy_48m", "l3-init-clkctrl:00c0:8"), + DT_CLK(NULL, "otg_60m_gfclk", "l3-init-clkctrl:0040:24"), + DT_CLK(NULL, "per_mcbsp4_gfclk", "l4-per-clkctrl:00c0:24"), + DT_CLK(NULL, "pmd_stm_clock_mux_ck", "emu-sys-clkctrl:0000:20"), + DT_CLK(NULL, "pmd_trace_clk_mux_ck", "emu-sys-clkctrl:0000:22"), + DT_CLK(NULL, "sgx_clk_mux", "l3-gfx-clkctrl:0000:24"), + DT_CLK(NULL, "slimbus1_fclk_0", "abe-clkctrl:0040:8"), + DT_CLK(NULL, "slimbus1_fclk_1", "abe-clkctrl:0040:9"), + DT_CLK(NULL, "slimbus1_fclk_2", "abe-clkctrl:0040:10"), + DT_CLK(NULL, "slimbus1_slimbus_clk", "abe-clkctrl:0040:11"), + DT_CLK(NULL, "slimbus2_fclk_0", "l4-per-clkctrl:0118:8"), + DT_CLK(NULL, "slimbus2_fclk_1", "l4-per-clkctrl:0118:9"), + DT_CLK(NULL, "slimbus2_slimbus_clk", "l4-per-clkctrl:0118:10"), + DT_CLK(NULL, "stm_clk_div_ck", "emu-sys-clkctrl:0000:27"), + DT_CLK(NULL, "timer5_sync_mux", "abe-clkctrl:0048:24"), + DT_CLK(NULL, "timer6_sync_mux", "abe-clkctrl:0050:24"), + DT_CLK(NULL, "timer7_sync_mux", "abe-clkctrl:0058:24"), + DT_CLK(NULL, "timer8_sync_mux", "abe-clkctrl:0060:24"), + DT_CLK(NULL, "trace_clk_div_div_ck", "emu-sys-clkctrl:0000:24"), + DT_CLK(NULL, "usb_host_hs_func48mclk", "l3-init-clkctrl:0038:15"), + DT_CLK(NULL, "usb_host_hs_hsic480m_p1_clk", "l3-init-clkctrl:0038:13"), + DT_CLK(NULL, "usb_host_hs_hsic480m_p2_clk", "l3-init-clkctrl:0038:14"), + DT_CLK(NULL, "usb_host_hs_hsic60m_p1_clk", "l3-init-clkctrl:0038:11"), + DT_CLK(NULL, "usb_host_hs_hsic60m_p2_clk", "l3-init-clkctrl:0038:12"), + DT_CLK(NULL, "usb_host_hs_utmi_p1_clk", "l3-init-clkctrl:0038:8"), + DT_CLK(NULL, "usb_host_hs_utmi_p2_clk", "l3-init-clkctrl:0038:9"), + DT_CLK(NULL, "usb_host_hs_utmi_p3_clk", "l3_init-clkctrl:0038:10"), + DT_CLK(NULL, "usb_otg_hs_xclk", "l3-init-clkctrl:0040:8"), + DT_CLK(NULL, "usb_tll_hs_usb_ch0_clk", "l3-init-clkctrl:0048:8"), + DT_CLK(NULL, "usb_tll_hs_usb_ch1_clk", "l3-init-clkctrl:0048:9"), + DT_CLK(NULL, "usb_tll_hs_usb_ch2_clk", "l3-init-clkctrl:0048:10"), + DT_CLK(NULL, "utmi_p1_gfclk", "l3-init-clkctrl:0038:24"), + DT_CLK(NULL, "utmi_p2_gfclk", "l3-init-clkctrl:0038:25"), { .node_name = NULL }, }; diff --git a/drivers/clk/ti/clk-54xx.c b/drivers/clk/ti/clk-54xx.c index 90e0a9ea6351..b4aff76eb373 100644 --- a/drivers/clk/ti/clk-54xx.c +++ b/drivers/clk/ti/clk-54xx.c @@ -50,7 +50,7 @@ static const struct omap_clkctrl_bit_data omap5_aess_bit_data[] __initconst = { }; static const char * const omap5_dmic_gfclk_parents[] __initconst = { - "abe_cm:clk:0018:26", + "abe-clkctrl:0018:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -70,7 +70,7 @@ static const struct omap_clkctrl_bit_data omap5_dmic_bit_data[] __initconst = { }; static const char * const omap5_mcbsp1_gfclk_parents[] __initconst = { - "abe_cm:clk:0028:26", + "abe-clkctrl:0028:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -83,7 +83,7 @@ static const struct omap_clkctrl_bit_data omap5_mcbsp1_bit_data[] __initconst = }; static const char * const omap5_mcbsp2_gfclk_parents[] __initconst = { - "abe_cm:clk:0030:26", + "abe-clkctrl:0030:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -96,7 +96,7 @@ static const struct omap_clkctrl_bit_data omap5_mcbsp2_bit_data[] __initconst = }; static const char * const omap5_mcbsp3_gfclk_parents[] __initconst = { - "abe_cm:clk:0038:26", + "abe-clkctrl:0038:26", "pad_clks_ck", "slimbus_clk", NULL, @@ -136,16 +136,16 @@ static const struct omap_clkctrl_bit_data omap5_timer8_bit_data[] __initconst = static const struct omap_clkctrl_reg_data omap5_abe_clkctrl_regs[] __initconst = { { OMAP5_L4_ABE_CLKCTRL, NULL, 0, "abe_iclk" }, - { OMAP5_AESS_CLKCTRL, omap5_aess_bit_data, CLKF_SW_SUP, "abe_cm:clk:0008:24" }, + { OMAP5_AESS_CLKCTRL, omap5_aess_bit_data, CLKF_SW_SUP, "abe-clkctrl:0008:24" }, { OMAP5_MCPDM_CLKCTRL, NULL, CLKF_SW_SUP, "pad_clks_ck" }, - { OMAP5_DMIC_CLKCTRL, omap5_dmic_bit_data, CLKF_SW_SUP, "abe_cm:clk:0018:24" }, - { OMAP5_MCBSP1_CLKCTRL, omap5_mcbsp1_bit_data, CLKF_SW_SUP, "abe_cm:clk:0028:24" }, - { OMAP5_MCBSP2_CLKCTRL, omap5_mcbsp2_bit_data, CLKF_SW_SUP, "abe_cm:clk:0030:24" }, - { OMAP5_MCBSP3_CLKCTRL, omap5_mcbsp3_bit_data, CLKF_SW_SUP, "abe_cm:clk:0038:24" }, - { OMAP5_TIMER5_CLKCTRL, omap5_timer5_bit_data, CLKF_SW_SUP, "abe_cm:clk:0048:24" }, - { OMAP5_TIMER6_CLKCTRL, omap5_timer6_bit_data, CLKF_SW_SUP, "abe_cm:clk:0050:24" }, - { OMAP5_TIMER7_CLKCTRL, omap5_timer7_bit_data, CLKF_SW_SUP, "abe_cm:clk:0058:24" }, - { OMAP5_TIMER8_CLKCTRL, omap5_timer8_bit_data, CLKF_SW_SUP, "abe_cm:clk:0060:24" }, + { OMAP5_DMIC_CLKCTRL, omap5_dmic_bit_data, CLKF_SW_SUP, "abe-clkctrl:0018:24" }, + { OMAP5_MCBSP1_CLKCTRL, omap5_mcbsp1_bit_data, CLKF_SW_SUP, "abe-clkctrl:0028:24" }, + { OMAP5_MCBSP2_CLKCTRL, omap5_mcbsp2_bit_data, CLKF_SW_SUP, "abe-clkctrl:0030:24" }, + { OMAP5_MCBSP3_CLKCTRL, omap5_mcbsp3_bit_data, CLKF_SW_SUP, "abe-clkctrl:0038:24" }, + { OMAP5_TIMER5_CLKCTRL, omap5_timer5_bit_data, CLKF_SW_SUP, "abe-clkctrl:0048:24" }, + { OMAP5_TIMER6_CLKCTRL, omap5_timer6_bit_data, CLKF_SW_SUP, "abe-clkctrl:0050:24" }, + { OMAP5_TIMER7_CLKCTRL, omap5_timer7_bit_data, CLKF_SW_SUP, "abe-clkctrl:0058:24" }, + { OMAP5_TIMER8_CLKCTRL, omap5_timer8_bit_data, CLKF_SW_SUP, "abe-clkctrl:0060:24" }, { 0 }, }; @@ -268,12 +268,12 @@ static const struct omap_clkctrl_bit_data omap5_gpio8_bit_data[] __initconst = { }; static const struct omap_clkctrl_reg_data omap5_l4per_clkctrl_regs[] __initconst = { - { OMAP5_TIMER10_CLKCTRL, omap5_timer10_bit_data, CLKF_SW_SUP, "l4per_cm:clk:0008:24" }, - { OMAP5_TIMER11_CLKCTRL, omap5_timer11_bit_data, CLKF_SW_SUP, "l4per_cm:clk:0010:24" }, - { OMAP5_TIMER2_CLKCTRL, omap5_timer2_bit_data, CLKF_SW_SUP, "l4per_cm:clk:0018:24" }, - { OMAP5_TIMER3_CLKCTRL, omap5_timer3_bit_data, CLKF_SW_SUP, "l4per_cm:clk:0020:24" }, - { OMAP5_TIMER4_CLKCTRL, omap5_timer4_bit_data, CLKF_SW_SUP, "l4per_cm:clk:0028:24" }, - { OMAP5_TIMER9_CLKCTRL, omap5_timer9_bit_data, CLKF_SW_SUP, "l4per_cm:clk:0030:24" }, + { OMAP5_TIMER10_CLKCTRL, omap5_timer10_bit_data, CLKF_SW_SUP, "l4per-clkctrl:0008:24" }, + { OMAP5_TIMER11_CLKCTRL, omap5_timer11_bit_data, CLKF_SW_SUP, "l4per-clkctrl:0010:24" }, + { OMAP5_TIMER2_CLKCTRL, omap5_timer2_bit_data, CLKF_SW_SUP, "l4per-clkctrl:0018:24" }, + { OMAP5_TIMER3_CLKCTRL, omap5_timer3_bit_data, CLKF_SW_SUP, "l4per-clkctrl:0020:24" }, + { OMAP5_TIMER4_CLKCTRL, omap5_timer4_bit_data, CLKF_SW_SUP, "l4per-clkctrl:0028:24" }, + { OMAP5_TIMER9_CLKCTRL, omap5_timer9_bit_data, CLKF_SW_SUP, "l4per-clkctrl:0030:24" }, { OMAP5_GPIO2_CLKCTRL, omap5_gpio2_bit_data, CLKF_HW_SUP, "l4_root_clk_div" }, { OMAP5_GPIO3_CLKCTRL, omap5_gpio3_bit_data, CLKF_HW_SUP, "l4_root_clk_div" }, { OMAP5_GPIO4_CLKCTRL, omap5_gpio4_bit_data, CLKF_HW_SUP, "l4_root_clk_div" }, @@ -345,7 +345,7 @@ static const struct omap_clkctrl_bit_data omap5_dss_core_bit_data[] __initconst }; static const struct omap_clkctrl_reg_data omap5_dss_clkctrl_regs[] __initconst = { - { OMAP5_DSS_CORE_CLKCTRL, omap5_dss_core_bit_data, CLKF_SW_SUP, "dss_cm:clk:0000:8" }, + { OMAP5_DSS_CORE_CLKCTRL, omap5_dss_core_bit_data, CLKF_SW_SUP, "dss-clkctrl:0000:8" }, { 0 }, }; @@ -378,7 +378,7 @@ static const struct omap_clkctrl_bit_data omap5_gpu_core_bit_data[] __initconst }; static const struct omap_clkctrl_reg_data omap5_gpu_clkctrl_regs[] __initconst = { - { OMAP5_GPU_CLKCTRL, omap5_gpu_core_bit_data, CLKF_SW_SUP, "gpu_cm:clk:0000:24" }, + { OMAP5_GPU_CLKCTRL, omap5_gpu_core_bit_data, CLKF_SW_SUP, "gpu-clkctrl:0000:24" }, { 0 }, }; @@ -389,7 +389,7 @@ static const char * const omap5_mmc1_fclk_mux_parents[] __initconst = { }; static const char * const omap5_mmc1_fclk_parents[] __initconst = { - "l3init_cm:clk:0008:24", + "l3init-clkctrl:0008:24", NULL, }; @@ -405,7 +405,7 @@ static const struct omap_clkctrl_bit_data omap5_mmc1_bit_data[] __initconst = { }; static const char * const omap5_mmc2_fclk_parents[] __initconst = { - "l3init_cm:clk:0010:24", + "l3init-clkctrl:0010:24", NULL, }; @@ -430,12 +430,12 @@ static const char * const omap5_usb_host_hs_hsic480m_p3_clk_parents[] __initcons }; static const char * const omap5_usb_host_hs_utmi_p1_clk_parents[] __initconst = { - "l3init_cm:clk:0038:24", + "l3init-clkctrl:0038:24", NULL, }; static const char * const omap5_usb_host_hs_utmi_p2_clk_parents[] __initconst = { - "l3init_cm:clk:0038:25", + "l3init-clkctrl:0038:25", NULL, }; @@ -494,8 +494,8 @@ static const struct omap_clkctrl_bit_data omap5_usb_otg_ss_bit_data[] __initcons }; static const struct omap_clkctrl_reg_data omap5_l3init_clkctrl_regs[] __initconst = { - { OMAP5_MMC1_CLKCTRL, omap5_mmc1_bit_data, CLKF_SW_SUP, "l3init_cm:clk:0008:25" }, - { OMAP5_MMC2_CLKCTRL, omap5_mmc2_bit_data, CLKF_SW_SUP, "l3init_cm:clk:0010:25" }, + { OMAP5_MMC1_CLKCTRL, omap5_mmc1_bit_data, CLKF_SW_SUP, "l3init-clkctrl:0008:25" }, + { OMAP5_MMC2_CLKCTRL, omap5_mmc2_bit_data, CLKF_SW_SUP, "l3init-clkctrl:0010:25" }, { OMAP5_USB_HOST_HS_CLKCTRL, omap5_usb_host_hs_bit_data, CLKF_SW_SUP, "l3init_60m_fclk" }, { OMAP5_USB_TLL_HS_CLKCTRL, omap5_usb_tll_hs_bit_data, CLKF_HW_SUP, "l4_root_clk_div" }, { OMAP5_SATA_CLKCTRL, omap5_sata_bit_data, CLKF_SW_SUP, "func_48m_fclk" }, @@ -519,7 +519,7 @@ static const struct omap_clkctrl_reg_data omap5_wkupaon_clkctrl_regs[] __initcon { OMAP5_L4_WKUP_CLKCTRL, NULL, 0, "wkupaon_iclk_mux" }, { OMAP5_WD_TIMER2_CLKCTRL, NULL, CLKF_SW_SUP, "sys_32k_ck" }, { OMAP5_GPIO1_CLKCTRL, omap5_gpio1_bit_data, CLKF_HW_SUP, "wkupaon_iclk_mux" }, - { OMAP5_TIMER1_CLKCTRL, omap5_timer1_bit_data, CLKF_SW_SUP, "wkupaon_cm:clk:0020:24" }, + { OMAP5_TIMER1_CLKCTRL, omap5_timer1_bit_data, CLKF_SW_SUP, "wkupaon-clkctrl:0020:24" }, { OMAP5_COUNTER_32K_CLKCTRL, NULL, 0, "wkupaon_iclk_mux" }, { OMAP5_KBD_CLKCTRL, NULL, CLKF_SW_SUP, "sys_32k_ck" }, { 0 }, @@ -549,58 +549,58 @@ const struct omap_clkctrl_data omap5_clkctrl_data[] __initconst = { static struct ti_dt_clk omap54xx_clks[] = { DT_CLK(NULL, "timer_32k_ck", "sys_32k_ck"), DT_CLK(NULL, "sys_clkin_ck", "sys_clkin"), - DT_CLK(NULL, "dmic_gfclk", "abe_cm:0018:24"), - DT_CLK(NULL, "dmic_sync_mux_ck", "abe_cm:0018:26"), - DT_CLK(NULL, "dss_32khz_clk", "dss_cm:0000:11"), - DT_CLK(NULL, "dss_48mhz_clk", "dss_cm:0000:9"), - DT_CLK(NULL, "dss_dss_clk", "dss_cm:0000:8"), - DT_CLK(NULL, "dss_sys_clk", "dss_cm:0000:10"), - DT_CLK(NULL, "gpio1_dbclk", "wkupaon_cm:0018:8"), - DT_CLK(NULL, "gpio2_dbclk", "l4per_cm:0040:8"), - DT_CLK(NULL, "gpio3_dbclk", "l4per_cm:0048:8"), - DT_CLK(NULL, "gpio4_dbclk", "l4per_cm:0050:8"), - DT_CLK(NULL, "gpio5_dbclk", "l4per_cm:0058:8"), - DT_CLK(NULL, "gpio6_dbclk", "l4per_cm:0060:8"), - DT_CLK(NULL, "gpio7_dbclk", "l4per_cm:00f0:8"), - DT_CLK(NULL, "gpio8_dbclk", "l4per_cm:00f8:8"), - DT_CLK(NULL, "mcbsp1_gfclk", "abe_cm:0028:24"), - DT_CLK(NULL, "mcbsp1_sync_mux_ck", "abe_cm:0028:26"), - DT_CLK(NULL, "mcbsp2_gfclk", "abe_cm:0030:24"), - DT_CLK(NULL, "mcbsp2_sync_mux_ck", "abe_cm:0030:26"), - DT_CLK(NULL, "mcbsp3_gfclk", "abe_cm:0038:24"), - DT_CLK(NULL, "mcbsp3_sync_mux_ck", "abe_cm:0038:26"), - DT_CLK(NULL, "mmc1_32khz_clk", "l3init_cm:0008:8"), - DT_CLK(NULL, "mmc1_fclk", "l3init_cm:0008:25"), - DT_CLK(NULL, "mmc1_fclk_mux", "l3init_cm:0008:24"), - DT_CLK(NULL, "mmc2_fclk", "l3init_cm:0010:25"), - DT_CLK(NULL, "mmc2_fclk_mux", "l3init_cm:0010:24"), - DT_CLK(NULL, "sata_ref_clk", "l3init_cm:0068:8"), - DT_CLK(NULL, "timer10_gfclk_mux", "l4per_cm:0008:24"), - DT_CLK(NULL, "timer11_gfclk_mux", "l4per_cm:0010:24"), - DT_CLK(NULL, "timer1_gfclk_mux", "wkupaon_cm:0020:24"), - DT_CLK(NULL, "timer2_gfclk_mux", "l4per_cm:0018:24"), - DT_CLK(NULL, "timer3_gfclk_mux", "l4per_cm:0020:24"), - DT_CLK(NULL, "timer4_gfclk_mux", "l4per_cm:0028:24"), - DT_CLK(NULL, "timer5_gfclk_mux", "abe_cm:0048:24"), - DT_CLK(NULL, "timer6_gfclk_mux", "abe_cm:0050:24"), - DT_CLK(NULL, "timer7_gfclk_mux", "abe_cm:0058:24"), - DT_CLK(NULL, "timer8_gfclk_mux", "abe_cm:0060:24"), - DT_CLK(NULL, "timer9_gfclk_mux", "l4per_cm:0030:24"), - DT_CLK(NULL, "usb_host_hs_hsic480m_p1_clk", "l3init_cm:0038:13"), - DT_CLK(NULL, "usb_host_hs_hsic480m_p2_clk", "l3init_cm:0038:14"), - DT_CLK(NULL, "usb_host_hs_hsic480m_p3_clk", "l3init_cm:0038:7"), - DT_CLK(NULL, "usb_host_hs_hsic60m_p1_clk", "l3init_cm:0038:11"), - DT_CLK(NULL, "usb_host_hs_hsic60m_p2_clk", "l3init_cm:0038:12"), - DT_CLK(NULL, "usb_host_hs_hsic60m_p3_clk", "l3init_cm:0038:6"), - DT_CLK(NULL, "usb_host_hs_utmi_p1_clk", "l3init_cm:0038:8"), - DT_CLK(NULL, "usb_host_hs_utmi_p2_clk", "l3init_cm:0038:9"), - DT_CLK(NULL, "usb_host_hs_utmi_p3_clk", "l3init_cm:0038:10"), - DT_CLK(NULL, "usb_otg_ss_refclk960m", "l3init_cm:00d0:8"), - DT_CLK(NULL, "usb_tll_hs_usb_ch0_clk", "l3init_cm:0048:8"), - DT_CLK(NULL, "usb_tll_hs_usb_ch1_clk", "l3init_cm:0048:9"), - DT_CLK(NULL, "usb_tll_hs_usb_ch2_clk", "l3init_cm:0048:10"), - DT_CLK(NULL, "utmi_p1_gfclk", "l3init_cm:0038:24"), - DT_CLK(NULL, "utmi_p2_gfclk", "l3init_cm:0038:25"), + DT_CLK(NULL, "dmic_gfclk", "abe-clkctrl:0018:24"), + DT_CLK(NULL, "dmic_sync_mux_ck", "abe-clkctrl:0018:26"), + DT_CLK(NULL, "dss_32khz_clk", "dss-clkctrl:0000:11"), + DT_CLK(NULL, "dss_48mhz_clk", "dss-clkctrl:0000:9"), + DT_CLK(NULL, "dss_dss_clk", "dss-clkctrl:0000:8"), + DT_CLK(NULL, "dss_sys_clk", "dss-clkctrl:0000:10"), + DT_CLK(NULL, "gpio1_dbclk", "wkupaon-clkctrl:0018:8"), + DT_CLK(NULL, "gpio2_dbclk", "l4per-clkctrl:0040:8"), + DT_CLK(NULL, "gpio3_dbclk", "l4per-clkctrl:0048:8"), + DT_CLK(NULL, "gpio4_dbclk", "l4per-clkctrl:0050:8"), + DT_CLK(NULL, "gpio5_dbclk", "l4per-clkctrl:0058:8"), + DT_CLK(NULL, "gpio6_dbclk", "l4per-clkctrl:0060:8"), + DT_CLK(NULL, "gpio7_dbclk", "l4per-clkctrl:00f0:8"), + DT_CLK(NULL, "gpio8_dbclk", "l4per-clkctrl:00f8:8"), + DT_CLK(NULL, "mcbsp1_gfclk", "abe-clkctrl:0028:24"), + DT_CLK(NULL, "mcbsp1_sync_mux_ck", "abe-clkctrl:0028:26"), + DT_CLK(NULL, "mcbsp2_gfclk", "abe-clkctrl:0030:24"), + DT_CLK(NULL, "mcbsp2_sync_mux_ck", "abe-clkctrl:0030:26"), + DT_CLK(NULL, "mcbsp3_gfclk", "abe-clkctrl:0038:24"), + DT_CLK(NULL, "mcbsp3_sync_mux_ck", "abe-clkctrl:0038:26"), + DT_CLK(NULL, "mmc1_32khz_clk", "l3init-clkctrl:0008:8"), + DT_CLK(NULL, "mmc1_fclk", "l3init-clkctrl:0008:25"), + DT_CLK(NULL, "mmc1_fclk_mux", "l3init-clkctrl:0008:24"), + DT_CLK(NULL, "mmc2_fclk", "l3init-clkctrl:0010:25"), + DT_CLK(NULL, "mmc2_fclk_mux", "l3init-clkctrl:0010:24"), + DT_CLK(NULL, "sata_ref_clk", "l3init-clkctrl:0068:8"), + DT_CLK(NULL, "timer10_gfclk_mux", "l4per-clkctrl:0008:24"), + DT_CLK(NULL, "timer11_gfclk_mux", "l4per-clkctrl:0010:24"), + DT_CLK(NULL, "timer1_gfclk_mux", "wkupaon-clkctrl:0020:24"), + DT_CLK(NULL, "timer2_gfclk_mux", "l4per-clkctrl:0018:24"), + DT_CLK(NULL, "timer3_gfclk_mux", "l4per-clkctrl:0020:24"), + DT_CLK(NULL, "timer4_gfclk_mux", "l4per-clkctrl:0028:24"), + DT_CLK(NULL, "timer5_gfclk_mux", "abe-clkctrl:0048:24"), + DT_CLK(NULL, "timer6_gfclk_mux", "abe-clkctrl:0050:24"), + DT_CLK(NULL, "timer7_gfclk_mux", "abe-clkctrl:0058:24"), + DT_CLK(NULL, "timer8_gfclk_mux", "abe-clkctrl:0060:24"), + DT_CLK(NULL, "timer9_gfclk_mux", "l4per-clkctrl:0030:24"), + DT_CLK(NULL, "usb_host_hs_hsic480m_p1_clk", "l3init-clkctrl:0038:13"), + DT_CLK(NULL, "usb_host_hs_hsic480m_p2_clk", "l3init-clkctrl:0038:14"), + DT_CLK(NULL, "usb_host_hs_hsic480m_p3_clk", "l3init-clkctrl:0038:7"), + DT_CLK(NULL, "usb_host_hs_hsic60m_p1_clk", "l3init-clkctrl:0038:11"), + DT_CLK(NULL, "usb_host_hs_hsic60m_p2_clk", "l3init-clkctrl:0038:12"), + DT_CLK(NULL, "usb_host_hs_hsic60m_p3_clk", "l3init-clkctrl:0038:6"), + DT_CLK(NULL, "usb_host_hs_utmi_p1_clk", "l3init-clkctrl:0038:8"), + DT_CLK(NULL, "usb_host_hs_utmi_p2_clk", "l3init-clkctrl:0038:9"), + DT_CLK(NULL, "usb_host_hs_utmi_p3_clk", "l3init-clkctrl:0038:10"), + DT_CLK(NULL, "usb_otg_ss_refclk960m", "l3init-clkctrl:00d0:8"), + DT_CLK(NULL, "usb_tll_hs_usb_ch0_clk", "l3init-clkctrl:0048:8"), + DT_CLK(NULL, "usb_tll_hs_usb_ch1_clk", "l3init-clkctrl:0048:9"), + DT_CLK(NULL, "usb_tll_hs_usb_ch2_clk", "l3init-clkctrl:0048:10"), + DT_CLK(NULL, "utmi_p1_gfclk", "l3init-clkctrl:0038:24"), + DT_CLK(NULL, "utmi_p2_gfclk", "l3init-clkctrl:0038:25"), { .node_name = NULL }, }; diff --git a/drivers/clk/ti/clk-816x.c b/drivers/clk/ti/clk-816x.c index 9daf3825f289..3b8e483aec92 100644 --- a/drivers/clk/ti/clk-816x.c +++ b/drivers/clk/ti/clk-816x.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #include #include diff --git a/drivers/clk/ti/clk-dra7-atl.c b/drivers/clk/ti/clk-dra7-atl.c index aa0950c4f498..f0f5bf68b6d2 100644 --- a/drivers/clk/ti/clk-dra7-atl.c +++ b/drivers/clk/ti/clk-dra7-atl.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * DRA7 ATL (Audio Tracking Logic) clock driver * * Copyright (C) 2013 Texas Instruments, Inc. * * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clk.c b/drivers/clk/ti/clk.c index 3463579220b5..ef2a445c63a3 100644 --- a/drivers/clk/ti/clk.c +++ b/drivers/clk/ti/clk.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI clock support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clkctrl.c b/drivers/clk/ti/clkctrl.c index 617360e20d86..ae5862879417 100644 --- a/drivers/clk/ti/clkctrl.c +++ b/drivers/clk/ti/clkctrl.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP clkctrl clock support * * Copyright (C) 2017 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include @@ -528,10 +520,6 @@ static void __init _ti_omap4_clkctrl_setup(struct device_node *node) char *c; u16 soc_mask = 0; - if (!(ti_clk_get_features()->flags & TI_CLK_CLKCTRL_COMPAT) && - of_node_name_eq(node, "clk")) - ti_clk_features.flags |= TI_CLK_CLKCTRL_COMPAT; - addrp = of_get_address(node, 0, NULL, NULL); addr = (u32)of_translate_address(node, addrp); diff --git a/drivers/clk/ti/clkt_dflt.c b/drivers/clk/ti/clkt_dflt.c index 91751dd26b16..a756ab1a5856 100644 --- a/drivers/clk/ti/clkt_dflt.c +++ b/drivers/clk/ti/clkt_dflt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Default clock type * @@ -8,15 +9,6 @@ * Richard Woodruff * Paul Walmsley * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/clock.h b/drivers/clk/ti/clock.h index c841d2d28111..37ab53339a9b 100644 --- a/drivers/clk/ti/clock.h +++ b/drivers/clk/ti/clock.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI Clock driver internal definitions * * Copyright (C) 2014 Texas Instruments, Inc * Tero Kristo (t-kristo@ti.com) - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DRIVERS_CLK_TI_CLOCK__ #define __DRIVERS_CLK_TI_CLOCK__ diff --git a/drivers/clk/ti/clockdomain.c b/drivers/clk/ti/clockdomain.c index 24179c907774..c897ad7e681e 100644 --- a/drivers/clk/ti/clockdomain.c +++ b/drivers/clk/ti/clockdomain.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP clockdomain support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/composite.c b/drivers/clk/ti/composite.c index 779b9900f636..77b771dd050a 100644 --- a/drivers/clk/ti/composite.c +++ b/drivers/clk/ti/composite.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI composite clock support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/divider.c b/drivers/clk/ti/divider.c index 9fbea0997b43..488d3da60c31 100644 --- a/drivers/clk/ti/divider.c +++ b/drivers/clk/ti/divider.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI Divider Clock * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/dpll.c b/drivers/clk/ti/dpll.c index 7c6dc8449b22..8ed43bc6b7cc 100644 --- a/drivers/clk/ti/dpll.c +++ b/drivers/clk/ti/dpll.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP DPLL clock support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/fapll.c b/drivers/clk/ti/fapll.c index 749c6b73abff..2db3fc4a443e 100644 --- a/drivers/clk/ti/fapll.c +++ b/drivers/clk/ti/fapll.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #include #include diff --git a/drivers/clk/ti/fixed-factor.c b/drivers/clk/ti/fixed-factor.c index 8cb00d0af966..c80cee0f5d3d 100644 --- a/drivers/clk/ti/fixed-factor.c +++ b/drivers/clk/ti/fixed-factor.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI Fixed Factor Clock * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/gate.c b/drivers/clk/ti/gate.c index 0033de9beb4c..307702921431 100644 --- a/drivers/clk/ti/gate.c +++ b/drivers/clk/ti/gate.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP gate clock support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/interface.c b/drivers/clk/ti/interface.c index dd2b455183a9..f47beeea211e 100644 --- a/drivers/clk/ti/interface.c +++ b/drivers/clk/ti/interface.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * OMAP interface clock support * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/ti/mux.c b/drivers/clk/ti/mux.c index 15de513d2d81..46b45b3e8319 100644 --- a/drivers/clk/ti/mux.c +++ b/drivers/clk/ti/mux.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI Multiplexer Clock * * Copyright (C) 2013 Texas Instruments, Inc. * * Tero Kristo - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/clk/x86/Makefile b/drivers/clk/x86/Makefile index 1244c4e568ff..c2088b3c4081 100644 --- a/drivers/clk/x86/Makefile +++ b/drivers/clk/x86/Makefile @@ -1,6 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_PMC_ATOM) += clk-pmc-atom.o obj-$(CONFIG_X86_AMD_PLATFORM_DEVICE) += clk-fch.o -clk-x86-lpss-y := clk-lpss-atom.o -obj-$(CONFIG_X86_INTEL_LPSS) += clk-x86-lpss.o +obj-$(CONFIG_X86_INTEL_LPSS) += clk-lpss-atom.o clk-pmc-atom.o obj-$(CONFIG_CLK_LGM_CGU) += clk-cgu.o clk-cgu-pll.o clk-lgm.o diff --git a/drivers/cpufreq/mvebu-cpufreq.c b/drivers/cpufreq/mvebu-cpufreq.c index 6d33a639f902..7f3cfe668f30 100644 --- a/drivers/cpufreq/mvebu-cpufreq.c +++ b/drivers/cpufreq/mvebu-cpufreq.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * CPUFreq support for Armada 370/XP platforms. * @@ -6,10 +7,6 @@ * Yehuda Yitschak * Gregory Clement * Thomas Petazzoni - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) "mvebu-pmsu: " fmt diff --git a/drivers/cpufreq/scpi-cpufreq.c b/drivers/cpufreq/scpi-cpufreq.c index bda3e7d42964..fd2c16821d54 100644 --- a/drivers/cpufreq/scpi-cpufreq.c +++ b/drivers/cpufreq/scpi-cpufreq.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * System Control and Power Interface (SCPI) based CPUFreq Interface driver * * Copyright (C) 2015 ARM Ltd. * Sudeep Holla - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/cpuidle/cpuidle-at91.c b/drivers/cpuidle/cpuidle-at91.c index 9c5853b6ca4a..45ee8e1e71ae 100644 --- a/drivers/cpuidle/cpuidle-at91.c +++ b/drivers/cpuidle/cpuidle-at91.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * based on arch/arm/mach-kirkwood/cpuidle.c * * CPU idle support for AT91 SoC * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The cpu idle uses wait-for-interrupt and RAM self refresh in order * to implement two idle states - * #1 wait-for-interrupt diff --git a/drivers/cpuidle/cpuidle-kirkwood.c b/drivers/cpuidle/cpuidle-kirkwood.c index 511c4f46027a..13bf743f885b 100644 --- a/drivers/cpuidle/cpuidle-kirkwood.c +++ b/drivers/cpuidle/cpuidle-kirkwood.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * CPU idle Marvell Kirkwood SoCs * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * The cpu idle uses wait-for-interrupt and DDR self refresh in order * to implement two idle states - * #1 wait-for-interrupt diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 487ed4ddc3be..a06d2a7627aa 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -85,6 +85,14 @@ config AMCC_PPC440SPE_ADMA help Enable support for the AMCC PPC440SPe RAID engines. +config APPLE_ADMAC + tristate "Apple ADMAC support" + depends on ARCH_APPLE || COMPILE_TEST + select DMA_ENGINE + default ARCH_APPLE + help + Enable support for Audio DMA Controller found on Apple Silicon SoCs. + config AT_HDMAC tristate "Atmel AHB DMA support" depends on ARCH_AT91 diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile index 2f1b87ffd7ab..10f7d4241001 100644 --- a/drivers/dma/Makefile +++ b/drivers/dma/Makefile @@ -17,6 +17,7 @@ obj-$(CONFIG_ALTERA_MSGDMA) += altera-msgdma.o obj-$(CONFIG_AMBA_PL08X) += amba-pl08x.o obj-$(CONFIG_AMCC_PPC440SPE_ADMA) += ppc4xx/ obj-$(CONFIG_AMD_PTDMA) += ptdma/ +obj-$(CONFIG_APPLE_ADMAC) += apple-admac.o obj-$(CONFIG_AT_HDMAC) += at_hdmac.o obj-$(CONFIG_AT_XDMAC) += at_xdmac.o obj-$(CONFIG_AXI_DMAC) += dma-axi-dmac.o diff --git a/drivers/dma/altera-msgdma.c b/drivers/dma/altera-msgdma.c index 6f56dfd375e3..4153c2edb049 100644 --- a/drivers/dma/altera-msgdma.c +++ b/drivers/dma/altera-msgdma.c @@ -749,7 +749,7 @@ static irqreturn_t msgdma_irq_handler(int irq, void *data) } /** - * msgdma_chan_remove - Channel remove function + * msgdma_dev_remove() - Device remove function * @mdev: Pointer to the Altera mSGDMA device structure */ static void msgdma_dev_remove(struct msgdma_device *mdev) @@ -918,7 +918,7 @@ fail: } /** - * msgdma_dma_remove - Driver remove function + * msgdma_remove() - Driver remove function * @pdev: Pointer to the platform_device structure * * Return: Always '0' diff --git a/drivers/dma/amba-pl08x.c b/drivers/dma/amba-pl08x.c index a4a794e62ac2..487a01aa207d 100644 --- a/drivers/dma/amba-pl08x.c +++ b/drivers/dma/amba-pl08x.c @@ -231,7 +231,7 @@ enum pl08x_dma_chan_state { /** * struct pl08x_dma_chan - this structure wraps a DMA ENGINE channel - * @vc: wrappped virtual channel + * @vc: wrapped virtual channel * @phychan: the physical channel utilized by this channel, if there is one * @name: name of channel * @cd: channel platform data diff --git a/drivers/dma/apple-admac.c b/drivers/dma/apple-admac.c new file mode 100644 index 000000000000..d1f74a3aa999 --- /dev/null +++ b/drivers/dma/apple-admac.c @@ -0,0 +1,818 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for Audio DMA Controller (ADMAC) on t8103 (M1) and other Apple chips + * + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmaengine.h" + +#define NCHANNELS_MAX 64 +#define IRQ_NOUTPUTS 4 + +#define RING_WRITE_SLOT GENMASK(1, 0) +#define RING_READ_SLOT GENMASK(5, 4) +#define RING_FULL BIT(9) +#define RING_EMPTY BIT(8) +#define RING_ERR BIT(10) + +#define STATUS_DESC_DONE BIT(0) +#define STATUS_ERR BIT(6) + +#define FLAG_DESC_NOTIFY BIT(16) + +#define REG_TX_START 0x0000 +#define REG_TX_STOP 0x0004 +#define REG_RX_START 0x0008 +#define REG_RX_STOP 0x000c + +#define REG_CHAN_CTL(ch) (0x8000 + (ch) * 0x200) +#define REG_CHAN_CTL_RST_RINGS BIT(0) + +#define REG_DESC_RING(ch) (0x8070 + (ch) * 0x200) +#define REG_REPORT_RING(ch) (0x8074 + (ch) * 0x200) + +#define REG_RESIDUE(ch) (0x8064 + (ch) * 0x200) + +#define REG_BUS_WIDTH(ch) (0x8040 + (ch) * 0x200) + +#define BUS_WIDTH_8BIT 0x00 +#define BUS_WIDTH_16BIT 0x01 +#define BUS_WIDTH_32BIT 0x02 +#define BUS_WIDTH_FRAME_2_WORDS 0x10 +#define BUS_WIDTH_FRAME_4_WORDS 0x20 + +#define CHAN_BUFSIZE 0x8000 + +#define REG_CHAN_FIFOCTL(ch) (0x8054 + (ch) * 0x200) +#define CHAN_FIFOCTL_LIMIT GENMASK(31, 16) +#define CHAN_FIFOCTL_THRESHOLD GENMASK(15, 0) + +#define REG_DESC_WRITE(ch) (0x10000 + ((ch) / 2) * 0x4 + ((ch) & 1) * 0x4000) +#define REG_REPORT_READ(ch) (0x10100 + ((ch) / 2) * 0x4 + ((ch) & 1) * 0x4000) + +#define REG_TX_INTSTATE(idx) (0x0030 + (idx) * 4) +#define REG_RX_INTSTATE(idx) (0x0040 + (idx) * 4) +#define REG_CHAN_INTSTATUS(ch, idx) (0x8010 + (ch) * 0x200 + (idx) * 4) +#define REG_CHAN_INTMASK(ch, idx) (0x8020 + (ch) * 0x200 + (idx) * 4) + +struct admac_data; +struct admac_tx; + +struct admac_chan { + unsigned int no; + struct admac_data *host; + struct dma_chan chan; + struct tasklet_struct tasklet; + + spinlock_t lock; + struct admac_tx *current_tx; + int nperiod_acks; + + /* + * We maintain a 'submitted' and 'issued' list mainly for interface + * correctness. Typical use of the driver (per channel) will be + * prepping, submitting and issuing a single cyclic transaction which + * will stay current until terminate_all is called. + */ + struct list_head submitted; + struct list_head issued; + + struct list_head to_free; +}; + +struct admac_data { + struct dma_device dma; + struct device *dev; + __iomem void *base; + + int irq_index; + int nchannels; + struct admac_chan channels[]; +}; + +struct admac_tx { + struct dma_async_tx_descriptor tx; + bool cyclic; + dma_addr_t buf_addr; + dma_addr_t buf_end; + size_t buf_len; + size_t period_len; + + size_t submitted_pos; + size_t reclaimed_pos; + + struct list_head node; +}; + +static void admac_modify(struct admac_data *ad, int reg, u32 mask, u32 val) +{ + void __iomem *addr = ad->base + reg; + u32 curr = readl_relaxed(addr); + + writel_relaxed((curr & ~mask) | (val & mask), addr); +} + +static struct admac_chan *to_admac_chan(struct dma_chan *chan) +{ + return container_of(chan, struct admac_chan, chan); +} + +static struct admac_tx *to_admac_tx(struct dma_async_tx_descriptor *tx) +{ + return container_of(tx, struct admac_tx, tx); +} + +static enum dma_transfer_direction admac_chan_direction(int channo) +{ + /* Channel directions are hardwired */ + return (channo & 1) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; +} + +static dma_cookie_t admac_tx_submit(struct dma_async_tx_descriptor *tx) +{ + struct admac_tx *adtx = to_admac_tx(tx); + struct admac_chan *adchan = to_admac_chan(tx->chan); + unsigned long flags; + dma_cookie_t cookie; + + spin_lock_irqsave(&adchan->lock, flags); + cookie = dma_cookie_assign(tx); + list_add_tail(&adtx->node, &adchan->submitted); + spin_unlock_irqrestore(&adchan->lock, flags); + + return cookie; +} + +static int admac_desc_free(struct dma_async_tx_descriptor *tx) +{ + kfree(to_admac_tx(tx)); + + return 0; +} + +static struct dma_async_tx_descriptor *admac_prep_dma_cyclic( + struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len, + size_t period_len, enum dma_transfer_direction direction, + unsigned long flags) +{ + struct admac_chan *adchan = container_of(chan, struct admac_chan, chan); + struct admac_tx *adtx; + + if (direction != admac_chan_direction(adchan->no)) + return NULL; + + adtx = kzalloc(sizeof(*adtx), GFP_NOWAIT); + if (!adtx) + return NULL; + + adtx->cyclic = true; + + adtx->buf_addr = buf_addr; + adtx->buf_len = buf_len; + adtx->buf_end = buf_addr + buf_len; + adtx->period_len = period_len; + + adtx->submitted_pos = 0; + adtx->reclaimed_pos = 0; + + dma_async_tx_descriptor_init(&adtx->tx, chan); + adtx->tx.tx_submit = admac_tx_submit; + adtx->tx.desc_free = admac_desc_free; + + return &adtx->tx; +} + +/* + * Write one hardware descriptor for a dmaengine cyclic transaction. + */ +static void admac_cyclic_write_one_desc(struct admac_data *ad, int channo, + struct admac_tx *tx) +{ + dma_addr_t addr; + + addr = tx->buf_addr + (tx->submitted_pos % tx->buf_len); + + /* If happens means we have buggy code */ + WARN_ON_ONCE(addr + tx->period_len > tx->buf_end); + + dev_dbg(ad->dev, "ch%d descriptor: addr=0x%pad len=0x%zx flags=0x%lx\n", + channo, &addr, tx->period_len, FLAG_DESC_NOTIFY); + + writel_relaxed(lower_32_bits(addr), ad->base + REG_DESC_WRITE(channo)); + writel_relaxed(upper_32_bits(addr), ad->base + REG_DESC_WRITE(channo)); + writel_relaxed(tx->period_len, ad->base + REG_DESC_WRITE(channo)); + writel_relaxed(FLAG_DESC_NOTIFY, ad->base + REG_DESC_WRITE(channo)); + + tx->submitted_pos += tx->period_len; + tx->submitted_pos %= 2 * tx->buf_len; +} + +/* + * Write all the hardware descriptors for a dmaengine cyclic + * transaction there is space for. + */ +static void admac_cyclic_write_desc(struct admac_data *ad, int channo, + struct admac_tx *tx) +{ + int i; + + for (i = 0; i < 4; i++) { + if (readl_relaxed(ad->base + REG_DESC_RING(channo)) & RING_FULL) + break; + admac_cyclic_write_one_desc(ad, channo, tx); + } +} + +static int admac_ring_noccupied_slots(int ringval) +{ + int wrslot = FIELD_GET(RING_WRITE_SLOT, ringval); + int rdslot = FIELD_GET(RING_READ_SLOT, ringval); + + if (wrslot != rdslot) { + return (wrslot + 4 - rdslot) % 4; + } else { + WARN_ON((ringval & (RING_FULL | RING_EMPTY)) == 0); + + if (ringval & RING_FULL) + return 4; + else + return 0; + } +} + +/* + * Read from hardware the residue of a cyclic dmaengine transaction. + */ +static u32 admac_cyclic_read_residue(struct admac_data *ad, int channo, + struct admac_tx *adtx) +{ + u32 ring1, ring2; + u32 residue1, residue2; + int nreports; + size_t pos; + + ring1 = readl_relaxed(ad->base + REG_REPORT_RING(channo)); + residue1 = readl_relaxed(ad->base + REG_RESIDUE(channo)); + ring2 = readl_relaxed(ad->base + REG_REPORT_RING(channo)); + residue2 = readl_relaxed(ad->base + REG_RESIDUE(channo)); + + if (residue2 > residue1) { + /* + * Controller must have loaded next descriptor between + * the two residue reads + */ + nreports = admac_ring_noccupied_slots(ring1) + 1; + } else { + /* No descriptor load between the two reads, ring2 is safe to use */ + nreports = admac_ring_noccupied_slots(ring2); + } + + pos = adtx->reclaimed_pos + adtx->period_len * (nreports + 1) - residue2; + + return adtx->buf_len - pos % adtx->buf_len; +} + +static enum dma_status admac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, + struct dma_tx_state *txstate) +{ + struct admac_chan *adchan = to_admac_chan(chan); + struct admac_data *ad = adchan->host; + struct admac_tx *adtx; + + enum dma_status ret; + size_t residue; + unsigned long flags; + + ret = dma_cookie_status(chan, cookie, txstate); + if (ret == DMA_COMPLETE || !txstate) + return ret; + + spin_lock_irqsave(&adchan->lock, flags); + adtx = adchan->current_tx; + + if (adtx && adtx->tx.cookie == cookie) { + ret = DMA_IN_PROGRESS; + residue = admac_cyclic_read_residue(ad, adchan->no, adtx); + } else { + ret = DMA_IN_PROGRESS; + residue = 0; + list_for_each_entry(adtx, &adchan->issued, node) { + if (adtx->tx.cookie == cookie) { + residue = adtx->buf_len; + break; + } + } + } + spin_unlock_irqrestore(&adchan->lock, flags); + + dma_set_residue(txstate, residue); + return ret; +} + +static void admac_start_chan(struct admac_chan *adchan) +{ + struct admac_data *ad = adchan->host; + u32 startbit = 1 << (adchan->no / 2); + + writel_relaxed(STATUS_DESC_DONE | STATUS_ERR, + ad->base + REG_CHAN_INTSTATUS(adchan->no, ad->irq_index)); + writel_relaxed(STATUS_DESC_DONE | STATUS_ERR, + ad->base + REG_CHAN_INTMASK(adchan->no, ad->irq_index)); + + switch (admac_chan_direction(adchan->no)) { + case DMA_MEM_TO_DEV: + writel_relaxed(startbit, ad->base + REG_TX_START); + break; + case DMA_DEV_TO_MEM: + writel_relaxed(startbit, ad->base + REG_RX_START); + break; + default: + break; + } + dev_dbg(adchan->host->dev, "ch%d start\n", adchan->no); +} + +static void admac_stop_chan(struct admac_chan *adchan) +{ + struct admac_data *ad = adchan->host; + u32 stopbit = 1 << (adchan->no / 2); + + switch (admac_chan_direction(adchan->no)) { + case DMA_MEM_TO_DEV: + writel_relaxed(stopbit, ad->base + REG_TX_STOP); + break; + case DMA_DEV_TO_MEM: + writel_relaxed(stopbit, ad->base + REG_RX_STOP); + break; + default: + break; + } + dev_dbg(adchan->host->dev, "ch%d stop\n", adchan->no); +} + +static void admac_reset_rings(struct admac_chan *adchan) +{ + struct admac_data *ad = adchan->host; + + writel_relaxed(REG_CHAN_CTL_RST_RINGS, + ad->base + REG_CHAN_CTL(adchan->no)); + writel_relaxed(0, ad->base + REG_CHAN_CTL(adchan->no)); +} + +static void admac_start_current_tx(struct admac_chan *adchan) +{ + struct admac_data *ad = adchan->host; + int ch = adchan->no; + + admac_reset_rings(adchan); + writel_relaxed(0, ad->base + REG_CHAN_CTL(ch)); + + admac_cyclic_write_one_desc(ad, ch, adchan->current_tx); + admac_start_chan(adchan); + admac_cyclic_write_desc(ad, ch, adchan->current_tx); +} + +static void admac_issue_pending(struct dma_chan *chan) +{ + struct admac_chan *adchan = to_admac_chan(chan); + struct admac_tx *tx; + unsigned long flags; + + spin_lock_irqsave(&adchan->lock, flags); + list_splice_tail_init(&adchan->submitted, &adchan->issued); + if (!list_empty(&adchan->issued) && !adchan->current_tx) { + tx = list_first_entry(&adchan->issued, struct admac_tx, node); + list_del(&tx->node); + + adchan->current_tx = tx; + adchan->nperiod_acks = 0; + admac_start_current_tx(adchan); + } + spin_unlock_irqrestore(&adchan->lock, flags); +} + +static int admac_pause(struct dma_chan *chan) +{ + struct admac_chan *adchan = to_admac_chan(chan); + + admac_stop_chan(adchan); + + return 0; +} + +static int admac_resume(struct dma_chan *chan) +{ + struct admac_chan *adchan = to_admac_chan(chan); + + admac_start_chan(adchan); + + return 0; +} + +static int admac_terminate_all(struct dma_chan *chan) +{ + struct admac_chan *adchan = to_admac_chan(chan); + unsigned long flags; + + spin_lock_irqsave(&adchan->lock, flags); + admac_stop_chan(adchan); + admac_reset_rings(adchan); + + adchan->current_tx = NULL; + /* + * Descriptors can only be freed after the tasklet + * has been killed (in admac_synchronize). + */ + list_splice_tail_init(&adchan->submitted, &adchan->to_free); + list_splice_tail_init(&adchan->issued, &adchan->to_free); + spin_unlock_irqrestore(&adchan->lock, flags); + + return 0; +} + +static void admac_synchronize(struct dma_chan *chan) +{ + struct admac_chan *adchan = to_admac_chan(chan); + struct admac_tx *adtx, *_adtx; + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&adchan->lock, flags); + list_splice_tail_init(&adchan->to_free, &head); + spin_unlock_irqrestore(&adchan->lock, flags); + + tasklet_kill(&adchan->tasklet); + + list_for_each_entry_safe(adtx, _adtx, &head, node) { + list_del(&adtx->node); + admac_desc_free(&adtx->tx); + } +} + +static int admac_alloc_chan_resources(struct dma_chan *chan) +{ + struct admac_chan *adchan = to_admac_chan(chan); + + dma_cookie_init(&adchan->chan); + return 0; +} + +static void admac_free_chan_resources(struct dma_chan *chan) +{ + admac_terminate_all(chan); + admac_synchronize(chan); +} + +static struct dma_chan *admac_dma_of_xlate(struct of_phandle_args *dma_spec, + struct of_dma *ofdma) +{ + struct admac_data *ad = (struct admac_data *) ofdma->of_dma_data; + unsigned int index; + + if (dma_spec->args_count != 1) + return NULL; + + index = dma_spec->args[0]; + + if (index >= ad->nchannels) { + dev_err(ad->dev, "channel index %u out of bounds\n", index); + return NULL; + } + + return &ad->channels[index].chan; +} + +static int admac_drain_reports(struct admac_data *ad, int channo) +{ + int count; + + for (count = 0; count < 4; count++) { + u32 countval_hi, countval_lo, unk1, flags; + + if (readl_relaxed(ad->base + REG_REPORT_RING(channo)) & RING_EMPTY) + break; + + countval_lo = readl_relaxed(ad->base + REG_REPORT_READ(channo)); + countval_hi = readl_relaxed(ad->base + REG_REPORT_READ(channo)); + unk1 = readl_relaxed(ad->base + REG_REPORT_READ(channo)); + flags = readl_relaxed(ad->base + REG_REPORT_READ(channo)); + + dev_dbg(ad->dev, "ch%d report: countval=0x%llx unk1=0x%x flags=0x%x\n", + channo, ((u64) countval_hi) << 32 | countval_lo, unk1, flags); + } + + return count; +} + +static void admac_handle_status_err(struct admac_data *ad, int channo) +{ + bool handled = false; + + if (readl_relaxed(ad->base + REG_DESC_RING(channo)) & RING_ERR) { + writel_relaxed(RING_ERR, ad->base + REG_DESC_RING(channo)); + dev_err_ratelimited(ad->dev, "ch%d descriptor ring error\n", channo); + handled = true; + } + + if (readl_relaxed(ad->base + REG_REPORT_RING(channo)) & RING_ERR) { + writel_relaxed(RING_ERR, ad->base + REG_REPORT_RING(channo)); + dev_err_ratelimited(ad->dev, "ch%d report ring error\n", channo); + handled = true; + } + + if (unlikely(!handled)) { + dev_err(ad->dev, "ch%d unknown error, masking errors as cause of IRQs\n", channo); + admac_modify(ad, REG_CHAN_INTMASK(channo, ad->irq_index), + STATUS_ERR, 0); + } +} + +static void admac_handle_status_desc_done(struct admac_data *ad, int channo) +{ + struct admac_chan *adchan = &ad->channels[channo]; + unsigned long flags; + int nreports; + + writel_relaxed(STATUS_DESC_DONE, + ad->base + REG_CHAN_INTSTATUS(channo, ad->irq_index)); + + spin_lock_irqsave(&adchan->lock, flags); + nreports = admac_drain_reports(ad, channo); + + if (adchan->current_tx) { + struct admac_tx *tx = adchan->current_tx; + + adchan->nperiod_acks += nreports; + tx->reclaimed_pos += nreports * tx->period_len; + tx->reclaimed_pos %= 2 * tx->buf_len; + + admac_cyclic_write_desc(ad, channo, tx); + tasklet_schedule(&adchan->tasklet); + } + spin_unlock_irqrestore(&adchan->lock, flags); +} + +static void admac_handle_chan_int(struct admac_data *ad, int no) +{ + u32 cause = readl_relaxed(ad->base + REG_CHAN_INTSTATUS(no, ad->irq_index)); + + if (cause & STATUS_ERR) + admac_handle_status_err(ad, no); + + if (cause & STATUS_DESC_DONE) + admac_handle_status_desc_done(ad, no); +} + +static irqreturn_t admac_interrupt(int irq, void *devid) +{ + struct admac_data *ad = devid; + u32 rx_intstate, tx_intstate; + int i; + + rx_intstate = readl_relaxed(ad->base + REG_RX_INTSTATE(ad->irq_index)); + tx_intstate = readl_relaxed(ad->base + REG_TX_INTSTATE(ad->irq_index)); + + if (!tx_intstate && !rx_intstate) + return IRQ_NONE; + + for (i = 0; i < ad->nchannels; i += 2) { + if (tx_intstate & 1) + admac_handle_chan_int(ad, i); + tx_intstate >>= 1; + } + + for (i = 1; i < ad->nchannels; i += 2) { + if (rx_intstate & 1) + admac_handle_chan_int(ad, i); + rx_intstate >>= 1; + } + + return IRQ_HANDLED; +} + +static void admac_chan_tasklet(struct tasklet_struct *t) +{ + struct admac_chan *adchan = from_tasklet(adchan, t, tasklet); + struct admac_tx *adtx; + struct dmaengine_desc_callback cb; + struct dmaengine_result tx_result; + int nacks; + + spin_lock_irq(&adchan->lock); + adtx = adchan->current_tx; + nacks = adchan->nperiod_acks; + adchan->nperiod_acks = 0; + spin_unlock_irq(&adchan->lock); + + if (!adtx || !nacks) + return; + + tx_result.result = DMA_TRANS_NOERROR; + tx_result.residue = 0; + + dmaengine_desc_get_callback(&adtx->tx, &cb); + while (nacks--) + dmaengine_desc_callback_invoke(&cb, &tx_result); +} + +static int admac_device_config(struct dma_chan *chan, + struct dma_slave_config *config) +{ + struct admac_chan *adchan = to_admac_chan(chan); + struct admac_data *ad = adchan->host; + bool is_tx = admac_chan_direction(adchan->no) == DMA_MEM_TO_DEV; + int wordsize = 0; + u32 bus_width = 0; + + switch (is_tx ? config->dst_addr_width : config->src_addr_width) { + case DMA_SLAVE_BUSWIDTH_1_BYTE: + wordsize = 1; + bus_width |= BUS_WIDTH_8BIT; + break; + case DMA_SLAVE_BUSWIDTH_2_BYTES: + wordsize = 2; + bus_width |= BUS_WIDTH_16BIT; + break; + case DMA_SLAVE_BUSWIDTH_4_BYTES: + wordsize = 4; + bus_width |= BUS_WIDTH_32BIT; + break; + default: + return -EINVAL; + } + + /* + * We take port_window_size to be the number of words in a frame. + * + * The controller has some means of out-of-band signalling, to the peripheral, + * of words position in a frame. That's where the importance of this control + * comes from. + */ + switch (is_tx ? config->dst_port_window_size : config->src_port_window_size) { + case 0 ... 1: + break; + case 2: + bus_width |= BUS_WIDTH_FRAME_2_WORDS; + break; + case 4: + bus_width |= BUS_WIDTH_FRAME_4_WORDS; + break; + default: + return -EINVAL; + } + + writel_relaxed(bus_width, ad->base + REG_BUS_WIDTH(adchan->no)); + + /* + * By FIFOCTL_LIMIT we seem to set the maximal number of bytes allowed to be + * held in controller's per-channel FIFO. Transfers seem to be triggered + * around the time FIFO occupancy touches FIFOCTL_THRESHOLD. + * + * The numbers we set are more or less arbitrary. + */ + writel_relaxed(FIELD_PREP(CHAN_FIFOCTL_LIMIT, 0x30 * wordsize) + | FIELD_PREP(CHAN_FIFOCTL_THRESHOLD, 0x18 * wordsize), + ad->base + REG_CHAN_FIFOCTL(adchan->no)); + + return 0; +} + +static int admac_probe(struct platform_device *pdev) +{ + struct device_node *np = pdev->dev.of_node; + struct admac_data *ad; + struct dma_device *dma; + int nchannels; + int err, irq, i; + + err = of_property_read_u32(np, "dma-channels", &nchannels); + if (err || nchannels > NCHANNELS_MAX) { + dev_err(&pdev->dev, "missing or invalid dma-channels property\n"); + return -EINVAL; + } + + ad = devm_kzalloc(&pdev->dev, struct_size(ad, channels, nchannels), GFP_KERNEL); + if (!ad) + return -ENOMEM; + + platform_set_drvdata(pdev, ad); + ad->dev = &pdev->dev; + ad->nchannels = nchannels; + + /* + * The controller has 4 IRQ outputs. Try them all until + * we find one we can use. + */ + for (i = 0; i < IRQ_NOUTPUTS; i++) { + irq = platform_get_irq_optional(pdev, i); + if (irq >= 0) { + ad->irq_index = i; + break; + } + } + + if (irq < 0) + return dev_err_probe(&pdev->dev, irq, "no usable interrupt\n"); + + err = devm_request_irq(&pdev->dev, irq, admac_interrupt, + 0, dev_name(&pdev->dev), ad); + if (err) + return dev_err_probe(&pdev->dev, err, + "unable to register interrupt\n"); + + ad->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(ad->base)) + return dev_err_probe(&pdev->dev, PTR_ERR(ad->base), + "unable to obtain MMIO resource\n"); + + dma = &ad->dma; + + dma_cap_set(DMA_PRIVATE, dma->cap_mask); + dma_cap_set(DMA_CYCLIC, dma->cap_mask); + + dma->dev = &pdev->dev; + dma->device_alloc_chan_resources = admac_alloc_chan_resources; + dma->device_free_chan_resources = admac_free_chan_resources; + dma->device_tx_status = admac_tx_status; + dma->device_issue_pending = admac_issue_pending; + dma->device_terminate_all = admac_terminate_all; + dma->device_synchronize = admac_synchronize; + dma->device_prep_dma_cyclic = admac_prep_dma_cyclic; + dma->device_config = admac_device_config; + dma->device_pause = admac_pause; + dma->device_resume = admac_resume; + + dma->directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); + dma->residue_granularity = DMA_RESIDUE_GRANULARITY_BURST; + dma->dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES); + + INIT_LIST_HEAD(&dma->channels); + for (i = 0; i < nchannels; i++) { + struct admac_chan *adchan = &ad->channels[i]; + + adchan->host = ad; + adchan->no = i; + adchan->chan.device = &ad->dma; + spin_lock_init(&adchan->lock); + INIT_LIST_HEAD(&adchan->submitted); + INIT_LIST_HEAD(&adchan->issued); + INIT_LIST_HEAD(&adchan->to_free); + list_add_tail(&adchan->chan.device_node, &dma->channels); + tasklet_setup(&adchan->tasklet, admac_chan_tasklet); + } + + err = dma_async_device_register(&ad->dma); + if (err) + return dev_err_probe(&pdev->dev, err, "failed to register DMA device\n"); + + err = of_dma_controller_register(pdev->dev.of_node, admac_dma_of_xlate, ad); + if (err) { + dma_async_device_unregister(&ad->dma); + return dev_err_probe(&pdev->dev, err, "failed to register with OF\n"); + } + + return 0; +} + +static int admac_remove(struct platform_device *pdev) +{ + struct admac_data *ad = platform_get_drvdata(pdev); + + of_dma_controller_free(pdev->dev.of_node); + dma_async_device_unregister(&ad->dma); + + return 0; +} + +static const struct of_device_id admac_of_match[] = { + { .compatible = "apple,admac", }, + { } +}; +MODULE_DEVICE_TABLE(of, admac_of_match); + +static struct platform_driver apple_admac_driver = { + .driver = { + .name = "apple-admac", + .of_match_table = admac_of_match, + }, + .probe = admac_probe, + .remove = admac_remove, +}; +module_platform_driver(apple_admac_driver); + +MODULE_AUTHOR("Martin Povišer "); +MODULE_DESCRIPTION("Driver for Audio DMA Controller (ADMAC) on Apple SoCs"); +MODULE_LICENSE("GPL"); diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 7b3e6030f7b4..b102d8eb5d83 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -649,7 +649,7 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan, } /* - * Only check that maxburst and addr width values are supported by the + * Only check that maxburst and addr width values are supported by * the controller but not that the configuration is good to perform the * transfer since we don't know the direction at this stage. */ diff --git a/drivers/dma/bcm-sba-raid.c b/drivers/dma/bcm-sba-raid.c index 64239da02e74..064761289a73 100644 --- a/drivers/dma/bcm-sba-raid.c +++ b/drivers/dma/bcm-sba-raid.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2017 Broadcom /* * Broadcom SBA RAID Driver diff --git a/drivers/dma/bestcomm/ata.c b/drivers/dma/bestcomm/ata.c index e169f18da551..502a45d76adc 100644 --- a/drivers/dma/bestcomm/ata.c +++ b/drivers/dma/bestcomm/ata.c @@ -1,16 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Bestcomm ATA task driver * - * * Patterned after bestcomm/fec.c by Dale Farnsworth * 2003-2004 (c) MontaVista, Software, Inc. * * Copyright (C) 2006-2007 Sylvain Munaut * Copyright (C) 2006 Freescale - John Rigby - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include @@ -154,4 +150,3 @@ EXPORT_SYMBOL_GPL(bcom_ata_release); MODULE_DESCRIPTION("BestComm ATA task driver"); MODULE_AUTHOR("John Rigby"); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/dma/bestcomm/bestcomm.c b/drivers/dma/bestcomm/bestcomm.c index 1822a7034630..eabbcfcaa7cb 100644 --- a/drivers/dma/bestcomm/bestcomm.c +++ b/drivers/dma/bestcomm/bestcomm.c @@ -1,16 +1,12 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for MPC52xx processor BestComm peripheral controller * - * * Copyright (C) 2006-2007 Sylvain Munaut * Copyright (C) 2005 Varma Electronics Oy, * ( by Andrey Volkov ) * Copyright (C) 2003-2004 MontaVista, Software, Inc. * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include @@ -528,4 +524,3 @@ MODULE_AUTHOR("Sylvain Munaut "); MODULE_AUTHOR("Andrey Volkov "); MODULE_AUTHOR("Dale Farnsworth "); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/dma/bestcomm/fec.c b/drivers/dma/bestcomm/fec.c index d203618ac11f..3a4a2f7910c6 100644 --- a/drivers/dma/bestcomm/fec.c +++ b/drivers/dma/bestcomm/fec.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Bestcomm FEC tasks driver * - * * Copyright (C) 2006-2007 Sylvain Munaut * Copyright (C) 2003-2004 MontaVista, Software, Inc. * ( by Dale Farnsworth ) - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include @@ -267,4 +263,3 @@ EXPORT_SYMBOL_GPL(bcom_fec_tx_release); MODULE_DESCRIPTION("BestComm FEC tasks driver"); MODULE_AUTHOR("Dale Farnsworth "); MODULE_LICENSE("GPL v2"); - diff --git a/drivers/dma/bestcomm/sram.c b/drivers/dma/bestcomm/sram.c index 2074e0e3fa21..c465758e7193 100644 --- a/drivers/dma/bestcomm/sram.c +++ b/drivers/dma/bestcomm/sram.c @@ -1,14 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Simple memory allocator for on-board SRAM * - * * Maintainer : Sylvain Munaut * * Copyright (C) 2005 Sylvain Munaut - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include @@ -176,4 +172,3 @@ void bcom_sram_free(void *ptr) spin_unlock(&bcom_sram->lock); } EXPORT_SYMBOL_GPL(bcom_sram_free); - diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index 5161b73c30c4..f30dabc99795 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -55,6 +56,9 @@ #define AXI_DMAC_DMA_DST_TYPE_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_TYPE_MSK, x) #define AXI_DMAC_DMA_DST_WIDTH_MSK GENMASK(3, 0) #define AXI_DMAC_DMA_DST_WIDTH_GET(x) FIELD_GET(AXI_DMAC_DMA_DST_WIDTH_MSK, x) +#define AXI_DMAC_REG_COHERENCY_DESC 0x14 +#define AXI_DMAC_DST_COHERENT_MSK BIT(0) +#define AXI_DMAC_DST_COHERENT_GET(x) FIELD_GET(AXI_DMAC_DST_COHERENT_MSK, x) #define AXI_DMAC_REG_IRQ_MASK 0x80 #define AXI_DMAC_REG_IRQ_PENDING 0x84 @@ -979,6 +983,18 @@ static int axi_dmac_probe(struct platform_device *pdev) axi_dmac_write(dmac, AXI_DMAC_REG_IRQ_MASK, 0x00); + if (of_dma_is_coherent(pdev->dev.of_node)) { + ret = axi_dmac_read(dmac, AXI_DMAC_REG_COHERENCY_DESC); + + if (version < ADI_AXI_PCORE_VER(4, 4, 'a') || + !AXI_DMAC_DST_COHERENT_GET(ret)) { + dev_err(dmac->dma_dev.dev, + "Coherent DMA not supported in hardware"); + ret = -EINVAL; + goto err_clk_disable; + } + } + ret = dma_async_device_register(dma_dev); if (ret) goto err_clk_disable; diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c index e2ec540e6519..2a483802d9ee 100644 --- a/drivers/dma/dma-jz4780.c +++ b/drivers/dma/dma-jz4780.c @@ -388,7 +388,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_slave_sg( if (i != (sg_len - 1) && !(jzdma->soc_data->flags & JZ_SOC_DATA_BREAK_LINKS)) { - /* Automatically proceeed to the next descriptor. */ + /* Automatically proceed to the next descriptor. */ desc->desc[i].dcm |= JZ_DMA_DCM_LINK; /* diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index e80feeea0e01..c741b6431958 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -1153,13 +1153,6 @@ int dma_async_device_register(struct dma_device *device) return -EIO; } - if (dma_has_cap(DMA_MEMCPY_SG, device->cap_mask) && !device->device_prep_dma_memcpy_sg) { - dev_err(device->dev, - "Device claims capability %s, but op is not defined\n", - "DMA_MEMCPY_SG"); - return -EIO; - } - if (dma_has_cap(DMA_XOR, device->cap_mask) && !device->device_prep_dma_xor) { dev_err(device->dev, "Device claims capability %s, but op is not defined\n", diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index f696246f57fd..9fe2ae794316 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -22,51 +22,50 @@ #include static unsigned int test_buf_size = 16384; -module_param(test_buf_size, uint, S_IRUGO | S_IWUSR); +module_param(test_buf_size, uint, 0644); MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); static char test_device[32]; -module_param_string(device, test_device, sizeof(test_device), - S_IRUGO | S_IWUSR); +module_param_string(device, test_device, sizeof(test_device), 0644); MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)"); static unsigned int threads_per_chan = 1; -module_param(threads_per_chan, uint, S_IRUGO | S_IWUSR); +module_param(threads_per_chan, uint, 0644); MODULE_PARM_DESC(threads_per_chan, "Number of threads to start per channel (default: 1)"); static unsigned int max_channels; -module_param(max_channels, uint, S_IRUGO | S_IWUSR); +module_param(max_channels, uint, 0644); MODULE_PARM_DESC(max_channels, "Maximum number of channels to use (default: all)"); static unsigned int iterations; -module_param(iterations, uint, S_IRUGO | S_IWUSR); +module_param(iterations, uint, 0644); MODULE_PARM_DESC(iterations, "Iterations before stopping test (default: infinite)"); static unsigned int dmatest; -module_param(dmatest, uint, S_IRUGO | S_IWUSR); +module_param(dmatest, uint, 0644); MODULE_PARM_DESC(dmatest, "dmatest 0-memcpy 1-memset (default: 0)"); static unsigned int xor_sources = 3; -module_param(xor_sources, uint, S_IRUGO | S_IWUSR); +module_param(xor_sources, uint, 0644); MODULE_PARM_DESC(xor_sources, "Number of xor source buffers (default: 3)"); static unsigned int pq_sources = 3; -module_param(pq_sources, uint, S_IRUGO | S_IWUSR); +module_param(pq_sources, uint, 0644); MODULE_PARM_DESC(pq_sources, "Number of p+q source buffers (default: 3)"); static int timeout = 3000; -module_param(timeout, int, S_IRUGO | S_IWUSR); +module_param(timeout, int, 0644); MODULE_PARM_DESC(timeout, "Transfer Timeout in msec (default: 3000), " "Pass -1 for infinite timeout"); static bool noverify; -module_param(noverify, bool, S_IRUGO | S_IWUSR); +module_param(noverify, bool, 0644); MODULE_PARM_DESC(noverify, "Disable data verification (default: verify)"); static bool norandom; @@ -74,7 +73,7 @@ module_param(norandom, bool, 0644); MODULE_PARM_DESC(norandom, "Disable random offset setup (default: random)"); static bool verbose; -module_param(verbose, bool, S_IRUGO | S_IWUSR); +module_param(verbose, bool, 0644); MODULE_PARM_DESC(verbose, "Enable \"success\" result messages (default: off)"); static int alignment = -1; @@ -86,7 +85,7 @@ module_param(transfer_size, uint, 0644); MODULE_PARM_DESC(transfer_size, "Optional custom transfer size in bytes (default: not used (0))"); static bool polled; -module_param(polled, bool, S_IRUGO | S_IWUSR); +module_param(polled, bool, 0644); MODULE_PARM_DESC(polled, "Use polling for completion instead of interrupts"); /** @@ -154,7 +153,7 @@ static const struct kernel_param_ops run_ops = { .get = dmatest_run_get, }; static bool dmatest_run; -module_param_cb(run, &run_ops, &dmatest_run, S_IRUGO | S_IWUSR); +module_param_cb(run, &run_ops, &dmatest_run, 0644); MODULE_PARM_DESC(run, "Run the test (default: false)"); static int dmatest_chan_set(const char *val, const struct kernel_param *kp); @@ -290,7 +289,7 @@ static const struct kernel_param_ops wait_ops = { .get = dmatest_wait_get, .set = param_set_bool, }; -module_param_cb(wait, &wait_ops, &wait, S_IRUGO); +module_param_cb(wait, &wait_ops, &wait, 0444); MODULE_PARM_DESC(wait, "Wait for tests to complete (default: false)"); static bool dmatest_match_channel(struct dmatest_params *params, @@ -579,10 +578,10 @@ static int dmatest_func(void *data) unsigned int total_tests = 0; dma_cookie_t cookie; enum dma_status status; - enum dma_ctrl_flags flags; + enum dma_ctrl_flags flags; u8 *pq_coefs = NULL; int ret; - unsigned int buf_size; + unsigned int buf_size; struct dmatest_data *src; struct dmatest_data *dst; int i; @@ -1095,8 +1094,8 @@ static void add_threaded_test(struct dmatest_info *info) /* Copy test parameters */ params->buf_size = test_buf_size; - strlcpy(params->channel, strim(test_channel), sizeof(params->channel)); - strlcpy(params->device, strim(test_device), sizeof(params->device)); + strscpy(params->channel, strim(test_channel), sizeof(params->channel)); + strscpy(params->device, strim(test_device), sizeof(params->device)); params->threads_per_chan = threads_per_chan; params->max_channels = max_channels; params->iterations = iterations; @@ -1240,7 +1239,7 @@ static int dmatest_chan_set(const char *val, const struct kernel_param *kp) dtc = list_last_entry(&info->channels, struct dmatest_chan, node); - strlcpy(chan_reset_val, + strscpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); ret = -EBUSY; @@ -1263,14 +1262,14 @@ static int dmatest_chan_set(const char *val, const struct kernel_param *kp) if ((strcmp(dma_chan_name(dtc->chan), strim(test_channel)) != 0) && (strcmp("", strim(test_channel)) != 0)) { ret = -EINVAL; - strlcpy(chan_reset_val, dma_chan_name(dtc->chan), + strscpy(chan_reset_val, dma_chan_name(dtc->chan), sizeof(chan_reset_val)); goto add_chan_err; } } else { /* Clear test_channel if no channels were added successfully */ - strlcpy(chan_reset_val, "", sizeof(chan_reset_val)); + strscpy(chan_reset_val, "", sizeof(chan_reset_val)); ret = -EBUSY; goto add_chan_err; } @@ -1295,7 +1294,7 @@ static int dmatest_chan_get(char *val, const struct kernel_param *kp) mutex_lock(&info->lock); if (!is_threaded_test_run(info) && !is_threaded_test_pending(info)) { stop_threaded_test(info); - strlcpy(test_channel, "", sizeof(test_channel)); + strscpy(test_channel, "", sizeof(test_channel)); } mutex_unlock(&info->lock); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index c741da02b67e..a183d93bd7e2 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -982,6 +982,11 @@ static int dw_axi_dma_chan_slave_config(struct dma_chan *dchan, static void axi_chan_dump_lli(struct axi_dma_chan *chan, struct axi_dma_hw_desc *desc) { + if (!desc->lli) { + dev_err(dchan2dev(&chan->vc.chan), "NULL LLI\n"); + return; + } + dev_err(dchan2dev(&chan->vc.chan), "SAR: 0x%llx DAR: 0x%llx LLP: 0x%llx BTS 0x%x CTL: 0x%x:%08x", le64_to_cpu(desc->lli->sar), @@ -1049,6 +1054,11 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) /* The completed descriptor currently is in the head of vc list */ vd = vchan_next_desc(&chan->vc); + if (!vd) { + dev_err(chan2dev(chan), "BUG: %s, IRQ with no descriptors\n", + axi_chan_name(chan)); + goto out; + } if (chan->cyclic) { desc = vd_to_axi_desc(vd); @@ -1078,6 +1088,7 @@ static void axi_chan_block_xfer_complete(struct axi_dma_chan *chan) axi_chan_start_first_queued(chan); } +out: spin_unlock_irqrestore(&chan->vc.lock, flags); } diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 607647dacc29..77e6cfe52e0a 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -417,19 +417,11 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) SET_CH_32(dw, chan->dir, chan->id, ch_control1, (DW_EDMA_V0_CCS | DW_EDMA_V0_LLE)); /* Linked list */ - - #ifdef CONFIG_64BIT /* llp is not aligned on 64bit -> keep 32bit accesses */ SET_CH_32(dw, chan->dir, chan->id, llp.lsb, lower_32_bits(chunk->ll_region.paddr)); SET_CH_32(dw, chan->dir, chan->id, llp.msb, upper_32_bits(chunk->ll_region.paddr)); - #else /* CONFIG_64BIT */ - SET_CH_32(dw, chan->dir, chan->id, llp.lsb, - lower_32_bits(chunk->ll_region.paddr)); - SET_CH_32(dw, chan->dir, chan->id, llp.msb, - upper_32_bits(chunk->ll_region.paddr)); - #endif /* CONFIG_64BIT */ } /* Doorbell */ SET_RW_32(dw, chan->dir, doorbell, diff --git a/drivers/dma/dw/core.c b/drivers/dma/dw/core.c index 7ab83fe601ed..97ba3bfc10b1 100644 --- a/drivers/dma/dw/core.c +++ b/drivers/dma/dw/core.c @@ -29,9 +29,6 @@ * (DW_ahb_dmac) which is used with various AMBA 2.0 systems (not all * of which use ARM any more). See the "Databook" from Synopsys for * information beyond what licensees probably provide. - * - * The driver has been tested with the Atmel AT32AP7000, which does not - * support descriptor writeback. */ /* The set of bus widths supported by the DMA controller */ diff --git a/drivers/dma/dw/rzn1-dmamux.c b/drivers/dma/dw/rzn1-dmamux.c index 11d254e450b0..f9912c3dd4d7 100644 --- a/drivers/dma/dw/rzn1-dmamux.c +++ b/drivers/dma/dw/rzn1-dmamux.c @@ -102,10 +102,12 @@ free_map: return ERR_PTR(ret); } +#ifdef CONFIG_OF static const struct of_device_id rzn1_dmac_match[] = { { .compatible = "renesas,rzn1-dma" }, {} }; +#endif static int rzn1_dmamux_probe(struct platform_device *pdev) { @@ -140,6 +142,7 @@ static const struct of_device_id rzn1_dmamux_match[] = { { .compatible = "renesas,rzn1-dmamux" }, {} }; +MODULE_DEVICE_TABLE(of, rzn1_dmamux_match); static struct platform_driver rzn1_dmamux_driver = { .driver = { diff --git a/drivers/dma/ep93xx_dma.c b/drivers/dma/ep93xx_dma.c index 971ff5f9ae84..d19ea885c63e 100644 --- a/drivers/dma/ep93xx_dma.c +++ b/drivers/dma/ep93xx_dma.c @@ -1183,7 +1183,7 @@ fail: * * Synchronizes the DMA channel termination to the current context. When this * function returns it is guaranteed that all transfers for previously issued - * descriptors have stopped and and it is safe to free the memory associated + * descriptors have stopped and it is safe to free the memory associated * with them. Furthermore it is guaranteed that all complete callback functions * for a previously submitted descriptor have finished running and it is safe to * free resources accessed from within the complete callbacks. diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c index 3ae05d1446a5..a06a1575a2a5 100644 --- a/drivers/dma/fsl-edma-common.c +++ b/drivers/dma/fsl-edma-common.c @@ -559,9 +559,6 @@ struct dma_async_tx_descriptor *fsl_edma_prep_slave_sg( } for_each_sg(sgl, sg, sg_len, i) { - /* get next sg's physical address */ - last_sg = fsl_desc->tcd[(i + 1) % sg_len].ptcd; - if (direction == DMA_MEM_TO_DEV) { src_addr = sg_dma_address(sg); dst_addr = fsl_chan->dma_dev_addr; diff --git a/drivers/dma/imx-dma.c b/drivers/dma/imx-dma.c index 3bffe3ecbd1b..65c6094ce063 100644 --- a/drivers/dma/imx-dma.c +++ b/drivers/dma/imx-dma.c @@ -1047,7 +1047,7 @@ static int __init imxdma_probe(struct platform_device *pdev) return -ENOMEM; imxdma->dev = &pdev->dev; - imxdma->devtype = (enum imx_dma_type)of_device_get_match_data(&pdev->dev); + imxdma->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); imxdma->base = devm_ioremap_resource(&pdev->dev, res); diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index f37a276f519e..fbea5f62dd98 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -183,12 +183,14 @@ BIT(DMA_DEV_TO_DEV)) #define SDMA_WATERMARK_LEVEL_N_FIFOS GENMASK(15, 12) +#define SDMA_WATERMARK_LEVEL_OFF_FIFOS GENMASK(19, 16) +#define SDMA_WATERMARK_LEVEL_WORDS_PER_FIFO GENMASK(31, 28) #define SDMA_WATERMARK_LEVEL_SW_DONE BIT(23) #define SDMA_DONE0_CONFIG_DONE_SEL BIT(7) #define SDMA_DONE0_CONFIG_DONE_DIS BIT(6) -/** +/* * struct sdma_script_start_addrs - SDMA script start pointers * * start addresses of the different functions in the physical @@ -424,6 +426,14 @@ struct sdma_desc { * @data: specific sdma interface structure * @bd_pool: dma_pool for bd * @terminate_worker: used to call back into terminate work function + * @terminated: terminated list + * @is_ram_script: flag for script in ram + * @n_fifos_src: number of source device fifos + * @n_fifos_dst: number of destination device fifos + * @sw_done: software done flag + * @stride_fifos_src: stride for source device FIFOs + * @stride_fifos_dst: stride for destination device FIFOs + * @words_per_fifo: copy number of words one time for one FIFO */ struct sdma_channel { struct virt_dma_chan vc; @@ -451,6 +461,9 @@ struct sdma_channel { bool is_ram_script; unsigned int n_fifos_src; unsigned int n_fifos_dst; + unsigned int stride_fifos_src; + unsigned int stride_fifos_dst; + unsigned int words_per_fifo; bool sw_done; }; @@ -1240,17 +1253,29 @@ static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac) static void sdma_set_watermarklevel_for_sais(struct sdma_channel *sdmac) { unsigned int n_fifos; + unsigned int stride_fifos; + unsigned int words_per_fifo; if (sdmac->sw_done) sdmac->watermark_level |= SDMA_WATERMARK_LEVEL_SW_DONE; - if (sdmac->direction == DMA_DEV_TO_MEM) + if (sdmac->direction == DMA_DEV_TO_MEM) { n_fifos = sdmac->n_fifos_src; - else + stride_fifos = sdmac->stride_fifos_src; + } else { n_fifos = sdmac->n_fifos_dst; + stride_fifos = sdmac->stride_fifos_dst; + } + + words_per_fifo = sdmac->words_per_fifo; sdmac->watermark_level |= FIELD_PREP(SDMA_WATERMARK_LEVEL_N_FIFOS, n_fifos); + sdmac->watermark_level |= + FIELD_PREP(SDMA_WATERMARK_LEVEL_OFF_FIFOS, stride_fifos); + if (words_per_fifo) + sdmac->watermark_level |= + FIELD_PREP(SDMA_WATERMARK_LEVEL_WORDS_PER_FIFO, (words_per_fifo - 1)); } static int sdma_config_channel(struct dma_chan *chan) @@ -1764,6 +1789,9 @@ static int sdma_config(struct dma_chan *chan, } sdmac->n_fifos_src = sdmacfg->n_fifos_src; sdmac->n_fifos_dst = sdmacfg->n_fifos_dst; + sdmac->stride_fifos_src = sdmacfg->stride_fifos_src; + sdmac->stride_fifos_dst = sdmacfg->stride_fifos_dst; + sdmac->words_per_fifo = sdmacfg->words_per_fifo; sdmac->sw_done = sdmacfg->sw_done; } @@ -2183,8 +2211,8 @@ static int sdma_probe(struct platform_device *pdev) if (ret) goto err_clk; - ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, "sdma", - sdma); + ret = devm_request_irq(&pdev->dev, irq, sdma_int_handler, 0, + dev_name(&pdev->dev), sdma); if (ret) goto err_irq; diff --git a/drivers/dma/mediatek/mtk-cqdma.c b/drivers/dma/mediatek/mtk-cqdma.c index f8847c48ba03..9ae92b8940ef 100644 --- a/drivers/dma/mediatek/mtk-cqdma.c +++ b/drivers/dma/mediatek/mtk-cqdma.c @@ -373,7 +373,7 @@ static void mtk_cqdma_tasklet_cb(struct tasklet_struct *t) /* * free child CVD after completion. - * the parent CVD would be freeed with desc_free by user. + * the parent CVD would be freed with desc_free by user. */ if (cvd->parent != cvd) kfree(cvd); diff --git a/drivers/dma/mediatek/mtk-hsdma.c b/drivers/dma/mediatek/mtk-hsdma.c index 9ebd9231f62f..f7717c44b887 100644 --- a/drivers/dma/mediatek/mtk-hsdma.c +++ b/drivers/dma/mediatek/mtk-hsdma.c @@ -138,7 +138,7 @@ struct mtk_hsdma_vdesc { /** * struct mtk_hsdma_cb - This is the struct holding extra info required for RX - * ring to know what relevant VD the the PD is being + * ring to know what relevant VD the PD is being * mapped to. * @vd: Pointer to the relevant VD. * @flag: Flag indicating what action should be taken when VD @@ -761,7 +761,7 @@ static void mtk_hsdma_free_active_desc(struct dma_chan *c) /* * Once issue_synchronize is being set, which means once the hardware * consumes all descriptors for the channel in the ring, the - * synchronization must be be notified immediately it is completed. + * synchronization must be notified immediately it is completed. */ spin_lock(&hvc->vc.lock); if (!list_empty(&hvc->desc_hw_processing)) { diff --git a/drivers/dma/moxart-dma.c b/drivers/dma/moxart-dma.c index 74755093e14b..7459382a8353 100644 --- a/drivers/dma/moxart-dma.c +++ b/drivers/dma/moxart-dma.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MOXA ART SoCs DMA Engine support. * * Copyright (C) 2013 Jonas Jensen * * Jonas Jensen - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c index f10b29034da1..f629ef6fd3c2 100644 --- a/drivers/dma/mv_xor_v2.c +++ b/drivers/dma/mv_xor_v2.c @@ -313,7 +313,7 @@ mv_xor_v2_tx_submit(struct dma_async_tx_descriptor *tx) "%s sw_desc %p: async_tx %p\n", __func__, sw_desc, &sw_desc->async_tx); - /* assign coookie */ + /* assign cookie */ spin_lock_bh(&xor_dev->lock); cookie = dma_cookie_assign(tx); diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c index 1f0bbaed4643..95a462a1f511 100644 --- a/drivers/dma/owl-dma.c +++ b/drivers/dma/owl-dma.c @@ -193,7 +193,7 @@ struct owl_dma_pchan { /** * struct owl_dma_pchan - Wrapper for DMA ENGINE channel - * @vc: wrappped virtual channel + * @vc: wrapped virtual channel * @pchan: the physical channel utilized by this channel * @txd: active transaction on this channel * @cfg: slave configuration for this channel diff --git a/drivers/dma/ppc4xx/adma.h b/drivers/dma/ppc4xx/adma.h index 26b7a5ed9ac7..f8a5d7c1fb40 100644 --- a/drivers/dma/ppc4xx/adma.h +++ b/drivers/dma/ppc4xx/adma.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * 2006-2009 (C) DENX Software Engineering. * * Author: Yuri Tikhonov - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of - * any kind, whether express or implied. */ #ifndef _PPC440SPE_ADMA_H diff --git a/drivers/dma/ppc4xx/dma.h b/drivers/dma/ppc4xx/dma.h index bcde2df2f373..1ff4be23db0f 100644 --- a/drivers/dma/ppc4xx/dma.h +++ b/drivers/dma/ppc4xx/dma.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * 440SPe's DMA engines support header file * * 2006-2009 (C) DENX Software Engineering. * * Author: Yuri Tikhonov - * - * This file is licensed under the term of the GNU General Public License - * version 2. The program licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _PPC440SPE_DMA_H diff --git a/drivers/dma/ppc4xx/xor.h b/drivers/dma/ppc4xx/xor.h index daed7384daac..da1230df2817 100644 --- a/drivers/dma/ppc4xx/xor.h +++ b/drivers/dma/ppc4xx/xor.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * 440SPe's XOR engines support header file * * 2006-2009 (C) DENX Software Engineering. * * Author: Yuri Tikhonov - * - * This file is licensed under the term of the GNU General Public License - * version 2. The program licensed "as is" without any warranty of any - * kind, whether express or implied. */ #ifndef _PPC440SPE_XOR_H diff --git a/drivers/dma/s3c24xx-dma.c b/drivers/dma/s3c24xx-dma.c index 8e14c72d03f0..f6ed7e889781 100644 --- a/drivers/dma/s3c24xx-dma.c +++ b/drivers/dma/s3c24xx-dma.c @@ -202,7 +202,7 @@ struct s3c24xx_dma_phy { * struct s3c24xx_dma_chan - this structure wraps a DMA ENGINE channel * @id: the id of the channel * @name: name of the channel - * @vc: wrappped virtual channel + * @vc: wrapped virtual channel * @phy: the physical channel utilized by this channel, if there is one * @runtime_addr: address for RX/TX according to the runtime config * @at: active transaction on this channel diff --git a/drivers/dma/sf-pdma/sf-pdma.c b/drivers/dma/sf-pdma/sf-pdma.c index db5a4ef76077..4f8b8498c5c6 100644 --- a/drivers/dma/sf-pdma/sf-pdma.c +++ b/drivers/dma/sf-pdma/sf-pdma.c @@ -52,16 +52,6 @@ static inline struct sf_pdma_desc *to_sf_pdma_desc(struct virt_dma_desc *vd) static struct sf_pdma_desc *sf_pdma_alloc_desc(struct sf_pdma_chan *chan) { struct sf_pdma_desc *desc; - unsigned long flags; - - spin_lock_irqsave(&chan->lock, flags); - - if (chan->desc && !chan->desc->in_use) { - spin_unlock_irqrestore(&chan->lock, flags); - return chan->desc; - } - - spin_unlock_irqrestore(&chan->lock, flags); desc = kzalloc(sizeof(*desc), GFP_NOWAIT); if (!desc) @@ -111,7 +101,6 @@ sf_pdma_prep_dma_memcpy(struct dma_chan *dchan, dma_addr_t dest, dma_addr_t src, desc->async_tx = vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); spin_lock_irqsave(&chan->vchan.lock, iflags); - chan->desc = desc; sf_pdma_fill_desc(desc, dest, src, len); spin_unlock_irqrestore(&chan->vchan.lock, iflags); @@ -170,11 +159,17 @@ static size_t sf_pdma_desc_residue(struct sf_pdma_chan *chan, unsigned long flags; u64 residue = 0; struct sf_pdma_desc *desc; - struct dma_async_tx_descriptor *tx; + struct dma_async_tx_descriptor *tx = NULL; spin_lock_irqsave(&chan->vchan.lock, flags); - tx = &chan->desc->vdesc.tx; + list_for_each_entry(vd, &chan->vchan.desc_submitted, node) + if (vd->tx.cookie == cookie) + tx = &vd->tx; + + if (!tx) + goto out; + if (cookie == tx->chan->completed_cookie) goto out; @@ -241,6 +236,19 @@ static void sf_pdma_enable_request(struct sf_pdma_chan *chan) writel(v, regs->ctrl); } +static struct sf_pdma_desc *sf_pdma_get_first_pending_desc(struct sf_pdma_chan *chan) +{ + struct virt_dma_chan *vchan = &chan->vchan; + struct virt_dma_desc *vdesc; + + if (list_empty(&vchan->desc_issued)) + return NULL; + + vdesc = list_first_entry(&vchan->desc_issued, struct virt_dma_desc, node); + + return container_of(vdesc, struct sf_pdma_desc, vdesc); +} + static void sf_pdma_xfer_desc(struct sf_pdma_chan *chan) { struct sf_pdma_desc *desc = chan->desc; @@ -268,8 +276,11 @@ static void sf_pdma_issue_pending(struct dma_chan *dchan) spin_lock_irqsave(&chan->vchan.lock, flags); - if (vchan_issue_pending(&chan->vchan) && chan->desc) + if (!chan->desc && vchan_issue_pending(&chan->vchan)) { + /* vchan_issue_pending has made a check that desc in not NULL */ + chan->desc = sf_pdma_get_first_pending_desc(chan); sf_pdma_xfer_desc(chan); + } spin_unlock_irqrestore(&chan->vchan.lock, flags); } @@ -298,6 +309,11 @@ static void sf_pdma_donebh_tasklet(struct tasklet_struct *t) spin_lock_irqsave(&chan->vchan.lock, flags); list_del(&chan->desc->vdesc.node); vchan_cookie_complete(&chan->desc->vdesc); + + chan->desc = sf_pdma_get_first_pending_desc(chan); + if (chan->desc) + sf_pdma_xfer_desc(chan); + spin_unlock_irqrestore(&chan->vchan.lock, flags); } diff --git a/drivers/dma/sh/rz-dmac.c b/drivers/dma/sh/rz-dmac.c index ee2872e7d64c..476847a4916b 100644 --- a/drivers/dma/sh/rz-dmac.c +++ b/drivers/dma/sh/rz-dmac.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -630,6 +631,21 @@ static void rz_dmac_virt_desc_free(struct virt_dma_desc *vd) */ } +static void rz_dmac_device_synchronize(struct dma_chan *chan) +{ + struct rz_dmac_chan *channel = to_rz_dmac_chan(chan); + struct rz_dmac *dmac = to_rz_dmac(chan->device); + u32 chstat; + int ret; + + ret = read_poll_timeout(rz_dmac_ch_readl, chstat, !(chstat & CHSTAT_EN), + 100, 100000, false, channel, CHSTAT, 1); + if (ret < 0) + dev_warn(dmac->dev, "DMA Timeout"); + + rz_dmac_set_dmars_register(dmac, channel->index, 0); +} + /* * ----------------------------------------------------------------------------- * IRQ handling @@ -909,6 +925,7 @@ static int rz_dmac_probe(struct platform_device *pdev) engine->device_config = rz_dmac_config; engine->device_terminate_all = rz_dmac_terminate_all; engine->device_issue_pending = rz_dmac_issue_pending; + engine->device_synchronize = rz_dmac_device_synchronize; engine->copy_align = DMAENGINE_ALIGN_1_BYTE; dma_set_max_seg_size(engine->dev, U32_MAX); diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 2138b80435ab..474d3ba8ec9f 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -1237,11 +1237,8 @@ static int sprd_dma_remove(struct platform_device *pdev) { struct sprd_dma_dev *sdev = platform_get_drvdata(pdev); struct sprd_dma_chn *c, *cn; - int ret; - ret = pm_runtime_get_sync(&pdev->dev); - if (ret < 0) - return ret; + pm_runtime_get_sync(&pdev->dev); /* explicitly free the irq */ if (sdev->irq > 0) diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index e1827393143f..f093e08c23b1 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -1970,7 +1970,7 @@ static int d40_config_memcpy(struct d40_chan *d40c) dma_has_cap(DMA_SLAVE, cap)) { d40c->dma_cfg = dma40_memcpy_conf_phy; - /* Generate interrrupt at end of transfer or relink. */ + /* Generate interrupt at end of transfer or relink. */ d40c->dst_def_cfg |= BIT(D40_SREG_CFG_TIM_POS); /* Generate interrupt on error. */ diff --git a/drivers/dma/stm32-mdma.c b/drivers/dma/stm32-mdma.c index caf0cce8f528..b11927ed4367 100644 --- a/drivers/dma/stm32-mdma.c +++ b/drivers/dma/stm32-mdma.c @@ -1328,12 +1328,7 @@ static irqreturn_t stm32_mdma_irq_handler(int irq, void *devid) return IRQ_NONE; } id = __ffs(status); - chan = &dmadev->chan[id]; - if (!chan) { - dev_warn(mdma2dev(dmadev), "MDMA channel not initialized\n"); - return IRQ_NONE; - } /* Handle interrupt for the channel */ spin_lock(&chan->vchan.lock); diff --git a/drivers/dma/sun4i-dma.c b/drivers/dma/sun4i-dma.c index 93f1645ae928..f291b1b4db32 100644 --- a/drivers/dma/sun4i-dma.c +++ b/drivers/dma/sun4i-dma.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -122,6 +123,15 @@ SUN4I_DDMA_PARA_DST_WAIT_CYCLES(2) | \ SUN4I_DDMA_PARA_SRC_WAIT_CYCLES(2)) +/* + * Normal DMA supports individual transfers (segments) up to 128k. + * Dedicated DMA supports transfers up to 16M. We can only report + * one size limit, so we have to use the smaller value. + */ +#define SUN4I_NDMA_MAX_SEG_SIZE SZ_128K +#define SUN4I_DDMA_MAX_SEG_SIZE SZ_16M +#define SUN4I_DMA_MAX_SEG_SIZE SUN4I_NDMA_MAX_SEG_SIZE + struct sun4i_dma_pchan { /* Register base of channel */ void __iomem *base; @@ -155,7 +165,8 @@ struct sun4i_dma_contract { struct virt_dma_desc vd; struct list_head demands; struct list_head completed_demands; - int is_cyclic; + bool is_cyclic : 1; + bool use_half_int : 1; }; struct sun4i_dma_dev { @@ -372,7 +383,7 @@ static int __execute_vchan_pending(struct sun4i_dma_dev *priv, if (promise) { vchan->contract = contract; vchan->pchan = pchan; - set_pchan_interrupt(priv, pchan, contract->is_cyclic, 1); + set_pchan_interrupt(priv, pchan, contract->use_half_int, 1); configure_pchan(pchan, promise); } @@ -735,12 +746,21 @@ sun4i_dma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf, size_t len, * * Which requires half the engine programming for the same * functionality. + * + * This only works if two periods fit in a single promise. That will + * always be the case for dedicated DMA, where the hardware has a much + * larger maximum transfer size than advertised to clients. */ - nr_periods = DIV_ROUND_UP(len / period_len, 2); + if (vchan->is_dedicated || period_len <= SUN4I_NDMA_MAX_SEG_SIZE / 2) { + period_len *= 2; + contract->use_half_int = 1; + } + + nr_periods = DIV_ROUND_UP(len, period_len); for (i = 0; i < nr_periods; i++) { /* Calculate the offset in the buffer and the length needed */ - offset = i * period_len * 2; - plength = min((len - offset), (period_len * 2)); + offset = i * period_len; + plength = min((len - offset), period_len); if (dir == DMA_MEM_TO_DEV) src = buf + offset; else @@ -1149,6 +1169,8 @@ static int sun4i_dma_probe(struct platform_device *pdev) platform_set_drvdata(pdev, priv); spin_lock_init(&priv->lock); + dma_set_max_seg_size(&pdev->dev, SUN4I_DMA_MAX_SEG_SIZE); + dma_cap_zero(priv->slave.cap_mask); dma_cap_set(DMA_PRIVATE, priv->slave.cap_mask); dma_cap_set(DMA_MEMCPY, priv->slave.cap_mask); diff --git a/drivers/dma/tegra186-gpc-dma.c b/drivers/dma/tegra186-gpc-dma.c index 05cd451f541d..fa9bda4a2bc6 100644 --- a/drivers/dma/tegra186-gpc-dma.c +++ b/drivers/dma/tegra186-gpc-dma.c @@ -157,8 +157,8 @@ * If any burst is in flight and DMA paused then this is the time to complete * on-flight burst and update DMA status register. */ -#define TEGRA_GPCDMA_BURST_COMPLETE_TIME 20 -#define TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT 100 +#define TEGRA_GPCDMA_BURST_COMPLETE_TIME 10 +#define TEGRA_GPCDMA_BURST_COMPLETION_TIMEOUT 5000 /* 5 msec */ /* Channel base address offset from GPCDMA base address */ #define TEGRA_GPCDMA_CHANNEL_BASE_ADD_OFFSET 0x20000 @@ -432,6 +432,17 @@ static int tegra_dma_device_resume(struct dma_chan *dc) return 0; } +static inline int tegra_dma_pause_noerr(struct tegra_dma_channel *tdc) +{ + /* Return 0 irrespective of PAUSE status. + * This is useful to recover channels that can exit out of flush + * state when the channel is disabled. + */ + + tegra_dma_pause(tdc); + return 0; +} + static void tegra_dma_disable(struct tegra_dma_channel *tdc) { u32 csr, status; @@ -1292,6 +1303,14 @@ static const struct tegra_dma_chip_data tegra194_dma_chip_data = { .terminate = tegra_dma_pause, }; +static const struct tegra_dma_chip_data tegra234_dma_chip_data = { + .nr_channels = 31, + .channel_reg_size = SZ_64K, + .max_dma_count = SZ_1G, + .hw_support_pause = true, + .terminate = tegra_dma_pause_noerr, +}; + static const struct of_device_id tegra_dma_of_match[] = { { .compatible = "nvidia,tegra186-gpcdma", @@ -1299,6 +1318,9 @@ static const struct of_device_id tegra_dma_of_match[] = { }, { .compatible = "nvidia,tegra194-gpcdma", .data = &tegra194_dma_chip_data, + }, { + .compatible = "nvidia,tegra234-gpcdma", + .data = &tegra234_dma_chip_data, }, { }, }; diff --git a/drivers/dma/ti/edma.c b/drivers/dma/ti/edma.c index 3ea8ef7f57df..4cbca80ee16e 100644 --- a/drivers/dma/ti/edma.c +++ b/drivers/dma/ti/edma.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI EDMA DMA engine driver * * Copyright 2012 Texas Instruments - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/dma/ti/k3-psil-j721s2.c b/drivers/dma/ti/k3-psil-j721s2.c index 4c4172a4d271..a488c2250623 100644 --- a/drivers/dma/ti/k3-psil-j721s2.c +++ b/drivers/dma/ti/k3-psil-j721s2.c @@ -112,6 +112,11 @@ static struct psil_ep j721s2_src_ep_map[] = { PSIL_PDMA_XY_PKT(0x4707), PSIL_PDMA_XY_PKT(0x4708), PSIL_PDMA_XY_PKT(0x4709), + /* MAIN SA2UL */ + PSIL_SA2UL(0x4a40, 0), + PSIL_SA2UL(0x4a41, 0), + PSIL_SA2UL(0x4a42, 0), + PSIL_SA2UL(0x4a43, 0), /* CPSW0 */ PSIL_ETHERNET(0x7000), /* MCU_PDMA0 (MCU_PDMA_MISC_G0) - SPI0 */ @@ -144,6 +149,9 @@ static struct psil_ep j721s2_src_ep_map[] = { /* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */ static struct psil_ep j721s2_dst_ep_map[] = { + /* MAIN SA2UL */ + PSIL_SA2UL(0xca40, 1), + PSIL_SA2UL(0xca41, 1), /* CPSW0 */ PSIL_ETHERNET(0xf000), PSIL_ETHERNET(0xf001), diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index cd62bbb50e8b..6276934d4d2b 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -2127,126 +2127,6 @@ error: return NULL; } -/** - * xilinx_cdma_prep_memcpy_sg - prepare descriptors for a memcpy_sg transaction - * @dchan: DMA channel - * @dst_sg: Destination scatter list - * @dst_sg_len: Number of entries in destination scatter list - * @src_sg: Source scatter list - * @src_sg_len: Number of entries in source scatter list - * @flags: transfer ack flags - * - * Return: Async transaction descriptor on success and NULL on failure - */ -static struct dma_async_tx_descriptor *xilinx_cdma_prep_memcpy_sg( - struct dma_chan *dchan, struct scatterlist *dst_sg, - unsigned int dst_sg_len, struct scatterlist *src_sg, - unsigned int src_sg_len, unsigned long flags) -{ - struct xilinx_dma_chan *chan = to_xilinx_chan(dchan); - struct xilinx_dma_tx_descriptor *desc; - struct xilinx_cdma_tx_segment *segment, *prev = NULL; - struct xilinx_cdma_desc_hw *hw; - size_t len, dst_avail, src_avail; - dma_addr_t dma_dst, dma_src; - - if (unlikely(dst_sg_len == 0 || src_sg_len == 0)) - return NULL; - - if (unlikely(!dst_sg || !src_sg)) - return NULL; - - desc = xilinx_dma_alloc_tx_descriptor(chan); - if (!desc) - return NULL; - - dma_async_tx_descriptor_init(&desc->async_tx, &chan->common); - desc->async_tx.tx_submit = xilinx_dma_tx_submit; - - dst_avail = sg_dma_len(dst_sg); - src_avail = sg_dma_len(src_sg); - /* - * loop until there is either no more source or no more destination - * scatterlist entry - */ - while (true) { - len = min_t(size_t, src_avail, dst_avail); - len = min_t(size_t, len, chan->xdev->max_buffer_len); - if (len == 0) - goto fetch; - - /* Allocate the link descriptor from DMA pool */ - segment = xilinx_cdma_alloc_tx_segment(chan); - if (!segment) - goto error; - - dma_dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - - dst_avail; - dma_src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - - src_avail; - hw = &segment->hw; - hw->control = len; - hw->src_addr = dma_src; - hw->dest_addr = dma_dst; - if (chan->ext_addr) { - hw->src_addr_msb = upper_32_bits(dma_src); - hw->dest_addr_msb = upper_32_bits(dma_dst); - } - - if (prev) { - prev->hw.next_desc = segment->phys; - if (chan->ext_addr) - prev->hw.next_desc_msb = - upper_32_bits(segment->phys); - } - - prev = segment; - dst_avail -= len; - src_avail -= len; - list_add_tail(&segment->node, &desc->segments); - -fetch: - /* Fetch the next dst scatterlist entry */ - if (dst_avail == 0) { - if (dst_sg_len == 0) - break; - dst_sg = sg_next(dst_sg); - if (dst_sg == NULL) - break; - dst_sg_len--; - dst_avail = sg_dma_len(dst_sg); - } - /* Fetch the next src scatterlist entry */ - if (src_avail == 0) { - if (src_sg_len == 0) - break; - src_sg = sg_next(src_sg); - if (src_sg == NULL) - break; - src_sg_len--; - src_avail = sg_dma_len(src_sg); - } - } - - if (list_empty(&desc->segments)) { - dev_err(chan->xdev->dev, - "%s: Zero-size SG transfer requested\n", __func__); - goto error; - } - - /* Link the last hardware descriptor with the first. */ - segment = list_first_entry(&desc->segments, - struct xilinx_cdma_tx_segment, node); - desc->async_tx.phys = segment->phys; - prev->hw.next_desc = segment->phys; - - return &desc->async_tx; - -error: - xilinx_dma_free_tx_descriptor(chan, desc); - return NULL; -} - /** * xilinx_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction * @dchan: DMA channel @@ -3240,9 +3120,7 @@ static int xilinx_dma_probe(struct platform_device *pdev) DMA_RESIDUE_GRANULARITY_SEGMENT; } else if (xdev->dma_config->dmatype == XDMA_TYPE_CDMA) { dma_cap_set(DMA_MEMCPY, xdev->common.cap_mask); - dma_cap_set(DMA_MEMCPY_SG, xdev->common.cap_mask); xdev->common.device_prep_dma_memcpy = xilinx_cdma_prep_memcpy; - xdev->common.device_prep_dma_memcpy_sg = xilinx_cdma_prep_memcpy_sg; /* Residue calculation is supported by only AXI DMA and CDMA */ xdev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT; diff --git a/drivers/dma/xilinx/xilinx_dpdma.c b/drivers/dma/xilinx/xilinx_dpdma.c index b0f4948b00a5..84dc5240a807 100644 --- a/drivers/dma/xilinx/xilinx_dpdma.c +++ b/drivers/dma/xilinx/xilinx_dpdma.c @@ -376,7 +376,7 @@ static ssize_t xilinx_dpdma_debugfs_read(struct file *f, char __user *buf, if (ret < 0) goto done; } else { - strlcpy(kern_buff, "No testcase executed", + strscpy(kern_buff, "No testcase executed", XILINX_DPDMA_DEBUGFS_READ_MAX_SIZE); } @@ -1652,10 +1652,8 @@ static int xilinx_dpdma_probe(struct platform_device *pdev) dpdma_hw_init(xdev); xdev->irq = platform_get_irq(pdev, 0); - if (xdev->irq < 0) { - dev_err(xdev->dev, "failed to get platform irq\n"); + if (xdev->irq < 0) return xdev->irq; - } ret = request_irq(xdev->irq, xilinx_dpdma_irq_handler, IRQF_SHARED, dev_name(xdev->dev), xdev); diff --git a/drivers/edac/Kconfig b/drivers/edac/Kconfig index d3e2477948c8..17562cf1fe97 100644 --- a/drivers/edac/Kconfig +++ b/drivers/edac/Kconfig @@ -263,6 +263,7 @@ config EDAC_I10NM config EDAC_PND2 tristate "Intel Pondicherry2" depends on PCI && X86_64 && X86_MCE_INTEL + select P2SB if X86 help Support for error detection and correction on the Intel Pondicherry2 Integrated Memory Controller. This SoC IP is diff --git a/drivers/edac/edac_pci.c b/drivers/edac/edac_pci.c index 2205d7e731db..64c142aecca7 100644 --- a/drivers/edac/edac_pci.c +++ b/drivers/edac/edac_pci.c @@ -1,13 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * EDAC PCI component * * Author: Dave Jiang * - * 2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * + * 2007 (c) MontaVista Software, Inc. */ #include #include diff --git a/drivers/edac/fsl_ddr_edac.c b/drivers/edac/fsl_ddr_edac.c index 6d8ea226010d..ac2102b25706 100644 --- a/drivers/edac/fsl_ddr_edac.c +++ b/drivers/edac/fsl_ddr_edac.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Freescale Memory Controller kernel module * @@ -9,10 +10,7 @@ * * Author: Dave Jiang * - * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. + * 2006-2007 (c) MontaVista Software, Inc. */ #include #include diff --git a/drivers/edac/fsl_ddr_edac.h b/drivers/edac/fsl_ddr_edac.h index 589b9b4a5e8a..332439d7b2d9 100644 --- a/drivers/edac/fsl_ddr_edac.h +++ b/drivers/edac/fsl_ddr_edac.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Freescale Memory Controller kernel module * @@ -7,11 +8,7 @@ * * Author: Dave Jiang * - * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * + * 2006-2007 (c) MontaVista Software, Inc. */ #ifndef _FSL_DDR_EDAC_H_ #define _FSL_DDR_EDAC_H_ diff --git a/drivers/edac/mpc85xx_edac.h b/drivers/edac/mpc85xx_edac.h index 3f6fb16ad34f..66a046ae33ee 100644 --- a/drivers/edac/mpc85xx_edac.h +++ b/drivers/edac/mpc85xx_edac.h @@ -1,12 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Freescale MPC85xx Memory Controller kernel module * Author: Dave Jiang * - * 2006-2007 (c) MontaVista Software, Inc. This file is licensed under - * the terms of the GNU General Public License version 2. This program - * is licensed "as is" without any warranty of any kind, whether express - * or implied. - * + * 2006-2007 (c) MontaVista Software, Inc. */ #ifndef _MPC85XX_EDAC_H_ #define _MPC85XX_EDAC_H_ diff --git a/drivers/edac/pnd2_edac.c b/drivers/edac/pnd2_edac.c index c94ca1f790c4..a20b299f1202 100644 --- a/drivers/edac/pnd2_edac.c +++ b/drivers/edac/pnd2_edac.c @@ -28,6 +28,8 @@ #include #include #include +#include + #include #include #include @@ -232,42 +234,14 @@ static u64 get_mem_ctrl_hub_base_addr(void) return U64_LSHIFT(hi.base, 32) | U64_LSHIFT(lo.base, 15); } -static u64 get_sideband_reg_base_addr(void) -{ - struct pci_dev *pdev; - u32 hi, lo; - u8 hidden; - - pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x19dd, NULL); - if (pdev) { - /* Unhide the P2SB device, if it's hidden */ - pci_read_config_byte(pdev, 0xe1, &hidden); - if (hidden) - pci_write_config_byte(pdev, 0xe1, 0); - - pci_read_config_dword(pdev, 0x10, &lo); - pci_read_config_dword(pdev, 0x14, &hi); - lo &= 0xfffffff0; - - /* Hide the P2SB device, if it was hidden before */ - if (hidden) - pci_write_config_byte(pdev, 0xe1, hidden); - - pci_dev_put(pdev); - return (U64_LSHIFT(hi, 32) | U64_LSHIFT(lo, 0)); - } else { - return 0xfd000000; - } -} - #define DNV_MCHBAR_SIZE 0x8000 #define DNV_SB_PORT_SIZE 0x10000 static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *name) { struct pci_dev *pdev; - char *base; - u64 addr; - unsigned long size; + void __iomem *base; + struct resource r; + int ret; if (op == 4) { pdev = pci_get_device(PCI_VENDOR_ID_INTEL, 0x1980, NULL); @@ -279,26 +253,30 @@ static int dnv_rd_reg(int port, int off, int op, void *data, size_t sz, char *na } else { /* MMIO via memory controller hub base address */ if (op == 0 && port == 0x4c) { - addr = get_mem_ctrl_hub_base_addr(); - if (!addr) + memset(&r, 0, sizeof(r)); + + r.start = get_mem_ctrl_hub_base_addr(); + if (!r.start) return -ENODEV; - size = DNV_MCHBAR_SIZE; + r.end = r.start + DNV_MCHBAR_SIZE - 1; } else { /* MMIO via sideband register base address */ - addr = get_sideband_reg_base_addr(); - if (!addr) - return -ENODEV; - addr += (port << 16); - size = DNV_SB_PORT_SIZE; + ret = p2sb_bar(NULL, 0, &r); + if (ret) + return ret; + + r.start += (port << 16); + r.end = r.start + DNV_SB_PORT_SIZE - 1; } - base = ioremap((resource_size_t)addr, size); + base = ioremap(r.start, resource_size(&r)); if (!base) return -ENODEV; if (sz == 8) - *(u32 *)(data + 4) = *(u32 *)(base + off + 4); - *(u32 *)data = *(u32 *)(base + off); + *(u64 *)data = readq(base + off); + else + *(u32 *)data = readl(base + off); iounmap(base); } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b01961999ced..0642f579196f 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -544,6 +544,7 @@ config GPIO_SAMA5D2_PIOBU tristate "SAMA5D2 PIOBU GPIO support" depends on MFD_SYSCON depends on OF_GPIO + depends on ARCH_AT91 || COMPILE_TEST select GPIO_SYSCON help Say yes here to use the PIOBU pins as GPIOs. @@ -690,12 +691,6 @@ config GPIO_VISCONTI help Say yes here to support GPIO on Tohisba Visconti. -config GPIO_VR41XX - tristate "NEC VR4100 series General-purpose I/O Unit support" - depends on CPU_VR41XX - help - Say yes here to support the NEC VR4100 series General-purpose I/O Unit. - config GPIO_VX855 tristate "VIA VX855/VX875 GPIO" depends on (X86 || COMPILE_TEST) && PCI @@ -829,11 +824,24 @@ endmenu menu "Port-mapped I/O GPIO drivers" depends on X86 # Unconditional I/O space access +config GPIO_I8255 + tristate + help + Enables support for the i8255 interface library functions. The i8255 + interface library provides functions to facilitate communication with + interfaces compatible with the venerable Intel 8255 Programmable + Peripheral Interface (PPI). The Intel 8255 PPI chip was first released + in the early 1970s but compatible interfaces are nowadays typically + found embedded in larger VLSI processing chips and FPGA components. + + If built as a module its name will be gpio-i8255. + config GPIO_104_DIO_48E tristate "ACCES 104-DIO-48E GPIO support" depends on PC104 select ISA_BUS_API select GPIOLIB_IRQCHIP + select GPIO_I8255 help Enables GPIO support for the ACCES 104-DIO-48E series (104-DIO-48E, 104-DIO-24E). The base port addresses for the devices may be @@ -857,6 +865,7 @@ config GPIO_104_IDI_48 depends on PC104 select ISA_BUS_API select GPIOLIB_IRQCHIP + select GPIO_I8255 help Enables GPIO support for the ACCES 104-IDI-48 family (104-IDI-48A, 104-IDI-48AC, 104-IDI-48B, 104-IDI-48BC). The base port addresses for @@ -877,6 +886,7 @@ config GPIO_GPIO_MM tristate "Diamond Systems GPIO-MM GPIO support" depends on PC104 select ISA_BUS_API + select GPIO_I8255 help Enables GPIO support for the Diamond Systems GPIO-MM and GPIO-MM-12. diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 14352f6dfe8e..a0985d30f51b 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -67,6 +67,7 @@ obj-$(CONFIG_GPIO_GW_PLD) += gpio-gw-pld.o obj-$(CONFIG_GPIO_HISI) += gpio-hisi.o obj-$(CONFIG_GPIO_HLWD) += gpio-hlwd.o obj-$(CONFIG_HTC_EGPIO) += gpio-htc-egpio.o +obj-$(CONFIG_GPIO_I8255) += gpio-i8255.o obj-$(CONFIG_GPIO_ICH) += gpio-ich.o obj-$(CONFIG_GPIO_IDT3243X) += gpio-idt3243x.o obj-$(CONFIG_GPIO_IOP) += gpio-iop.o @@ -169,7 +170,6 @@ obj-$(CONFIG_GPIO_VF610) += gpio-vf610.o obj-$(CONFIG_GPIO_VIPERBOARD) += gpio-viperboard.o obj-$(CONFIG_GPIO_VIRTIO) += gpio-virtio.o obj-$(CONFIG_GPIO_VISCONTI) += gpio-visconti.o -obj-$(CONFIG_GPIO_VR41XX) += gpio-vr41xx.o obj-$(CONFIG_GPIO_VX855) += gpio-vx855.o obj-$(CONFIG_GPIO_WCD934X) += gpio-wcd934x.o obj-$(CONFIG_GPIO_WHISKEY_COVE) += gpio-wcove.o diff --git a/drivers/gpio/gpio-104-dio-48e.c b/drivers/gpio/gpio-104-dio-48e.c index f118ad9bcd33..a41551870759 100644 --- a/drivers/gpio/gpio-104-dio-48e.c +++ b/drivers/gpio/gpio-104-dio-48e.c @@ -6,8 +6,7 @@ * This driver supports the following ACCES devices: 104-DIO-48E and * 104-DIO-24E. */ -#include -#include +#include #include #include #include @@ -20,6 +19,11 @@ #include #include #include +#include + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define DIO48E_EXTENT 16 #define MAX_NUM_DIO48E max_num_isa_dev(DIO48E_EXTENT) @@ -33,34 +37,54 @@ static unsigned int irq[MAX_NUM_DIO48E]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-DIO-48E interrupt line numbers"); +#define DIO48E_NUM_PPI 2 + +/** + * struct dio48e_reg - device register structure + * @ppi: Programmable Peripheral Interface groups + * @enable_buffer: Enable/Disable Buffer groups + * @unused1: Unused + * @enable_interrupt: Write: Enable Interrupt + * Read: Disable Interrupt + * @unused2: Unused + * @enable_counter: Write: Enable Counter/Timer Addressing + * Read: Disable Counter/Timer Addressing + * @unused3: Unused + * @clear_interrupt: Clear Interrupt + */ +struct dio48e_reg { + struct i8255 ppi[DIO48E_NUM_PPI]; + u8 enable_buffer[DIO48E_NUM_PPI]; + u8 unused1; + u8 enable_interrupt; + u8 unused2; + u8 enable_counter; + u8 unused3; + u8 clear_interrupt; +}; + /** * struct dio48e_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state - * @control: Control registers state - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device - * @irq_mask: I/O bits affected by interrupts + * @chip: instance of the gpio_chip + * @ppi_state: PPI device states + * @lock: synchronization lock to prevent I/O race conditions + * @reg: I/O address offset for the device registers + * @irq_mask: I/O bits affected by interrupts */ struct dio48e_gpio { struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; - unsigned char control[2]; + struct i8255_state ppi_state[DIO48E_NUM_PPI]; raw_spinlock_t lock; - void __iomem *base; + struct dio48e_reg __iomem *reg; unsigned char irq_mask; }; static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - if (dio48egpio->io_state[port] & mask) - return GPIO_LINE_DIRECTION_IN; + if (i8255_get_direction(dio48egpio->ppi_state, offset)) + return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; } @@ -68,38 +92,9 @@ static int dio48e_gpio_get_direction(struct gpio_chip *chip, unsigned int offset static int dio48e_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4; - unsigned long flags; - unsigned int control; - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - dio48egpio->io_state[io_port] |= 0xF0; - dio48egpio->control[control_port] |= BIT(3); - } else { - dio48egpio->io_state[io_port] |= 0x0F; - dio48egpio->control[control_port] |= BIT(0); - } - } else { - dio48egpio->io_state[io_port] |= 0xFF; - if (io_port == 0 || io_port == 3) - dio48egpio->control[control_port] |= BIT(4); - else - dio48egpio->control[control_port] |= BIT(1); - } - - control = BIT(7) | dio48egpio->control[control_port]; - iowrite8(control, control_addr); - control &= ~BIT(7); - iowrite8(control, control_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_direction_input(dio48egpio->reg->ppi, dio48egpio->ppi_state, + offset); return 0; } @@ -108,48 +103,9 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off int value) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - const unsigned int mask = BIT(offset % 8); - void __iomem *const control_addr = dio48egpio->base + 3 + control_port * 4; - const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; - unsigned long flags; - unsigned int control; - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - dio48egpio->io_state[io_port] &= 0x0F; - dio48egpio->control[control_port] &= ~BIT(3); - } else { - dio48egpio->io_state[io_port] &= 0xF0; - dio48egpio->control[control_port] &= ~BIT(0); - } - } else { - dio48egpio->io_state[io_port] &= 0x00; - if (io_port == 0 || io_port == 3) - dio48egpio->control[control_port] &= ~BIT(4); - else - dio48egpio->control[control_port] &= ~BIT(1); - } - - if (value) - dio48egpio->out_state[io_port] |= mask; - else - dio48egpio->out_state[io_port] &= ~mask; - - control = BIT(7) | dio48egpio->control[control_port]; - iowrite8(control, control_addr); - - iowrite8(dio48egpio->out_state[io_port], dio48egpio->base + out_port); - - control &= ~BIT(7); - iowrite8(control, control_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_direction_output(dio48egpio->reg->ppi, dio48egpio->ppi_state, + offset, value); return 0; } @@ -157,47 +113,16 @@ static int dio48e_gpio_direction_output(struct gpio_chip *chip, unsigned int off static int dio48e_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int in_port = (port > 2) ? port + 1 : port; - unsigned long flags; - unsigned int port_state; - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - /* ensure that GPIO is set for input */ - if (!(dio48egpio->io_state[port] & mask)) { - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - return -EINVAL; - } - - port_state = ioread8(dio48egpio->base + in_port); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - - return !!(port_state & mask); + return i8255_get(dio48egpio->reg->ppi, offset); } -static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *port_addr; - unsigned long port_state; - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = dio48egpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(dio48egpio->reg->ppi, mask, bits, chip->ngpio); return 0; } @@ -205,49 +130,17 @@ static int dio48e_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, static void dio48e_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (port > 2) ? port + 1 : port; - unsigned long flags; - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - if (value) - dio48egpio->out_state[port] |= mask; - else - dio48egpio->out_state[port] &= ~mask; - - iowrite8(dio48egpio->out_state[port], dio48egpio->base + out_port); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); + i8255_set(dio48egpio->reg->ppi, dio48egpio->ppi_state, offset, value); } static void dio48e_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct dio48e_gpio *const dio48egpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - void __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - port_addr = dio48egpio->base + ports[index]; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - raw_spin_lock_irqsave(&dio48egpio->lock, flags); - - /* update output state data and set device gpio register */ - dio48egpio->out_state[index] &= ~gpio_mask; - dio48egpio->out_state[index] |= bitmask; - iowrite8(dio48egpio->out_state[index], port_addr); - - raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); - } + i8255_set_multiple(dio48egpio->reg->ppi, dio48egpio->ppi_state, mask, + bits, chip->ngpio); } static void dio48e_irq_ack(struct irq_data *data) @@ -274,7 +167,7 @@ static void dio48e_irq_mask(struct irq_data *data) if (!dio48egpio->irq_mask) /* disable interrupts */ - ioread8(dio48egpio->base + 0xB); + ioread8(&dio48egpio->reg->enable_interrupt); raw_spin_unlock_irqrestore(&dio48egpio->lock, flags); } @@ -294,8 +187,8 @@ static void dio48e_irq_unmask(struct irq_data *data) if (!dio48egpio->irq_mask) { /* enable interrupts */ - iowrite8(0x00, dio48egpio->base + 0xF); - iowrite8(0x00, dio48egpio->base + 0xB); + iowrite8(0x00, &dio48egpio->reg->clear_interrupt); + iowrite8(0x00, &dio48egpio->reg->enable_interrupt); } if (offset == 19) @@ -341,7 +234,7 @@ static irqreturn_t dio48e_irq_handler(int irq, void *dev_id) raw_spin_lock(&dio48egpio->lock); - iowrite8(0x00, dio48egpio->base + 0xF); + iowrite8(0x00, &dio48egpio->reg->clear_interrupt); raw_spin_unlock(&dio48egpio->lock); @@ -373,11 +266,26 @@ static int dio48e_irq_init_hw(struct gpio_chip *gc) struct dio48e_gpio *const dio48egpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - ioread8(dio48egpio->base + 0xB); + ioread8(&dio48egpio->reg->enable_interrupt); return 0; } +static void dio48e_init_ppi(struct i8255 __iomem *const ppi, + struct i8255_state *const ppi_state) +{ + const unsigned long ngpio = 24; + const unsigned long mask = GENMASK(ngpio - 1, 0); + const unsigned long bits = 0; + unsigned long i; + + /* Initialize all GPIO to output 0 */ + for (i = 0; i < DIO48E_NUM_PPI; i++) { + i8255_mode0_output(&ppi[i]); + i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio); + } +} + static int dio48e_probe(struct device *dev, unsigned int id) { struct dio48e_gpio *dio48egpio; @@ -395,8 +303,8 @@ static int dio48e_probe(struct device *dev, unsigned int id) return -EBUSY; } - dio48egpio->base = devm_ioport_map(dev, base[id], DIO48E_EXTENT); - if (!dio48egpio->base) + dio48egpio->reg = devm_ioport_map(dev, base[id], DIO48E_EXTENT); + if (!dio48egpio->reg) return -ENOMEM; dio48egpio->chip.label = name; @@ -425,17 +333,8 @@ static int dio48e_probe(struct device *dev, unsigned int id) raw_spin_lock_init(&dio48egpio->lock); - /* initialize all GPIO as output */ - iowrite8(0x80, dio48egpio->base + 3); - iowrite8(0x00, dio48egpio->base); - iowrite8(0x00, dio48egpio->base + 1); - iowrite8(0x00, dio48egpio->base + 2); - iowrite8(0x00, dio48egpio->base + 3); - iowrite8(0x80, dio48egpio->base + 7); - iowrite8(0x00, dio48egpio->base + 4); - iowrite8(0x00, dio48egpio->base + 5); - iowrite8(0x00, dio48egpio->base + 6); - iowrite8(0x00, dio48egpio->base + 7); + i8255_state_init(dio48egpio->ppi_state, DIO48E_NUM_PPI); + dio48e_init_ppi(dio48egpio->reg->ppi, dio48egpio->ppi_state); err = devm_gpiochip_add_data(dev, &dio48egpio->chip, dio48egpio); if (err) { diff --git a/drivers/gpio/gpio-104-idi-48.c b/drivers/gpio/gpio-104-idi-48.c index 9521ece3ebef..40be76efeed7 100644 --- a/drivers/gpio/gpio-104-idi-48.c +++ b/drivers/gpio/gpio-104-idi-48.c @@ -6,8 +6,7 @@ * This driver supports the following ACCES devices: 104-IDI-48A, * 104-IDI-48AC, 104-IDI-48B, and 104-IDI-48BC. */ -#include -#include +#include #include #include #include @@ -20,6 +19,11 @@ #include #include #include +#include + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define IDI_48_EXTENT 8 #define MAX_NUM_IDI_48 max_num_isa_dev(IDI_48_EXTENT) @@ -33,73 +37,62 @@ static unsigned int irq[MAX_NUM_IDI_48]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDI-48 interrupt line numbers"); +/** + * struct idi_48_reg - device register structure + * @port0: Port 0 Inputs + * @unused: Unused + * @port1: Port 1 Inputs + * @irq: Read: IRQ Status Register/IRQ Clear + * Write: IRQ Enable/Disable + */ +struct idi_48_reg { + u8 port0[3]; + u8 unused; + u8 port1[3]; + u8 irq; +}; + /** * struct idi_48_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @lock: synchronization lock to prevent I/O race conditions - * @ack_lock: synchronization lock to prevent IRQ handler race conditions * @irq_mask: input bits affected by interrupts - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers * @cos_enb: Change-Of-State IRQ enable boundaries mask */ struct idi_48_gpio { struct gpio_chip chip; - raw_spinlock_t lock; - spinlock_t ack_lock; + spinlock_t lock; unsigned char irq_mask[6]; - void __iomem *base; + struct idi_48_reg __iomem *reg; unsigned char cos_enb; }; -static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { return GPIO_LINE_DIRECTION_IN; } -static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { return 0; } -static int idi_48_gpio_get(struct gpio_chip *chip, unsigned offset) +static int idi_48_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned i; - static const unsigned int register_offset[6] = { 0, 1, 2, 4, 5, 6 }; - void __iomem *port_addr; - unsigned mask; + void __iomem *const ppi = idi48gpio->reg; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - port_addr = idi48gpio->base + register_offset[i / 8]; - mask = BIT(offset - i); - - return !!(ioread8(port_addr) & mask); - } - - /* The following line should never execute since offset < 48 */ - return 0; + return i8255_get(ppi, offset); } static int idi_48_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - void __iomem *port_addr; - unsigned long port_state; + void __iomem *const ppi = idi48gpio->reg; - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = idi48gpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(ppi, mask, bits, chip->ngpio); return 0; } @@ -112,67 +105,56 @@ static void idi_48_irq_mask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - const unsigned offset = irqd_to_hwirq(data); - unsigned i; - unsigned mask; - unsigned boundary; + const unsigned int offset = irqd_to_hwirq(data); + const unsigned long boundary = offset / 8; + const unsigned long mask = BIT(offset % 8); unsigned long flags; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - mask = BIT(offset - i); - boundary = i / 8; + spin_lock_irqsave(&idi48gpio->lock, flags); - idi48gpio->irq_mask[boundary] &= ~mask; + idi48gpio->irq_mask[boundary] &= ~mask; - if (!idi48gpio->irq_mask[boundary]) { - idi48gpio->cos_enb &= ~BIT(boundary); + /* Exit early if there are still input lines with IRQ unmasked */ + if (idi48gpio->irq_mask[boundary]) + goto exit; - raw_spin_lock_irqsave(&idi48gpio->lock, flags); + idi48gpio->cos_enb &= ~BIT(boundary); - iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7); + iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq); - raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); - } - - return; - } +exit: + spin_unlock_irqrestore(&idi48gpio->lock, flags); } static void idi_48_irq_unmask(struct irq_data *data) { struct gpio_chip *chip = irq_data_get_irq_chip_data(data); struct idi_48_gpio *const idi48gpio = gpiochip_get_data(chip); - const unsigned offset = irqd_to_hwirq(data); - unsigned i; - unsigned mask; - unsigned boundary; - unsigned prev_irq_mask; + const unsigned int offset = irqd_to_hwirq(data); + const unsigned long boundary = offset / 8; + const unsigned long mask = BIT(offset % 8); + unsigned int prev_irq_mask; unsigned long flags; - for (i = 0; i < 48; i += 8) - if (offset < i + 8) { - mask = BIT(offset - i); - boundary = i / 8; - prev_irq_mask = idi48gpio->irq_mask[boundary]; + spin_lock_irqsave(&idi48gpio->lock, flags); - idi48gpio->irq_mask[boundary] |= mask; + prev_irq_mask = idi48gpio->irq_mask[boundary]; - if (!prev_irq_mask) { - idi48gpio->cos_enb |= BIT(boundary); + idi48gpio->irq_mask[boundary] |= mask; - raw_spin_lock_irqsave(&idi48gpio->lock, flags); + /* Exit early if IRQ was already unmasked for this boundary */ + if (prev_irq_mask) + goto exit; - iowrite8(idi48gpio->cos_enb, idi48gpio->base + 7); + idi48gpio->cos_enb |= BIT(boundary); - raw_spin_unlock_irqrestore(&idi48gpio->lock, flags); - } + iowrite8(idi48gpio->cos_enb, &idi48gpio->reg->irq); - return; - } +exit: + spin_unlock_irqrestore(&idi48gpio->lock, flags); } -static int idi_48_irq_set_type(struct irq_data *data, unsigned flow_type) +static int idi_48_irq_set_type(struct irq_data *data, unsigned int flow_type) { /* The only valid irq types are none and both-edges */ if (flow_type != IRQ_TYPE_NONE && @@ -200,17 +182,13 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) unsigned long gpio; struct gpio_chip *const chip = &idi48gpio->chip; - spin_lock(&idi48gpio->ack_lock); + spin_lock(&idi48gpio->lock); - raw_spin_lock(&idi48gpio->lock); - - cos_status = ioread8(idi48gpio->base + 7); - - raw_spin_unlock(&idi48gpio->lock); + cos_status = ioread8(&idi48gpio->reg->irq); /* IRQ Status (bit 6) is active low (0 = IRQ generated by device) */ if (cos_status & BIT(6)) { - spin_unlock(&idi48gpio->ack_lock); + spin_unlock(&idi48gpio->lock); return IRQ_NONE; } @@ -228,7 +206,7 @@ static irqreturn_t idi_48_irq_handler(int irq, void *dev_id) } } - spin_unlock(&idi48gpio->ack_lock); + spin_unlock(&idi48gpio->lock); return IRQ_HANDLED; } @@ -250,8 +228,8 @@ static int idi_48_irq_init_hw(struct gpio_chip *gc) struct idi_48_gpio *const idi48gpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - iowrite8(0, idi48gpio->base + 7); - ioread8(idi48gpio->base + 7); + iowrite8(0, &idi48gpio->reg->irq); + ioread8(&idi48gpio->reg->irq); return 0; } @@ -273,8 +251,8 @@ static int idi_48_probe(struct device *dev, unsigned int id) return -EBUSY; } - idi48gpio->base = devm_ioport_map(dev, base[id], IDI_48_EXTENT); - if (!idi48gpio->base) + idi48gpio->reg = devm_ioport_map(dev, base[id], IDI_48_EXTENT); + if (!idi48gpio->reg) return -ENOMEM; idi48gpio->chip.label = name; @@ -298,8 +276,7 @@ static int idi_48_probe(struct device *dev, unsigned int id) girq->handler = handle_edge_irq; girq->init_hw = idi_48_irq_init_hw; - raw_spin_lock_init(&idi48gpio->lock); - spin_lock_init(&idi48gpio->ack_lock); + spin_lock_init(&idi48gpio->lock); err = devm_gpiochip_add_data(dev, &idi48gpio->chip, idi48gpio); if (err) { diff --git a/drivers/gpio/gpio-104-idio-16.c b/drivers/gpio/gpio-104-idio-16.c index 45f7ad8573e1..65a5f581d981 100644 --- a/drivers/gpio/gpio-104-idio-16.c +++ b/drivers/gpio/gpio-104-idio-16.c @@ -6,7 +6,7 @@ * This driver supports the following ACCES devices: 104-IDIO-16, * 104-IDIO-16E, 104-IDO-16, 104-IDIO-8, 104-IDIO-8E, and 104-IDO-8. */ -#include +#include #include #include #include @@ -19,6 +19,7 @@ #include #include #include +#include #define IDIO_16_EXTENT 8 #define MAX_NUM_IDIO_16 max_num_isa_dev(IDIO_16_EXTENT) @@ -32,19 +33,42 @@ static unsigned int irq[MAX_NUM_IDIO_16]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "ACCES 104-IDIO-16 interrupt line numbers"); +/** + * struct idio_16_reg - device registers structure + * @out0_7: Read: N/A + * Write: FET Drive Outputs 0-7 + * @in0_7: Read: Isolated Inputs 0-7 + * Write: Clear Interrupt + * @irq_ctl: Read: Enable IRQ + * Write: Disable IRQ + * @unused: N/A + * @out8_15: Read: N/A + * Write: FET Drive Outputs 8-15 + * @in8_15: Read: Isolated Inputs 8-15 + * Write: N/A + */ +struct idio_16_reg { + u8 out0_7; + u8 in0_7; + u8 irq_ctl; + u8 unused; + u8 out8_15; + u8 in8_15; +}; + /** * struct idio_16_gpio - GPIO device private data structure * @chip: instance of the gpio_chip * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers * @out_state: output bits state */ struct idio_16_gpio { struct gpio_chip chip; raw_spinlock_t lock; unsigned long irq_mask; - void __iomem *base; + struct idio_16_reg __iomem *reg; unsigned int out_state; }; @@ -79,9 +103,9 @@ static int idio_16_gpio_get(struct gpio_chip *chip, unsigned int offset) return -EINVAL; if (offset < 24) - return !!(ioread8(idio16gpio->base + 1) & mask); + return !!(ioread8(&idio16gpio->reg->in0_7) & mask); - return !!(ioread8(idio16gpio->base + 5) & (mask>>8)); + return !!(ioread8(&idio16gpio->reg->in8_15) & (mask>>8)); } static int idio_16_gpio_get_multiple(struct gpio_chip *chip, @@ -91,9 +115,9 @@ static int idio_16_gpio_get_multiple(struct gpio_chip *chip, *bits = 0; if (*mask & GENMASK(23, 16)) - *bits |= (unsigned long)ioread8(idio16gpio->base + 1) << 16; + *bits |= (unsigned long)ioread8(&idio16gpio->reg->in0_7) << 16; if (*mask & GENMASK(31, 24)) - *bits |= (unsigned long)ioread8(idio16gpio->base + 5) << 24; + *bits |= (unsigned long)ioread8(&idio16gpio->reg->in8_15) << 24; return 0; } @@ -116,9 +140,9 @@ static void idio_16_gpio_set(struct gpio_chip *chip, unsigned int offset, idio16gpio->out_state &= ~mask; if (offset > 7) - iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4); + iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15); else - iowrite8(idio16gpio->out_state, idio16gpio->base); + iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -135,9 +159,9 @@ static void idio_16_gpio_set_multiple(struct gpio_chip *chip, idio16gpio->out_state |= *mask & *bits; if (*mask & 0xFF) - iowrite8(idio16gpio->out_state, idio16gpio->base); + iowrite8(idio16gpio->out_state, &idio16gpio->reg->out0_7); if ((*mask >> 8) & 0xFF) - iowrite8(idio16gpio->out_state >> 8, idio16gpio->base + 4); + iowrite8(idio16gpio->out_state >> 8, &idio16gpio->reg->out8_15); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -158,7 +182,7 @@ static void idio_16_irq_mask(struct irq_data *data) if (!idio16gpio->irq_mask) { raw_spin_lock_irqsave(&idio16gpio->lock, flags); - iowrite8(0, idio16gpio->base + 2); + iowrite8(0, &idio16gpio->reg->irq_ctl); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -177,7 +201,7 @@ static void idio_16_irq_unmask(struct irq_data *data) if (!prev_irq_mask) { raw_spin_lock_irqsave(&idio16gpio->lock, flags); - ioread8(idio16gpio->base + 2); + ioread8(&idio16gpio->reg->irq_ctl); raw_spin_unlock_irqrestore(&idio16gpio->lock, flags); } @@ -212,7 +236,7 @@ static irqreturn_t idio_16_irq_handler(int irq, void *dev_id) raw_spin_lock(&idio16gpio->lock); - iowrite8(0, idio16gpio->base + 1); + iowrite8(0, &idio16gpio->reg->in0_7); raw_spin_unlock(&idio16gpio->lock); @@ -232,8 +256,8 @@ static int idio_16_irq_init_hw(struct gpio_chip *gc) struct idio_16_gpio *const idio16gpio = gpiochip_get_data(gc); /* Disable IRQ by default */ - iowrite8(0, idio16gpio->base + 2); - iowrite8(0, idio16gpio->base + 1); + iowrite8(0, &idio16gpio->reg->irq_ctl); + iowrite8(0, &idio16gpio->reg->in0_7); return 0; } @@ -255,8 +279,8 @@ static int idio_16_probe(struct device *dev, unsigned int id) return -EBUSY; } - idio16gpio->base = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); - if (!idio16gpio->base) + idio16gpio->reg = devm_ioport_map(dev, base[id], IDIO_16_EXTENT); + if (!idio16gpio->reg) return -ENOMEM; idio16gpio->chip.label = name; diff --git a/drivers/gpio/gpio-74xx-mmio.c b/drivers/gpio/gpio-74xx-mmio.c index 173e06758e6c..0464f1ecd20d 100644 --- a/drivers/gpio/gpio-74xx-mmio.c +++ b/drivers/gpio/gpio-74xx-mmio.c @@ -5,15 +5,17 @@ * Copyright (C) 2014 Alexander Shiyan */ +#include #include -#include -#include #include +#include +#include #include +#include -#define MMIO_74XX_DIR_IN (0 << 8) -#define MMIO_74XX_DIR_OUT (1 << 8) -#define MMIO_74XX_BIT_CNT(x) ((x) & 0xff) +#define MMIO_74XX_DIR_IN BIT(8) +#define MMIO_74XX_DIR_OUT BIT(9) +#define MMIO_74XX_BIT_CNT(x) ((x) & GENMASK(7, 0)) struct mmio_74xx_gpio_priv { struct gpio_chip gc; @@ -87,7 +89,10 @@ static int mmio_74xx_dir_in(struct gpio_chip *gc, unsigned int gpio) { struct mmio_74xx_gpio_priv *priv = gpiochip_get_data(gc); - return (priv->flags & MMIO_74XX_DIR_OUT) ? -ENOTSUPP : 0; + if (priv->flags & MMIO_74XX_DIR_IN) + return 0; + + return -ENOTSUPP; } static int mmio_74xx_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) @@ -112,7 +117,7 @@ static int mmio_74xx_gpio_probe(struct platform_device *pdev) if (!priv) return -ENOMEM; - priv->flags = (uintptr_t)of_device_get_match_data(&pdev->dev); + priv->flags = (uintptr_t)device_get_match_data(&pdev->dev); dat = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(dat)) diff --git a/drivers/gpio/gpio-adnp.c b/drivers/gpio/gpio-adnp.c index cc349d4e4973..a6439e3daff0 100644 --- a/drivers/gpio/gpio-adnp.c +++ b/drivers/gpio/gpio-adnp.c @@ -6,8 +6,9 @@ #include #include #include +#include #include -#include +#include #include #include @@ -485,22 +486,17 @@ static int adnp_gpio_setup(struct adnp *adnp, unsigned int num_gpios, return 0; } -static int adnp_i2c_probe(struct i2c_client *client, - const struct i2c_device_id *id) +static int adnp_i2c_probe(struct i2c_client *client) { - struct device_node *np = client->dev.of_node; + struct device *dev = &client->dev; struct adnp *adnp; u32 num_gpios; int err; - err = of_property_read_u32(np, "nr-gpios", &num_gpios); + err = device_property_read_u32(dev, "nr-gpios", &num_gpios); if (err < 0) return err; - client->irq = irq_of_parse_and_map(np, 0); - if (!client->irq) - return -EPROBE_DEFER; - adnp = devm_kzalloc(&client->dev, sizeof(*adnp), GFP_KERNEL); if (!adnp) return -ENOMEM; @@ -508,8 +504,7 @@ static int adnp_i2c_probe(struct i2c_client *client, mutex_init(&adnp->i2c_lock); adnp->client = client; - err = adnp_gpio_setup(adnp, num_gpios, - of_property_read_bool(np, "interrupt-controller")); + err = adnp_gpio_setup(adnp, num_gpios, device_property_read_bool(dev, "interrupt-controller")); if (err) return err; @@ -535,7 +530,7 @@ static struct i2c_driver adnp_i2c_driver = { .name = "gpio-adnp", .of_match_table = adnp_of_match, }, - .probe = adnp_i2c_probe, + .probe_new = adnp_i2c_probe, .id_table = adnp_i2c_id, }; module_i2c_driver(adnp_i2c_driver); diff --git a/drivers/gpio/gpio-adp5588.c b/drivers/gpio/gpio-adp5588.c index e388e75103f4..d49f12560cde 100644 --- a/drivers/gpio/gpio-adp5588.c +++ b/drivers/gpio/gpio-adp5588.c @@ -6,20 +6,18 @@ * Copyright 2009-2010 Analog Devices Inc. */ -#include -#include -#include -#include -#include #include +#include +#include #include #include -#include +#include +#include +#include +#include #include -#define DRV_NAME "adp5588-gpio" - /* * Early pre 4.0 Silicon required to delay readout by at least 25ms, * since the Event Counter Register updated 25ms after the interrupt @@ -422,23 +420,21 @@ static int adp5588_gpio_remove(struct i2c_client *client) } static const struct i2c_device_id adp5588_gpio_id[] = { - {DRV_NAME, 0}, + { "adp5588-gpio" }, {} }; MODULE_DEVICE_TABLE(i2c, adp5588_gpio_id); -#ifdef CONFIG_OF static const struct of_device_id adp5588_gpio_of_id[] = { - { .compatible = "adi," DRV_NAME, }, - {}, + { .compatible = "adi,adp5588-gpio" }, + {} }; MODULE_DEVICE_TABLE(of, adp5588_gpio_of_id); -#endif static struct i2c_driver adp5588_gpio_driver = { .driver = { - .name = DRV_NAME, - .of_match_table = of_match_ptr(adp5588_gpio_of_id), + .name = "adp5588-gpio", + .of_match_table = adp5588_gpio_of_id, }, .probe_new = adp5588_gpio_probe, .remove = adp5588_gpio_remove, diff --git a/drivers/gpio/gpio-bcm-kona.c b/drivers/gpio/gpio-bcm-kona.c index e84474494429..70770429ba48 100644 --- a/drivers/gpio/gpio-bcm-kona.c +++ b/drivers/gpio/gpio-bcm-kona.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Broadcom Kona GPIO Driver * * Author: Broadcom Corporation * Copyright (C) 2012-2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/gpio/gpio-brcmstb.c b/drivers/gpio/gpio-brcmstb.c index 6b7439b44690..c55b35da61a0 100644 --- a/drivers/gpio/gpio-brcmstb.c +++ b/drivers/gpio/gpio-brcmstb.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015-2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015-2017 Broadcom #include #include @@ -385,12 +375,7 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) { struct brcmstb_gpio_priv *priv = platform_get_drvdata(pdev); struct brcmstb_gpio_bank *bank; - int offset, ret = 0, virq; - - if (!priv) { - dev_err(&pdev->dev, "called %s without drvdata!\n", __func__); - return -EFAULT; - } + int offset, virq; if (priv->parent_irq > 0) irq_set_chained_handler_and_data(priv->parent_irq, NULL, NULL); @@ -411,7 +396,7 @@ static int brcmstb_gpio_remove(struct platform_device *pdev) list_for_each_entry(bank, &priv->bank_list, node) gpiochip_remove(&bank->gc); - return ret; + return 0; } static int brcmstb_gpio_of_xlate(struct gpio_chip *gc, diff --git a/drivers/gpio/gpio-davinci.c b/drivers/gpio/gpio-davinci.c index f960587f86a3..59c4c48d8296 100644 --- a/drivers/gpio/gpio-davinci.c +++ b/drivers/gpio/gpio-davinci.c @@ -22,6 +22,7 @@ #include #include #include +#include #include @@ -62,6 +63,8 @@ struct davinci_gpio_controller { void __iomem *regs[MAX_REGS_BANKS]; int gpio_unbanked; int irqs[MAX_INT_PER_BANK]; + struct davinci_gpio_regs context[MAX_REGS_BANKS]; + u32 binten_context; }; static inline u32 __gpio_mask(unsigned gpio) @@ -622,6 +625,85 @@ done: return 0; } +static void davinci_gpio_save_context(struct davinci_gpio_controller *chips, + u32 nbank) +{ + struct davinci_gpio_regs __iomem *g; + struct davinci_gpio_regs *context; + u32 bank; + void __iomem *base; + + base = chips->regs[0] - offset_array[0]; + chips->binten_context = readl_relaxed(base + BINTEN); + + for (bank = 0; bank < nbank; bank++) { + g = chips->regs[bank]; + context = &chips->context[bank]; + context->dir = readl_relaxed(&g->dir); + context->set_data = readl_relaxed(&g->set_data); + context->set_rising = readl_relaxed(&g->set_rising); + context->set_falling = readl_relaxed(&g->set_falling); + } + + /* Clear Bank interrupt enable bit */ + writel_relaxed(0, base + BINTEN); + + /* Clear all interrupt status registers */ + writel_relaxed(GENMASK(31, 0), &g->intstat); +} + +static void davinci_gpio_restore_context(struct davinci_gpio_controller *chips, + u32 nbank) +{ + struct davinci_gpio_regs __iomem *g; + struct davinci_gpio_regs *context; + u32 bank; + void __iomem *base; + + base = chips->regs[0] - offset_array[0]; + + if (readl_relaxed(base + BINTEN) != chips->binten_context) + writel_relaxed(chips->binten_context, base + BINTEN); + + for (bank = 0; bank < nbank; bank++) { + g = chips->regs[bank]; + context = &chips->context[bank]; + if (readl_relaxed(&g->dir) != context->dir) + writel_relaxed(context->dir, &g->dir); + if (readl_relaxed(&g->set_data) != context->set_data) + writel_relaxed(context->set_data, &g->set_data); + if (readl_relaxed(&g->set_rising) != context->set_rising) + writel_relaxed(context->set_rising, &g->set_rising); + if (readl_relaxed(&g->set_falling) != context->set_falling) + writel_relaxed(context->set_falling, &g->set_falling); + } +} + +static int davinci_gpio_suspend(struct device *dev) +{ + struct davinci_gpio_controller *chips = dev_get_drvdata(dev); + struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); + u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); + + davinci_gpio_save_context(chips, nbank); + + return 0; +} + +static int davinci_gpio_resume(struct device *dev) +{ + struct davinci_gpio_controller *chips = dev_get_drvdata(dev); + struct davinci_gpio_platform_data *pdata = dev_get_platdata(dev); + u32 nbank = DIV_ROUND_UP(pdata->ngpio, 32); + + davinci_gpio_restore_context(chips, nbank); + + return 0; +} + +DEFINE_SIMPLE_DEV_PM_OPS(davinci_gpio_dev_pm_ops, davinci_gpio_suspend, + davinci_gpio_resume); + static const struct of_device_id davinci_gpio_ids[] = { { .compatible = "ti,keystone-gpio", keystone_gpio_get_irq_chip}, { .compatible = "ti,am654-gpio", keystone_gpio_get_irq_chip}, @@ -634,6 +716,7 @@ static struct platform_driver davinci_gpio_driver = { .probe = davinci_gpio_probe, .driver = { .name = "davinci_gpio", + .pm = pm_sleep_ptr(&davinci_gpio_dev_pm_ops), .of_match_table = of_match_ptr(davinci_gpio_ids), }, }; diff --git a/drivers/gpio/gpio-gpio-mm.c b/drivers/gpio/gpio-gpio-mm.c index 097a06463d01..2689671b6b01 100644 --- a/drivers/gpio/gpio-gpio-mm.c +++ b/drivers/gpio/gpio-gpio-mm.c @@ -6,8 +6,6 @@ * This driver supports the following Diamond Systems devices: GPIO-MM and * GPIO-MM-12. */ -#include -#include #include #include #include @@ -17,7 +15,10 @@ #include #include #include -#include + +#include "gpio-i8255.h" + +MODULE_IMPORT_NS(I8255); #define GPIOMM_EXTENT 8 #define MAX_NUM_GPIOMM max_num_isa_dev(GPIOMM_EXTENT) @@ -27,32 +28,26 @@ static unsigned int num_gpiomm; module_param_hw_array(base, uint, ioport, &num_gpiomm, 0); MODULE_PARM_DESC(base, "Diamond Systems GPIO-MM base addresses"); +#define GPIOMM_NUM_PPI 2 + /** * struct gpiomm_gpio - GPIO device private data structure - * @chip: instance of the gpio_chip - * @io_state: bit I/O state (whether bit is set to input or output) - * @out_state: output bits state - * @control: Control registers state - * @lock: synchronization lock to prevent I/O race conditions - * @base: base port address of the GPIO device + * @chip: instance of the gpio_chip + * @ppi_state: Programmable Peripheral Interface group states + * @ppi: Programmable Peripheral Interface groups */ struct gpiomm_gpio { struct gpio_chip chip; - unsigned char io_state[6]; - unsigned char out_state[6]; - unsigned char control[2]; - spinlock_t lock; - void __iomem *base; + struct i8255_state ppi_state[GPIOMM_NUM_PPI]; + struct i8255 __iomem *ppi; }; static int gpiomm_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - if (gpiommgpio->io_state[port] & mask) + if (i8255_get_direction(gpiommgpio->ppi_state, offset)) return GPIO_LINE_DIRECTION_IN; return GPIO_LINE_DIRECTION_OUT; @@ -62,35 +57,8 @@ static int gpiomm_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - unsigned long flags; - unsigned int control; - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - gpiommgpio->io_state[io_port] |= 0xF0; - gpiommgpio->control[control_port] |= BIT(3); - } else { - gpiommgpio->io_state[io_port] |= 0x0F; - gpiommgpio->control[control_port] |= BIT(0); - } - } else { - gpiommgpio->io_state[io_port] |= 0xFF; - if (io_port == 0 || io_port == 3) - gpiommgpio->control[control_port] |= BIT(4); - else - gpiommgpio->control[control_port] |= BIT(1); - } - - control = BIT(7) | gpiommgpio->control[control_port]; - iowrite8(control, gpiommgpio->base + 3 + control_port*4); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_direction_input(gpiommgpio->ppi, gpiommgpio->ppi_state, offset); return 0; } @@ -99,44 +67,9 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int io_port = offset / 8; - const unsigned int control_port = io_port / 3; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (io_port > 2) ? io_port + 1 : io_port; - unsigned long flags; - unsigned int control; - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* Check if configuring Port C */ - if (io_port == 2 || io_port == 5) { - /* Port C can be configured by nibble */ - if (offset % 8 > 3) { - gpiommgpio->io_state[io_port] &= 0x0F; - gpiommgpio->control[control_port] &= ~BIT(3); - } else { - gpiommgpio->io_state[io_port] &= 0xF0; - gpiommgpio->control[control_port] &= ~BIT(0); - } - } else { - gpiommgpio->io_state[io_port] &= 0x00; - if (io_port == 0 || io_port == 3) - gpiommgpio->control[control_port] &= ~BIT(4); - else - gpiommgpio->control[control_port] &= ~BIT(1); - } - - if (value) - gpiommgpio->out_state[io_port] |= mask; - else - gpiommgpio->out_state[io_port] &= ~mask; - - control = BIT(7) | gpiommgpio->control[control_port]; - iowrite8(control, gpiommgpio->base + 3 + control_port*4); - - iowrite8(gpiommgpio->out_state[io_port], gpiommgpio->base + out_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_direction_output(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, + value); return 0; } @@ -144,47 +77,16 @@ static int gpiomm_gpio_direction_output(struct gpio_chip *chip, static int gpiomm_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int in_port = (port > 2) ? port + 1 : port; - unsigned long flags; - unsigned int port_state; - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* ensure that GPIO is set for input */ - if (!(gpiommgpio->io_state[port] & mask)) { - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - return -EINVAL; - } - - port_state = ioread8(gpiommgpio->base + in_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - - return !!(port_state & mask); + return i8255_get(gpiommgpio->ppi, offset); } -static const size_t ports[] = { 0, 1, 2, 4, 5, 6 }; - static int gpiomm_gpio_get_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - void __iomem *port_addr; - unsigned long port_state; - /* clear bits array to a clean slate */ - bitmap_zero(bits, chip->ngpio); - - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - port_addr = gpiommgpio->base + ports[offset / 8]; - port_state = ioread8(port_addr) & gpio_mask; - - bitmap_set_value8(bits, port_state, offset); - } + i8255_get_multiple(gpiommgpio->ppi, mask, bits, chip->ngpio); return 0; } @@ -193,49 +95,17 @@ static void gpiomm_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - const unsigned int port = offset / 8; - const unsigned int mask = BIT(offset % 8); - const unsigned int out_port = (port > 2) ? port + 1 : port; - unsigned long flags; - spin_lock_irqsave(&gpiommgpio->lock, flags); - - if (value) - gpiommgpio->out_state[port] |= mask; - else - gpiommgpio->out_state[port] &= ~mask; - - iowrite8(gpiommgpio->out_state[port], gpiommgpio->base + out_port); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); + i8255_set(gpiommgpio->ppi, gpiommgpio->ppi_state, offset, value); } static void gpiomm_gpio_set_multiple(struct gpio_chip *chip, unsigned long *mask, unsigned long *bits) { struct gpiomm_gpio *const gpiommgpio = gpiochip_get_data(chip); - unsigned long offset; - unsigned long gpio_mask; - size_t index; - void __iomem *port_addr; - unsigned long bitmask; - unsigned long flags; - for_each_set_clump8(offset, gpio_mask, mask, ARRAY_SIZE(ports) * 8) { - index = offset / 8; - port_addr = gpiommgpio->base + ports[index]; - - bitmask = bitmap_get_value8(bits, offset) & gpio_mask; - - spin_lock_irqsave(&gpiommgpio->lock, flags); - - /* update output state data and set device gpio register */ - gpiommgpio->out_state[index] &= ~gpio_mask; - gpiommgpio->out_state[index] |= bitmask; - iowrite8(gpiommgpio->out_state[index], port_addr); - - spin_unlock_irqrestore(&gpiommgpio->lock, flags); - } + i8255_set_multiple(gpiommgpio->ppi, gpiommgpio->ppi_state, mask, bits, + chip->ngpio); } #define GPIOMM_NGPIO 48 @@ -250,6 +120,21 @@ static const char *gpiomm_names[GPIOMM_NGPIO] = { "Port 2C2", "Port 2C3", "Port 2C4", "Port 2C5", "Port 2C6", "Port 2C7", }; +static void gpiomm_init_dio(struct i8255 __iomem *const ppi, + struct i8255_state *const ppi_state) +{ + const unsigned long ngpio = 24; + const unsigned long mask = GENMASK(ngpio - 1, 0); + const unsigned long bits = 0; + unsigned long i; + + /* Initialize all GPIO to output 0 */ + for (i = 0; i < GPIOMM_NUM_PPI; i++) { + i8255_mode0_output(&ppi[i]); + i8255_set_multiple(&ppi[i], &ppi_state[i], &mask, &bits, ngpio); + } +} + static int gpiomm_probe(struct device *dev, unsigned int id) { struct gpiomm_gpio *gpiommgpio; @@ -266,8 +151,8 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return -EBUSY; } - gpiommgpio->base = devm_ioport_map(dev, base[id], GPIOMM_EXTENT); - if (!gpiommgpio->base) + gpiommgpio->ppi = devm_ioport_map(dev, base[id], GPIOMM_EXTENT); + if (!gpiommgpio->ppi) return -ENOMEM; gpiommgpio->chip.label = name; @@ -284,7 +169,8 @@ static int gpiomm_probe(struct device *dev, unsigned int id) gpiommgpio->chip.set = gpiomm_gpio_set; gpiommgpio->chip.set_multiple = gpiomm_gpio_set_multiple; - spin_lock_init(&gpiommgpio->lock); + i8255_state_init(gpiommgpio->ppi_state, GPIOMM_NUM_PPI); + gpiomm_init_dio(gpiommgpio->ppi, gpiommgpio->ppi_state); err = devm_gpiochip_add_data(dev, &gpiommgpio->chip, gpiommgpio); if (err) { @@ -292,16 +178,6 @@ static int gpiomm_probe(struct device *dev, unsigned int id) return err; } - /* initialize all GPIO as output */ - iowrite8(0x80, gpiommgpio->base + 3); - iowrite8(0x00, gpiommgpio->base); - iowrite8(0x00, gpiommgpio->base + 1); - iowrite8(0x00, gpiommgpio->base + 2); - iowrite8(0x80, gpiommgpio->base + 7); - iowrite8(0x00, gpiommgpio->base + 4); - iowrite8(0x00, gpiommgpio->base + 5); - iowrite8(0x00, gpiommgpio->base + 6); - return 0; } diff --git a/drivers/gpio/gpio-i8255.c b/drivers/gpio/gpio-i8255.c new file mode 100644 index 000000000000..9b97db418df1 --- /dev/null +++ b/drivers/gpio/gpio-i8255.c @@ -0,0 +1,287 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel 8255 Programmable Peripheral Interface + * Copyright (C) 2022 William Breathitt Gray + */ +#include +#include +#include +#include +#include +#include +#include + +#include "gpio-i8255.h" + +#define I8255_CONTROL_PORTC_LOWER_DIRECTION BIT(0) +#define I8255_CONTROL_PORTB_DIRECTION BIT(1) +#define I8255_CONTROL_PORTC_UPPER_DIRECTION BIT(3) +#define I8255_CONTROL_PORTA_DIRECTION BIT(4) +#define I8255_CONTROL_MODE_SET BIT(7) +#define I8255_PORTA 0 +#define I8255_PORTB 1 +#define I8255_PORTC 2 + +static int i8255_get_port(struct i8255 __iomem *const ppi, + const unsigned long io_port, const unsigned long mask) +{ + const unsigned long bank = io_port / 3; + const unsigned long ppi_port = io_port % 3; + + return ioread8(&ppi[bank].port[ppi_port]) & mask; +} + +static u8 i8255_direction_mask(const unsigned long offset) +{ + const unsigned long port_offset = offset % 8; + const unsigned long io_port = offset / 8; + const unsigned long ppi_port = io_port % 3; + + switch (ppi_port) { + case I8255_PORTA: + return I8255_CONTROL_PORTA_DIRECTION; + case I8255_PORTB: + return I8255_CONTROL_PORTB_DIRECTION; + case I8255_PORTC: + /* Port C can be configured by nibble */ + if (port_offset >= 4) + return I8255_CONTROL_PORTC_UPPER_DIRECTION; + return I8255_CONTROL_PORTC_LOWER_DIRECTION; + default: + /* Should never reach this path */ + return 0; + } +} + +static void i8255_set_port(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long io_port, + const unsigned long mask, const unsigned long bits) +{ + const unsigned long bank = io_port / 3; + const unsigned long ppi_port = io_port % 3; + unsigned long flags; + unsigned long out_state; + + spin_lock_irqsave(&state[bank].lock, flags); + + out_state = ioread8(&ppi[bank].port[ppi_port]); + out_state = (out_state & ~mask) | (bits & mask); + iowrite8(out_state, &ppi[bank].port[ppi_port]); + + spin_unlock_irqrestore(&state[bank].lock, flags); +} + +/** + * i8255_direction_input - configure signal offset as input + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: signal offset to configure as input + * + * Configures a signal @offset as input for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks. The @state control_state + * values are updated to reflect the new configuration. + */ +void i8255_direction_input(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + unsigned long flags; + + spin_lock_irqsave(&state[bank].lock, flags); + + state[bank].control_state |= I8255_CONTROL_MODE_SET; + state[bank].control_state |= i8255_direction_mask(offset); + + iowrite8(state[bank].control_state, &ppi[bank].control); + + spin_unlock_irqrestore(&state[bank].lock, flags); +} +EXPORT_SYMBOL_NS_GPL(i8255_direction_input, I8255); + +/** + * i8255_direction_output - configure signal offset as output + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: signal offset to configure as output + * @value: signal value to output + * + * Configures a signal @offset as output for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks and sets the respective signal + * output to the desired @value. The @state control_state values are updated to + * reflect the new configuration. + */ +void i8255_direction_output(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long offset, + const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + unsigned long flags; + + spin_lock_irqsave(&state[bank].lock, flags); + + state[bank].control_state |= I8255_CONTROL_MODE_SET; + state[bank].control_state &= ~i8255_direction_mask(offset); + + iowrite8(state[bank].control_state, &ppi[bank].control); + + spin_unlock_irqrestore(&state[bank].lock, flags); + + i8255_set(ppi, state, offset, value); +} +EXPORT_SYMBOL_NS_GPL(i8255_direction_output, I8255); + +/** + * i8255_get - get signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @offset: offset of signal to get + * + * Returns the signal value (0=low, 1=high) for the signal at @offset for the + * respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +int i8255_get(struct i8255 __iomem *const ppi, const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long offset_mask = BIT(offset % 8); + + return !!i8255_get_port(ppi, io_port, offset_mask); +} +EXPORT_SYMBOL_NS_GPL(i8255_get, I8255); + +/** + * i8255_get_direction - get the I/O direction for a signal offset + * @state: devices states of the respective PPI banks + * @offset: offset of signal to get direction + * + * Returns the signal direction (0=output, 1=input) for the signal at @offset. + */ +int i8255_get_direction(const struct i8255_state *const state, + const unsigned long offset) +{ + const unsigned long io_port = offset / 8; + const unsigned long bank = io_port / 3; + + return !!(state[bank].control_state & i8255_direction_mask(offset)); +} +EXPORT_SYMBOL_NS_GPL(i8255_get_direction, I8255); + +/** + * i8255_get_multiple - get multiple signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @mask: mask of signals to get + * @bits: bitmap to store signal values + * @ngpio: number of GPIO signals of the respective PPI banks + * + * Stores in @bits the values (0=low, 1=high) for the signals defined by @mask + * for the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_get_multiple(struct i8255 __iomem *const ppi, + const unsigned long *const mask, + unsigned long *const bits, const unsigned long ngpio) +{ + unsigned long offset; + unsigned long port_mask; + unsigned long io_port; + unsigned long port_state; + + bitmap_zero(bits, ngpio); + + for_each_set_clump8(offset, port_mask, mask, ngpio) { + io_port = offset / 8; + port_state = i8255_get_port(ppi, io_port, port_mask); + + bitmap_set_value8(bits, port_state, offset); + } +} +EXPORT_SYMBOL_NS_GPL(i8255_get_multiple, I8255); + +/** + * i8255_mode0_output - configure all PPI ports to MODE 0 output mode + * @ppi: Intel 8255 Programmable Peripheral Interface bank + * + * Configures all Intel 8255 Programmable Peripheral Interface (@ppi) ports to + * MODE 0 (Basic Input/Output) output mode. + */ +void i8255_mode0_output(struct i8255 __iomem *const ppi) +{ + iowrite8(I8255_CONTROL_MODE_SET, &ppi->control); +} +EXPORT_SYMBOL_NS_GPL(i8255_mode0_output, I8255); + +/** + * i8255_set - set signal value at signal offset + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @offset: offset of signal to set + * @value: value of signal to set + * + * Assigns output @value for the signal at @offset for the respective Intel 8255 + * Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_set(struct i8255 __iomem *const ppi, struct i8255_state *const state, + const unsigned long offset, const unsigned long value) +{ + const unsigned long io_port = offset / 8; + const unsigned long port_offset = offset % 8; + const unsigned long mask = BIT(port_offset); + const unsigned long bits = value << port_offset; + + i8255_set_port(ppi, state, io_port, mask, bits); +} +EXPORT_SYMBOL_NS_GPL(i8255_set, I8255); + +/** + * i8255_set_multiple - set signal values at multiple signal offsets + * @ppi: Intel 8255 Programmable Peripheral Interface banks + * @state: devices states of the respective PPI banks + * @mask: mask of signals to set + * @bits: bitmap of signal output values + * @ngpio: number of GPIO signals of the respective PPI banks + * + * Assigns output values defined by @bits for the signals defined by @mask for + * the respective Intel 8255 Programmable Peripheral Interface (@ppi) banks. + */ +void i8255_set_multiple(struct i8255 __iomem *const ppi, + struct i8255_state *const state, + const unsigned long *const mask, + const unsigned long *const bits, + const unsigned long ngpio) +{ + unsigned long offset; + unsigned long port_mask; + unsigned long io_port; + unsigned long value; + + for_each_set_clump8(offset, port_mask, mask, ngpio) { + io_port = offset / 8; + value = bitmap_get_value8(bits, offset); + i8255_set_port(ppi, state, io_port, port_mask, value); + } +} +EXPORT_SYMBOL_NS_GPL(i8255_set_multiple, I8255); + +/** + * i8255_state_init - initialize i8255_state structure + * @state: devices states of the respective PPI banks + * @nbanks: number of Intel 8255 Programmable Peripheral Interface banks + * + * Initializes the @state of each Intel 8255 Programmable Peripheral Interface + * bank for use in i8255 library functions. + */ +void i8255_state_init(struct i8255_state *const state, + const unsigned long nbanks) +{ + unsigned long bank; + + for (bank = 0; bank < nbanks; bank++) + spin_lock_init(&state[bank].lock); +} +EXPORT_SYMBOL_NS_GPL(i8255_state_init, I8255); + +MODULE_AUTHOR("William Breathitt Gray"); +MODULE_DESCRIPTION("Intel 8255 Programmable Peripheral Interface"); +MODULE_LICENSE("GPL"); diff --git a/drivers/gpio/gpio-i8255.h b/drivers/gpio/gpio-i8255.h new file mode 100644 index 000000000000..d9084aae9446 --- /dev/null +++ b/drivers/gpio/gpio-i8255.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* Copyright 2022 William Breathitt Gray */ +#ifndef _I8255_H_ +#define _I8255_H_ + +#include +#include + +/** + * struct i8255 - Intel 8255 register structure + * @port: Port A, B, and C + * @control: Control register + */ +struct i8255 { + u8 port[3]; + u8 control; +}; + +/** + * struct i8255_state - Intel 8255 state structure + * @lock: synchronization lock for accessing device state + * @control_state: Control register state + */ +struct i8255_state { + spinlock_t lock; + u8 control_state; +}; + +void i8255_direction_input(struct i8255 __iomem *ppi, struct i8255_state *state, + unsigned long offset); +void i8255_direction_output(struct i8255 __iomem *ppi, + struct i8255_state *state, unsigned long offset, + unsigned long value); +int i8255_get(struct i8255 __iomem *ppi, unsigned long offset); +int i8255_get_direction(const struct i8255_state *state, unsigned long offset); +void i8255_get_multiple(struct i8255 __iomem *ppi, const unsigned long *mask, + unsigned long *bits, unsigned long ngpio); +void i8255_mode0_output(struct i8255 __iomem *const ppi); +void i8255_set(struct i8255 __iomem *ppi, struct i8255_state *state, + unsigned long offset, unsigned long value); +void i8255_set_multiple(struct i8255 __iomem *ppi, struct i8255_state *state, + const unsigned long *mask, const unsigned long *bits, + unsigned long ngpio); +void i8255_state_init(struct i8255_state *const state, unsigned long nbanks); + +#endif /* _I8255_H_ */ diff --git a/drivers/gpio/gpio-lp3943.c b/drivers/gpio/gpio-lp3943.c index 8a30fb185aab..79edd5db49d2 100644 --- a/drivers/gpio/gpio-lp3943.c +++ b/drivers/gpio/gpio-lp3943.c @@ -42,7 +42,7 @@ struct lp3943_gpio { u16 input_mask; /* 1 = GPIO is input direction, 0 = output */ }; -static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_request(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); struct lp3943 *lp3943 = lp3943_gpio->lp3943; @@ -54,7 +54,7 @@ static int lp3943_gpio_request(struct gpio_chip *chip, unsigned offset) return 0; } -static void lp3943_gpio_free(struct gpio_chip *chip, unsigned offset) +static void lp3943_gpio_free(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); struct lp3943 *lp3943 = lp3943_gpio->lp3943; @@ -72,7 +72,7 @@ static int lp3943_gpio_set_mode(struct lp3943_gpio *lp3943_gpio, u8 offset, val << mux[offset].shift); } -static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); @@ -82,7 +82,7 @@ static int lp3943_gpio_direction_input(struct gpio_chip *chip, unsigned offset) } static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio, - struct gpio_chip *chip, unsigned offset) + struct gpio_chip *chip, unsigned int offset) { u8 addr, read; int err; @@ -107,7 +107,7 @@ static int lp3943_get_gpio_in_status(struct lp3943_gpio *lp3943_gpio, } static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio, - struct gpio_chip *chip, unsigned offset) + struct gpio_chip *chip, unsigned int offset) { struct lp3943 *lp3943 = lp3943_gpio->lp3943; const struct lp3943_reg_cfg *mux = lp3943->mux_cfg; @@ -128,7 +128,7 @@ static int lp3943_get_gpio_out_status(struct lp3943_gpio *lp3943_gpio, return -EINVAL; } -static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset) +static int lp3943_gpio_get(struct gpio_chip *chip, unsigned int offset) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); @@ -147,7 +147,7 @@ static int lp3943_gpio_get(struct gpio_chip *chip, unsigned offset) return lp3943_get_gpio_out_status(lp3943_gpio, chip, offset); } -static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value) +static void lp3943_gpio_set(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); u8 data; @@ -160,7 +160,7 @@ static void lp3943_gpio_set(struct gpio_chip *chip, unsigned offset, int value) lp3943_gpio_set_mode(lp3943_gpio, offset, data); } -static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned offset, +static int lp3943_gpio_direction_output(struct gpio_chip *chip, unsigned int offset, int value) { struct lp3943_gpio *lp3943_gpio = gpiochip_get_data(chip); diff --git a/drivers/gpio/gpio-lp873x.c b/drivers/gpio/gpio-lp873x.c index 70fad87ff2db..5c79ba1f229c 100644 --- a/drivers/gpio/gpio-lp873x.c +++ b/drivers/gpio/gpio-lp873x.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ * Keerthy * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver */ diff --git a/drivers/gpio/gpio-lp87565.c b/drivers/gpio/gpio-lp87565.c index fcde6708b5df..d3ce027de081 100644 --- a/drivers/gpio/gpio-lp87565.c +++ b/drivers/gpio/gpio-lp87565.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2017 Texas Instruments Incorporated - http://www.ti.com/ * Keerthy * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the LP873X driver */ diff --git a/drivers/gpio/gpio-mpc8xxx.c b/drivers/gpio/gpio-mpc8xxx.c index a964e25ea620..15049822937a 100644 --- a/drivers/gpio/gpio-mpc8xxx.c +++ b/drivers/gpio/gpio-mpc8xxx.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * GPIOs on MPC512x/8349/8572/8610/QorIQ and compatible * * Copyright (C) 2008 Peter Korsgaard * Copyright (C) 2016 Freescale Semiconductor Inc. - * - * This file is licensed under the terms of the GNU General Public License - * version 2. This program is licensed "as is" without any warranty of any - * kind, whether express or implied. */ #include diff --git a/drivers/gpio/gpio-mvebu.c b/drivers/gpio/gpio-mvebu.c index 2db19cd640a4..aa126ab80f0c 100644 --- a/drivers/gpio/gpio-mvebu.c +++ b/drivers/gpio/gpio-mvebu.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * GPIO driver for Marvell SoCs * @@ -7,10 +8,6 @@ * Andrew Lunn * Sebastian Hesselbarth * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * This driver is a fairly straightforward GPIO driver for the * complete family of Marvell EBU SoC platforms (Orion, Dove, * Kirkwood, Discovery, Armada 370/XP). The only complexity of this diff --git a/drivers/gpio/gpio-pca9570.c b/drivers/gpio/gpio-pca9570.c index cb2b2f735c15..ab2a652964ec 100644 --- a/drivers/gpio/gpio-pca9570.c +++ b/drivers/gpio/gpio-pca9570.c @@ -121,12 +121,14 @@ static int pca9570_probe(struct i2c_client *client) static const struct i2c_device_id pca9570_id_table[] = { { "pca9570", 4 }, + { "pca9571", 8 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(i2c, pca9570_id_table); static const struct of_device_id pca9570_of_match_table[] = { { .compatible = "nxp,pca9570", .data = (void *)4 }, + { .compatible = "nxp,pca9571", .data = (void *)8 }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, pca9570_of_match_table); diff --git a/drivers/gpio/gpio-pch.c b/drivers/gpio/gpio-pch.c index 3a0bd8795741..ee37ecb615cb 100644 --- a/drivers/gpio/gpio-pch.c +++ b/drivers/gpio/gpio-pch.c @@ -37,6 +37,11 @@ struct pch_regs { u32 reset; }; +#define PCI_DEVICE_ID_INTEL_EG20T_PCH 0x8803 +#define PCI_DEVICE_ID_ROHM_ML7223m_IOH 0x8014 +#define PCI_DEVICE_ID_ROHM_ML7223n_IOH 0x8043 +#define PCI_DEVICE_ID_ROHM_EG20T_PCH 0x8803 + enum pch_type_t { INTEL_EG20T_PCH, OKISEMI_ML7223m_IOH, /* LAPIS Semiconductor ML7223 IOH PCIe Bus-m */ @@ -357,16 +362,12 @@ static int pch_gpio_probe(struct pci_dev *pdev, chip->dev = dev; ret = pcim_enable_device(pdev); - if (ret) { - dev_err(dev, "pci_enable_device FAILED"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable PCI device\n"); ret = pcim_iomap_regions(pdev, BIT(1), KBUILD_MODNAME); - if (ret) { - dev_err(dev, "pci_request_regions FAILED-%d", ret); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request and map PCI regions\n"); chip->base = pcim_iomap_table(pdev)[1]; chip->ioh = id->driver_data; @@ -376,10 +377,8 @@ static int pch_gpio_probe(struct pci_dev *pdev, pch_gpio_setup(chip); ret = devm_gpiochip_add_data(dev, &chip->gpio, chip); - if (ret) { - dev_err(dev, "PCH gpio: Failed to register GPIO\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to register GPIO\n"); irq_base = devm_irq_alloc_descs(dev, -1, 0, gpio_pins[chip->ioh], NUMA_NO_NODE); @@ -396,10 +395,8 @@ static int pch_gpio_probe(struct pci_dev *pdev, ret = devm_request_irq(dev, pdev->irq, pch_gpio_handler, IRQF_SHARED, KBUILD_MODNAME, chip); - if (ret) { - dev_err(dev, "request_irq failed\n"); - return ret; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to request IRQ\n"); return pch_gpio_alloc_generic_chip(chip, irq_base, gpio_pins[chip->ioh]); } @@ -433,15 +430,11 @@ static int __maybe_unused pch_gpio_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(pch_gpio_pm_ops, pch_gpio_suspend, pch_gpio_resume); static const struct pci_device_id pch_gpio_pcidev_id[] = { - { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x8803), - .driver_data = INTEL_EG20T_PCH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8014), - .driver_data = OKISEMI_ML7223m_IOH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8043), - .driver_data = OKISEMI_ML7223n_IOH }, - { PCI_DEVICE(PCI_VENDOR_ID_ROHM, 0x8803), - .driver_data = INTEL_EG20T_PCH }, - { 0, } + { PCI_DEVICE_DATA(INTEL, EG20T_PCH, INTEL_EG20T_PCH) }, + { PCI_DEVICE_DATA(ROHM, ML7223m_IOH, OKISEMI_ML7223m_IOH) }, + { PCI_DEVICE_DATA(ROHM, ML7223n_IOH, OKISEMI_ML7223n_IOH) }, + { PCI_DEVICE_DATA(ROHM, EG20T_PCH, INTEL_EG20T_PCH) }, + { } }; MODULE_DEVICE_TABLE(pci, pch_gpio_pcidev_id); diff --git a/drivers/gpio/gpio-pisosr.c b/drivers/gpio/gpio-pisosr.c index 81a47ae09ff8..67071bea08c2 100644 --- a/drivers/gpio/gpio-pisosr.c +++ b/drivers/gpio/gpio-pisosr.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include diff --git a/drivers/gpio/gpio-rockchip.c b/drivers/gpio/gpio-rockchip.c index e342a6dc4c6c..f91e876fd969 100644 --- a/drivers/gpio/gpio-rockchip.c +++ b/drivers/gpio/gpio-rockchip.c @@ -27,6 +27,7 @@ #define GPIO_TYPE_V1 (0) /* GPIO Version ID reserved */ #define GPIO_TYPE_V2 (0x01000C2B) /* GPIO Version ID 0x01000C2B */ +#define GPIO_TYPE_V2_1 (0x0101157C) /* GPIO Version ID 0x0101157C */ static const struct rockchip_gpio_regs gpio_regs_v1 = { .port_dr = 0x00, @@ -664,7 +665,7 @@ static int rockchip_get_bank_data(struct rockchip_pin_bank *bank) id = readl(bank->reg_base + gpio_regs_v2.version_id); /* If not gpio v2, that is default to v1. */ - if (id == GPIO_TYPE_V2) { + if (id == GPIO_TYPE_V2 || id == GPIO_TYPE_V2_1) { bank->gpio_regs = &gpio_regs_v2; bank->gpio_type = GPIO_TYPE_V2; bank->db_clk = of_clk_get(bank->of_node, 1); diff --git a/drivers/gpio/gpio-spear-spics.c b/drivers/gpio/gpio-spear-spics.c index 49aac2bb8d2c..51539185400d 100644 --- a/drivers/gpio/gpio-spear-spics.c +++ b/drivers/gpio/gpio-spear-spics.c @@ -1,12 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SPEAr platform SPI chipselect abstraction over gpiolib * * Copyright (C) 2012 ST Microelectronics * Shiraz Hashim - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/gpio/gpio-tpic2810.c b/drivers/gpio/gpio-tpic2810.c index 99d5a84a9129..a09b1e69b072 100644 --- a/drivers/gpio/gpio-tpic2810.c +++ b/drivers/gpio/gpio-tpic2810.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/ * Andrew F. Davis - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include diff --git a/drivers/gpio/gpio-ts4800.c b/drivers/gpio/gpio-ts4800.c index 8c0d82d926dd..95d80ba14bee 100644 --- a/drivers/gpio/gpio-ts4800.c +++ b/drivers/gpio/gpio-ts4800.c @@ -1,11 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * GPIO driver for the TS-4800 board * * Copyright (c) 2016 - Savoir-faire Linux - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include diff --git a/drivers/gpio/gpio-twl4030.c b/drivers/gpio/gpio-twl4030.c index de249726230e..5046e51af8df 100644 --- a/drivers/gpio/gpio-twl4030.c +++ b/drivers/gpio/gpio-twl4030.c @@ -593,27 +593,13 @@ out: /* Cannot use as gpio_twl4030_probe() calls us */ static int gpio_twl4030_remove(struct platform_device *pdev) { - struct twl4030_gpio_platform_data *pdata = dev_get_platdata(&pdev->dev); struct gpio_twl4030_priv *priv = platform_get_drvdata(pdev); - int status; - - if (pdata && pdata->teardown) { - status = pdata->teardown(&pdev->dev, priv->gpio_chip.base, - TWL4030_GPIO_MAX); - if (status) { - dev_dbg(&pdev->dev, "teardown --> %d\n", status); - return status; - } - } gpiochip_remove(&priv->gpio_chip); - if (is_module()) - return 0; - /* REVISIT no support yet for deregistering all the IRQs */ - WARN_ON(1); - return -EIO; + WARN_ON(!is_module()); + return 0; } static const struct of_device_id twl_gpio_match[] = { diff --git a/drivers/gpio/gpio-ucb1400.c b/drivers/gpio/gpio-ucb1400.c index d2a8644864c3..386e69300332 100644 --- a/drivers/gpio/gpio-ucb1400.c +++ b/drivers/gpio/gpio-ucb1400.c @@ -64,34 +64,14 @@ static int ucb1400_gpio_probe(struct platform_device *dev) ucb->gc.can_sleep = true; err = devm_gpiochip_add_data(&dev->dev, &ucb->gc, ucb); - if (err) - goto err; - - if (ucb->gpio_setup) - err = ucb->gpio_setup(&dev->dev, ucb->gc.ngpio); err: return err; } -static int ucb1400_gpio_remove(struct platform_device *dev) -{ - int err = 0; - struct ucb1400_gpio *ucb = platform_get_drvdata(dev); - - if (ucb && ucb->gpio_teardown) { - err = ucb->gpio_teardown(&dev->dev, ucb->gc.ngpio); - if (err) - return err; - } - - return err; -} - static struct platform_driver ucb1400_gpio_driver = { .probe = ucb1400_gpio_probe, - .remove = ucb1400_gpio_remove, .driver = { .name = "ucb1400_gpio" }, diff --git a/drivers/gpio/gpio-vr41xx.c b/drivers/gpio/gpio-vr41xx.c deleted file mode 100644 index 8d09b619c166..000000000000 --- a/drivers/gpio/gpio-vr41xx.c +++ /dev/null @@ -1,541 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Driver for NEC VR4100 series General-purpose I/O Unit. - * - * Copyright (C) 2002 MontaVista Software Inc. - * Author: Yoichi Yuasa - * Copyright (C) 2003-2009 Yoichi Yuasa - */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -MODULE_AUTHOR("Yoichi Yuasa "); -MODULE_DESCRIPTION("NEC VR4100 series General-purpose I/O Unit driver"); -MODULE_LICENSE("GPL"); - -#define GIUIOSELL 0x00 -#define GIUIOSELH 0x02 -#define GIUPIODL 0x04 -#define GIUPIODH 0x06 -#define GIUINTSTATL 0x08 -#define GIUINTSTATH 0x0a -#define GIUINTENL 0x0c -#define GIUINTENH 0x0e -#define GIUINTTYPL 0x10 -#define GIUINTTYPH 0x12 -#define GIUINTALSELL 0x14 -#define GIUINTALSELH 0x16 -#define GIUINTHTSELL 0x18 -#define GIUINTHTSELH 0x1a -#define GIUPODATL 0x1c -#define GIUPODATEN 0x1c -#define GIUPODATH 0x1e - #define PIOEN0 0x0100 - #define PIOEN1 0x0200 -#define GIUPODAT 0x1e -#define GIUFEDGEINHL 0x20 -#define GIUFEDGEINHH 0x22 -#define GIUREDGEINHL 0x24 -#define GIUREDGEINHH 0x26 - -#define GIUUSEUPDN 0x1e0 -#define GIUTERMUPDN 0x1e2 - -#define GPIO_HAS_PULLUPDOWN_IO 0x0001 -#define GPIO_HAS_OUTPUT_ENABLE 0x0002 -#define GPIO_HAS_INTERRUPT_EDGE_SELECT 0x0100 - -enum { - GPIO_INPUT, - GPIO_OUTPUT, -}; - -static DEFINE_SPINLOCK(giu_lock); -static unsigned long giu_flags; - -static void __iomem *giu_base; -static struct gpio_chip vr41xx_gpio_chip; - -#define giu_read(offset) readw(giu_base + (offset)) -#define giu_write(offset, value) writew((value), giu_base + (offset)) - -#define GPIO_PIN_OF_IRQ(irq) ((irq) - GIU_IRQ_BASE) -#define GIUINT_HIGH_OFFSET 16 -#define GIUINT_HIGH_MAX 32 - -static inline u16 giu_set(u16 offset, u16 set) -{ - u16 data; - - data = giu_read(offset); - data |= set; - giu_write(offset, data); - - return data; -} - -static inline u16 giu_clear(u16 offset, u16 clear) -{ - u16 data; - - data = giu_read(offset); - data &= ~clear; - giu_write(offset, data); - - return data; -} - -static void ack_giuint_low(struct irq_data *d) -{ - giu_write(GIUINTSTATL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static void mask_giuint_low(struct irq_data *d) -{ - giu_clear(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static void mask_ack_giuint_low(struct irq_data *d) -{ - unsigned int pin; - - pin = GPIO_PIN_OF_IRQ(d->irq); - giu_clear(GIUINTENL, 1 << pin); - giu_write(GIUINTSTATL, 1 << pin); -} - -static void unmask_giuint_low(struct irq_data *d) -{ - giu_set(GIUINTENL, 1 << GPIO_PIN_OF_IRQ(d->irq)); -} - -static unsigned int startup_giuint(struct irq_data *data) -{ - int ret; - - ret = gpiochip_lock_as_irq(&vr41xx_gpio_chip, irqd_to_hwirq(data)); - if (ret) { - dev_err(vr41xx_gpio_chip.parent, - "unable to lock HW IRQ %lu for IRQ\n", - data->hwirq); - return ret; - } - - /* Satisfy the .enable semantics by unmasking the line */ - unmask_giuint_low(data); - return 0; -} - -static void shutdown_giuint(struct irq_data *data) -{ - mask_giuint_low(data); - gpiochip_unlock_as_irq(&vr41xx_gpio_chip, data->hwirq); -} - -static struct irq_chip giuint_low_irq_chip = { - .name = "GIUINTL", - .irq_ack = ack_giuint_low, - .irq_mask = mask_giuint_low, - .irq_mask_ack = mask_ack_giuint_low, - .irq_unmask = unmask_giuint_low, - .irq_startup = startup_giuint, - .irq_shutdown = shutdown_giuint, -}; - -static void ack_giuint_high(struct irq_data *d) -{ - giu_write(GIUINTSTATH, - 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static void mask_giuint_high(struct irq_data *d) -{ - giu_clear(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static void mask_ack_giuint_high(struct irq_data *d) -{ - unsigned int pin; - - pin = GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET; - giu_clear(GIUINTENH, 1 << pin); - giu_write(GIUINTSTATH, 1 << pin); -} - -static void unmask_giuint_high(struct irq_data *d) -{ - giu_set(GIUINTENH, 1 << (GPIO_PIN_OF_IRQ(d->irq) - GIUINT_HIGH_OFFSET)); -} - -static struct irq_chip giuint_high_irq_chip = { - .name = "GIUINTH", - .irq_ack = ack_giuint_high, - .irq_mask = mask_giuint_high, - .irq_mask_ack = mask_ack_giuint_high, - .irq_unmask = unmask_giuint_high, -}; - -static int giu_get_irq(unsigned int irq) -{ - u16 pendl, pendh, maskl, maskh; - int i; - - pendl = giu_read(GIUINTSTATL); - pendh = giu_read(GIUINTSTATH); - maskl = giu_read(GIUINTENL); - maskh = giu_read(GIUINTENH); - - maskl &= pendl; - maskh &= pendh; - - if (maskl) { - for (i = 0; i < 16; i++) { - if (maskl & (1 << i)) - return GIU_IRQ(i); - } - } else if (maskh) { - for (i = 0; i < 16; i++) { - if (maskh & (1 << i)) - return GIU_IRQ(i + GIUINT_HIGH_OFFSET); - } - } - - printk(KERN_ERR "spurious GIU interrupt: %04x(%04x),%04x(%04x)\n", - maskl, pendl, maskh, pendh); - - return -EINVAL; -} - -void vr41xx_set_irq_trigger(unsigned int pin, irq_trigger_t trigger, - irq_signal_t signal) -{ - u16 mask; - - if (pin < GIUINT_HIGH_OFFSET) { - mask = 1 << pin; - if (trigger != IRQ_TRIGGER_LEVEL) { - giu_set(GIUINTTYPL, mask); - if (signal == IRQ_SIGNAL_HOLD) - giu_set(GIUINTHTSELL, mask); - else - giu_clear(GIUINTHTSELL, mask); - if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { - switch (trigger) { - case IRQ_TRIGGER_EDGE_FALLING: - giu_set(GIUFEDGEINHL, mask); - giu_clear(GIUREDGEINHL, mask); - break; - case IRQ_TRIGGER_EDGE_RISING: - giu_clear(GIUFEDGEINHL, mask); - giu_set(GIUREDGEINHL, mask); - break; - default: - giu_set(GIUFEDGEINHL, mask); - giu_set(GIUREDGEINHL, mask); - break; - } - } - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_low_irq_chip, - handle_edge_irq); - } else { - giu_clear(GIUINTTYPL, mask); - giu_clear(GIUINTHTSELL, mask); - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_low_irq_chip, - handle_level_irq); - } - giu_write(GIUINTSTATL, mask); - } else if (pin < GIUINT_HIGH_MAX) { - mask = 1 << (pin - GIUINT_HIGH_OFFSET); - if (trigger != IRQ_TRIGGER_LEVEL) { - giu_set(GIUINTTYPH, mask); - if (signal == IRQ_SIGNAL_HOLD) - giu_set(GIUINTHTSELH, mask); - else - giu_clear(GIUINTHTSELH, mask); - if (giu_flags & GPIO_HAS_INTERRUPT_EDGE_SELECT) { - switch (trigger) { - case IRQ_TRIGGER_EDGE_FALLING: - giu_set(GIUFEDGEINHH, mask); - giu_clear(GIUREDGEINHH, mask); - break; - case IRQ_TRIGGER_EDGE_RISING: - giu_clear(GIUFEDGEINHH, mask); - giu_set(GIUREDGEINHH, mask); - break; - default: - giu_set(GIUFEDGEINHH, mask); - giu_set(GIUREDGEINHH, mask); - break; - } - } - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_high_irq_chip, - handle_edge_irq); - } else { - giu_clear(GIUINTTYPH, mask); - giu_clear(GIUINTHTSELH, mask); - irq_set_chip_and_handler(GIU_IRQ(pin), - &giuint_high_irq_chip, - handle_level_irq); - } - giu_write(GIUINTSTATH, mask); - } -} -EXPORT_SYMBOL_GPL(vr41xx_set_irq_trigger); - -void vr41xx_set_irq_level(unsigned int pin, irq_level_t level) -{ - u16 mask; - - if (pin < GIUINT_HIGH_OFFSET) { - mask = 1 << pin; - if (level == IRQ_LEVEL_HIGH) - giu_set(GIUINTALSELL, mask); - else - giu_clear(GIUINTALSELL, mask); - giu_write(GIUINTSTATL, mask); - } else if (pin < GIUINT_HIGH_MAX) { - mask = 1 << (pin - GIUINT_HIGH_OFFSET); - if (level == IRQ_LEVEL_HIGH) - giu_set(GIUINTALSELH, mask); - else - giu_clear(GIUINTALSELH, mask); - giu_write(GIUINTSTATH, mask); - } -} -EXPORT_SYMBOL_GPL(vr41xx_set_irq_level); - -static int giu_set_direction(struct gpio_chip *chip, unsigned pin, int dir) -{ - u16 offset, mask, reg; - unsigned long flags; - - if (pin >= chip->ngpio) - return -EINVAL; - - if (pin < 16) { - offset = GIUIOSELL; - mask = 1 << pin; - } else if (pin < 32) { - offset = GIUIOSELH; - mask = 1 << (pin - 16); - } else { - if (giu_flags & GPIO_HAS_OUTPUT_ENABLE) { - offset = GIUPODATEN; - mask = 1 << (pin - 32); - } else { - switch (pin) { - case 48: - offset = GIUPODATH; - mask = PIOEN0; - break; - case 49: - offset = GIUPODATH; - mask = PIOEN1; - break; - default: - return -EINVAL; - } - } - } - - spin_lock_irqsave(&giu_lock, flags); - - reg = giu_read(offset); - if (dir == GPIO_OUTPUT) - reg |= mask; - else - reg &= ~mask; - giu_write(offset, reg); - - spin_unlock_irqrestore(&giu_lock, flags); - - return 0; -} - -static int vr41xx_gpio_get(struct gpio_chip *chip, unsigned pin) -{ - u16 reg, mask; - - if (pin >= chip->ngpio) - return -EINVAL; - - if (pin < 16) { - reg = giu_read(GIUPIODL); - mask = 1 << pin; - } else if (pin < 32) { - reg = giu_read(GIUPIODH); - mask = 1 << (pin - 16); - } else if (pin < 48) { - reg = giu_read(GIUPODATL); - mask = 1 << (pin - 32); - } else { - reg = giu_read(GIUPODATH); - mask = 1 << (pin - 48); - } - - if (reg & mask) - return 1; - - return 0; -} - -static void vr41xx_gpio_set(struct gpio_chip *chip, unsigned pin, - int value) -{ - u16 offset, mask, reg; - unsigned long flags; - - if (pin >= chip->ngpio) - return; - - if (pin < 16) { - offset = GIUPIODL; - mask = 1 << pin; - } else if (pin < 32) { - offset = GIUPIODH; - mask = 1 << (pin - 16); - } else if (pin < 48) { - offset = GIUPODATL; - mask = 1 << (pin - 32); - } else { - offset = GIUPODATH; - mask = 1 << (pin - 48); - } - - spin_lock_irqsave(&giu_lock, flags); - - reg = giu_read(offset); - if (value) - reg |= mask; - else - reg &= ~mask; - giu_write(offset, reg); - - spin_unlock_irqrestore(&giu_lock, flags); -} - - -static int vr41xx_gpio_direction_input(struct gpio_chip *chip, unsigned offset) -{ - return giu_set_direction(chip, offset, GPIO_INPUT); -} - -static int vr41xx_gpio_direction_output(struct gpio_chip *chip, unsigned offset, - int value) -{ - vr41xx_gpio_set(chip, offset, value); - - return giu_set_direction(chip, offset, GPIO_OUTPUT); -} - -static int vr41xx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) -{ - if (offset >= chip->ngpio) - return -EINVAL; - - return GIU_IRQ_BASE + offset; -} - -static struct gpio_chip vr41xx_gpio_chip = { - .label = "vr41xx", - .owner = THIS_MODULE, - .direction_input = vr41xx_gpio_direction_input, - .get = vr41xx_gpio_get, - .direction_output = vr41xx_gpio_direction_output, - .set = vr41xx_gpio_set, - .to_irq = vr41xx_gpio_to_irq, -}; - -static int giu_probe(struct platform_device *pdev) -{ - unsigned int trigger, i, pin; - struct irq_chip *chip; - int irq; - - switch (pdev->id) { - case GPIO_50PINS_PULLUPDOWN: - giu_flags = GPIO_HAS_PULLUPDOWN_IO; - vr41xx_gpio_chip.ngpio = 50; - break; - case GPIO_36PINS: - vr41xx_gpio_chip.ngpio = 36; - break; - case GPIO_48PINS_EDGE_SELECT: - giu_flags = GPIO_HAS_INTERRUPT_EDGE_SELECT; - vr41xx_gpio_chip.ngpio = 48; - break; - default: - dev_err(&pdev->dev, "GIU: unknown ID %d\n", pdev->id); - return -ENODEV; - } - - giu_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(giu_base)) - return PTR_ERR(giu_base); - - vr41xx_gpio_chip.parent = &pdev->dev; - - if (gpiochip_add_data(&vr41xx_gpio_chip, NULL)) - return -ENODEV; - - giu_write(GIUINTENL, 0); - giu_write(GIUINTENH, 0); - - trigger = giu_read(GIUINTTYPH) << 16; - trigger |= giu_read(GIUINTTYPL); - for (i = GIU_IRQ_BASE; i <= GIU_IRQ_LAST; i++) { - pin = GPIO_PIN_OF_IRQ(i); - if (pin < GIUINT_HIGH_OFFSET) - chip = &giuint_low_irq_chip; - else - chip = &giuint_high_irq_chip; - - if (trigger & (1 << pin)) - irq_set_chip_and_handler(i, chip, handle_edge_irq); - else - irq_set_chip_and_handler(i, chip, handle_level_irq); - - } - - irq = platform_get_irq(pdev, 0); - if (irq < 0 || irq >= nr_irqs) - return -EBUSY; - - return cascade_irq(irq, giu_get_irq); -} - -static int giu_remove(struct platform_device *pdev) -{ - if (giu_base) { - giu_base = NULL; - } - - return 0; -} - -static struct platform_driver giu_device_driver = { - .probe = giu_probe, - .remove = giu_remove, - .driver = { - .name = "GIU", - }, -}; - -module_platform_driver(giu_device_driver); diff --git a/drivers/gpio/gpio-ws16c48.c b/drivers/gpio/gpio-ws16c48.c index 5078631d8014..b098f2dc196b 100644 --- a/drivers/gpio/gpio-ws16c48.c +++ b/drivers/gpio/gpio-ws16c48.c @@ -4,7 +4,6 @@ * Copyright (C) 2016 William Breathitt Gray */ #include -#include #include #include #include @@ -17,8 +16,9 @@ #include #include #include +#include -#define WS16C48_EXTENT 16 +#define WS16C48_EXTENT 10 #define MAX_NUM_WS16C48 max_num_isa_dev(WS16C48_EXTENT) static unsigned int base[MAX_NUM_WS16C48]; @@ -30,6 +30,20 @@ static unsigned int irq[MAX_NUM_WS16C48]; module_param_hw_array(irq, uint, irq, NULL, 0); MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); +/** + * struct ws16c48_reg - device register structure + * @port: Port 0 through 5 I/O + * @int_pending: Interrupt Pending + * @page_lock: Register page (Bits 7-6) and I/O port lock (Bits 5-0) + * @pol_enab_int_id: Interrupt polarity, enable, and ID + */ +struct ws16c48_reg { + u8 port[6]; + u8 int_pending; + u8 page_lock; + u8 pol_enab_int_id[3]; +}; + /** * struct ws16c48_gpio - GPIO device private data structure * @chip: instance of the gpio_chip @@ -38,7 +52,7 @@ MODULE_PARM_DESC(irq, "WinSystems WS16C48 interrupt line numbers"); * @lock: synchronization lock to prevent I/O race conditions * @irq_mask: I/O bits affected by interrupts * @flow_mask: IRQ flow type mask for the respective I/O bits - * @base: base port address of the GPIO device + * @reg: I/O address offset for the device registers */ struct ws16c48_gpio { struct gpio_chip chip; @@ -47,7 +61,7 @@ struct ws16c48_gpio { raw_spinlock_t lock; unsigned long irq_mask; unsigned long flow_mask; - void __iomem *base; + struct ws16c48_reg __iomem *reg; }; static int ws16c48_gpio_get_direction(struct gpio_chip *chip, unsigned offset) @@ -73,7 +87,7 @@ static int ws16c48_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ws16c48gpio->io_state[port] |= mask; ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -95,7 +109,7 @@ static int ws16c48_gpio_direction_output(struct gpio_chip *chip, ws16c48gpio->out_state[port] |= mask; else ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -118,7 +132,7 @@ static int ws16c48_gpio_get(struct gpio_chip *chip, unsigned offset) return -EINVAL; } - port_state = ioread8(ws16c48gpio->base + port); + port_state = ioread8(ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -131,14 +145,16 @@ static int ws16c48_gpio_get_multiple(struct gpio_chip *chip, struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(chip); unsigned long offset; unsigned long gpio_mask; - void __iomem *port_addr; + size_t index; + u8 __iomem *port_addr; unsigned long port_state; /* clear bits array to a clean slate */ bitmap_zero(bits, chip->ngpio); for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { - port_addr = ws16c48gpio->base + offset / 8; + index = offset / 8; + port_addr = ws16c48gpio->reg->port + index; port_state = ioread8(port_addr) & gpio_mask; bitmap_set_value8(bits, port_state, offset); @@ -166,7 +182,7 @@ static void ws16c48_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ws16c48gpio->out_state[port] |= mask; else ws16c48gpio->out_state[port] &= ~mask; - iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->base + port); + iowrite8(ws16c48gpio->out_state[port], ws16c48gpio->reg->port + port); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -178,13 +194,13 @@ static void ws16c48_gpio_set_multiple(struct gpio_chip *chip, unsigned long offset; unsigned long gpio_mask; size_t index; - void __iomem *port_addr; + u8 __iomem *port_addr; unsigned long bitmask; unsigned long flags; for_each_set_clump8(offset, gpio_mask, mask, chip->ngpio) { index = offset / 8; - port_addr = ws16c48gpio->base + index; + port_addr = ws16c48gpio->reg->port + index; /* mask out GPIO configured for input */ gpio_mask &= ~ws16c48gpio->io_state[index]; @@ -219,10 +235,15 @@ static void ws16c48_irq_ack(struct irq_data *data) port_state = ws16c48gpio->irq_mask >> (8*port); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(port_state & ~mask, ws16c48gpio->base + 8 + port); - iowrite8(port_state | mask, ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Clear pending interrupt */ + iowrite8(port_state & ~mask, ws16c48gpio->reg->pol_enab_int_id + port); + iowrite8(port_state | mask, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -235,6 +256,7 @@ static void ws16c48_irq_mask(struct irq_data *data) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -243,10 +265,16 @@ static void ws16c48_irq_mask(struct irq_data *data) raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); ws16c48gpio->irq_mask &= ~mask; + port_state = ws16c48gpio->irq_mask >> (8 * port); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Disable interrupt */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -259,6 +287,7 @@ static void ws16c48_irq_unmask(struct irq_data *data) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -267,10 +296,16 @@ static void ws16c48_irq_unmask(struct irq_data *data) raw_spin_lock_irqsave(&ws16c48gpio->lock, flags); ws16c48gpio->irq_mask |= mask; + port_state = ws16c48gpio->irq_mask >> (8 * port); - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->irq_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Enable interrupt */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); } @@ -283,6 +318,7 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) const unsigned long mask = BIT(offset); const unsigned port = offset / 8; unsigned long flags; + unsigned long port_state; /* only the first 3 ports support interrupts */ if (port > 2) @@ -304,9 +340,16 @@ static int ws16c48_irq_set_type(struct irq_data *data, unsigned flow_type) return -EINVAL; } - iowrite8(0x40, ws16c48gpio->base + 7); - iowrite8(ws16c48gpio->flow_mask >> (8*port), ws16c48gpio->base + 8 + port); - iowrite8(0xC0, ws16c48gpio->base + 7); + port_state = ws16c48gpio->flow_mask >> (8 * port); + + /* Select Register Page 1; Unlock all I/O ports */ + iowrite8(0x40, &ws16c48gpio->reg->page_lock); + + /* Set interrupt polarity */ + iowrite8(port_state, ws16c48gpio->reg->pol_enab_int_id + port); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); raw_spin_unlock_irqrestore(&ws16c48gpio->lock, flags); @@ -325,25 +368,26 @@ static irqreturn_t ws16c48_irq_handler(int irq, void *dev_id) { struct ws16c48_gpio *const ws16c48gpio = dev_id; struct gpio_chip *const chip = &ws16c48gpio->chip; + struct ws16c48_reg __iomem *const reg = ws16c48gpio->reg; unsigned long int_pending; unsigned long port; unsigned long int_id; unsigned long gpio; - int_pending = ioread8(ws16c48gpio->base + 6) & 0x7; + int_pending = ioread8(®->int_pending) & 0x7; if (!int_pending) return IRQ_NONE; /* loop until all pending interrupts are handled */ do { for_each_set_bit(port, &int_pending, 3) { - int_id = ioread8(ws16c48gpio->base + 8 + port); + int_id = ioread8(reg->pol_enab_int_id + port); for_each_set_bit(gpio, &int_id, 8) generic_handle_domain_irq(chip->irq.domain, gpio + 8*port); } - int_pending = ioread8(ws16c48gpio->base + 6) & 0x7; + int_pending = ioread8(®->int_pending) & 0x7; } while (int_pending); return IRQ_HANDLED; @@ -369,12 +413,16 @@ static int ws16c48_irq_init_hw(struct gpio_chip *gc) { struct ws16c48_gpio *const ws16c48gpio = gpiochip_get_data(gc); - /* Disable IRQ by default */ - iowrite8(0x80, ws16c48gpio->base + 7); - iowrite8(0, ws16c48gpio->base + 8); - iowrite8(0, ws16c48gpio->base + 9); - iowrite8(0, ws16c48gpio->base + 10); - iowrite8(0xC0, ws16c48gpio->base + 7); + /* Select Register Page 2; Unlock all I/O ports */ + iowrite8(0x80, &ws16c48gpio->reg->page_lock); + + /* Disable interrupts for all lines */ + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[0]); + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[1]); + iowrite8(0, &ws16c48gpio->reg->pol_enab_int_id[2]); + + /* Select Register Page 3; Unlock all I/O ports */ + iowrite8(0xC0, &ws16c48gpio->reg->page_lock); return 0; } @@ -396,8 +444,8 @@ static int ws16c48_probe(struct device *dev, unsigned int id) return -EBUSY; } - ws16c48gpio->base = devm_ioport_map(dev, base[id], WS16C48_EXTENT); - if (!ws16c48gpio->base) + ws16c48gpio->reg = devm_ioport_map(dev, base[id], WS16C48_EXTENT); + if (!ws16c48gpio->reg) return -ENOMEM; ws16c48gpio->chip.label = name; diff --git a/drivers/gpio/gpio-xgs-iproc.c b/drivers/gpio/gpio-xgs-iproc.c index 43ca52fa6f9a..fd88500399c6 100644 --- a/drivers/gpio/gpio-xgs-iproc.c +++ b/drivers/gpio/gpio-xgs-iproc.c @@ -281,11 +281,7 @@ static int iproc_gpio_probe(struct platform_device *pdev) static int iproc_gpio_remove(struct platform_device *pdev) { - struct iproc_gpio_chip *chip; - - chip = platform_get_drvdata(pdev); - if (!chip) - return -ENODEV; + struct iproc_gpio_chip *chip = platform_get_drvdata(pdev); if (chip->intr) { u32 val; diff --git a/drivers/gpio/gpio-xilinx.c b/drivers/gpio/gpio-xilinx.c index 7f8e2fed2988..2fc6b6ff7f16 100644 --- a/drivers/gpio/gpio-xilinx.c +++ b/drivers/gpio/gpio-xilinx.c @@ -117,12 +117,14 @@ static inline int xgpio_regoffset(struct xgpio_instance *chip, int ch) static void xgpio_read_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_set_value32(a, bit, xgpio_readreg(addr)); } static void xgpio_write_ch(struct xgpio_instance *chip, int reg, int bit, unsigned long *a) { void __iomem *addr = chip->regs + reg + xgpio_regoffset(chip, bit / 32); + xgpio_writereg(addr, xgpio_get_value32(a, bit)); } diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c index c2523ac26fac..9be1376f9a62 100644 --- a/drivers/gpio/gpiolib-acpi.c +++ b/drivers/gpio/gpiolib-acpi.c @@ -687,6 +687,9 @@ int acpi_gpio_update_gpiod_lookup_flags(unsigned long *lookupflags, case ACPI_PIN_CONFIG_PULLDOWN: *lookupflags |= GPIO_PULL_DOWN; break; + case ACPI_PIN_CONFIG_NOPULL: + *lookupflags |= GPIO_PULL_DISABLE; + break; default: break; } diff --git a/drivers/gpio/gpiolib-cdev.c b/drivers/gpio/gpiolib-cdev.c index b26e64338376..f8041d4898d1 100644 --- a/drivers/gpio/gpiolib-cdev.c +++ b/drivers/gpio/gpiolib-cdev.c @@ -434,12 +434,15 @@ struct line { struct linereq *req; unsigned int irq; /* - * eflags is set by edge_detector_setup(), edge_detector_stop() and - * edge_detector_update(), which are themselves mutually exclusive, - * and is accessed by edge_irq_thread() and debounce_work_func(), - * which can both live with a slightly stale value. + * The flags for the active edge detector configuration. + * + * edflags is set by linereq_create(), linereq_free(), and + * linereq_set_config_unlocked(), which are themselves mutually + * exclusive, and is accessed by edge_irq_thread(), + * process_hw_ts_thread() and debounce_work_func(), + * which can all live with a slightly stale value. */ - u64 eflags; + u64 edflags; /* * timestamp_ns and req_seqno are accessed only by * edge_irq_handler() and edge_irq_thread(), which are themselves @@ -469,9 +472,7 @@ struct line { * stale value. */ unsigned int level; - /* - * -- hte specific fields -- - */ +#ifdef CONFIG_HTE struct hte_ts_desc hdesc; /* * HTE provider sets line level at the time of event. The valid @@ -488,6 +489,7 @@ struct line { * last sequence number before debounce period expires. */ u32 last_seqno; +#endif /* CONFIG_HTE */ }; /** @@ -545,6 +547,12 @@ struct linereq { GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ GPIO_V2_LINE_BIAS_FLAGS) +/* subset of flags relevant for edge detector configuration */ +#define GPIO_V2_LINE_EDGE_DETECTOR_FLAGS \ + (GPIO_V2_LINE_FLAG_ACTIVE_LOW | \ + GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE | \ + GPIO_V2_LINE_EDGE_FLAGS) + static void linereq_put_event(struct linereq *lr, struct gpio_v2_line_event *le) { @@ -567,19 +575,28 @@ static u64 line_event_timestamp(struct line *line) { if (test_bit(FLAG_EVENT_CLOCK_REALTIME, &line->desc->flags)) return ktime_get_real_ns(); - else if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) + else if (IS_ENABLED(CONFIG_HTE) && + test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) return line->timestamp_ns; return ktime_get_ns(); } +static u32 line_event_id(int level) +{ + return level ? GPIO_V2_LINE_EVENT_RISING_EDGE : + GPIO_V2_LINE_EVENT_FALLING_EDGE; +} + +#ifdef CONFIG_HTE + static enum hte_return process_hw_ts_thread(void *p) { struct line *line; struct linereq *lr; struct gpio_v2_line_event le; + u64 edflags; int level; - u64 eflags; if (!p) return HTE_CB_HANDLED; @@ -590,29 +607,26 @@ static enum hte_return process_hw_ts_thread(void *p) memset(&le, 0, sizeof(le)); le.timestamp_ns = line->timestamp_ns; - eflags = READ_ONCE(line->eflags); + edflags = READ_ONCE(line->edflags); - if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { - if (line->raw_level >= 0) { - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) - level = !line->raw_level; - else - level = line->raw_level; - } else { - level = gpiod_get_value_cansleep(line->desc); - } + switch (edflags & GPIO_V2_LINE_EDGE_FLAGS) { + case GPIO_V2_LINE_FLAG_EDGE_BOTH: + level = (line->raw_level >= 0) ? + line->raw_level : + gpiod_get_raw_value_cansleep(line->desc); - if (level) - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { - /* Emit low-to-high event */ + if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) + level = !level; + + le.id = line_event_id(level); + break; + case GPIO_V2_LINE_FLAG_EDGE_RISING: le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { - /* Emit high-to-low event */ + break; + case GPIO_V2_LINE_FLAG_EDGE_FALLING: le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else { + break; + default: return HTE_CB_HANDLED; } le.line_seqno = line->line_seqno; @@ -659,12 +673,47 @@ static enum hte_return process_hw_ts(struct hte_ts_data *ts, void *p) return HTE_CB_HANDLED; } +static int hte_edge_setup(struct line *line, u64 eflags) +{ + int ret; + unsigned long flags = 0; + struct hte_ts_desc *hdesc = &line->hdesc; + + if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_FALLING_EDGE_TS : + HTE_RISING_EDGE_TS; + if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) + flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? + HTE_RISING_EDGE_TS : + HTE_FALLING_EDGE_TS; + + line->total_discard_seq = 0; + + hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, NULL, + line->desc); + + ret = hte_ts_get(NULL, hdesc, 0); + if (ret) + return ret; + + return hte_request_ts_ns(hdesc, process_hw_ts, process_hw_ts_thread, + line); +} + +#else + +static int hte_edge_setup(struct line *line, u64 eflags) +{ + return 0; +} +#endif /* CONFIG_HTE */ + static irqreturn_t edge_irq_thread(int irq, void *p) { struct line *line = p; struct linereq *lr = line->req; struct gpio_v2_line_event le; - u64 eflags; /* Do not leak kernel stack to userspace */ memset(&le, 0, sizeof(le)); @@ -683,23 +732,17 @@ static irqreturn_t edge_irq_thread(int irq, void *p) } line->timestamp_ns = 0; - eflags = READ_ONCE(line->eflags); - if (eflags == GPIO_V2_LINE_FLAG_EDGE_BOTH) { - int level = gpiod_get_value_cansleep(line->desc); - - if (level) - /* Emit low-to-high event */ - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_RISING) { - /* Emit low-to-high event */ + switch (READ_ONCE(line->edflags) & GPIO_V2_LINE_EDGE_FLAGS) { + case GPIO_V2_LINE_FLAG_EDGE_BOTH: + le.id = line_event_id(gpiod_get_value_cansleep(line->desc)); + break; + case GPIO_V2_LINE_FLAG_EDGE_RISING: le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - } else if (eflags == GPIO_V2_LINE_FLAG_EDGE_FALLING) { - /* Emit high-to-low event */ + break; + case GPIO_V2_LINE_FLAG_EDGE_FALLING: le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; - } else { + break; + default: return IRQ_NONE; } line->line_seqno++; @@ -764,16 +807,16 @@ static void debounce_work_func(struct work_struct *work) struct gpio_v2_line_event le; struct line *line = container_of(work, struct line, work.work); struct linereq *lr; - int level, diff_seqno; - u64 eflags; + u64 eflags, edflags = READ_ONCE(line->edflags); + int level = -1; +#ifdef CONFIG_HTE + int diff_seqno; - if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) level = line->raw_level; - if (level < 0) - level = gpiod_get_raw_value_cansleep(line->desc); - } else { +#endif + if (level < 0) level = gpiod_get_raw_value_cansleep(line->desc); - } if (level < 0) { pr_debug_ratelimited("debouncer failed to read line value\n"); return; @@ -785,12 +828,12 @@ static void debounce_work_func(struct work_struct *work) WRITE_ONCE(line->level, level); /* -- edge detection -- */ - eflags = READ_ONCE(line->eflags); + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (!eflags) return; /* switch from physical level to logical - if they differ */ - if (test_bit(FLAG_ACTIVE_LOW, &line->desc->flags)) + if (edflags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) level = !level; /* ignore edges that are not being monitored */ @@ -804,7 +847,8 @@ static void debounce_work_func(struct work_struct *work) lr = line->req; le.timestamp_ns = line_event_timestamp(line); le.offset = gpio_chip_hwgpio(line->desc); - if (test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags)) { +#ifdef CONFIG_HTE + if (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) { /* discard events except the last one */ line->total_discard_seq -= 1; diff_seqno = line->last_seqno - line->total_discard_seq - @@ -813,51 +857,21 @@ static void debounce_work_func(struct work_struct *work) le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : atomic_add_return(diff_seqno, &lr->seqno); - } else { + } else +#endif /* CONFIG_HTE */ + { line->line_seqno++; le.line_seqno = line->line_seqno; le.seqno = (lr->num_lines == 1) ? le.line_seqno : atomic_inc_return(&lr->seqno); } - if (level) - /* Emit low-to-high event */ - le.id = GPIO_V2_LINE_EVENT_RISING_EDGE; - else - /* Emit high-to-low event */ - le.id = GPIO_V2_LINE_EVENT_FALLING_EDGE; + le.id = line_event_id(level); linereq_put_event(lr, &le); } -static int hte_edge_setup(struct line *line, u64 eflags) -{ - int ret; - unsigned long flags = 0; - struct hte_ts_desc *hdesc = &line->hdesc; - - if (eflags & GPIO_V2_LINE_FLAG_EDGE_RISING) - flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? - HTE_FALLING_EDGE_TS : HTE_RISING_EDGE_TS; - if (eflags & GPIO_V2_LINE_FLAG_EDGE_FALLING) - flags |= test_bit(FLAG_ACTIVE_LOW, &line->desc->flags) ? - HTE_RISING_EDGE_TS : HTE_FALLING_EDGE_TS; - - line->total_discard_seq = 0; - - hte_init_line_attr(hdesc, desc_to_gpio(line->desc), flags, - NULL, line->desc); - - ret = hte_ts_get(NULL, hdesc, 0); - if (ret) - return ret; - - return hte_request_ts_ns(hdesc, process_hw_ts, - process_hw_ts_thread, line); -} - -static int debounce_setup(struct line *line, - unsigned int debounce_period_us, bool hte_req) +static int debounce_setup(struct line *line, unsigned int debounce_period_us) { unsigned long irqflags; int ret, level, irq; @@ -877,7 +891,8 @@ static int debounce_setup(struct line *line, if (level < 0) return level; - if (!hte_req) { + if (!(IS_ENABLED(CONFIG_HTE) && + test_bit(FLAG_EVENT_CLOCK_HTE, &line->desc->flags))) { irq = gpiod_to_irq(line->desc); if (irq < 0) return -ENXIO; @@ -889,9 +904,7 @@ static int debounce_setup(struct line *line, return ret; line->irq = irq; } else { - ret = hte_edge_setup(line, - GPIO_V2_LINE_FLAG_EDGE_RISING | - GPIO_V2_LINE_FLAG_EDGE_FALLING); + ret = hte_edge_setup(line, GPIO_V2_LINE_FLAG_EDGE_BOTH); if (ret) return ret; } @@ -930,19 +943,21 @@ static u32 gpio_v2_line_config_debounce_period(struct gpio_v2_line_config *lc, return 0; } -static void edge_detector_stop(struct line *line, bool hte_en) +static void edge_detector_stop(struct line *line) { - if (line->irq && !hte_en) { + if (line->irq) { free_irq(line->irq, line); line->irq = 0; } - if (hte_en) +#ifdef CONFIG_HTE + if (READ_ONCE(line->edflags) & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) hte_ts_put(&line->hdesc); +#endif cancel_delayed_work_sync(&line->work); WRITE_ONCE(line->sw_debounced, 0); - WRITE_ONCE(line->eflags, 0); + WRITE_ONCE(line->edflags, 0); if (line->desc) WRITE_ONCE(line->desc->debounce_period_us, 0); /* do not change line->level - see comment in debounced_value() */ @@ -950,23 +965,23 @@ static void edge_detector_stop(struct line *line, bool hte_en) static int edge_detector_setup(struct line *line, struct gpio_v2_line_config *lc, - unsigned int line_idx, - u64 eflags, bool hte_req) + unsigned int line_idx, u64 edflags) { u32 debounce_period_us; unsigned long irqflags = 0; + u64 eflags; int irq, ret; + eflags = edflags & GPIO_V2_LINE_EDGE_FLAGS; if (eflags && !kfifo_initialized(&line->req->events)) { ret = kfifo_alloc(&line->req->events, line->req->event_buffer_size, GFP_KERNEL); if (ret) return ret; } - WRITE_ONCE(line->eflags, eflags); if (gpio_v2_line_config_debounced(lc, line_idx)) { debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - ret = debounce_setup(line, debounce_period_us, hte_req); + ret = debounce_setup(line, debounce_period_us); if (ret) return ret; WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); @@ -976,8 +991,9 @@ static int edge_detector_setup(struct line *line, if (!eflags || READ_ONCE(line->sw_debounced)) return 0; - if (hte_req) - return hte_edge_setup(line, eflags); + if (IS_ENABLED(CONFIG_HTE) && + (edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) + return hte_edge_setup(line, edflags); irq = gpiod_to_irq(line->desc); if (irq < 0) @@ -1003,35 +1019,29 @@ static int edge_detector_setup(struct line *line, static int edge_detector_update(struct line *line, struct gpio_v2_line_config *lc, - unsigned int line_idx, - u64 flags, bool polarity_change, - bool prev_hte_flag) + unsigned int line_idx, u64 edflags) { - u64 eflags = flags & GPIO_V2_LINE_EDGE_FLAGS; + u64 active_edflags = READ_ONCE(line->edflags); unsigned int debounce_period_us = gpio_v2_line_config_debounce_period(lc, line_idx); - bool hte_change = (prev_hte_flag != - ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) != 0)); - if ((READ_ONCE(line->eflags) == eflags) && !polarity_change && - (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us) - && !hte_change) + if ((active_edflags == edflags) && + (READ_ONCE(line->desc->debounce_period_us) == debounce_period_us)) return 0; /* sw debounced and still will be...*/ if (debounce_period_us && READ_ONCE(line->sw_debounced)) { - WRITE_ONCE(line->eflags, eflags); WRITE_ONCE(line->desc->debounce_period_us, debounce_period_us); return 0; } /* reconfiguring edge detection or sw debounce being disabled */ - if ((line->irq && !READ_ONCE(line->sw_debounced)) || prev_hte_flag || + if ((line->irq && !READ_ONCE(line->sw_debounced)) || + (active_edflags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE) || (!debounce_period_us && READ_ONCE(line->sw_debounced))) - edge_detector_stop(line, prev_hte_flag); + edge_detector_stop(line); - return edge_detector_setup(line, lc, line_idx, eflags, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + return edge_detector_setup(line, lc, line_idx, edflags); } static u64 gpio_v2_line_config_flags(struct gpio_v2_line_config *lc, @@ -1067,6 +1077,11 @@ static int gpio_v2_line_flags_validate(u64 flags) /* Return an error if an unknown flag is set */ if (flags & ~GPIO_V2_LINE_VALID_FLAGS) return -EINVAL; + + if (!IS_ENABLED(CONFIG_HTE) && + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) + return -EOPNOTSUPP; + /* * Do not allow both INPUT and OUTPUT flags to be set as they are * contradictory. @@ -1076,7 +1091,8 @@ static int gpio_v2_line_flags_validate(u64 flags) return -EINVAL; /* Only allow one event clock source */ - if ((flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && + if (IS_ENABLED(CONFIG_HTE) && + (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_REALTIME) && (flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE)) return -EINVAL; @@ -1300,22 +1316,17 @@ static long linereq_set_config_unlocked(struct linereq *lr, struct gpio_v2_line_config *lc) { struct gpio_desc *desc; + struct line *line; unsigned int i; - u64 flags; - bool polarity_change; - bool prev_hte_flag; + u64 flags, edflags; int ret; for (i = 0; i < lr->num_lines; i++) { + line = &lr->lines[i]; desc = lr->lines[i].desc; flags = gpio_v2_line_config_flags(lc, i); - polarity_change = - (!!test_bit(FLAG_ACTIVE_LOW, &desc->flags) != - ((flags & GPIO_V2_LINE_FLAG_ACTIVE_LOW) != 0)); - - prev_hte_flag = !!test_bit(FLAG_EVENT_CLOCK_HTE, &desc->flags); - gpio_v2_line_config_flags_to_desc_flags(flags, &desc->flags); + edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -1323,7 +1334,7 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (flags & GPIO_V2_LINE_FLAG_OUTPUT) { int val = gpio_v2_line_config_output_value(lc, i); - edge_detector_stop(&lr->lines[i], prev_hte_flag); + edge_detector_stop(line); ret = gpiod_direction_output(desc, val); if (ret) return ret; @@ -1332,12 +1343,13 @@ static long linereq_set_config_unlocked(struct linereq *lr, if (ret) return ret; - ret = edge_detector_update(&lr->lines[i], lc, i, - flags, polarity_change, prev_hte_flag); + ret = edge_detector_update(line, lc, i, edflags); if (ret) return ret; } + WRITE_ONCE(line->edflags, edflags); + blocking_notifier_call_chain(&desc->gdev->notifier, GPIO_V2_LINE_CHANGED_CONFIG, desc); @@ -1464,15 +1476,12 @@ static ssize_t linereq_read(struct file *file, static void linereq_free(struct linereq *lr) { unsigned int i; - bool hte = false; for (i = 0; i < lr->num_lines; i++) { - if (lr->lines[i].desc) - hte = !!test_bit(FLAG_EVENT_CLOCK_HTE, - &lr->lines[i].desc->flags); - edge_detector_stop(&lr->lines[i], hte); - if (lr->lines[i].desc) + if (lr->lines[i].desc) { + edge_detector_stop(&lr->lines[i]); gpiod_free(lr->lines[i].desc); + } } kfifo_free(&lr->events); kfree(lr->label); @@ -1506,7 +1515,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) struct gpio_v2_line_config *lc; struct linereq *lr; struct file *file; - u64 flags; + u64 flags, edflags; unsigned int i; int fd, ret; @@ -1580,6 +1589,7 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) if (ret < 0) goto out_free_linereq; + edflags = flags & GPIO_V2_LINE_EDGE_DETECTOR_FLAGS; /* * Lines have to be requested explicitly for input * or output, else the line will be treated "as is". @@ -1596,12 +1606,13 @@ static int linereq_create(struct gpio_device *gdev, void __user *ip) goto out_free_linereq; ret = edge_detector_setup(&lr->lines[i], lc, i, - flags & GPIO_V2_LINE_EDGE_FLAGS, - flags & GPIO_V2_LINE_FLAG_EVENT_CLOCK_HTE); + edflags); if (ret) goto out_free_linereq; } + lr->lines[i].edflags = edflags; + blocking_notifier_call_chain(&desc->gdev->notifier, GPIO_V2_LINE_CHANGED_REQUESTED, desc); diff --git a/drivers/gpio/gpiolib-devres.c b/drivers/gpio/gpiolib-devres.c index 79da85d17b71..16a696249229 100644 --- a/drivers/gpio/gpiolib-devres.c +++ b/drivers/gpio/gpiolib-devres.c @@ -375,9 +375,6 @@ void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs) } EXPORT_SYMBOL_GPL(devm_gpiod_put_array); - - - static void devm_gpio_release(struct device *dev, void *res) { unsigned *gpio = res; @@ -385,13 +382,6 @@ static void devm_gpio_release(struct device *dev, void *res) gpio_free(*gpio); } -static int devm_gpio_match(struct device *dev, void *res, void *data) -{ - unsigned *this = res, *gpio = data; - - return *this == *gpio; -} - /** * devm_gpio_request - request a GPIO for a managed device * @dev: device to request the GPIO for @@ -402,11 +392,7 @@ static int devm_gpio_match(struct device *dev, void *res, void *data) * same arguments and performs the same function as * gpio_request(). GPIOs requested with this function will be * automatically freed on driver detach. - * - * If an GPIO allocated with this function needs to be freed - * separately, devm_gpio_free() must be used. */ - int devm_gpio_request(struct device *dev, unsigned gpio, const char *label) { unsigned *dr; @@ -459,24 +445,6 @@ int devm_gpio_request_one(struct device *dev, unsigned gpio, } EXPORT_SYMBOL_GPL(devm_gpio_request_one); -/** - * devm_gpio_free - free a GPIO - * @dev: device to free GPIO for - * @gpio: GPIO to free - * - * Except for the extra @dev argument, this function takes the - * same arguments and performs the same function as gpio_free(). - * This function instead of gpio_free() should be used to manually - * free GPIOs allocated with devm_gpio_request(). - */ -void devm_gpio_free(struct device *dev, unsigned int gpio) -{ - - WARN_ON(devres_release(dev, devm_gpio_release, devm_gpio_match, - &gpio)); -} -EXPORT_SYMBOL_GPL(devm_gpio_free); - static void devm_gpio_chip_release(void *data) { struct gpio_chip *gc = data; diff --git a/drivers/gpio/gpiolib-of.c b/drivers/gpio/gpiolib-of.c index 3d6c3ffd5576..a037b50bef33 100644 --- a/drivers/gpio/gpiolib-of.c +++ b/drivers/gpio/gpiolib-of.c @@ -354,6 +354,9 @@ struct gpio_desc *gpiod_get_from_of_node(const struct device_node *node, if (flags & OF_GPIO_PULL_DOWN) lflags |= GPIO_PULL_DOWN; + if (flags & OF_GPIO_PULL_DISABLE) + lflags |= GPIO_PULL_DISABLE; + ret = gpiod_configure_flags(desc, propname, lflags, dflags); if (ret < 0) { gpiod_put(desc); @@ -556,6 +559,8 @@ struct gpio_desc *of_find_gpio(struct device *dev, const char *con_id, *flags |= GPIO_PULL_UP; if (of_flags & OF_GPIO_PULL_DOWN) *flags |= GPIO_PULL_DOWN; + if (of_flags & OF_GPIO_PULL_DISABLE) + *flags |= GPIO_PULL_DISABLE; return desc; } @@ -621,6 +626,8 @@ static struct gpio_desc *of_parse_own_gpio(struct device_node *np, *lflags |= GPIO_PULL_UP; if (xlate_flags & OF_GPIO_PULL_DOWN) *lflags |= GPIO_PULL_DOWN; + if (xlate_flags & OF_GPIO_PULL_DISABLE) + *lflags |= GPIO_PULL_DISABLE; if (of_property_read_bool(np, "input")) *dflags |= GPIOD_IN; @@ -720,7 +727,7 @@ static void of_gpiochip_remove_hog(struct gpio_chip *chip, static int of_gpiochip_match_node(struct gpio_chip *chip, void *data) { - return chip->gpiodev->dev.of_node == data; + return device_match_of_node(&chip->gpiodev->dev, data); } static struct gpio_chip *of_find_gpiochip_by_node(struct device_node *np) @@ -860,7 +867,8 @@ int of_mm_gpiochip_add_data(struct device_node *np, if (mm_gc->save_regs) mm_gc->save_regs(mm_gc); - mm_gc->gc.of_node = np; + of_node_put(mm_gc->gc.of_node); + mm_gc->gc.of_node = of_node_get(np); ret = gpiochip_add_data(gc, data); if (ret) @@ -868,6 +876,7 @@ int of_mm_gpiochip_add_data(struct device_node *np, return 0; err2: + of_node_put(np); iounmap(mm_gc->regs); err1: kfree(gc->label); diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c index 68d9f95d7799..cc9c0a12259e 100644 --- a/drivers/gpio/gpiolib.c +++ b/drivers/gpio/gpiolib.c @@ -3942,9 +3942,11 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, if (lflags & GPIO_OPEN_SOURCE) set_bit(FLAG_OPEN_SOURCE, &desc->flags); - if ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) { + if (((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DOWN)) || + ((lflags & GPIO_PULL_UP) && (lflags & GPIO_PULL_DISABLE)) || + ((lflags & GPIO_PULL_DOWN) && (lflags & GPIO_PULL_DISABLE))) { gpiod_err(desc, - "both pull-up and pull-down enabled, invalid configuration\n"); + "multiple pull-up, pull-down or pull-disable enabled, invalid configuration\n"); return -EINVAL; } @@ -3952,6 +3954,8 @@ int gpiod_configure_flags(struct gpio_desc *desc, const char *con_id, set_bit(FLAG_PULL_UP, &desc->flags); else if (lflags & GPIO_PULL_DOWN) set_bit(FLAG_PULL_DOWN, &desc->flags); + else if (lflags & GPIO_PULL_DISABLE) + set_bit(FLAG_BIAS_DISABLE, &desc->flags); ret = gpiod_set_transitory(desc, (lflags & GPIO_TRANSITORY)); if (ret < 0) diff --git a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c index 63b2d32545cc..726a5bba40b2 100644 --- a/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c +++ b/drivers/gpu/drm/amd/amdgpu/psp_v13_0.c @@ -22,6 +22,7 @@ */ #include #include +#include #include "amdgpu.h" #include "amdgpu_psp.h" #include "amdgpu_ucode.h" diff --git a/drivers/hid/.kunitconfig b/drivers/hid/.kunitconfig new file mode 100644 index 000000000000..04daeff5c970 --- /dev/null +++ b/drivers/hid/.kunitconfig @@ -0,0 +1,5 @@ +CONFIG_KUNIT=y +CONFIG_USB=y +CONFIG_USB_HID=y +CONFIG_HID_UCLOGIC=y +CONFIG_HID_KUNIT_TEST=y diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 70da5931082f..6ce92830b5d1 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -1306,6 +1306,22 @@ config HID_MCP2221 To compile this driver as a module, choose M here: the module will be called hid-mcp2221.ko. +config HID_KUNIT_TEST + bool "KUnit tests for HID" if !KUNIT_ALL_TESTS + depends on KUNIT=y + depends on HID_UCLOGIC + default KUNIT_ALL_TESTS + help + This builds unit tests for HID. This option is not useful for + distributions or general kernels, but only for kernel + developers working on HID and associated drivers. + + For more information on KUnit and unit tests in general, + please refer to the KUnit documentation in + Documentation/dev-tools/kunit/. + + If in doubt, say "N". + endmenu endif # HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cac2cbe26d11..b0bef8098139 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -144,6 +144,9 @@ obj-$(CONFIG_HID_WIIMOTE) += hid-wiimote.o obj-$(CONFIG_HID_SENSOR_HUB) += hid-sensor-hub.o obj-$(CONFIG_HID_SENSOR_CUSTOM_SENSOR) += hid-sensor-custom.o +obj-$(CONFIG_HID_KUNIT_TEST) += hid-uclogic-rdesc.o \ + hid-uclogic-rdesc-test.o + obj-$(CONFIG_USB_HID) += usbhid/ obj-$(CONFIG_USB_MOUSE) += usbhid/ obj-$(CONFIG_USB_KBD) += usbhid/ diff --git a/drivers/hid/amd-sfh-hid/Makefile b/drivers/hid/amd-sfh-hid/Makefile index 35e704da5612..0222170ab7ad 100644 --- a/drivers/hid/amd-sfh-hid/Makefile +++ b/drivers/hid/amd-sfh-hid/Makefile @@ -9,5 +9,8 @@ amd_sfh-objs := amd_sfh_hid.o amd_sfh-objs += amd_sfh_client.o amd_sfh-objs += amd_sfh_pcie.o amd_sfh-objs += hid_descriptor/amd_sfh_hid_desc.o +amd_sfh-objs += sfh1_1/amd_sfh_init.o +amd_sfh-objs += sfh1_1/amd_sfh_interface.o +amd_sfh-objs += sfh1_1/amd_sfh_desc.o ccflags-y += -I $(srctree)/$(src)/ diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_client.c b/drivers/hid/amd-sfh-hid/amd_sfh_client.c index 0f770a2b47ff..8275bba63611 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_client.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_client.c @@ -18,18 +18,6 @@ #include "amd_sfh_pcie.h" #include "amd_sfh_hid.h" - -struct request_list { - struct hid_device *hid; - struct list_head list; - u8 report_id; - u8 sensor_idx; - u8 report_type; - u8 current_index; -}; - -static struct request_list req_list; - void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type) { @@ -50,6 +38,7 @@ int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type) { struct amdtp_hid_data *hid_data = hid->driver_data; struct amdtp_cl_data *cli_data = hid_data->cli_data; + struct request_list *req_list = &cli_data->req_list; int i; for (i = 0; i < cli_data->num_hid_devices; i++) { @@ -66,7 +55,7 @@ int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type) new->report_id = report_id; cli_data->report_id[i] = report_id; cli_data->request_done[i] = false; - list_add(&new->list, &req_list.list); + list_add(&new->list, &req_list->list); break; } } @@ -74,16 +63,19 @@ int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type) return 0; } -static void amd_sfh_work(struct work_struct *work) +void amd_sfh_work(struct work_struct *work) { struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work.work); + struct request_list *req_list = &cli_data->req_list; struct amd_input_data *in_data = cli_data->in_data; struct request_list *req_node; u8 current_index, sensor_index; + struct amd_mp2_ops *mp2_ops; + struct amd_mp2_dev *mp2; u8 report_id, node_type; u8 report_size = 0; - req_node = list_last_entry(&req_list.list, struct request_list, list); + req_node = list_last_entry(&req_list->list, struct request_list, list); list_del(&req_node->list); current_index = req_node->current_index; sensor_index = req_node->sensor_idx; @@ -91,9 +83,11 @@ static void amd_sfh_work(struct work_struct *work) node_type = req_node->report_type; kfree(req_node); + mp2 = container_of(in_data, struct amd_mp2_dev, in_data); + mp2_ops = mp2->mp2_ops; if (node_type == HID_FEATURE_REPORT) { - report_size = get_feature_report(sensor_index, report_id, - cli_data->feature_report[current_index]); + report_size = mp2_ops->get_feat_rep(sensor_index, report_id, + cli_data->feature_report[current_index]); if (report_size) hid_input_report(cli_data->hid_sensor_hubs[current_index], cli_data->report_type[current_index], @@ -102,7 +96,7 @@ static void amd_sfh_work(struct work_struct *work) pr_err("AMDSFH: Invalid report size\n"); } else if (node_type == HID_INPUT_REPORT) { - report_size = get_input_report(current_index, sensor_index, report_id, in_data); + report_size = mp2_ops->get_in_rep(current_index, sensor_index, report_id, in_data); if (report_size) hid_input_report(cli_data->hid_sensor_hubs[current_index], cli_data->report_type[current_index], @@ -115,17 +109,19 @@ static void amd_sfh_work(struct work_struct *work) amdtp_hid_wakeup(cli_data->hid_sensor_hubs[current_index]); } -static void amd_sfh_work_buffer(struct work_struct *work) +void amd_sfh_work_buffer(struct work_struct *work) { struct amdtp_cl_data *cli_data = container_of(work, struct amdtp_cl_data, work_buffer.work); struct amd_input_data *in_data = cli_data->in_data; + struct amd_mp2_dev *mp2; u8 report_size; int i; for (i = 0; i < cli_data->num_hid_devices; i++) { if (cli_data->sensor_sts[i] == SENSOR_ENABLED) { - report_size = get_input_report - (i, cli_data->sensor_idx[i], cli_data->report_id[i], in_data); + mp2 = container_of(in_data, struct amd_mp2_dev, in_data); + report_size = mp2->mp2_ops->get_in_rep(i, cli_data->sensor_idx[i], + cli_data->report_id[i], in_data); hid_input_report(cli_data->hid_sensor_hubs[i], HID_INPUT_REPORT, in_data->input_report[i], report_size, 0); } @@ -133,7 +129,7 @@ static void amd_sfh_work_buffer(struct work_struct *work) schedule_delayed_work(&cli_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); } -u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) +static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) { if (mp2->mp2_ops->response) sensor_sts = mp2->mp2_ops->response(mp2, sid, sensor_sts); @@ -141,7 +137,7 @@ u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts) return sensor_sts; } -const char *get_sensor_name(int idx) +static const char *get_sensor_name(int idx) { switch (idx) { case accel_idx: @@ -159,24 +155,82 @@ const char *get_sensor_name(int idx) } } +static void amd_sfh_resume(struct amd_mp2_dev *mp2) +{ + struct amdtp_cl_data *cl_data = mp2->cl_data; + struct amd_mp2_sensor_info info; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_sts[i] == SENSOR_DISABLED) { + info.period = AMD_SFH_IDLE_LOOP; + info.sensor_idx = cl_data->sensor_idx[i]; + info.dma_address = cl_data->sensor_dma_addr[i]; + mp2->mp2_ops->start(mp2, info); + status = amd_sfh_wait_for_response + (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED); + if (status == SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_ENABLED; + dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + } + } + + schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); + amd_sfh_clear_intr(mp2); +} + +static void amd_sfh_suspend(struct amd_mp2_dev *mp2) +{ + struct amdtp_cl_data *cl_data = mp2->cl_data; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_idx[i] != HPD_IDX && + cl_data->sensor_sts[i] == SENSOR_ENABLED) { + mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED); + if (status != SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + } + } + + cancel_delayed_work_sync(&cl_data->work_buffer); + amd_sfh_clear_intr(mp2); +} + int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) { struct amd_input_data *in_data = &privdata->in_data; struct amdtp_cl_data *cl_data = privdata->cl_data; + struct amd_mp2_ops *mp2_ops = privdata->mp2_ops; struct amd_mp2_sensor_info info; + struct request_list *req_list; struct device *dev; u32 feature_report_size; u32 input_report_size; int rc, i, status; u8 cl_idx; + req_list = &cl_data->req_list; dev = &privdata->pdev->dev; + amd_sfh_set_desc_ops(mp2_ops); + + mp2_ops->suspend = amd_sfh_suspend; + mp2_ops->resume = amd_sfh_resume; cl_data->num_hid_devices = amd_mp2_get_sensor_num(privdata, &cl_data->sensor_idx[0]); + if (cl_data->num_hid_devices == 0) + return -ENODEV; INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work); INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer); - INIT_LIST_HEAD(&req_list.list); + INIT_LIST_HEAD(&req_list->list); cl_data->in_data = in_data; for (i = 0; i < cl_data->num_hid_devices; i++) { @@ -187,17 +241,17 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) cl_data->sensor_requested_cnt[i] = 0; cl_data->cur_hid_dev = i; cl_idx = cl_data->sensor_idx[i]; - cl_data->report_descr_sz[i] = get_descr_sz(cl_idx, descr_size); + cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size); if (!cl_data->report_descr_sz[i]) { rc = -EINVAL; goto cleanup; } - feature_report_size = get_descr_sz(cl_idx, feature_size); + feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size); if (!feature_report_size) { rc = -EINVAL; goto cleanup; } - input_report_size = get_descr_sz(cl_idx, input_size); + input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size); if (!input_report_size) { rc = -EINVAL; goto cleanup; @@ -222,17 +276,17 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) rc = -ENOMEM; goto cleanup; } - rc = get_report_descriptor(cl_idx, cl_data->report_descr[i]); + rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]); if (rc) return rc; - privdata->mp2_ops->start(privdata, info); + mp2_ops->start(privdata, info); status = amd_sfh_wait_for_response (privdata, cl_data->sensor_idx[i], SENSOR_ENABLED); if (status == SENSOR_ENABLED) { cl_data->sensor_sts[i] = SENSOR_ENABLED; rc = amdtp_hid_probe(cl_data->cur_hid_dev, cl_data); if (rc) { - privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); + mp2_ops->stop(privdata, cl_data->sensor_idx[i]); status = amd_sfh_wait_for_response (privdata, cl_data->sensor_idx[i], SENSOR_DISABLED); if (status != SENSOR_ENABLED) @@ -248,8 +302,7 @@ int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata) cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), cl_data->sensor_sts[i]); } - if (privdata->mp2_ops->discovery_status && - privdata->mp2_ops->discovery_status(privdata) == 0) { + if (mp2_ops->discovery_status && mp2_ops->discovery_status(privdata) == 0) { amd_sfh_hid_client_deinit(privdata); for (i = 0; i < cl_data->num_hid_devices; i++) { devm_kfree(dev, cl_data->feature_report[i]); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_common.h b/drivers/hid/amd-sfh-hid/amd_sfh_common.h new file mode 100644 index 000000000000..2643bb14fee2 --- /dev/null +++ b/drivers/hid/amd-sfh-hid/amd_sfh_common.h @@ -0,0 +1,76 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD MP2 common macros and structures + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Basavaraj Natikar + */ +#ifndef AMD_SFH_COMMON_H +#define AMD_SFH_COMMON_H + +#include +#include "amd_sfh_hid.h" + +#define PCI_DEVICE_ID_AMD_MP2 0x15E4 +#define PCI_DEVICE_ID_AMD_MP2_1_1 0x164A + +#define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4)) +#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4)) + +#define SENSOR_ENABLED 4 +#define SENSOR_DISABLED 5 + +#define AMD_SFH_IDLE_LOOP 200 + +enum cmd_id { + NO_OP, + ENABLE_SENSOR, + DISABLE_SENSOR, + STOP_ALL_SENSORS = 8, +}; + +struct amd_mp2_sensor_info { + u8 sensor_idx; + u32 period; + dma_addr_t dma_address; +}; + +struct amd_mp2_dev { + struct pci_dev *pdev; + struct amdtp_cl_data *cl_data; + void __iomem *mmio; + void __iomem *vsbase; + const struct amd_sfh1_1_ops *sfh1_1_ops; + struct amd_mp2_ops *mp2_ops; + struct amd_input_data in_data; + /* mp2 active control status */ + u32 mp2_acs; +}; + +struct amd_mp2_ops { + void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info); + void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx); + void (*stop_all)(struct amd_mp2_dev *privdata); + int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts); + void (*clear_intr)(struct amd_mp2_dev *privdata); + int (*init_intr)(struct amd_mp2_dev *privdata); + int (*discovery_status)(struct amd_mp2_dev *privdata); + void (*suspend)(struct amd_mp2_dev *mp2); + void (*resume)(struct amd_mp2_dev *mp2); + void (*remove)(void *privdata); + int (*get_rep_desc)(int sensor_idx, u8 rep_desc[]); + u32 (*get_desc_sz)(int sensor_idx, int descriptor_name); + u8 (*get_feat_rep)(int sensor_idx, int report_id, u8 *feature_report); + u8 (*get_in_rep)(u8 current_index, int sensor_idx, int report_id, + struct amd_input_data *in_data); +}; + +void amd_sfh_work(struct work_struct *work); +void amd_sfh_work_buffer(struct work_struct *work); +void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata); +int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata); +void amd_sfh_clear_intr(struct amd_mp2_dev *privdata); +int amd_sfh_irq_init(struct amd_mp2_dev *privdata); +#endif diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c index 1089134030b0..1b18291fc5af 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.c @@ -101,11 +101,15 @@ static int amdtp_wait_for_response(struct hid_device *hid) void amdtp_hid_wakeup(struct hid_device *hid) { - struct amdtp_hid_data *hid_data = hid->driver_data; - struct amdtp_cl_data *cli_data = hid_data->cli_data; + struct amdtp_hid_data *hid_data; + struct amdtp_cl_data *cli_data; - cli_data->request_done[cli_data->cur_hid_dev] = true; - wake_up_interruptible(&hid_data->hid_wait); + if (hid) { + hid_data = hid->driver_data; + cli_data = hid_data->cli_data; + cli_data->request_done[cli_data->cur_hid_dev] = true; + wake_up_interruptible(&hid_data->hid_wait); + } } static struct hid_ll_driver amdtp_hid_ll_driver = { diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h index ad264db63180..3754fb423e3a 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_hid.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_hid.h @@ -15,6 +15,15 @@ #define AMD_SFH_HID_VENDOR 0x1022 #define AMD_SFH_HID_PRODUCT 0x0001 +struct request_list { + struct hid_device *hid; + struct list_head list; + u8 report_id; + u8 sensor_idx; + u8 report_type; + u8 current_index; +}; + struct amd_input_data { u32 *sensor_virt_addr[MAX_HID_DEVICES]; u8 *input_report[MAX_HID_DEVICES]; @@ -43,6 +52,7 @@ struct amdtp_cl_data { struct amd_input_data *in_data; struct delayed_work work; struct delayed_work work_buffer; + struct request_list req_list; }; /** @@ -69,6 +79,4 @@ void amdtp_hid_remove(struct amdtp_cl_data *cli_data); int amd_sfh_get_report(struct hid_device *hid, int report_id, int report_type); void amd_sfh_set_report(struct hid_device *hid, int report_id, int report_type); void amdtp_hid_wakeup(struct hid_device *hid); -u8 get_input_report(u8 current_index, int sensor_idx, int report_id, - struct amd_input_data *in_data); #endif diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c index dadc491bbf6b..4b90c86ee5f8 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.c @@ -19,6 +19,7 @@ #include #include "amd_sfh_pcie.h" +#include "sfh1_1/amd_sfh_init.h" #define DRIVER_NAME "pcie_mp2_amd" #define DRIVER_DESC "AMD(R) PCIe MP2 Communication Driver" @@ -92,7 +93,7 @@ static void amd_stop_all_sensor_v2(struct amd_mp2_dev *privdata) writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); } -static void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) +void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) { if (readl(privdata->mmio + AMD_P2C_MSG(4))) { writel(0, privdata->mmio + AMD_P2C_MSG(4)); @@ -100,7 +101,7 @@ static void amd_sfh_clear_intr_v2(struct amd_mp2_dev *privdata) } } -static void amd_sfh_clear_intr(struct amd_mp2_dev *privdata) +void amd_sfh_clear_intr(struct amd_mp2_dev *privdata) { if (privdata->mp2_ops->clear_intr) privdata->mp2_ops->clear_intr(privdata); @@ -113,7 +114,7 @@ static irqreturn_t amd_sfh_irq_handler(int irq, void *data) return IRQ_HANDLED; } -static int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) +int amd_sfh_irq_init_v2(struct amd_mp2_dev *privdata) { int rc; @@ -136,7 +137,7 @@ static int amd_sfh_dis_sts_v2(struct amd_mp2_dev *privdata) SENSOR_DISCOVERY_STATUS_MASK) >> SENSOR_DISCOVERY_STATUS_SHIFT; } -void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) +static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) { union sfh_cmd_param cmd_param; union sfh_cmd_base cmd_base; @@ -157,7 +158,7 @@ void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info i writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); } -void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) +static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) { union sfh_cmd_base cmd_base; @@ -171,7 +172,7 @@ void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG0); } -void amd_stop_all_sensors(struct amd_mp2_dev *privdata) +static void amd_stop_all_sensors(struct amd_mp2_dev *privdata) { union sfh_cmd_base cmd_base; @@ -244,7 +245,7 @@ static void amd_mp2_pci_remove(void *privdata) amd_sfh_clear_intr(mp2); } -static const struct amd_mp2_ops amd_sfh_ops_v2 = { +static struct amd_mp2_ops amd_sfh_ops_v2 = { .start = amd_start_sensor_v2, .stop = amd_stop_sensor_v2, .stop_all = amd_stop_all_sensor_v2, @@ -252,12 +253,14 @@ static const struct amd_mp2_ops amd_sfh_ops_v2 = { .clear_intr = amd_sfh_clear_intr_v2, .init_intr = amd_sfh_irq_init_v2, .discovery_status = amd_sfh_dis_sts_v2, + .remove = amd_mp2_pci_remove, }; -static const struct amd_mp2_ops amd_sfh_ops = { +static struct amd_mp2_ops amd_sfh_ops = { .start = amd_start_sensor, .stop = amd_stop_sensor, .stop_all = amd_stop_all_sensors, + .remove = amd_mp2_pci_remove, }; static void mp2_select_ops(struct amd_mp2_dev *privdata) @@ -277,7 +280,7 @@ static void mp2_select_ops(struct amd_mp2_dev *privdata) } } -static int amd_sfh_irq_init(struct amd_mp2_dev *privdata) +int amd_sfh_irq_init(struct amd_mp2_dev *privdata) { if (privdata->mp2_ops->init_intr) return privdata->mp2_ops->init_intr(privdata); @@ -316,6 +319,14 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i if (!privdata->cl_data) return -ENOMEM; + privdata->sfh1_1_ops = (const struct amd_sfh1_1_ops *)id->driver_data; + if (privdata->sfh1_1_ops) { + rc = privdata->sfh1_1_ops->init(privdata); + if (rc) + return rc; + goto init_done; + } + mp2_select_ops(privdata); rc = amd_sfh_irq_init(privdata); @@ -327,40 +338,22 @@ static int amd_mp2_pci_probe(struct pci_dev *pdev, const struct pci_device_id *i rc = amd_sfh_hid_client_init(privdata); if (rc) { amd_sfh_clear_intr(privdata); - dev_err(&pdev->dev, "amd_sfh_hid_client_init failed\n"); + if (rc != -EOPNOTSUPP) + dev_err(&pdev->dev, "amd_sfh_hid_client_init failed\n"); return rc; } +init_done: amd_sfh_clear_intr(privdata); - return devm_add_action_or_reset(&pdev->dev, amd_mp2_pci_remove, privdata); + return devm_add_action_or_reset(&pdev->dev, privdata->mp2_ops->remove, privdata); } static int __maybe_unused amd_mp2_pci_resume(struct device *dev) { struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); - struct amdtp_cl_data *cl_data = mp2->cl_data; - struct amd_mp2_sensor_info info; - int i, status; - for (i = 0; i < cl_data->num_hid_devices; i++) { - if (cl_data->sensor_sts[i] == SENSOR_DISABLED) { - info.period = AMD_SFH_IDLE_LOOP; - info.sensor_idx = cl_data->sensor_idx[i]; - info.dma_address = cl_data->sensor_dma_addr[i]; - mp2->mp2_ops->start(mp2, info); - status = amd_sfh_wait_for_response - (mp2, cl_data->sensor_idx[i], SENSOR_ENABLED); - if (status == SENSOR_ENABLED) - cl_data->sensor_sts[i] = SENSOR_ENABLED; - dev_dbg(dev, "suspend sid 0x%x (%s) status 0x%x\n", - cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), - cl_data->sensor_sts[i]); - } - } - - schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); - amd_sfh_clear_intr(mp2); + mp2->mp2_ops->resume(mp2); return 0; } @@ -368,25 +361,8 @@ static int __maybe_unused amd_mp2_pci_resume(struct device *dev) static int __maybe_unused amd_mp2_pci_suspend(struct device *dev) { struct amd_mp2_dev *mp2 = dev_get_drvdata(dev); - struct amdtp_cl_data *cl_data = mp2->cl_data; - int i, status; - for (i = 0; i < cl_data->num_hid_devices; i++) { - if (cl_data->sensor_idx[i] != HPD_IDX && - cl_data->sensor_sts[i] == SENSOR_ENABLED) { - mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]); - status = amd_sfh_wait_for_response - (mp2, cl_data->sensor_idx[i], SENSOR_DISABLED); - if (status != SENSOR_ENABLED) - cl_data->sensor_sts[i] = SENSOR_DISABLED; - dev_dbg(dev, "suspend sid 0x%x (%s) status 0x%x\n", - cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), - cl_data->sensor_sts[i]); - } - } - - cancel_delayed_work_sync(&cl_data->work_buffer); - amd_sfh_clear_intr(mp2); + mp2->mp2_ops->suspend(mp2); return 0; } @@ -396,6 +372,8 @@ static SIMPLE_DEV_PM_OPS(amd_mp2_pm_ops, amd_mp2_pci_suspend, static const struct pci_device_id amd_mp2_pci_tbl[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2) }, + { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_MP2_1_1), + .driver_data = (kernel_ulong_t)&sfh1_1_ops }, { } }; MODULE_DEVICE_TABLE(pci, amd_mp2_pci_tbl); diff --git a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h index 8c760526132a..dfb7cabd82ef 100644 --- a/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h +++ b/drivers/hid/amd-sfh-hid/amd_sfh_pcie.h @@ -10,35 +10,20 @@ #ifndef PCIE_MP2_AMD_H #define PCIE_MP2_AMD_H -#include -#include "amd_sfh_hid.h" - -#define PCI_DEVICE_ID_AMD_MP2 0x15E4 - -#define ENABLE_SENSOR 1 -#define DISABLE_SENSOR 2 -#define STOP_ALL_SENSORS 8 +#include "amd_sfh_common.h" /* MP2 C2P Message Registers */ #define AMD_C2P_MSG0 0x10500 #define AMD_C2P_MSG1 0x10504 #define AMD_C2P_MSG2 0x10508 -#define AMD_C2P_MSG(regno) (0x10500 + ((regno) * 4)) -#define AMD_P2C_MSG(regno) (0x10680 + ((regno) * 4)) - /* MP2 P2C Message Registers */ #define AMD_P2C_MSG3 0x1068C /* Supported Sensors info */ #define V2_STATUS 0x2 -#define SENSOR_ENABLED 4 -#define SENSOR_DISABLED 5 - #define HPD_IDX 16 -#define AMD_SFH_IDLE_LOOP 200 - #define SENSOR_DISCOVERY_STATUS_MASK GENMASK(5, 3) #define SENSOR_DISCOVERY_STATUS_SHIFT 3 @@ -96,22 +81,6 @@ enum sensor_idx { als_idx = 19 }; -struct amd_mp2_dev { - struct pci_dev *pdev; - struct amdtp_cl_data *cl_data; - void __iomem *mmio; - const struct amd_mp2_ops *mp2_ops; - struct amd_input_data in_data; - /* mp2 active control status */ - u32 mp2_acs; -}; - -struct amd_mp2_sensor_info { - u8 sensor_idx; - u32 period; - dma_addr_t dma_address; -}; - enum mem_use_type { USE_DRAM, USE_C2P_REG, @@ -129,24 +98,9 @@ struct hpd_status { }; }; -void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info); -void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx); -void amd_stop_all_sensors(struct amd_mp2_dev *privdata); int amd_mp2_get_sensor_num(struct amd_mp2_dev *privdata, u8 *sensor_id); int amd_sfh_hid_client_init(struct amd_mp2_dev *privdata); int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata); -u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts); -void amd_mp2_suspend(struct amd_mp2_dev *mp2); -void amd_mp2_resume(struct amd_mp2_dev *mp2); -const char *get_sensor_name(int idx); +void amd_sfh_set_desc_ops(struct amd_mp2_ops *mp2_ops); -struct amd_mp2_ops { - void (*start)(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info); - void (*stop)(struct amd_mp2_dev *privdata, u16 sensor_idx); - void (*stop_all)(struct amd_mp2_dev *privdata); - int (*response)(struct amd_mp2_dev *mp2, u8 sid, u32 sensor_sts); - void (*clear_intr)(struct amd_mp2_dev *privdata); - int (*init_intr)(struct amd_mp2_dev *privdata); - int (*discovery_status)(struct amd_mp2_dev *privdata); -}; #endif diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c index 76095bd53c65..f9a8c02d5a7b 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.c @@ -29,7 +29,7 @@ #define HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM 0x04 #define ILLUMINANCE_MASK GENMASK(14, 0) -int get_report_descriptor(int sensor_idx, u8 *rep_desc) +static int get_report_descriptor(int sensor_idx, u8 *rep_desc) { switch (sensor_idx) { case accel_idx: /* accel */ @@ -63,7 +63,7 @@ int get_report_descriptor(int sensor_idx, u8 *rep_desc) return 0; } -u32 get_descr_sz(int sensor_idx, int descriptor_name) +static u32 get_descr_sz(int sensor_idx, int descriptor_name) { switch (sensor_idx) { case accel_idx: @@ -133,7 +133,7 @@ static void get_common_features(struct common_feature_property *common, int repo common->report_interval = HID_DEFAULT_REPORT_INTERVAL; } -u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report) +static u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report) { struct accel3_feature_report acc_feature; struct gyro_feature_report gyro_feature; @@ -200,7 +200,8 @@ static void get_common_inputs(struct common_input_property *common, int report_i common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM; } -u8 get_input_report(u8 current_index, int sensor_idx, int report_id, struct amd_input_data *in_data) +static u8 get_input_report(u8 current_index, int sensor_idx, int report_id, + struct amd_input_data *in_data) { struct amd_mp2_dev *privdata = container_of(in_data, struct amd_mp2_dev, in_data); u32 *sensor_virt_addr = in_data->sensor_virt_addr[current_index]; @@ -267,3 +268,11 @@ u8 get_input_report(u8 current_index, int sensor_idx, int report_id, struct amd_ } return report_size; } + +void amd_sfh_set_desc_ops(struct amd_mp2_ops *mp2_ops) +{ + mp2_ops->get_rep_desc = get_report_descriptor; + mp2_ops->get_feat_rep = get_feature_report; + mp2_ops->get_in_rep = get_input_report; + mp2_ops->get_desc_sz = get_descr_sz; +} diff --git a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h index 70b1b7abe2c6..ebd55675eb62 100644 --- a/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h +++ b/drivers/hid/amd-sfh-hid/hid_descriptor/amd_sfh_hid_desc.h @@ -111,7 +111,4 @@ struct hpd_input_report { u8 human_presence; } __packed; -int get_report_descriptor(int sensor_idx, u8 rep_desc[]); -u32 get_descr_sz(int sensor_idx, int descriptor_name); -u8 get_feature_report(int sensor_idx, int report_id, u8 *feature_report); #endif diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c new file mode 100644 index 000000000000..0609fea581c9 --- /dev/null +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c @@ -0,0 +1,300 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD MP2 1.1 descriptor interfaces + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Basavaraj Natikar + */ + +#include + +#include "amd_sfh_interface.h" +#include "../hid_descriptor/amd_sfh_hid_desc.h" +#include "../hid_descriptor/amd_sfh_hid_report_desc.h" + +#define SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM 0x41 +#define SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM 0x51 +#define HID_DEFAULT_REPORT_INTERVAL 0x50 +#define HID_DEFAULT_MIN_VALUE 0X7F +#define HID_DEFAULT_MAX_VALUE 0x80 +#define HID_DEFAULT_SENSITIVITY 0x7F +#define HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM 0x01 +/* state enums */ +#define HID_USAGE_SENSOR_STATE_READY_ENUM 0x02 +#define HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM 0x05 +#define HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM 0x04 + +static int get_report_desc(int sensor_idx, u8 *rep_desc) +{ + switch (sensor_idx) { + case ACCEL_IDX: /* accelerometer */ + memset(rep_desc, 0, sizeof(accel3_report_descriptor)); + memcpy(rep_desc, accel3_report_descriptor, + sizeof(accel3_report_descriptor)); + break; + case GYRO_IDX: /* gyroscope */ + memset(rep_desc, 0, sizeof(gyro3_report_descriptor)); + memcpy(rep_desc, gyro3_report_descriptor, + sizeof(gyro3_report_descriptor)); + break; + case MAG_IDX: /* magnetometer */ + memset(rep_desc, 0, sizeof(comp3_report_descriptor)); + memcpy(rep_desc, comp3_report_descriptor, + sizeof(comp3_report_descriptor)); + break; + case ALS_IDX: /* ambient light sensor */ + memset(rep_desc, 0, sizeof(als_report_descriptor)); + memcpy(rep_desc, als_report_descriptor, + sizeof(als_report_descriptor)); + break; + case HPD_IDX: /* HPD sensor */ + memset(rep_desc, 0, sizeof(hpd_report_descriptor)); + memcpy(rep_desc, hpd_report_descriptor, + sizeof(hpd_report_descriptor)); + break; + } + return 0; +} + +static void get_common_features(struct common_feature_property *common, int report_id) +{ + common->report_id = report_id; + common->connection_type = HID_USAGE_SENSOR_PROPERTY_CONNECTION_TYPE_PC_INTEGRATED_ENUM; + common->report_state = SENSOR_PROP_REPORTING_STATE_ALL_EVENTS_ENUM; + common->power_state = SENSOR_PROP_POWER_STATE_D0_FULL_POWER_ENUM; + common->sensor_state = HID_USAGE_SENSOR_STATE_INITIALIZING_ENUM; + common->report_interval = HID_DEFAULT_REPORT_INTERVAL; +} + +static u8 get_feature_rep(int sensor_idx, int report_id, u8 *feature_report) +{ + struct magno_feature_report magno_feature; + struct accel3_feature_report acc_feature; + struct gyro_feature_report gyro_feature; + struct hpd_feature_report hpd_feature; + struct als_feature_report als_feature; + u8 report_size = 0; + + if (!feature_report) + return report_size; + + switch (sensor_idx) { + case ACCEL_IDX: /* accelerometer */ + get_common_features(&acc_feature.common_property, report_id); + acc_feature.accel_change_sesnitivity = HID_DEFAULT_SENSITIVITY; + acc_feature.accel_sensitivity_min = HID_DEFAULT_MIN_VALUE; + acc_feature.accel_sensitivity_max = HID_DEFAULT_MAX_VALUE; + memcpy(feature_report, &acc_feature, sizeof(acc_feature)); + report_size = sizeof(acc_feature); + break; + case GYRO_IDX: /* gyroscope */ + get_common_features(&gyro_feature.common_property, report_id); + gyro_feature.gyro_change_sesnitivity = HID_DEFAULT_SENSITIVITY; + gyro_feature.gyro_sensitivity_min = HID_DEFAULT_MIN_VALUE; + gyro_feature.gyro_sensitivity_max = HID_DEFAULT_MAX_VALUE; + memcpy(feature_report, &gyro_feature, sizeof(gyro_feature)); + report_size = sizeof(gyro_feature); + break; + case MAG_IDX: /* magnetometer */ + get_common_features(&magno_feature.common_property, report_id); + magno_feature.magno_headingchange_sensitivity = HID_DEFAULT_SENSITIVITY; + magno_feature.heading_min = HID_DEFAULT_MIN_VALUE; + magno_feature.heading_max = HID_DEFAULT_MAX_VALUE; + magno_feature.flux_change_sensitivity = HID_DEFAULT_MIN_VALUE; + magno_feature.flux_min = HID_DEFAULT_MIN_VALUE; + magno_feature.flux_max = HID_DEFAULT_MAX_VALUE; + memcpy(feature_report, &magno_feature, sizeof(magno_feature)); + report_size = sizeof(magno_feature); + break; + case ALS_IDX: /* ambient light sensor */ + get_common_features(&als_feature.common_property, report_id); + als_feature.als_change_sesnitivity = HID_DEFAULT_SENSITIVITY; + als_feature.als_sensitivity_min = HID_DEFAULT_MIN_VALUE; + als_feature.als_sensitivity_max = HID_DEFAULT_MAX_VALUE; + memcpy(feature_report, &als_feature, sizeof(als_feature)); + report_size = sizeof(als_feature); + break; + case HPD_IDX: /* human presence detection sensor */ + get_common_features(&hpd_feature.common_property, report_id); + memcpy(feature_report, &hpd_feature, sizeof(hpd_feature)); + report_size = sizeof(hpd_feature); + break; + } + return report_size; +} + +static void get_common_inputs(struct common_input_property *common, int report_id) +{ + common->report_id = report_id; + common->sensor_state = HID_USAGE_SENSOR_STATE_READY_ENUM; + common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM; +} + +static int float_to_int(u32 float32) +{ + int fraction, shift, mantissa, sign, exp, zeropre; + + mantissa = float32 & GENMASK(22, 0); + sign = (float32 & BIT(31)) ? -1 : 1; + exp = (float32 & ~BIT(31)) >> 23; + + if (!exp && !mantissa) + return 0; + + exp -= 127; + if (exp < 0) { + exp = -exp; + zeropre = (((BIT(23) + mantissa) * 100) >> 23) >> exp; + return zeropre >= 50 ? sign : 0; + } + + shift = 23 - exp; + float32 = BIT(exp) + (mantissa >> shift); + fraction = mantissa & GENMASK(shift - 1, 0); + + return (((fraction * 100) >> shift) >= 50) ? sign * (float32 + 1) : sign * float32; +} + +static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, + struct amd_input_data *in_data) +{ + struct amd_mp2_dev *mp2 = container_of(in_data, struct amd_mp2_dev, in_data); + u8 *input_report = in_data->input_report[current_index]; + struct magno_input_report magno_input; + struct accel3_input_report acc_input; + struct gyro_input_report gyro_input; + struct als_input_report als_input; + struct hpd_input_report hpd_input; + struct sfh_accel_data accel_data; + struct sfh_gyro_data gyro_data; + struct sfh_mag_data mag_data; + struct sfh_als_data als_data; + struct hpd_status hpdstatus; + void __iomem *sensoraddr; + u8 report_size = 0; + + if (!input_report) + return report_size; + + switch (sensor_idx) { + case ACCEL_IDX: /* accelerometer */ + sensoraddr = mp2->vsbase + (ACCEL_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) + + OFFSET_SENSOR_DATA_DEFAULT; + memcpy_fromio(&accel_data, sensoraddr, sizeof(struct sfh_accel_data)); + get_common_inputs(&acc_input.common_property, report_id); + acc_input.in_accel_x_value = float_to_int(accel_data.acceldata.x) / 100; + acc_input.in_accel_y_value = float_to_int(accel_data.acceldata.y) / 100; + acc_input.in_accel_z_value = float_to_int(accel_data.acceldata.z) / 100; + memcpy(input_report, &acc_input, sizeof(acc_input)); + report_size = sizeof(acc_input); + break; + case GYRO_IDX: /* gyroscope */ + sensoraddr = mp2->vsbase + (GYRO_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) + + OFFSET_SENSOR_DATA_DEFAULT; + memcpy_fromio(&gyro_data, sensoraddr, sizeof(struct sfh_gyro_data)); + get_common_inputs(&gyro_input.common_property, report_id); + gyro_input.in_angel_x_value = float_to_int(gyro_data.gyrodata.x) / 1000; + gyro_input.in_angel_y_value = float_to_int(gyro_data.gyrodata.y) / 1000; + gyro_input.in_angel_z_value = float_to_int(gyro_data.gyrodata.z) / 1000; + memcpy(input_report, &gyro_input, sizeof(gyro_input)); + report_size = sizeof(gyro_input); + break; + case MAG_IDX: /* magnetometer */ + sensoraddr = mp2->vsbase + (MAG_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) + + OFFSET_SENSOR_DATA_DEFAULT; + memcpy_fromio(&mag_data, sensoraddr, sizeof(struct sfh_mag_data)); + get_common_inputs(&magno_input.common_property, report_id); + magno_input.in_magno_x = float_to_int(mag_data.magdata.x) / 100; + magno_input.in_magno_y = float_to_int(mag_data.magdata.y) / 100; + magno_input.in_magno_z = float_to_int(mag_data.magdata.z) / 100; + magno_input.in_magno_accuracy = mag_data.accuracy / 100; + memcpy(input_report, &magno_input, sizeof(magno_input)); + report_size = sizeof(magno_input); + break; + case ALS_IDX: + sensoraddr = mp2->vsbase + (ALS_IDX * SENSOR_DATA_MEM_SIZE_DEFAULT) + + OFFSET_SENSOR_DATA_DEFAULT; + memcpy_fromio(&als_data, sensoraddr, sizeof(struct sfh_als_data)); + get_common_inputs(&als_input.common_property, report_id); + als_input.illuminance_value = als_data.lux; + report_size = sizeof(als_input); + memcpy(input_report, &als_input, sizeof(als_input)); + break; + case HPD_IDX: + get_common_inputs(&hpd_input.common_property, report_id); + hpdstatus.val = readl(mp2->mmio + AMD_C2P_MSG(4)); + hpd_input.human_presence = hpdstatus.shpd.presence; + report_size = sizeof(hpd_input); + memcpy(input_report, &hpd_input, sizeof(hpd_input)); + break; + } + return report_size; +} + +static u32 get_desc_size(int sensor_idx, int descriptor_name) +{ + switch (sensor_idx) { + case ACCEL_IDX: + switch (descriptor_name) { + case descr_size: + return sizeof(accel3_report_descriptor); + case input_size: + return sizeof(struct accel3_input_report); + case feature_size: + return sizeof(struct accel3_feature_report); + } + break; + case GYRO_IDX: + switch (descriptor_name) { + case descr_size: + return sizeof(gyro3_report_descriptor); + case input_size: + return sizeof(struct gyro_input_report); + case feature_size: + return sizeof(struct gyro_feature_report); + } + break; + case MAG_IDX: + switch (descriptor_name) { + case descr_size: + return sizeof(comp3_report_descriptor); + case input_size: + return sizeof(struct magno_input_report); + case feature_size: + return sizeof(struct magno_feature_report); + } + break; + case ALS_IDX: + switch (descriptor_name) { + case descr_size: + return sizeof(als_report_descriptor); + case input_size: + return sizeof(struct als_input_report); + case feature_size: + return sizeof(struct als_feature_report); + } + break; + case HPD_IDX: + switch (descriptor_name) { + case descr_size: + return sizeof(hpd_report_descriptor); + case input_size: + return sizeof(struct hpd_input_report); + case feature_size: + return sizeof(struct hpd_feature_report); + } + break; + } + + return 0; +} + +void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops) +{ + mp2_ops->get_rep_desc = get_report_desc; + mp2_ops->get_feat_rep = get_feature_rep; + mp2_ops->get_desc_sz = get_desc_size; + mp2_ops->get_in_rep = get_input_rep; +} diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c new file mode 100644 index 000000000000..70436f9fad2f --- /dev/null +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.c @@ -0,0 +1,324 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD MP2 1.1 communication driver + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Basavaraj Natikar + */ + +#include +#include + +#include "amd_sfh_init.h" +#include "amd_sfh_interface.h" +#include "../hid_descriptor/amd_sfh_hid_desc.h" + +static int amd_sfh_get_sensor_num(struct amd_mp2_dev *mp2, u8 *sensor_id) +{ + struct sfh_sensor_list *slist; + struct sfh_base_info binfo; + int num_of_sensors = 0; + int i; + + memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info)); + slist = &binfo.sbase.s_list; + + for (i = 0; i < MAX_IDX; i++) { + switch (i) { + case ACCEL_IDX: + case GYRO_IDX: + case MAG_IDX: + case ALS_IDX: + case HPD_IDX: + if (BIT(i) & slist->sl.sensors) + sensor_id[num_of_sensors++] = i; + break; + } + } + + return num_of_sensors; +} + +static u32 amd_sfh_wait_for_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id) +{ + if (mp2->mp2_ops->response) + return mp2->mp2_ops->response(mp2, sid, cmd_id); + + return 0; +} + +static const char *get_sensor_name(int idx) +{ + switch (idx) { + case ACCEL_IDX: + return "accelerometer"; + case GYRO_IDX: + return "gyroscope"; + case MAG_IDX: + return "magnetometer"; + case ALS_IDX: + return "ALS"; + case HPD_IDX: + return "HPD"; + default: + return "unknown sensor type"; + } +} + +static int amd_sfh_hid_client_deinit(struct amd_mp2_dev *privdata) +{ + struct amdtp_cl_data *cl_data = privdata->cl_data; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_sts[i] == SENSOR_ENABLED) { + privdata->mp2_ops->stop(privdata, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], DISABLE_SENSOR); + if (status == 0) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(&privdata->pdev->dev, "stopping sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + } + } + + cancel_delayed_work_sync(&cl_data->work); + cancel_delayed_work_sync(&cl_data->work_buffer); + amdtp_hid_remove(cl_data); + + return 0; +} + +static int amd_sfh1_1_hid_client_init(struct amd_mp2_dev *privdata) +{ + struct amd_input_data *in_data = &privdata->in_data; + struct amdtp_cl_data *cl_data = privdata->cl_data; + struct amd_mp2_ops *mp2_ops = privdata->mp2_ops; + struct amd_mp2_sensor_info info; + struct request_list *req_list; + u32 feature_report_size; + u32 input_report_size; + struct device *dev; + int rc, i, status; + u8 cl_idx; + + req_list = &cl_data->req_list; + dev = &privdata->pdev->dev; + amd_sfh1_1_set_desc_ops(mp2_ops); + + cl_data->num_hid_devices = amd_sfh_get_sensor_num(privdata, &cl_data->sensor_idx[0]); + + INIT_DELAYED_WORK(&cl_data->work, amd_sfh_work); + INIT_DELAYED_WORK(&cl_data->work_buffer, amd_sfh_work_buffer); + INIT_LIST_HEAD(&req_list->list); + cl_data->in_data = in_data; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + cl_data->sensor_sts[i] = SENSOR_DISABLED; + cl_data->sensor_requested_cnt[i] = 0; + cl_data->cur_hid_dev = i; + cl_idx = cl_data->sensor_idx[i]; + + cl_data->report_descr_sz[i] = mp2_ops->get_desc_sz(cl_idx, descr_size); + if (!cl_data->report_descr_sz[i]) { + rc = -EINVAL; + goto cleanup; + } + feature_report_size = mp2_ops->get_desc_sz(cl_idx, feature_size); + if (!feature_report_size) { + rc = -EINVAL; + goto cleanup; + } + input_report_size = mp2_ops->get_desc_sz(cl_idx, input_size); + if (!input_report_size) { + rc = -EINVAL; + goto cleanup; + } + cl_data->feature_report[i] = devm_kzalloc(dev, feature_report_size, GFP_KERNEL); + if (!cl_data->feature_report[i]) { + rc = -ENOMEM; + goto cleanup; + } + in_data->input_report[i] = devm_kzalloc(dev, input_report_size, GFP_KERNEL); + if (!in_data->input_report[i]) { + rc = -ENOMEM; + goto cleanup; + } + + info.sensor_idx = cl_idx; + + cl_data->report_descr[i] = + devm_kzalloc(dev, cl_data->report_descr_sz[i], GFP_KERNEL); + if (!cl_data->report_descr[i]) { + rc = -ENOMEM; + goto cleanup; + } + rc = mp2_ops->get_rep_desc(cl_idx, cl_data->report_descr[i]); + if (rc) + return rc; + + writel(0, privdata->mmio + AMD_P2C_MSG(0)); + mp2_ops->start(privdata, info); + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], ENABLE_SENSOR); + + status = (status == 0) ? SENSOR_ENABLED : SENSOR_DISABLED; + + if (status == SENSOR_ENABLED) { + cl_data->sensor_sts[i] = SENSOR_ENABLED; + rc = amdtp_hid_probe(i, cl_data); + if (rc) { + mp2_ops->stop(privdata, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (privdata, cl_data->sensor_idx[i], DISABLE_SENSOR); + if (status == 0) + status = SENSOR_DISABLED; + if (status != SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], + get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + goto cleanup; + } + } + dev_dbg(dev, "sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + } + + schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); + return 0; + +cleanup: + amd_sfh_hid_client_deinit(privdata); + for (i = 0; i < cl_data->num_hid_devices; i++) { + devm_kfree(dev, cl_data->feature_report[i]); + devm_kfree(dev, in_data->input_report[i]); + devm_kfree(dev, cl_data->report_descr[i]); + } + return rc; +} + +static void amd_sfh_resume(struct amd_mp2_dev *mp2) +{ + struct amdtp_cl_data *cl_data = mp2->cl_data; + struct amd_mp2_sensor_info info; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_sts[i] == SENSOR_DISABLED) { + info.sensor_idx = cl_data->sensor_idx[i]; + mp2->mp2_ops->start(mp2, info); + status = amd_sfh_wait_for_response + (mp2, cl_data->sensor_idx[i], ENABLE_SENSOR); + if (status == 0) + status = SENSOR_ENABLED; + if (status == SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_ENABLED; + dev_dbg(&mp2->pdev->dev, "resume sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + } + } + + schedule_delayed_work(&cl_data->work_buffer, msecs_to_jiffies(AMD_SFH_IDLE_LOOP)); + amd_sfh_clear_intr(mp2); +} + +static void amd_sfh_suspend(struct amd_mp2_dev *mp2) +{ + struct amdtp_cl_data *cl_data = mp2->cl_data; + int i, status; + + for (i = 0; i < cl_data->num_hid_devices; i++) { + if (cl_data->sensor_idx[i] != HPD_IDX && + cl_data->sensor_sts[i] == SENSOR_ENABLED) { + mp2->mp2_ops->stop(mp2, cl_data->sensor_idx[i]); + status = amd_sfh_wait_for_response + (mp2, cl_data->sensor_idx[i], DISABLE_SENSOR); + if (status == 0) + status = SENSOR_DISABLED; + if (status != SENSOR_ENABLED) + cl_data->sensor_sts[i] = SENSOR_DISABLED; + dev_dbg(&mp2->pdev->dev, "suspend sid 0x%x (%s) status 0x%x\n", + cl_data->sensor_idx[i], get_sensor_name(cl_data->sensor_idx[i]), + cl_data->sensor_sts[i]); + } + } + + cancel_delayed_work_sync(&cl_data->work_buffer); + amd_sfh_clear_intr(mp2); +} + +static void amd_mp2_pci_remove(void *privdata) +{ + struct amd_mp2_dev *mp2 = privdata; + + amd_sfh_hid_client_deinit(privdata); + mp2->mp2_ops->stop_all(mp2); + pci_intx(mp2->pdev, false); + amd_sfh_clear_intr(mp2); +} + +static void amd_sfh_set_ops(struct amd_mp2_dev *mp2) +{ + struct amd_mp2_ops *mp2_ops; + + sfh_interface_init(mp2); + mp2_ops = mp2->mp2_ops; + mp2_ops->clear_intr = amd_sfh_clear_intr_v2, + mp2_ops->init_intr = amd_sfh_irq_init_v2, + mp2_ops->suspend = amd_sfh_suspend; + mp2_ops->resume = amd_sfh_resume; + mp2_ops->remove = amd_mp2_pci_remove; +} + +int amd_sfh1_1_init(struct amd_mp2_dev *mp2) +{ + u32 phy_base = readl(mp2->mmio + AMD_C2P_MSG(22)); + struct device *dev = &mp2->pdev->dev; + struct sfh_base_info binfo; + int rc; + + phy_base <<= 21; + if (!devm_request_mem_region(dev, phy_base, 128 * 1024, "amd_sfh")) { + dev_err(dev, "can't reserve mmio registers\n"); + return -ENOMEM; + } + + mp2->vsbase = devm_ioremap(dev, phy_base, 128 * 1024); + if (!mp2->vsbase) { + dev_err(dev, "failed to remap vsbase\n"); + return -ENOMEM; + } + + /* Before accessing give time for SFH firmware for processing configuration */ + msleep(5000); + + memcpy_fromio(&binfo, mp2->vsbase, sizeof(struct sfh_base_info)); + if (binfo.sbase.fw_info.fw_ver == 0 || binfo.sbase.s_list.sl.sensors == 0) { + dev_err(dev, "failed to get sensors\n"); + return -EOPNOTSUPP; + } + dev_dbg(dev, "firmware version 0x%x\n", binfo.sbase.fw_info.fw_ver); + + amd_sfh_set_ops(mp2); + + rc = amd_sfh_irq_init(mp2); + if (rc) { + dev_err(dev, "amd_sfh_irq_init failed\n"); + return rc; + } + + rc = amd_sfh1_1_hid_client_init(mp2); + if (rc) { + dev_err(dev, "amd_sfh1_1_hid_client_init failed\n"); + return rc; + } + + return rc; +} diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.h b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.h new file mode 100644 index 000000000000..21c44990bbeb --- /dev/null +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_init.h @@ -0,0 +1,26 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD MP2 1.1 initialization structures + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Basavaraj Natikar + */ + +#ifndef AMD_SFH_INIT_H +#define AMD_SFH_INIT_H + +#include "../amd_sfh_common.h" + +struct amd_sfh1_1_ops { + int (*init)(struct amd_mp2_dev *mp2); +}; + +int amd_sfh1_1_init(struct amd_mp2_dev *mp2); + +static const struct amd_sfh1_1_ops __maybe_unused sfh1_1_ops = { + .init = amd_sfh1_1_init, +}; + +#endif diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c new file mode 100644 index 000000000000..c6df959ec725 --- /dev/null +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD MP2 1.1 communication interfaces + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Basavaraj Natikar + */ +#include +#include + +#include "amd_sfh_interface.h" + +static int amd_sfh_wait_response(struct amd_mp2_dev *mp2, u8 sid, u32 cmd_id) +{ + struct sfh_cmd_response cmd_resp; + + /* Get response with status within a max of 1600 ms timeout */ + if (!readl_poll_timeout(mp2->mmio + AMD_P2C_MSG(0), cmd_resp.resp, + (cmd_resp.response.response == 0 && + cmd_resp.response.cmd_id == cmd_id && (sid == 0xff || + cmd_resp.response.sensor_id == sid)), 500, 1600000)) + return cmd_resp.response.response; + + return -1; +} + +static void amd_start_sensor(struct amd_mp2_dev *privdata, struct amd_mp2_sensor_info info) +{ + struct sfh_cmd_base cmd_base; + + cmd_base.ul = 0; + cmd_base.cmd.cmd_id = ENABLE_SENSOR; + cmd_base.cmd.intr_disable = 0; + cmd_base.cmd.sensor_id = info.sensor_idx; + + writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0)); +} + +static void amd_stop_sensor(struct amd_mp2_dev *privdata, u16 sensor_idx) +{ + struct sfh_cmd_base cmd_base; + + cmd_base.ul = 0; + cmd_base.cmd.cmd_id = DISABLE_SENSOR; + cmd_base.cmd.intr_disable = 0; + cmd_base.cmd.sensor_id = sensor_idx; + + writeq(0x0, privdata->mmio + AMD_C2P_MSG(1)); + writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0)); +} + +static void amd_stop_all_sensor(struct amd_mp2_dev *privdata) +{ + struct sfh_cmd_base cmd_base; + + cmd_base.ul = 0; + cmd_base.cmd.cmd_id = STOP_ALL_SENSORS; + cmd_base.cmd.intr_disable = 0; + + writel(cmd_base.ul, privdata->mmio + AMD_C2P_MSG(0)); +} + +static struct amd_mp2_ops amd_sfh_ops = { + .start = amd_start_sensor, + .stop = amd_stop_sensor, + .stop_all = amd_stop_all_sensor, + .response = amd_sfh_wait_response, +}; + +void sfh_interface_init(struct amd_mp2_dev *mp2) +{ + mp2->mp2_ops = &amd_sfh_ops; +} diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h new file mode 100644 index 000000000000..ae47a369dc05 --- /dev/null +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_interface.h @@ -0,0 +1,154 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD MP2 1.1 communication interfaces + * + * Copyright (c) 2022, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Basavaraj Natikar + */ + +#ifndef AMD_SFH_INTERFACE_H +#define AMD_SFH_INTERFACE_H + +#include "../amd_sfh_common.h" + +#define SENSOR_DATA_MEM_SIZE_DEFAULT 256 +#define TOTAL_STATIC_MEM_DEFAULT 1024 +#define OFFSET_SFH_INFO_BASE_DEFAULT 0 +#define OFFSET_SENSOR_DATA_DEFAULT (OFFSET_SFH_INFO_BASE_DEFAULT + \ + TOTAL_STATIC_MEM_DEFAULT) +enum sensor_index { + ACCEL_IDX, + GYRO_IDX, + MAG_IDX, + ALS_IDX = 4, + HPD_IDX = 5, + MAX_IDX = 15, +}; + +struct sfh_cmd_base { + union { + u32 ul; + struct { + u32 sensor_id : 4; + u32 cmd_id : 4; + u32 sub_cmd_id : 6; + u32 length : 12; + u32 rsvd : 5; + u32 intr_disable : 1; + } cmd; + }; +}; + +struct sfh_cmd_response { + union { + u32 resp; + struct { + u32 response : 8; + u32 sensor_id : 4; + u32 cmd_id : 4; + u32 sub_cmd : 6; + u32 rsvd2 : 10; + } response; + }; +}; + +struct sfh_platform_info { + union { + u32 pi; + struct { + u32 cust_id : 16; + u32 plat_id : 6; + u32 interface_id : 4; + u32 rsvd : 6; + } pinfo; + }; +}; + +struct sfh_firmware_info { + union { + u32 fw_ver; + struct { + u32 minor_rev : 8; + u32 major_rev : 8; + u32 minor_ver : 8; + u32 major_ver : 8; + } fver; + }; +}; + +struct sfh_sensor_list { + union { + u32 slist; + struct { + u32 sensors : 16; + u32 rsvd : 16; + } sl; + }; +}; + +struct sfh_base_info { + union { + u32 sfh_base[24]; + struct { + struct sfh_platform_info plat_info; + struct sfh_firmware_info fw_info; + struct sfh_sensor_list s_list; + } sbase; + }; +}; + +struct sfh_common_data { + u64 timestamp; + u32 intr_cnt; + u32 featvalid : 16; + u32 rsvd : 13; + u32 sensor_state : 3; +}; + +struct sfh_float32 { + u32 x; + u32 y; + u32 z; +}; + +struct sfh_accel_data { + struct sfh_common_data commondata; + struct sfh_float32 acceldata; + u32 accelstatus; +}; + +struct sfh_gyro_data { + struct sfh_common_data commondata; + struct sfh_float32 gyrodata; + u32 result; +}; + +struct sfh_mag_data { + struct sfh_common_data commondata; + struct sfh_float32 magdata; + u32 accuracy; +}; + +struct sfh_als_data { + struct sfh_common_data commondata; + u16 lux; +}; + +struct hpd_status { + union { + struct { + u32 distance : 16; + u32 probablity : 8; + u32 presence : 2; + u32 rsvd : 5; + u32 state : 1; + } shpd; + u32 val; + }; +}; + +void sfh_interface_init(struct amd_mp2_dev *mp2); +void amd_sfh1_1_set_desc_ops(struct amd_mp2_ops *mp2_ops); +#endif diff --git a/drivers/hid/hid-alps.c b/drivers/hid/hid-alps.c index 2b986d0dbde4..db146d0f7937 100644 --- a/drivers/hid/hid-alps.c +++ b/drivers/hid/hid-alps.c @@ -830,6 +830,8 @@ static const struct hid_device_id alps_id[] = { USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_DUAL) }, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1) }, + { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, + USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_U1_UNICORN_LEGACY) }, { HID_DEVICE(HID_BUS_ANY, HID_GROUP_ANY, USB_VENDOR_ID_ALPS_JP, HID_DEVICE_ID_ALPS_T4_BTNLESS) }, { } diff --git a/drivers/hid/hid-apple.c b/drivers/hid/hid-apple.c index 42a568902f49..6970797cdc56 100644 --- a/drivers/hid/hid-apple.c +++ b/drivers/hid/hid-apple.c @@ -36,7 +36,7 @@ #define APPLE_NUMLOCK_EMULATION BIT(8) #define APPLE_RDESC_BATTERY BIT(9) #define APPLE_BACKLIGHT_CTL BIT(10) -#define APPLE_IS_KEYCHRON BIT(11) +#define APPLE_IS_NON_APPLE BIT(11) #define APPLE_FLAG_FKEY 0x01 @@ -65,6 +65,10 @@ MODULE_PARM_DESC(swap_fn_leftctrl, "Swap the Fn and left Control keys. " "(For people who want to keep PC keyboard muscle memory. " "[0] = as-is, Mac layout, 1 = swapped, PC layout)"); +struct apple_non_apple_keyboard { + char *name; +}; + struct apple_sc_backlight { struct led_classdev cdev; struct hid_device *hdev; @@ -313,6 +317,27 @@ static const struct apple_key_translation swapped_fn_leftctrl_keys[] = { { } }; +static const struct apple_non_apple_keyboard non_apple_keyboards[] = { + { "SONiX USB DEVICE" }, + { "Keychron" }, + { "AONE" }, + { "GANSS" } +}; + +static bool apple_is_non_apple_keyboard(struct hid_device *hdev) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(non_apple_keyboards); i++) { + char *non_apple = non_apple_keyboards[i].name; + + if (strncmp(hdev->name, non_apple, strlen(non_apple)) == 0) + return true; + } + + return false; +} + static inline void apple_setup_key_translation(struct input_dev *input, const struct apple_key_translation *table) { @@ -363,7 +388,7 @@ static int hidinput_apple_event(struct hid_device *hid, struct input_dev *input, } if (fnmode == 3) { - real_fnmode = (asc->quirks & APPLE_IS_KEYCHRON) ? 2 : 1; + real_fnmode = (asc->quirks & APPLE_IS_NON_APPLE) ? 2 : 1; } else { real_fnmode = fnmode; } @@ -669,9 +694,9 @@ static int apple_input_configured(struct hid_device *hdev, asc->quirks &= ~APPLE_HAS_FN; } - if (strncmp(hdev->name, "Keychron", 8) == 0) { - hid_info(hdev, "Keychron keyboard detected; function keys will default to fnmode=2 behavior\n"); - asc->quirks |= APPLE_IS_KEYCHRON; + if (apple_is_non_apple_keyboard(hdev)) { + hid_info(hdev, "Non-apple keyboard detected; function keys will default to fnmode=2 behavior\n"); + asc->quirks |= APPLE_IS_NON_APPLE; } return 0; diff --git a/drivers/hid/hid-core.c b/drivers/hid/hid-core.c index 00154a1cd2d8..b7f5566e338d 100644 --- a/drivers/hid/hid-core.c +++ b/drivers/hid/hid-core.c @@ -1662,7 +1662,7 @@ static void hid_process_report(struct hid_device *hid, /* first retrieve all incoming values in data */ for (a = 0; a < report->maxfield; a++) - hid_input_fetch_field(hid, field = report->field[a], data); + hid_input_fetch_field(hid, report->field[a], data); if (!list_empty(&report->field_entry_list)) { /* INPUT_REPORT, we have a priority list of fields */ diff --git a/drivers/hid/hid-cp2112.c b/drivers/hid/hid-cp2112.c index ece147d1a278..1e16b0fa310d 100644 --- a/drivers/hid/hid-cp2112.c +++ b/drivers/hid/hid-cp2112.c @@ -790,6 +790,11 @@ static int cp2112_xfer(struct i2c_adapter *adap, u16 addr, data->word = le16_to_cpup((__le16 *)buf); break; case I2C_SMBUS_I2C_BLOCK_DATA: + if (read_length > I2C_SMBUS_BLOCK_MAX) { + ret = -EINVAL; + goto power_normal; + } + memcpy(data->block + 1, buf, read_length); break; case I2C_SMBUS_BLOCK_DATA: diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index d9eb676abe96..0fb720a96399 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -413,6 +413,7 @@ #define USB_DEVICE_ID_ASUS_UX550VE_TOUCHSCREEN 0x2544 #define USB_DEVICE_ID_ASUS_UX550_TOUCHSCREEN 0x2706 #define I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN 0x261A +#define I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN 0x2A1C #define USB_VENDOR_ID_ELECOM 0x056e #define USB_DEVICE_ID_ELECOM_BM084 0x0061 @@ -1278,6 +1279,7 @@ #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G540 0x0075 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640 0x0094 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01 0x0042 +#define USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L 0x0935 #define USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06 0x0078 #define USB_DEVICE_ID_UGEE_TABLET_G5 0x0074 #define USB_DEVICE_ID_UGEE_TABLET_EX07S 0x0071 diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index c6b27aab9041..48c1c02c69f4 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -381,6 +381,8 @@ static const struct hid_device_id hid_battery_quirks[] = { HID_BATTERY_QUIRK_IGNORE }, { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO_TOUCHSCREEN), HID_BATTERY_QUIRK_IGNORE }, + { HID_I2C_DEVICE(USB_VENDOR_ID_ELAN, I2C_DEVICE_ID_SURFACE_GO2_TOUCHSCREEN), + HID_BATTERY_QUIRK_IGNORE }, {} }; diff --git a/drivers/hid/hid-lg-g15.c b/drivers/hid/hid-lg-g15.c index b2a08233f8d5..c8f82bcbf1ab 100644 --- a/drivers/hid/hid-lg-g15.c +++ b/drivers/hid/hid-lg-g15.c @@ -766,7 +766,7 @@ static int lg_g15_probe(struct hid_device *hdev, const struct hid_device_id *id) /* * Some models have multiple interfaces, we want the interface with - * with the f000.0000 application input report. + * the f000.0000 application input report. */ rep_enum = &hdev->report_enum[HID_INPUT_REPORT]; list_for_each_entry(rep, &rep_enum->report_list, list) { diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index 81de88ab2ecc..68f9e9d207f4 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -1694,7 +1694,7 @@ static int hidpp_battery_get_property(struct power_supply *psy, val->strval = hidpp->hid_dev->uniq; break; case POWER_SUPPLY_PROP_VOLTAGE_NOW: - /* hardware reports voltage in in mV. sysfs expects uV */ + /* hardware reports voltage in mV. sysfs expects uV */ val->intval = hidpp->battery.voltage * 1000; break; case POWER_SUPPLY_PROP_CHARGE_TYPE: diff --git a/drivers/hid/hid-mcp2221.c b/drivers/hid/hid-mcp2221.c index 4211b9839209..de52e9f7bb8c 100644 --- a/drivers/hid/hid-mcp2221.c +++ b/drivers/hid/hid-mcp2221.c @@ -385,6 +385,9 @@ static int mcp_smbus_write(struct mcp2221 *mcp, u16 addr, data_len = 7; break; default: + if (len > I2C_SMBUS_BLOCK_MAX) + return -EINVAL; + memcpy(&mcp->txbuf[5], buf, len); data_len = len + 5; } diff --git a/drivers/hid/hid-multitouch.c b/drivers/hid/hid-multitouch.c index 6bb3890b0f2c..2e72922e36f5 100644 --- a/drivers/hid/hid-multitouch.c +++ b/drivers/hid/hid-multitouch.c @@ -194,6 +194,7 @@ static void mt_post_parse(struct mt_device *td, struct mt_application *app); #define MT_CLS_WIN_8_FORCE_MULTI_INPUT 0x0015 #define MT_CLS_WIN_8_DISABLE_WAKEUP 0x0016 #define MT_CLS_WIN_8_NO_STICKY_FINGERS 0x0017 +#define MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU 0x0018 /* vendor specific classes */ #define MT_CLS_3M 0x0101 @@ -286,6 +287,15 @@ static const struct mt_class mt_classes[] = { MT_QUIRK_WIN8_PTP_BUTTONS | MT_QUIRK_FORCE_MULTI_INPUT, .export_all_inputs = true }, + { .name = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, + .quirks = MT_QUIRK_IGNORE_DUPLICATES | + MT_QUIRK_HOVERING | + MT_QUIRK_CONTACT_CNT_ACCURATE | + MT_QUIRK_STICKY_FINGERS | + MT_QUIRK_WIN8_PTP_BUTTONS | + MT_QUIRK_FORCE_MULTI_INPUT | + MT_QUIRK_NOT_SEEN_MEANS_UP, + .export_all_inputs = true }, { .name = MT_CLS_WIN_8_DISABLE_WAKEUP, .quirks = MT_QUIRK_ALWAYS_VALID | MT_QUIRK_IGNORE_DUPLICATES | @@ -783,6 +793,7 @@ static int mt_touch_input_mapping(struct hid_device *hdev, struct hid_input *hi, case HID_DG_CONFIDENCE: if ((cls->name == MT_CLS_WIN_8 || cls->name == MT_CLS_WIN_8_FORCE_MULTI_INPUT || + cls->name == MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU || cls->name == MT_CLS_WIN_8_DISABLE_WAKEUP) && (field->application == HID_DG_TOUCHPAD || field->application == HID_DG_TOUCHSCREEN)) @@ -2035,7 +2046,7 @@ static const struct hid_device_id mt_devices[] = { USB_DEVICE_ID_LENOVO_X1_TAB3) }, /* Lenovo X12 TAB Gen 1 */ - { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT, + { .driver_data = MT_CLS_WIN_8_FORCE_MULTI_INPUT_NSMU, HID_DEVICE(BUS_USB, HID_GROUP_MULTITOUCH_WIN_8, USB_VENDOR_ID_LENOVO, USB_DEVICE_ID_LENOVO_X12_TAB) }, diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 2204de889739..92ac4f605f13 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -292,6 +292,7 @@ static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] = { }; static const u16 JC_RUMBLE_DFLT_LOW_FREQ = 160; static const u16 JC_RUMBLE_DFLT_HIGH_FREQ = 320; +static const unsigned short JC_RUMBLE_ZERO_AMP_PKT_CNT = 5; #endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */ static const u16 JC_RUMBLE_PERIOD_MS = 50; @@ -402,8 +403,6 @@ struct joycon_input_report { #define JC_RUMBLE_DATA_SIZE 8 #define JC_RUMBLE_QUEUE_SIZE 8 -static const unsigned short JC_RUMBLE_ZERO_AMP_PKT_CNT = 5; - static const char * const joycon_player_led_names[] = { LED_FUNCTION_PLAYER1, LED_FUNCTION_PLAYER2, @@ -1586,6 +1585,7 @@ static const unsigned int joycon_button_inputs_r[] = { /* We report joy-con d-pad inputs as buttons and pro controller as a hat. */ static const unsigned int joycon_dpad_inputs_jc[] = { BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, + 0 /* 0 signals end of array */ }; static int joycon_input_create(struct joycon_ctlr *ctlr) @@ -1634,6 +1634,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) ctlr->input->id.version = hdev->version; ctlr->input->uniq = ctlr->mac_addr_str; ctlr->input->name = name; + ctlr->input->phys = hdev->phys; input_set_drvdata(ctlr->input, ctlr); /* set up sticks and buttons */ @@ -1713,6 +1714,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) ctlr->imu_input->id.version = hdev->version; ctlr->imu_input->uniq = ctlr->mac_addr_str; ctlr->imu_input->name = imu_name; + ctlr->imu_input->phys = hdev->phys; input_set_drvdata(ctlr->imu_input, ctlr); /* configure imu axes */ diff --git a/drivers/hid/hid-uclogic-core.c b/drivers/hid/hid-uclogic-core.c index c0fe66e50c58..47a17375c7fc 100644 --- a/drivers/hid/hid-uclogic-core.c +++ b/drivers/hid/hid-uclogic-core.c @@ -521,6 +521,8 @@ static const struct hid_device_id uclogic_devices[] = { USB_DEVICE_ID_UGEE_XPPEN_TABLET_G640) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO01) }, + { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L) }, { HID_USB_DEVICE(USB_VENDOR_ID_UGEE, USB_DEVICE_ID_UGEE_XPPEN_TABLET_STAR06) }, { } diff --git a/drivers/hid/hid-uclogic-params.c b/drivers/hid/hid-uclogic-params.c index db838f16282d..c11fa239e6a2 100644 --- a/drivers/hid/hid-uclogic-params.c +++ b/drivers/hid/hid-uclogic-params.c @@ -23,11 +23,11 @@ /** * uclogic_params_pen_inrange_to_str() - Convert a pen in-range reporting type * to a string. - * * @inrange: The in-range reporting type to convert. * - * Returns: - * The string representing the type, or NULL if the type is unknown. + * Return: + * * The string representing the type, or + * * %NULL if the type is unknown. */ static const char *uclogic_params_pen_inrange_to_str( enum uclogic_params_pen_inrange inrange) @@ -45,10 +45,12 @@ static const char *uclogic_params_pen_inrange_to_str( } /** - * Dump tablet interface pen parameters with hid_dbg(), indented with one tab. - * + * uclogic_params_pen_hid_dbg() - Dump tablet interface pen parameters * @hdev: The HID device the pen parameters describe. * @pen: The pen parameters to dump. + * + * Dump tablet interface pen parameters with hid_dbg(). The dump is indented + * with a tab. */ static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, const struct uclogic_params_pen *pen) @@ -77,11 +79,12 @@ static void uclogic_params_pen_hid_dbg(const struct hid_device *hdev, } /** - * Dump tablet interface frame parameters with hid_dbg(), indented with two - * tabs. - * + * uclogic_params_frame_hid_dbg() - Dump tablet interface frame parameters * @hdev: The HID device the pen parameters describe. * @frame: The frame parameters to dump. + * + * Dump tablet interface frame parameters with hid_dbg(). The dump is + * indented with two tabs. */ static void uclogic_params_frame_hid_dbg( const struct hid_device *hdev, @@ -102,10 +105,11 @@ static void uclogic_params_frame_hid_dbg( } /** - * Dump tablet interface parameters with hid_dbg(). - * + * uclogic_params_hid_dbg() - Dump tablet interface parameters * @hdev: The HID device the parameters describe. * @params: The parameters to dump. + * + * Dump tablet interface parameters with hid_dbg(). */ void uclogic_params_hid_dbg(const struct hid_device *hdev, const struct uclogic_params *params) @@ -234,7 +238,7 @@ static int uclogic_params_pen_init_v1(struct uclogic_params_pen *pen, const int len = 12; s32 resolution; /* Pen report descriptor template parameters */ - s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; __u8 *desc_ptr = NULL; /* Check arguments */ @@ -379,7 +383,7 @@ static int uclogic_params_pen_init_v2(struct uclogic_params_pen *pen, size_t i; s32 resolution; /* Pen report descriptor template parameters */ - s32 desc_params[UCLOGIC_RDESC_PEN_PH_ID_NUM]; + s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; __u8 *desc_ptr = NULL; /* Check arguments */ @@ -1002,6 +1006,197 @@ cleanup: return rc; } +/** + * uclogic_probe_interface() - some tablets, like the Parblo A610 PLUS V2 or + * the XP-PEN Deco Mini 7, need to be initialized by sending them magic data. + * + * @hdev: The HID device of the tablet interface to initialize and get + * parameters from. Cannot be NULL. + * @magic_arr: The magic data that should be sent to probe the interface. + * Cannot be NULL. + * @magic_size: Size of the magic data. + * @endpoint: Endpoint where the magic data should be sent. + * + * Returns: + * Zero, if successful. A negative errno code on error. + */ +static int uclogic_probe_interface(struct hid_device *hdev, u8 *magic_arr, + int magic_size, int endpoint) +{ + struct usb_device *udev; + unsigned int pipe = 0; + int sent; + u8 *buf = NULL; + int rc = 0; + + if (!hdev || !magic_arr) { + rc = -EINVAL; + goto cleanup; + } + + buf = kmemdup(magic_arr, magic_size, GFP_KERNEL); + if (!buf) { + rc = -ENOMEM; + goto cleanup; + } + + udev = hid_to_usb_dev(hdev); + pipe = usb_sndintpipe(udev, endpoint); + + rc = usb_interrupt_msg(udev, pipe, buf, magic_size, &sent, 1000); + if (rc || sent != magic_size) { + hid_err(hdev, "Interface probing failed: %d\n", rc); + rc = -1; + goto cleanup; + } + + rc = 0; +cleanup: + kfree(buf); + return rc; +} + +/** + * uclogic_params_ugee_v2_init() - initialize a UGEE graphics tablets by + * discovering their parameters. + * + * These tables, internally designed as v2 to differentiate them from older + * models, expect a payload of magic data in orther to be switched to the fully + * functional mode and expose their parameters in a similar way to the + * information present in uclogic_params_pen_init_v1() but with some + * differences. + * + * @params: Parameters to fill in (to be cleaned with + * uclogic_params_cleanup()). Not modified in case of error. + * Cannot be NULL. + * @hdev: The HID device of the tablet interface to initialize and get + * parameters from. Cannot be NULL. + * + * Returns: + * Zero, if successful. A negative errno code on error. + */ +static int uclogic_params_ugee_v2_init(struct uclogic_params *params, + struct hid_device *hdev) +{ + int rc = 0; + struct usb_interface *iface; + __u8 bInterfaceNumber; + const int str_desc_len = 12; + __u8 *str_desc = NULL; + __u8 *rdesc_pen = NULL; + __u8 *rdesc_frame = NULL; + s32 desc_params[UCLOGIC_RDESC_PH_ID_NUM]; + s32 resolution; + __u8 magic_arr[] = { + 0x02, 0xb0, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + /* The resulting parameters (noop) */ + struct uclogic_params p = {0, }; + + if (!params || !hdev) { + rc = -EINVAL; + goto cleanup; + } + + iface = to_usb_interface(hdev->dev.parent); + bInterfaceNumber = iface->cur_altsetting->desc.bInterfaceNumber; + if (bInterfaceNumber != 2) { + uclogic_params_init_invalid(&p); + goto output; + } + + /* + * Initialize the interface by sending magic data. + * The specific data was discovered by sniffing the Windows driver + * traffic. + */ + rc = uclogic_probe_interface(hdev, magic_arr, sizeof(magic_arr), 0x03); + if (rc) { + uclogic_params_init_invalid(&p); + goto output; + } + + /* + * Read the string descriptor containing pen and frame parameters. + * The specific string descriptor and data were discovered by sniffing + * the Windows driver traffic. + */ + rc = uclogic_params_get_str_desc(&str_desc, hdev, 100, str_desc_len); + if (rc != str_desc_len) { + hid_err(hdev, "failed retrieving pen and frame parameters: %d\n", rc); + uclogic_params_init_invalid(&p); + goto output; + } + + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] = + get_unaligned_le16(str_desc + 2); + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = + get_unaligned_le16(str_desc + 4); + desc_params[UCLOGIC_RDESC_FRAME_PH_ID_UM] = str_desc[6]; + desc_params[UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = + get_unaligned_le16(str_desc + 8); + resolution = get_unaligned_le16(str_desc + 10); + if (resolution == 0) { + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0; + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0; + } else { + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_PM] = + desc_params[UCLOGIC_RDESC_PEN_PH_ID_X_LM] * 1000 / + resolution; + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = + desc_params[UCLOGIC_RDESC_PEN_PH_ID_Y_LM] * 1000 / + resolution; + } + kfree(str_desc); + str_desc = NULL; + + /* Initialize the pen interface */ + rdesc_pen = uclogic_rdesc_template_apply( + uclogic_rdesc_ugee_v2_pen_template_arr, + uclogic_rdesc_ugee_v2_pen_template_size, + desc_params, ARRAY_SIZE(desc_params)); + if (!rdesc_pen) { + rc = -ENOMEM; + goto cleanup; + } + + p.pen.desc_ptr = rdesc_pen; + p.pen.desc_size = uclogic_rdesc_ugee_v2_pen_template_size; + p.pen.id = 0x02; + p.pen.subreport_list[0].value = 0xf0; + p.pen.subreport_list[0].id = UCLOGIC_RDESC_V1_FRAME_ID; + + /* Initialize the frame interface */ + rdesc_frame = uclogic_rdesc_template_apply( + uclogic_rdesc_ugee_v2_frame_btn_template_arr, + uclogic_rdesc_ugee_v2_frame_btn_template_size, + desc_params, ARRAY_SIZE(desc_params)); + if (!rdesc_frame) { + rc = -ENOMEM; + goto cleanup; + } + + rc = uclogic_params_frame_init_with_desc(&p.frame_list[0], + rdesc_frame, + uclogic_rdesc_ugee_v2_frame_btn_template_size, + UCLOGIC_RDESC_V1_FRAME_ID); + kfree(rdesc_frame); + if (rc) { + uclogic_params_init_invalid(&p); + goto output; + } + +output: + /* Output parameters */ + memcpy(params, &p, sizeof(*params)); + memset(&p, 0, sizeof(p)); + rc = 0; +cleanup: + kfree(str_desc); + uclogic_params_cleanup(&p); + return rc; +} + /** * uclogic_params_init() - initialize a tablet interface and discover its * parameters. @@ -1237,6 +1432,12 @@ int uclogic_params_init(struct uclogic_params *params, uclogic_params_init_invalid(&p); } break; + case VID_PID(USB_VENDOR_ID_UGEE, + USB_DEVICE_ID_UGEE_XPPEN_TABLET_DECO_L): + rc = uclogic_params_ugee_v2_init(&p, hdev); + if (rc != 0) + goto cleanup; + break; case VID_PID(USB_VENDOR_ID_TRUST, USB_DEVICE_ID_TRUST_PANORA_TABLET): case VID_PID(USB_VENDOR_ID_UGEE, diff --git a/drivers/hid/hid-uclogic-rdesc-test.c b/drivers/hid/hid-uclogic-rdesc-test.c new file mode 100644 index 000000000000..ebebffef5f8a --- /dev/null +++ b/drivers/hid/hid-uclogic-rdesc-test.c @@ -0,0 +1,219 @@ +// SPDX-License-Identifier: GPL-2.0+ + +/* + * HID driver for UC-Logic devices not fully compliant with HID standard + * + * Copyright (c) 2022 José Expósito + */ + +#include +#include "./hid-uclogic-rdesc.h" + +struct uclogic_template_case { + const char *name; + const __u8 *template; + size_t template_size; + const s32 *param_list; + size_t param_num; + const __u8 *expected; +}; + +static const s32 params_pen_all[UCLOGIC_RDESC_PH_ID_NUM] = { + [UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xAA, + [UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0xBB, + [UCLOGIC_RDESC_PEN_PH_ID_Y_LM] = 0xCC, + [UCLOGIC_RDESC_PEN_PH_ID_Y_PM] = 0xDD, + [UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM] = 0xEE, +}; + +static const s32 params_pen_some[] = { + [UCLOGIC_RDESC_PEN_PH_ID_X_LM] = 0xAA, + [UCLOGIC_RDESC_PEN_PH_ID_X_PM] = 0xBB, +}; + +static const s32 params_frame_all[UCLOGIC_RDESC_PH_ID_NUM] = { + [UCLOGIC_RDESC_FRAME_PH_ID_UM] = 0xFF, +}; + +static const __u8 template_empty[] = { }; +static const __u8 template_small[] = { 0x00 }; +static const __u8 template_no_ph[] = { 0xAA, 0xFE, 0xAA, 0xED, 0x1D }; + +static const __u8 template_pen_ph_end[] = { + 0xAA, 0xBB, UCLOGIC_RDESC_PEN_PH_HEAD +}; + +static const __u8 template_btn_ph_end[] = { + 0xAA, 0xBB, UCLOGIC_RDESC_FRAME_PH_BTN_HEAD +}; + +static const __u8 template_pen_all_params[] = { + UCLOGIC_RDESC_PEN_PH(X_LM), + 0x47, UCLOGIC_RDESC_PEN_PH(X_PM), + 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), + UCLOGIC_RDESC_PEN_PH(Y_PM), + 0x00, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), +}; + +static const __u8 expected_pen_all_params[] = { + 0xAA, 0x00, 0x00, 0x00, + 0x47, 0xBB, 0x00, 0x00, 0x00, + 0x27, 0xCC, 0x00, 0x00, 0x00, + 0xDD, 0x00, 0x00, 0x00, + 0x00, 0xEE, 0x00, 0x00, 0x00, +}; + +static const __u8 template_frame_all_params[] = { + 0x01, 0x02, + UCLOGIC_RDESC_FRAME_PH_BTN, + 0x99, +}; + +static const __u8 expected_frame_all_params[] = { + 0x01, 0x02, + 0x2A, 0xFF, 0x00, + 0x99, +}; + +static const __u8 template_pen_some_params[] = { + 0x01, 0x02, + UCLOGIC_RDESC_PEN_PH(X_LM), + 0x03, UCLOGIC_RDESC_PEN_PH(X_PM), + 0x04, 0x05, +}; + +static const __u8 expected_pen_some_params[] = { + 0x01, 0x02, + 0xAA, 0x00, 0x00, 0x00, + 0x03, 0xBB, 0x00, 0x00, 0x00, + 0x04, 0x05, +}; + +static const __u8 template_params_none[] = { + 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), + UCLOGIC_RDESC_PEN_PH(Y_PM), + 0x00, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), +}; + +static struct uclogic_template_case uclogic_template_cases[] = { + { + .name = "Empty template", + .template = template_empty, + .template_size = sizeof(template_empty), + .param_list = params_pen_all, + .param_num = ARRAY_SIZE(params_pen_all), + .expected = template_empty, + }, + { + .name = "Template smaller than the placeholder", + .template = template_small, + .template_size = sizeof(template_small), + .param_list = params_pen_all, + .param_num = ARRAY_SIZE(params_pen_all), + .expected = template_small, + }, + { + .name = "No placeholder", + .template = template_no_ph, + .template_size = sizeof(template_no_ph), + .param_list = params_pen_all, + .param_num = ARRAY_SIZE(params_pen_all), + .expected = template_no_ph, + }, + { + .name = "Pen placeholder at the end, without ID", + .template = template_pen_ph_end, + .template_size = sizeof(template_pen_ph_end), + .param_list = params_pen_all, + .param_num = ARRAY_SIZE(params_pen_all), + .expected = template_pen_ph_end, + }, + { + .name = "Frame button placeholder at the end, without ID", + .template = template_btn_ph_end, + .template_size = sizeof(template_btn_ph_end), + .param_list = params_frame_all, + .param_num = ARRAY_SIZE(params_frame_all), + .expected = template_btn_ph_end, + }, + { + .name = "All params present in the pen template", + .template = template_pen_all_params, + .template_size = sizeof(template_pen_all_params), + .param_list = params_pen_all, + .param_num = ARRAY_SIZE(params_pen_all), + .expected = expected_pen_all_params, + }, + { + .name = "All params present in the frame template", + .template = template_frame_all_params, + .template_size = sizeof(template_frame_all_params), + .param_list = params_frame_all, + .param_num = ARRAY_SIZE(params_frame_all), + .expected = expected_frame_all_params, + }, + { + .name = "Some params present in the pen template (complete param list)", + .template = template_pen_some_params, + .template_size = sizeof(template_pen_some_params), + .param_list = params_pen_all, + .param_num = ARRAY_SIZE(params_pen_all), + .expected = expected_pen_some_params, + }, + { + .name = "Some params present in the pen template (incomplete param list)", + .template = template_pen_some_params, + .template_size = sizeof(template_pen_some_params), + .param_list = params_pen_some, + .param_num = ARRAY_SIZE(params_pen_some), + .expected = expected_pen_some_params, + }, + { + .name = "No params present in the template", + .template = template_params_none, + .template_size = sizeof(template_params_none), + .param_list = params_pen_some, + .param_num = ARRAY_SIZE(params_pen_some), + .expected = template_params_none, + }, +}; + +static void uclogic_template_case_desc(struct uclogic_template_case *t, + char *desc) +{ + strscpy(desc, t->name, KUNIT_PARAM_DESC_SIZE); +} + +KUNIT_ARRAY_PARAM(uclogic_template, uclogic_template_cases, + uclogic_template_case_desc); + +static void uclogic_template_test(struct kunit *test) +{ + __u8 *res; + const struct uclogic_template_case *params = test->param_value; + + res = uclogic_rdesc_template_apply(params->template, + params->template_size, + params->param_list, + params->param_num); + KUNIT_ASSERT_NOT_ERR_OR_NULL(test, res); + KUNIT_EXPECT_EQ(test, 0, + memcmp(res, params->expected, params->template_size)); + kfree(res); +} + +static struct kunit_case hid_uclogic_rdesc_test_cases[] = { + KUNIT_CASE_PARAM(uclogic_template_test, uclogic_template_gen_params), + {} +}; + +static struct kunit_suite hid_uclogic_rdesc_test_suite = { + .name = "hid-uclogic-rdesc-test", + .test_cases = hid_uclogic_rdesc_test_cases, +}; + +kunit_test_suite(hid_uclogic_rdesc_test_suite); + +MODULE_DESCRIPTION("KUnit tests for the UC-Logic driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("José Expósito "); diff --git a/drivers/hid/hid-uclogic-rdesc.c b/drivers/hid/hid-uclogic-rdesc.c index 13f9ce73f1b1..3d68e8b0784d 100644 --- a/drivers/hid/hid-uclogic-rdesc.c +++ b/drivers/hid/hid-uclogic-rdesc.c @@ -859,6 +859,108 @@ const __u8 uclogic_rdesc_v2_frame_dial_arr[] = { const size_t uclogic_rdesc_v2_frame_dial_size = sizeof(uclogic_rdesc_v2_frame_dial_arr); +/* Fixed report descriptor template for UGEE v2 pen reports */ +const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[] = { + 0x05, 0x0d, /* Usage Page (Digitizers), */ + 0x09, 0x01, /* Usage (Digitizer), */ + 0xa1, 0x01, /* Collection (Application), */ + 0x85, 0x02, /* Report ID (2), */ + 0x09, 0x20, /* Usage (Stylus), */ + 0xa1, 0x00, /* Collection (Physical), */ + 0x09, 0x42, /* Usage (Tip Switch), */ + 0x09, 0x44, /* Usage (Barrel Switch), */ + 0x09, 0x46, /* Usage (Tablet Pick), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x09, 0x32, /* Usage (In Range), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x02, /* Report Count (2), */ + 0x81, 0x03, /* Input (Constant, Variable), */ + 0x75, 0x10, /* Report Size (16), */ + 0x95, 0x01, /* Report Count (1), */ + 0x35, 0x00, /* Physical Minimum (0), */ + 0xa4, /* Push, */ + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x30, /* Usage (X), */ + 0x65, 0x13, /* Unit (Inch), */ + 0x55, 0x0d, /* Unit Exponent (-3), */ + 0x27, UCLOGIC_RDESC_PEN_PH(X_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(X_PM), + /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x31, /* Usage (Y), */ + 0x27, UCLOGIC_RDESC_PEN_PH(Y_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x47, UCLOGIC_RDESC_PEN_PH(Y_PM), + /* Physical Maximum (PLACEHOLDER), */ + 0x81, 0x02, /* Input (Variable), */ + 0xb4, /* Pop, */ + 0x09, 0x30, /* Usage (Tip Pressure), */ + 0x45, 0x00, /* Physical Maximum (0), */ + 0x27, UCLOGIC_RDESC_PEN_PH(PRESSURE_LM), + /* Logical Maximum (PLACEHOLDER), */ + 0x75, 0x0D, /* Report Size (13), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x03, /* Report Count (3), */ + 0x81, 0x01, /* Input (Constant), */ + 0x09, 0x3d, /* Usage (X Tilt), */ + 0x35, 0xC3, /* Physical Minimum (-61), */ + 0x45, 0x3C, /* Physical Maximum (60), */ + 0x15, 0xC3, /* Logical Minimum (-61), */ + 0x25, 0x3C, /* Logical Maximum (60), */ + 0x75, 0x08, /* Report Size (8), */ + 0x95, 0x01, /* Report Count (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x09, 0x3e, /* Usage (Y Tilt), */ + 0x35, 0xC3, /* Physical Minimum (-61), */ + 0x45, 0x3C, /* Physical Maximum (60), */ + 0x15, 0xC3, /* Logical Minimum (-61), */ + 0x25, 0x3C, /* Logical Maximum (60), */ + 0x81, 0x02, /* Input (Variable), */ + 0xc0, /* End Collection, */ + 0xc0, /* End Collection */ +}; +const size_t uclogic_rdesc_ugee_v2_pen_template_size = + sizeof(uclogic_rdesc_ugee_v2_pen_template_arr); + +/* Fixed report descriptor template for UGEE v2 frame reports (buttons only) */ +const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[] = { + 0x05, 0x01, /* Usage Page (Desktop), */ + 0x09, 0x07, /* Usage (Keypad), */ + 0xA1, 0x01, /* Collection (Application), */ + 0x85, UCLOGIC_RDESC_V1_FRAME_ID, + /* Report ID, */ + 0x05, 0x0D, /* Usage Page (Digitizer), */ + 0x09, 0x39, /* Usage (Tablet Function Keys), */ + 0xA0, /* Collection (Physical), */ + 0x75, 0x01, /* Report Size (1), */ + 0x95, 0x08, /* Report Count (8), */ + 0x81, 0x01, /* Input (Constant), */ + 0x05, 0x09, /* Usage Page (Button), */ + 0x19, 0x01, /* Usage Minimum (01h), */ + UCLOGIC_RDESC_FRAME_PH_BTN, + /* Usage Maximum (PLACEHOLDER), */ + 0x95, 0x0A, /* Report Count (10), */ + 0x14, /* Logical Minimum (0), */ + 0x25, 0x01, /* Logical Maximum (1), */ + 0x81, 0x02, /* Input (Variable), */ + 0x95, 0x46, /* Report Count (70), */ + 0x81, 0x01, /* Input (Constant), */ + 0xC0, /* End Collection, */ + 0xC0 /* End Collection */ +}; +const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size = + sizeof(uclogic_rdesc_ugee_v2_frame_btn_template_arr); + /* Fixed report descriptor for Ugee EX07 frame */ const __u8 uclogic_rdesc_ugee_ex07_frame_arr[] = { 0x05, 0x01, /* Usage Page (Desktop), */ @@ -979,7 +1081,7 @@ const size_t uclogic_rdesc_xppen_deco01_frame_size = * uclogic_rdesc_template_apply() - apply report descriptor parameters to a * report descriptor template, creating a report descriptor. Copies the * template over to the new report descriptor and replaces every occurrence of - * UCLOGIC_RDESC_PH_HEAD, followed by an index byte, with the value from the + * the template placeholders, followed by an index byte, with the value from the * parameter list at that index. * * @template_ptr: Pointer to the template buffer. @@ -996,7 +1098,8 @@ __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, const s32 *param_list, size_t param_num) { - static const __u8 head[] = {UCLOGIC_RDESC_PH_HEAD}; + static const __u8 btn_head[] = {UCLOGIC_RDESC_FRAME_PH_BTN_HEAD}; + static const __u8 pen_head[] = {UCLOGIC_RDESC_PEN_PH_HEAD}; __u8 *rdesc_ptr; __u8 *p; s32 v; @@ -1005,12 +1108,19 @@ __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, if (rdesc_ptr == NULL) return NULL; - for (p = rdesc_ptr; p + sizeof(head) < rdesc_ptr + template_size;) { - if (memcmp(p, head, sizeof(head)) == 0 && - p[sizeof(head)] < param_num) { - v = param_list[p[sizeof(head)]]; + for (p = rdesc_ptr; p + sizeof(btn_head) < rdesc_ptr + template_size;) { + if (p + sizeof(pen_head) < rdesc_ptr + template_size && + memcmp(p, pen_head, sizeof(pen_head)) == 0 && + p[sizeof(pen_head)] < param_num) { + v = param_list[p[sizeof(pen_head)]]; put_unaligned(cpu_to_le32(v), (s32 *)p); - p += sizeof(head) + 1; + p += sizeof(pen_head) + 1; + } else if (memcmp(p, btn_head, sizeof(btn_head)) == 0 && + p[sizeof(btn_head)] < param_num) { + v = param_list[p[sizeof(btn_head)]]; + put_unaligned((__u8)0x2A, p); /* Usage Maximum */ + put_unaligned_le16((__force u16)cpu_to_le16(v), p + 1); + p += sizeof(btn_head) + 1; } else { p++; } diff --git a/drivers/hid/hid-uclogic-rdesc.h b/drivers/hid/hid-uclogic-rdesc.h index 0c6e95e8bde7..86e64a9ee6bd 100644 --- a/drivers/hid/hid-uclogic-rdesc.h +++ b/drivers/hid/hid-uclogic-rdesc.h @@ -81,7 +81,8 @@ extern __u8 uclogic_rdesc_twha60_fixed1_arr[]; extern const size_t uclogic_rdesc_twha60_fixed1_size; /* Report descriptor template placeholder head */ -#define UCLOGIC_RDESC_PH_HEAD 0xFE, 0xED, 0x1D +#define UCLOGIC_RDESC_PEN_PH_HEAD 0xFE, 0xED, 0x1D +#define UCLOGIC_RDESC_FRAME_PH_BTN_HEAD 0xFE, 0xED /* Apply report descriptor parameters to a report descriptor template */ extern __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, @@ -89,19 +90,24 @@ extern __u8 *uclogic_rdesc_template_apply(const __u8 *template_ptr, const s32 *param_list, size_t param_num); -/* Pen report descriptor template placeholder IDs */ -enum uclogic_rdesc_pen_ph_id { +/* Report descriptor template placeholder IDs */ +enum uclogic_rdesc_ph_id { UCLOGIC_RDESC_PEN_PH_ID_X_LM, UCLOGIC_RDESC_PEN_PH_ID_X_PM, UCLOGIC_RDESC_PEN_PH_ID_Y_LM, UCLOGIC_RDESC_PEN_PH_ID_Y_PM, UCLOGIC_RDESC_PEN_PH_ID_PRESSURE_LM, - UCLOGIC_RDESC_PEN_PH_ID_NUM + UCLOGIC_RDESC_FRAME_PH_ID_UM, + UCLOGIC_RDESC_PH_ID_NUM }; /* Report descriptor pen template placeholder */ #define UCLOGIC_RDESC_PEN_PH(_ID) \ - UCLOGIC_RDESC_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID + UCLOGIC_RDESC_PEN_PH_HEAD, UCLOGIC_RDESC_PEN_PH_ID_##_ID + +/* Report descriptor frame buttons template placeholder */ +#define UCLOGIC_RDESC_FRAME_PH_BTN \ + UCLOGIC_RDESC_FRAME_PH_BTN_HEAD, UCLOGIC_RDESC_FRAME_PH_ID_UM /* Report ID for v1 pen reports */ #define UCLOGIC_RDESC_V1_PEN_ID 0x07 @@ -155,6 +161,14 @@ extern const size_t uclogic_rdesc_v2_frame_dial_size; /* Device ID byte offset in v2 frame dial reports */ #define UCLOGIC_RDESC_V2_FRAME_DIAL_DEV_ID_BYTE 0x4 +/* Fixed report descriptor template for UGEE v2 pen reports */ +extern const __u8 uclogic_rdesc_ugee_v2_pen_template_arr[]; +extern const size_t uclogic_rdesc_ugee_v2_pen_template_size; + +/* Fixed report descriptor template for UGEE v2 frame reports (buttons only) */ +extern const __u8 uclogic_rdesc_ugee_v2_frame_btn_template_arr[]; +extern const size_t uclogic_rdesc_ugee_v2_frame_btn_template_size; + /* Fixed report descriptor for Ugee EX07 frame */ extern const __u8 uclogic_rdesc_ugee_ex07_frame_arr[]; extern const size_t uclogic_rdesc_ugee_ex07_frame_size; diff --git a/drivers/hid/i2c-hid/Kconfig b/drivers/hid/i2c-hid/Kconfig index a16c6a69680b..5273ee2bb134 100644 --- a/drivers/hid/i2c-hid/Kconfig +++ b/drivers/hid/i2c-hid/Kconfig @@ -32,6 +32,21 @@ config I2C_HID_OF will be called i2c-hid-of. It will also build/depend on the module i2c-hid. +config I2C_HID_OF_ELAN + tristate "Driver for Elan hid-i2c based devices on OF systems" + default n + depends on I2C && INPUT && OF + help + Say Y here if you want support for Elan i2c devices that use + the i2c-hid protocol on Open Firmware (Device Tree)-based + systems. + + If unsure, say N. + + This support is also available as a module. If so, the module + will be called i2c-hid-of-elan. It will also build/depend on + the module i2c-hid. + config I2C_HID_OF_GOODIX tristate "Driver for Goodix hid-i2c based devices on OF systems" default n diff --git a/drivers/hid/i2c-hid/Makefile b/drivers/hid/i2c-hid/Makefile index 302545a771f3..55bd5e0f35af 100644 --- a/drivers/hid/i2c-hid/Makefile +++ b/drivers/hid/i2c-hid/Makefile @@ -10,4 +10,5 @@ i2c-hid-$(CONFIG_DMI) += i2c-hid-dmi-quirks.o obj-$(CONFIG_I2C_HID_ACPI) += i2c-hid-acpi.o obj-$(CONFIG_I2C_HID_OF) += i2c-hid-of.o +obj-$(CONFIG_I2C_HID_OF_ELAN) += i2c-hid-of-elan.o obj-$(CONFIG_I2C_HID_OF_GOODIX) += i2c-hid-of-goodix.o diff --git a/drivers/hid/i2c-hid/i2c-hid-of-elan.c b/drivers/hid/i2c-hid/i2c-hid-of-elan.c new file mode 100644 index 000000000000..2d991325e734 --- /dev/null +++ b/drivers/hid/i2c-hid/i2c-hid-of-elan.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for Elan touchscreens that use the i2c-hid protocol. + * + * Copyright 2020 Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "i2c-hid.h" + +struct elan_i2c_hid_chip_data { + unsigned int post_gpio_reset_delay_ms; + unsigned int post_power_delay_ms; + u16 hid_descriptor_address; +}; + +struct i2c_hid_of_elan { + struct i2chid_ops ops; + + struct regulator *vcc33; + struct regulator *vccio; + struct gpio_desc *reset_gpio; + const struct elan_i2c_hid_chip_data *chip_data; +}; + +static int elan_i2c_hid_power_up(struct i2chid_ops *ops) +{ + struct i2c_hid_of_elan *ihid_elan = + container_of(ops, struct i2c_hid_of_elan, ops); + int ret; + + ret = regulator_enable(ihid_elan->vcc33); + if (ret) + return ret; + + ret = regulator_enable(ihid_elan->vccio); + if (ret) { + regulator_disable(ihid_elan->vcc33); + return ret; + } + + if (ihid_elan->chip_data->post_power_delay_ms) + msleep(ihid_elan->chip_data->post_power_delay_ms); + + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 0); + if (ihid_elan->chip_data->post_gpio_reset_delay_ms) + msleep(ihid_elan->chip_data->post_gpio_reset_delay_ms); + + return 0; +} + +static void elan_i2c_hid_power_down(struct i2chid_ops *ops) +{ + struct i2c_hid_of_elan *ihid_elan = + container_of(ops, struct i2c_hid_of_elan, ops); + + gpiod_set_value_cansleep(ihid_elan->reset_gpio, 1); + regulator_disable(ihid_elan->vccio); + regulator_disable(ihid_elan->vcc33); +} + +static int i2c_hid_of_elan_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_hid_of_elan *ihid_elan; + + ihid_elan = devm_kzalloc(&client->dev, sizeof(*ihid_elan), GFP_KERNEL); + if (!ihid_elan) + return -ENOMEM; + + ihid_elan->ops.power_up = elan_i2c_hid_power_up; + ihid_elan->ops.power_down = elan_i2c_hid_power_down; + + /* Start out with reset asserted */ + ihid_elan->reset_gpio = + devm_gpiod_get_optional(&client->dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(ihid_elan->reset_gpio)) + return PTR_ERR(ihid_elan->reset_gpio); + + ihid_elan->vccio = devm_regulator_get(&client->dev, "vccio"); + if (IS_ERR(ihid_elan->vccio)) + return PTR_ERR(ihid_elan->vccio); + + ihid_elan->vcc33 = devm_regulator_get(&client->dev, "vcc33"); + if (IS_ERR(ihid_elan->vcc33)) + return PTR_ERR(ihid_elan->vcc33); + + ihid_elan->chip_data = device_get_match_data(&client->dev); + + return i2c_hid_core_probe(client, &ihid_elan->ops, + ihid_elan->chip_data->hid_descriptor_address, 0); +} + +static const struct elan_i2c_hid_chip_data elan_ekth6915_chip_data = { + .post_power_delay_ms = 1, + .post_gpio_reset_delay_ms = 300, + .hid_descriptor_address = 0x0001, +}; + +static const struct of_device_id elan_i2c_hid_of_match[] = { + { .compatible = "elan,ekth6915", .data = &elan_ekth6915_chip_data }, + { } +}; +MODULE_DEVICE_TABLE(of, elan_i2c_hid_of_match); + +static struct i2c_driver elan_i2c_hid_ts_driver = { + .driver = { + .name = "i2c_hid_of_elan", + .pm = &i2c_hid_core_pm, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .of_match_table = of_match_ptr(elan_i2c_hid_of_match), + }, + .probe = i2c_hid_of_elan_probe, + .remove = i2c_hid_core_remove, + .shutdown = i2c_hid_core_shutdown, +}; +module_i2c_driver(elan_i2c_hid_ts_driver); + +MODULE_AUTHOR("Douglas Anderson "); +MODULE_DESCRIPTION("Elan i2c-hid touchscreen driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hid/intel-ish-hid/ipc/ipc.c b/drivers/hid/intel-ish-hid/ipc/ipc.c index 8ccb246b0114..15e14239af82 100644 --- a/drivers/hid/intel-ish-hid/ipc/ipc.c +++ b/drivers/hid/intel-ish-hid/ipc/ipc.c @@ -578,7 +578,7 @@ static void _ish_sync_fw_clock(struct ishtp_device *dev) static unsigned long prev_sync; uint64_t usec; - if (prev_sync && jiffies - prev_sync < 20 * HZ) + if (prev_sync && time_before(jiffies, prev_sync + 20 * HZ)) return; prev_sync = jiffies; diff --git a/drivers/hid/intel-ish-hid/ishtp-hid-client.c b/drivers/hid/intel-ish-hid/ishtp-hid-client.c index 4338c9b68a43..e3d70c5460e9 100644 --- a/drivers/hid/intel-ish-hid/ishtp-hid-client.c +++ b/drivers/hid/intel-ish-hid/ishtp-hid-client.c @@ -328,7 +328,7 @@ do_get_report: /** * ish_cl_event_cb() - bus driver callback for incoming message/packet - * @device: Pointer to the the ishtp client device for which this message + * @device: Pointer to the ishtp client device for which this message * is targeted * * Remove the packet from the list and process the message by calling diff --git a/drivers/hid/surface-hid/surface_hid_core.c b/drivers/hid/surface-hid/surface_hid_core.c index e46330b2e561..87637f813de2 100644 --- a/drivers/hid/surface-hid/surface_hid_core.c +++ b/drivers/hid/surface-hid/surface_hid_core.c @@ -19,12 +19,30 @@ #include "surface_hid_core.h" +/* -- Utility functions. ---------------------------------------------------- */ + +static bool surface_hid_is_hot_removed(struct surface_hid_device *shid) +{ + /* + * Non-ssam client devices, i.e. platform client devices, cannot be + * hot-removed. + */ + if (!is_ssam_device(shid->dev)) + return false; + + return ssam_device_is_hot_removed(to_ssam_device(shid->dev)); +} + + /* -- Device descriptor access. --------------------------------------------- */ static int surface_hid_load_hid_descriptor(struct surface_hid_device *shid) { int status; + if (surface_hid_is_hot_removed(shid)) + return -ENODEV; + status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_HID, (u8 *)&shid->hid_desc, sizeof(shid->hid_desc)); if (status) @@ -61,6 +79,9 @@ static int surface_hid_load_device_attributes(struct surface_hid_device *shid) { int status; + if (surface_hid_is_hot_removed(shid)) + return -ENODEV; + status = shid->ops.get_descriptor(shid, SURFACE_HID_DESC_ATTRS, (u8 *)&shid->attrs, sizeof(shid->attrs)); if (status) @@ -88,9 +109,18 @@ static int surface_hid_start(struct hid_device *hid) static void surface_hid_stop(struct hid_device *hid) { struct surface_hid_device *shid = hid->driver_data; + bool hot_removed; + + /* + * Communication may fail for devices that have been hot-removed. This + * also includes unregistration of HID events, so we need to check this + * here. Only if the device has not been marked as hot-removed, we can + * safely disable events. + */ + hot_removed = surface_hid_is_hot_removed(shid); /* Note: This call will log errors for us, so ignore them here. */ - ssam_notifier_unregister(shid->ctrl, &shid->notif); + __ssam_notifier_unregister(shid->ctrl, &shid->notif, !hot_removed); } static int surface_hid_open(struct hid_device *hid) @@ -109,6 +139,9 @@ static int surface_hid_parse(struct hid_device *hid) u8 *buf; int status; + if (surface_hid_is_hot_removed(shid)) + return -ENODEV; + buf = kzalloc(len, GFP_KERNEL); if (!buf) return -ENOMEM; @@ -126,6 +159,9 @@ static int surface_hid_raw_request(struct hid_device *hid, unsigned char reportn { struct surface_hid_device *shid = hid->driver_data; + if (surface_hid_is_hot_removed(shid)) + return -ENODEV; + if (rtype == HID_OUTPUT_REPORT && reqtype == HID_REQ_SET_REPORT) return shid->ops.output_report(shid, reportnum, buf, len); diff --git a/drivers/hid/wacom.h b/drivers/hid/wacom.h index 203d27d198b8..3f8b24a57014 100644 --- a/drivers/hid/wacom.h +++ b/drivers/hid/wacom.h @@ -91,6 +91,7 @@ #include #include #include +#include #include /* @@ -167,6 +168,7 @@ struct wacom { struct delayed_work init_work; struct wacom_remote *remote; struct work_struct mode_change_work; + struct timer_list idleprox_timer; bool generic_has_leds; struct wacom_leds { struct wacom_group_leds *groups; @@ -239,4 +241,5 @@ struct wacom_led *wacom_led_find(struct wacom *wacom, unsigned int group, struct wacom_led *wacom_led_next(struct wacom *wacom, struct wacom_led *cur); int wacom_equivalent_usage(int usage); int wacom_initialize_leds(struct wacom *wacom); +void wacom_idleprox_timeout(struct timer_list *list); #endif diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 620fe74f5676..194a2e327591 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -2121,7 +2121,7 @@ static int wacom_register_inputs(struct wacom *wacom) error = wacom_setup_pad_input_capabilities(pad_input_dev, wacom_wac); if (error) { - /* no pad in use on this interface */ + /* no pad events using this interface */ input_free_device(pad_input_dev); wacom_wac->pad_input = NULL; pad_input_dev = NULL; @@ -2781,6 +2781,7 @@ static int wacom_probe(struct hid_device *hdev, INIT_WORK(&wacom->battery_work, wacom_battery_work); INIT_WORK(&wacom->remote_work, wacom_remote_work); INIT_WORK(&wacom->mode_change_work, wacom_mode_change_work); + timer_setup(&wacom->idleprox_timer, &wacom_idleprox_timeout, TIMER_DEFERRABLE); /* ask for the report descriptor to be loaded by HID */ error = hid_parse(hdev); @@ -2821,6 +2822,7 @@ static void wacom_remove(struct hid_device *hdev) cancel_work_sync(&wacom->battery_work); cancel_work_sync(&wacom->remote_work); cancel_work_sync(&wacom->mode_change_work); + del_timer_sync(&wacom->idleprox_timer); if (hdev->bus == BUS_BLUETOOTH) device_remove_file(&hdev->dev, &dev_attr_speed); diff --git a/drivers/hid/wacom_wac.c b/drivers/hid/wacom_wac.c index 9470c2b0b529..d049239256a2 100644 --- a/drivers/hid/wacom_wac.c +++ b/drivers/hid/wacom_wac.c @@ -11,6 +11,7 @@ #include "wacom_wac.h" #include "wacom.h" #include +#include /* resolution for penabled devices */ #define WACOM_PL_RES 20 @@ -41,6 +42,43 @@ static int wacom_numbered_button_to_key(int n); static void wacom_update_led(struct wacom *wacom, int button_count, int mask, int group); + +static void wacom_force_proxout(struct wacom_wac *wacom_wac) +{ + struct input_dev *input = wacom_wac->pen_input; + + wacom_wac->shared->stylus_in_proximity = 0; + + input_report_key(input, BTN_TOUCH, 0); + input_report_key(input, BTN_STYLUS, 0); + input_report_key(input, BTN_STYLUS2, 0); + input_report_key(input, BTN_STYLUS3, 0); + input_report_key(input, wacom_wac->tool[0], 0); + if (wacom_wac->serial[0]) { + input_report_abs(input, ABS_MISC, 0); + } + input_report_abs(input, ABS_PRESSURE, 0); + + wacom_wac->tool[0] = 0; + wacom_wac->id[0] = 0; + wacom_wac->serial[0] = 0; + + input_sync(input); +} + +void wacom_idleprox_timeout(struct timer_list *list) +{ + struct wacom *wacom = from_timer(wacom, list, idleprox_timer); + struct wacom_wac *wacom_wac = &wacom->wacom_wac; + + if (!wacom_wac->hid_data.sense_state) { + return; + } + + hid_warn(wacom->hdev, "%s: tool appears to be hung in-prox. forcing it out.\n", __func__); + wacom_force_proxout(wacom_wac); +} + /* * Percent of battery capacity for Graphire. * 8th value means AC online and show 100% capacity. @@ -638,9 +676,26 @@ static int wacom_intuos_id_mangle(int tool_id) return (tool_id & ~0xFFF) << 4 | (tool_id & 0xFFF); } +static bool wacom_is_art_pen(int tool_id) +{ + bool is_art_pen = false; + + switch (tool_id) { + case 0x885: /* Intuos3 Marker Pen */ + case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */ + case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */ + is_art_pen = true; + break; + } + return is_art_pen; +} + static int wacom_intuos_get_tool_type(int tool_id) { - int tool_type; + int tool_type = BTN_TOOL_PEN; + + if (wacom_is_art_pen(tool_id)) + return tool_type; switch (tool_id) { case 0x812: /* Inking pen */ @@ -655,12 +710,9 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x852: case 0x823: /* Intuos3 Grip Pen */ case 0x813: /* Intuos3 Classic Pen */ - case 0x885: /* Intuos3 Marker Pen */ case 0x802: /* Intuos4/5 13HD/24HD General Pen */ - case 0x804: /* Intuos4/5 13HD/24HD Marker Pen */ case 0x8e2: /* IntuosHT2 pen */ case 0x022: - case 0x10804: /* Intuos4/5 13HD/24HD Art Pen */ case 0x10842: /* MobileStudio Pro Pro Pen slim */ case 0x14802: /* Intuos4/5 13HD/24HD Classic Pen */ case 0x16802: /* Cintiq 13HD Pro Pen */ @@ -718,10 +770,6 @@ static int wacom_intuos_get_tool_type(int tool_id) case 0x10902: /* Intuos4/5 13HD/24HD Airbrush */ tool_type = BTN_TOOL_AIRBRUSH; break; - - default: /* Unknown tool */ - tool_type = BTN_TOOL_PEN; - break; } return tool_type; } @@ -2009,7 +2057,6 @@ static void wacom_wac_pad_usage_mapping(struct hid_device *hdev, wacom_wac->has_mute_touch_switch = true; usage->type = EV_SW; usage->code = SW_MUTE_DEVICE; - features->device_type |= WACOM_DEVICETYPE_PAD; break; case WACOM_HID_WD_TOUCHSTRIP: wacom_map_usage(input, usage, field, EV_ABS, ABS_RX, 0); @@ -2089,6 +2136,30 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field wacom_wac->hid_data.inrange_state |= value; } + /* Process touch switch state first since it is reported through touch interface, + * which is indepentent of pad interface. In the case when there are no other pad + * events, the pad interface will not even be created. + */ + if ((equivalent_usage == WACOM_HID_WD_MUTE_DEVICE) || + (equivalent_usage == WACOM_HID_WD_TOUCHONOFF)) { + if (wacom_wac->shared->touch_input) { + bool *is_touch_on = &wacom_wac->shared->is_touch_on; + + if (equivalent_usage == WACOM_HID_WD_MUTE_DEVICE && value) + *is_touch_on = !(*is_touch_on); + else if (equivalent_usage == WACOM_HID_WD_TOUCHONOFF) + *is_touch_on = value; + + input_report_switch(wacom_wac->shared->touch_input, + SW_MUTE_DEVICE, !(*is_touch_on)); + input_sync(wacom_wac->shared->touch_input); + } + return; + } + + if (!input) + return; + switch (equivalent_usage) { case WACOM_HID_WD_TOUCHRING: /* @@ -2124,22 +2195,6 @@ static void wacom_wac_pad_event(struct hid_device *hdev, struct hid_field *field input_event(input, usage->type, usage->code, 0); break; - case WACOM_HID_WD_MUTE_DEVICE: - case WACOM_HID_WD_TOUCHONOFF: - if (wacom_wac->shared->touch_input) { - bool *is_touch_on = &wacom_wac->shared->is_touch_on; - - if (equivalent_usage == WACOM_HID_WD_MUTE_DEVICE && value) - *is_touch_on = !(*is_touch_on); - else if (equivalent_usage == WACOM_HID_WD_TOUCHONOFF) - *is_touch_on = value; - - input_report_switch(wacom_wac->shared->touch_input, - SW_MUTE_DEVICE, !(*is_touch_on)); - input_sync(wacom_wac->shared->touch_input); - } - break; - case WACOM_HID_WD_MODE_CHANGE: if (wacom_wac->is_direct_mode != value) { wacom_wac->is_direct_mode = value; @@ -2312,6 +2367,7 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field value = field->logical_maximum - value; break; case HID_DG_INRANGE: + mod_timer(&wacom->idleprox_timer, jiffies + msecs_to_jiffies(100)); wacom_wac->hid_data.inrange_state = value; if (!(features->quirks & WACOM_QUIRK_SENSE)) wacom_wac->hid_data.sense_state = value; @@ -2336,6 +2392,9 @@ static void wacom_wac_pen_event(struct hid_device *hdev, struct hid_field *field } return; case HID_DG_TWIST: + /* don't modify the value if the pen doesn't support the feature */ + if (!wacom_is_art_pen(wacom_wac->id[0])) return; + /* * Userspace expects pen twist to have its zero point when * the buttons/finger is on the tablet's left. HID values @@ -2822,7 +2881,7 @@ void wacom_wac_event(struct hid_device *hdev, struct hid_field *field, /* usage tests must precede field tests */ if (WACOM_BATTERY_USAGE(usage)) wacom_wac_battery_event(hdev, field, usage, value); - else if (WACOM_PAD_FIELD(field) && wacom->wacom_wac.pad_input) + else if (WACOM_PAD_FIELD(field)) wacom_wac_pad_event(hdev, field, usage, value); else if (WACOM_PEN_FIELD(field) && wacom->wacom_wac.pen_input) wacom_wac_pen_event(hdev, field, usage, value); diff --git a/drivers/i2c/busses/Kconfig b/drivers/i2c/busses/Kconfig index e95d6bc34070..7284206b278b 100644 --- a/drivers/i2c/busses/Kconfig +++ b/drivers/i2c/busses/Kconfig @@ -108,6 +108,7 @@ config I2C_HIX5HD2 config I2C_I801 tristate "Intel 82801 (ICH/PCH)" depends on PCI + select P2SB if X86 select CHECK_SIGNATURE if X86 && DMI select I2C_SMBUS help diff --git a/drivers/i2c/busses/i2c-bcm-iproc.c b/drivers/i2c/busses/i2c-bcm-iproc.c index 6304d1dd2dd6..85d8a6b04885 100644 --- a/drivers/i2c/busses/i2c-bcm-iproc.c +++ b/drivers/i2c/busses/i2c-bcm-iproc.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/i2c/busses/i2c-bcm-kona.c b/drivers/i2c/busses/i2c-bcm-kona.c index 8e350f20cde0..16bf41f1f086 100644 --- a/drivers/i2c/busses/i2c-bcm-kona.c +++ b/drivers/i2c/busses/i2c-bcm-kona.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Broadcom Corporation #include #include diff --git a/drivers/i2c/busses/i2c-brcmstb.c b/drivers/i2c/busses/i2c-brcmstb.c index 3ba6cbbe84ac..2ae187e2b642 100644 --- a/drivers/i2c/busses/i2c-brcmstb.c +++ b/drivers/i2c/busses/i2c-brcmstb.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/i2c/busses/i2c-i801.c b/drivers/i2c/busses/i2c-i801.c index 9e5b87e107ba..81d0da2547bd 100644 --- a/drivers/i2c/busses/i2c-i801.c +++ b/drivers/i2c/busses/i2c-i801.c @@ -112,6 +112,7 @@ #include #include #include +#include #include #include @@ -141,7 +142,6 @@ #define TCOBASE 0x050 #define TCOCTL 0x054 -#define SBREG_BAR 0x10 #define SBREG_SMBCTRL 0xc6000c #define SBREG_SMBCTRL_DNV 0xcf000c @@ -1485,45 +1485,24 @@ i801_add_tco_spt(struct i801_priv *priv, struct pci_dev *pci_dev, .version = 4, }; struct resource *res; - unsigned int devfn; - u64 base64_addr; - u32 base_addr; - u8 hidden; + int ret; /* * We must access the NO_REBOOT bit over the Primary to Sideband - * bridge (P2SB). The BIOS prevents the P2SB device from being - * enumerated by the PCI subsystem, so we need to unhide/hide it - * to lookup the P2SB BAR. + * (P2SB) bridge. */ - pci_lock_rescan_remove(); - - devfn = PCI_DEVFN(PCI_SLOT(pci_dev->devfn), 1); - - /* Unhide the P2SB device, if it is hidden */ - pci_bus_read_config_byte(pci_dev->bus, devfn, 0xe1, &hidden); - if (hidden) - pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, 0x0); - - pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR, &base_addr); - base64_addr = base_addr & 0xfffffff0; - - pci_bus_read_config_dword(pci_dev->bus, devfn, SBREG_BAR + 0x4, &base_addr); - base64_addr |= (u64)base_addr << 32; - - /* Hide the P2SB device, if it was hidden before */ - if (hidden) - pci_bus_write_config_byte(pci_dev->bus, devfn, 0xe1, hidden); - pci_unlock_rescan_remove(); res = &tco_res[1]; + ret = p2sb_bar(pci_dev->bus, 0, res); + if (ret) + return ERR_PTR(ret); + if (pci_dev->device == PCI_DEVICE_ID_INTEL_DNV_SMBUS) - res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL_DNV; + res->start += SBREG_SMBCTRL_DNV; else - res->start = (resource_size_t)base64_addr + SBREG_SMBCTRL; + res->start += SBREG_SMBCTRL; res->end = res->start + 3; - res->flags = IORESOURCE_MEM; return platform_device_register_resndata(&pci_dev->dev, "iTCO_wdt", -1, tco_res, 2, &pldata, sizeof(pldata)); diff --git a/drivers/infiniband/Kconfig b/drivers/infiniband/Kconfig index 33d3ce9c888e..aa36ac618e72 100644 --- a/drivers/infiniband/Kconfig +++ b/drivers/infiniband/Kconfig @@ -78,20 +78,21 @@ config INFINIBAND_VIRT_DMA def_bool !HIGHMEM if INFINIBAND_USER_ACCESS || !INFINIBAND_USER_ACCESS -source "drivers/infiniband/hw/mthca/Kconfig" -source "drivers/infiniband/hw/qib/Kconfig" +source "drivers/infiniband/hw/bnxt_re/Kconfig" source "drivers/infiniband/hw/cxgb4/Kconfig" source "drivers/infiniband/hw/efa/Kconfig" +source "drivers/infiniband/hw/erdma/Kconfig" +source "drivers/infiniband/hw/hfi1/Kconfig" +source "drivers/infiniband/hw/hns/Kconfig" source "drivers/infiniband/hw/irdma/Kconfig" source "drivers/infiniband/hw/mlx4/Kconfig" source "drivers/infiniband/hw/mlx5/Kconfig" +source "drivers/infiniband/hw/mthca/Kconfig" source "drivers/infiniband/hw/ocrdma/Kconfig" -source "drivers/infiniband/hw/vmw_pvrdma/Kconfig" -source "drivers/infiniband/hw/usnic/Kconfig" -source "drivers/infiniband/hw/hns/Kconfig" -source "drivers/infiniband/hw/bnxt_re/Kconfig" -source "drivers/infiniband/hw/hfi1/Kconfig" source "drivers/infiniband/hw/qedr/Kconfig" +source "drivers/infiniband/hw/qib/Kconfig" +source "drivers/infiniband/hw/usnic/Kconfig" +source "drivers/infiniband/hw/vmw_pvrdma/Kconfig" source "drivers/infiniband/sw/rdmavt/Kconfig" source "drivers/infiniband/sw/rxe/Kconfig" source "drivers/infiniband/sw/siw/Kconfig" diff --git a/drivers/infiniband/core/cma.c b/drivers/infiniband/core/cma.c index fabca5e51e3d..46d06678dfbe 100644 --- a/drivers/infiniband/core/cma.c +++ b/drivers/infiniband/core/cma.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include +#include #include #include #include @@ -168,6 +170,9 @@ static struct ib_sa_client sa_client; static LIST_HEAD(dev_list); static LIST_HEAD(listen_any_list); static DEFINE_MUTEX(lock); +static struct rb_root id_table = RB_ROOT; +/* Serialize operations of id_table tree */ +static DEFINE_SPINLOCK(id_table_lock); static struct workqueue_struct *cma_wq; static unsigned int cma_pernet_id; @@ -202,6 +207,11 @@ struct xarray *cma_pernet_xa(struct net *net, enum rdma_ucm_port_space ps) } } +struct id_table_entry { + struct list_head id_list; + struct rb_node rb_node; +}; + struct cma_device { struct list_head list; struct ib_device *device; @@ -420,11 +430,21 @@ static inline u8 cma_get_ip_ver(const struct cma_hdr *hdr) return hdr->ip_version >> 4; } -static inline void cma_set_ip_ver(struct cma_hdr *hdr, u8 ip_ver) +static void cma_set_ip_ver(struct cma_hdr *hdr, u8 ip_ver) { hdr->ip_version = (ip_ver << 4) | (hdr->ip_version & 0xF); } +static struct sockaddr *cma_src_addr(struct rdma_id_private *id_priv) +{ + return (struct sockaddr *)&id_priv->id.route.addr.src_addr; +} + +static inline struct sockaddr *cma_dst_addr(struct rdma_id_private *id_priv) +{ + return (struct sockaddr *)&id_priv->id.route.addr.dst_addr; +} + static int cma_igmp_send(struct net_device *ndev, union ib_gid *mgid, bool join) { struct in_device *in_dev = NULL; @@ -445,6 +465,117 @@ static int cma_igmp_send(struct net_device *ndev, union ib_gid *mgid, bool join) return (in_dev) ? 0 : -ENODEV; } +static int compare_netdev_and_ip(int ifindex_a, struct sockaddr *sa, + struct id_table_entry *entry_b) +{ + struct rdma_id_private *id_priv = list_first_entry( + &entry_b->id_list, struct rdma_id_private, id_list_entry); + int ifindex_b = id_priv->id.route.addr.dev_addr.bound_dev_if; + struct sockaddr *sb = cma_dst_addr(id_priv); + + if (ifindex_a != ifindex_b) + return (ifindex_a > ifindex_b) ? 1 : -1; + + if (sa->sa_family != sb->sa_family) + return sa->sa_family - sb->sa_family; + + if (sa->sa_family == AF_INET) + return memcmp((char *)&((struct sockaddr_in *)sa)->sin_addr, + (char *)&((struct sockaddr_in *)sb)->sin_addr, + sizeof(((struct sockaddr_in *)sa)->sin_addr)); + + return ipv6_addr_cmp(&((struct sockaddr_in6 *)sa)->sin6_addr, + &((struct sockaddr_in6 *)sb)->sin6_addr); +} + +static int cma_add_id_to_tree(struct rdma_id_private *node_id_priv) +{ + struct rb_node **new, *parent = NULL; + struct id_table_entry *this, *node; + unsigned long flags; + int result; + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) + return -ENOMEM; + + spin_lock_irqsave(&id_table_lock, flags); + new = &id_table.rb_node; + while (*new) { + this = container_of(*new, struct id_table_entry, rb_node); + result = compare_netdev_and_ip( + node_id_priv->id.route.addr.dev_addr.bound_dev_if, + cma_dst_addr(node_id_priv), this); + + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else { + list_add_tail(&node_id_priv->id_list_entry, + &this->id_list); + kfree(node); + goto unlock; + } + } + + INIT_LIST_HEAD(&node->id_list); + list_add_tail(&node_id_priv->id_list_entry, &node->id_list); + + rb_link_node(&node->rb_node, parent, new); + rb_insert_color(&node->rb_node, &id_table); + +unlock: + spin_unlock_irqrestore(&id_table_lock, flags); + return 0; +} + +static struct id_table_entry * +node_from_ndev_ip(struct rb_root *root, int ifindex, struct sockaddr *sa) +{ + struct rb_node *node = root->rb_node; + struct id_table_entry *data; + int result; + + while (node) { + data = container_of(node, struct id_table_entry, rb_node); + result = compare_netdev_and_ip(ifindex, sa, data); + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + + return NULL; +} + +static void cma_remove_id_from_tree(struct rdma_id_private *id_priv) +{ + struct id_table_entry *data; + unsigned long flags; + + spin_lock_irqsave(&id_table_lock, flags); + if (list_empty(&id_priv->id_list_entry)) + goto out; + + data = node_from_ndev_ip(&id_table, + id_priv->id.route.addr.dev_addr.bound_dev_if, + cma_dst_addr(id_priv)); + if (!data) + goto out; + + list_del_init(&id_priv->id_list_entry); + if (list_empty(&data->id_list)) { + rb_erase(&data->rb_node, &id_table); + kfree(data); + } +out: + spin_unlock_irqrestore(&id_table_lock, flags); +} + static void _cma_attach_to_dev(struct rdma_id_private *id_priv, struct cma_device *cma_dev) { @@ -481,16 +612,6 @@ static void cma_release_dev(struct rdma_id_private *id_priv) mutex_unlock(&lock); } -static inline struct sockaddr *cma_src_addr(struct rdma_id_private *id_priv) -{ - return (struct sockaddr *) &id_priv->id.route.addr.src_addr; -} - -static inline struct sockaddr *cma_dst_addr(struct rdma_id_private *id_priv) -{ - return (struct sockaddr *) &id_priv->id.route.addr.dst_addr; -} - static inline unsigned short cma_family(struct rdma_id_private *id_priv) { return id_priv->id.route.addr.src_addr.ss_family; @@ -861,6 +982,7 @@ __rdma_create_id(struct net *net, rdma_cm_event_handler event_handler, refcount_set(&id_priv->refcount, 1); mutex_init(&id_priv->handler_mutex); INIT_LIST_HEAD(&id_priv->device_item); + INIT_LIST_HEAD(&id_priv->id_list_entry); INIT_LIST_HEAD(&id_priv->listen_list); INIT_LIST_HEAD(&id_priv->mc_list); get_random_bytes(&id_priv->seq_num, sizeof id_priv->seq_num); @@ -1883,6 +2005,7 @@ static void _destroy_id(struct rdma_id_private *id_priv, cma_cancel_operation(id_priv, state); rdma_restrack_del(&id_priv->res); + cma_remove_id_from_tree(id_priv); if (id_priv->cma_dev) { if (rdma_cap_ib_cm(id_priv->id.device, 1)) { if (id_priv->cm_id.ib) @@ -3172,8 +3295,11 @@ int rdma_resolve_route(struct rdma_cm_id *id, unsigned long timeout_ms) cma_id_get(id_priv); if (rdma_cap_ib_sa(id->device, id->port_num)) ret = cma_resolve_ib_route(id_priv, timeout_ms); - else if (rdma_protocol_roce(id->device, id->port_num)) + else if (rdma_protocol_roce(id->device, id->port_num)) { ret = cma_resolve_iboe_route(id_priv); + if (!ret) + cma_add_id_to_tree(id_priv); + } else if (rdma_protocol_iwarp(id->device, id->port_num)) ret = cma_resolve_iw_route(id_priv); else @@ -4922,10 +5048,87 @@ out: return ret; } +static void cma_netevent_work_handler(struct work_struct *_work) +{ + struct rdma_id_private *id_priv = + container_of(_work, struct rdma_id_private, id.net_work); + struct rdma_cm_event event = {}; + + mutex_lock(&id_priv->handler_mutex); + + if (READ_ONCE(id_priv->state) == RDMA_CM_DESTROYING || + READ_ONCE(id_priv->state) == RDMA_CM_DEVICE_REMOVAL) + goto out_unlock; + + event.event = RDMA_CM_EVENT_UNREACHABLE; + event.status = -ETIMEDOUT; + + if (cma_cm_event_handler(id_priv, &event)) { + __acquire(&id_priv->handler_mutex); + id_priv->cm_id.ib = NULL; + cma_id_put(id_priv); + destroy_id_handler_unlock(id_priv); + return; + } + +out_unlock: + mutex_unlock(&id_priv->handler_mutex); + cma_id_put(id_priv); +} + +static int cma_netevent_callback(struct notifier_block *self, + unsigned long event, void *ctx) +{ + struct id_table_entry *ips_node = NULL; + struct rdma_id_private *current_id; + struct neighbour *neigh = ctx; + unsigned long flags; + + if (event != NETEVENT_NEIGH_UPDATE) + return NOTIFY_DONE; + + spin_lock_irqsave(&id_table_lock, flags); + if (neigh->tbl->family == AF_INET6) { + struct sockaddr_in6 neigh_sock_6; + + neigh_sock_6.sin6_family = AF_INET6; + neigh_sock_6.sin6_addr = *(struct in6_addr *)neigh->primary_key; + ips_node = node_from_ndev_ip(&id_table, neigh->dev->ifindex, + (struct sockaddr *)&neigh_sock_6); + } else if (neigh->tbl->family == AF_INET) { + struct sockaddr_in neigh_sock_4; + + neigh_sock_4.sin_family = AF_INET; + neigh_sock_4.sin_addr.s_addr = *(__be32 *)(neigh->primary_key); + ips_node = node_from_ndev_ip(&id_table, neigh->dev->ifindex, + (struct sockaddr *)&neigh_sock_4); + } else + goto out; + + if (!ips_node) + goto out; + + list_for_each_entry(current_id, &ips_node->id_list, id_list_entry) { + if (!memcmp(current_id->id.route.addr.dev_addr.dst_dev_addr, + neigh->ha, ETH_ALEN)) + continue; + INIT_WORK(¤t_id->id.net_work, cma_netevent_work_handler); + cma_id_get(current_id); + queue_work(cma_wq, ¤t_id->id.net_work); + } +out: + spin_unlock_irqrestore(&id_table_lock, flags); + return NOTIFY_DONE; +} + static struct notifier_block cma_nb = { .notifier_call = cma_netdev_callback }; +static struct notifier_block cma_netevent_cb = { + .notifier_call = cma_netevent_callback +}; + static void cma_send_device_removal_put(struct rdma_id_private *id_priv) { struct rdma_cm_event event = { .event = RDMA_CM_EVENT_DEVICE_REMOVAL }; @@ -5148,6 +5351,7 @@ static int __init cma_init(void) ib_sa_register_client(&sa_client); register_netdevice_notifier(&cma_nb); + register_netevent_notifier(&cma_netevent_cb); ret = ib_register_client(&cma_client); if (ret) @@ -5162,6 +5366,7 @@ static int __init cma_init(void) err_ib: ib_unregister_client(&cma_client); err: + unregister_netevent_notifier(&cma_netevent_cb); unregister_netdevice_notifier(&cma_nb); ib_sa_unregister_client(&sa_client); unregister_pernet_subsys(&cma_pernet_operations); @@ -5174,6 +5379,7 @@ static void __exit cma_cleanup(void) { cma_configfs_exit(); ib_unregister_client(&cma_client); + unregister_netevent_notifier(&cma_netevent_cb); unregister_netdevice_notifier(&cma_nb); ib_sa_unregister_client(&sa_client); unregister_pernet_subsys(&cma_pernet_operations); diff --git a/drivers/infiniband/core/cma_priv.h b/drivers/infiniband/core/cma_priv.h index 757a0ef79872..b7354c94cf1b 100644 --- a/drivers/infiniband/core/cma_priv.h +++ b/drivers/infiniband/core/cma_priv.h @@ -64,6 +64,7 @@ struct rdma_id_private { struct list_head listen_item; struct list_head listen_list; }; + struct list_head id_list_entry; struct cma_device *cma_dev; struct list_head mc_list; diff --git a/drivers/infiniband/core/rdma_core.c b/drivers/infiniband/core/rdma_core.c index 94d83b665a2f..29b1ab1d5f93 100644 --- a/drivers/infiniband/core/rdma_core.c +++ b/drivers/infiniband/core/rdma_core.c @@ -68,7 +68,7 @@ static int uverbs_try_lock_object(struct ib_uobject *uobj, * In exclusive access mode, we check that the counter is zero (nobody * claimed this object) and we set it to -1. Releasing a shared access * lock is done simply by decreasing the counter. As for exclusive - * access locks, since only a single one of them is is allowed + * access locks, since only a single one of them is allowed * concurrently, setting the counter to zero is enough for releasing * this lock. */ diff --git a/drivers/infiniband/core/roce_gid_mgmt.c b/drivers/infiniband/core/roce_gid_mgmt.c index 68197e576433..e958c43dd28f 100644 --- a/drivers/infiniband/core/roce_gid_mgmt.c +++ b/drivers/infiniband/core/roce_gid_mgmt.c @@ -250,7 +250,7 @@ static bool upper_device_filter(struct ib_device *ib_dev, u32 port, /** * is_upper_ndev_bond_master_filter - Check if a given netdevice - * is bond master device of netdevice of the the RDMA device of port. + * is bond master device of netdevice of the RDMA device of port. * @ib_dev: IB device to check * @port: Port to consider for adding default GID * @rdma_ndev: Pointer to rdma netdevice diff --git a/drivers/infiniband/hw/Makefile b/drivers/infiniband/hw/Makefile index fba0b3be903e..6b3a88046125 100644 --- a/drivers/infiniband/hw/Makefile +++ b/drivers/infiniband/hw/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_INFINIBAND_HFI1) += hfi1/ obj-$(CONFIG_INFINIBAND_HNS) += hns/ obj-$(CONFIG_INFINIBAND_QEDR) += qedr/ obj-$(CONFIG_INFINIBAND_BNXT_RE) += bnxt_re/ +obj-$(CONFIG_INFINIBAND_ERDMA) += erdma/ diff --git a/drivers/infiniband/hw/bnxt_re/bnxt_re.h b/drivers/infiniband/hw/bnxt_re/bnxt_re.h index 79401e6c6aa9..785c37cae3c0 100644 --- a/drivers/infiniband/hw/bnxt_re/bnxt_re.h +++ b/drivers/infiniband/hw/bnxt_re/bnxt_re.h @@ -173,7 +173,7 @@ struct bnxt_re_dev { /* Max of 2 lossless traffic class supported per port */ u16 cosq[2]; - /* QP for for handling QP1 packets */ + /* QP for handling QP1 packets */ struct bnxt_re_gsi_context gsi_ctx; struct bnxt_re_stats stats; atomic_t nq_alloc_cnt; diff --git a/drivers/infiniband/hw/erdma/Kconfig b/drivers/infiniband/hw/erdma/Kconfig new file mode 100644 index 000000000000..169038e3ceb1 --- /dev/null +++ b/drivers/infiniband/hw/erdma/Kconfig @@ -0,0 +1,12 @@ +# SPDX-License-Identifier: GPL-2.0-only +config INFINIBAND_ERDMA + tristate "Alibaba Elastic RDMA Adapter (ERDMA) support" + depends on PCI_MSI && 64BIT + depends on INFINIBAND_ADDR_TRANS + depends on INFINIBAND_USER_ACCESS + help + This is a RDMA/iWarp driver for Alibaba Elastic RDMA Adapter(ERDMA), + which supports RDMA features in Alibaba cloud environment. + + To compile this driver as module, choose M here. The module will be + called erdma. diff --git a/drivers/infiniband/hw/erdma/Makefile b/drivers/infiniband/hw/erdma/Makefile new file mode 100644 index 000000000000..51d2ef91905a --- /dev/null +++ b/drivers/infiniband/hw/erdma/Makefile @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_INFINIBAND_ERDMA) := erdma.o + +erdma-y := erdma_cm.o erdma_main.o erdma_cmdq.o erdma_cq.o erdma_verbs.o erdma_qp.o erdma_eq.o diff --git a/drivers/infiniband/hw/erdma/erdma.h b/drivers/infiniband/hw/erdma/erdma.h new file mode 100644 index 000000000000..2aae635c1c8d --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma.h @@ -0,0 +1,287 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#ifndef __ERDMA_H__ +#define __ERDMA_H__ + +#include +#include +#include +#include + +#include "erdma_hw.h" + +#define DRV_MODULE_NAME "erdma" +#define ERDMA_NODE_DESC "Elastic RDMA(iWARP) stack" + +struct erdma_eq { + void *qbuf; + dma_addr_t qbuf_dma_addr; + + spinlock_t lock; + + u32 depth; + + u16 ci; + u16 rsvd; + + atomic64_t event_num; + atomic64_t notify_num; + + u64 __iomem *db_addr; + u64 *db_record; +}; + +struct erdma_cmdq_sq { + void *qbuf; + dma_addr_t qbuf_dma_addr; + + spinlock_t lock; + + u32 depth; + u16 ci; + u16 pi; + + u16 wqebb_cnt; + + u64 *db_record; +}; + +struct erdma_cmdq_cq { + void *qbuf; + dma_addr_t qbuf_dma_addr; + + spinlock_t lock; + + u32 depth; + u32 ci; + u32 cmdsn; + + u64 *db_record; + + atomic64_t armed_num; +}; + +enum { + ERDMA_CMD_STATUS_INIT, + ERDMA_CMD_STATUS_ISSUED, + ERDMA_CMD_STATUS_FINISHED, + ERDMA_CMD_STATUS_TIMEOUT +}; + +struct erdma_comp_wait { + struct completion wait_event; + u32 cmd_status; + u32 ctx_id; + u16 sq_pi; + u8 comp_status; + u8 rsvd; + u32 comp_data[4]; +}; + +enum { + ERDMA_CMDQ_STATE_OK_BIT = 0, + ERDMA_CMDQ_STATE_TIMEOUT_BIT = 1, + ERDMA_CMDQ_STATE_CTX_ERR_BIT = 2, +}; + +#define ERDMA_CMDQ_TIMEOUT_MS 15000 +#define ERDMA_REG_ACCESS_WAIT_MS 20 +#define ERDMA_WAIT_DEV_DONE_CNT 500 + +struct erdma_cmdq { + unsigned long *comp_wait_bitmap; + struct erdma_comp_wait *wait_pool; + spinlock_t lock; + + bool use_event; + + struct erdma_cmdq_sq sq; + struct erdma_cmdq_cq cq; + struct erdma_eq eq; + + unsigned long state; + + struct semaphore credits; + u16 max_outstandings; +}; + +#define COMPROMISE_CC ERDMA_CC_CUBIC +enum erdma_cc_alg { + ERDMA_CC_NEWRENO = 0, + ERDMA_CC_CUBIC, + ERDMA_CC_HPCC_RTT, + ERDMA_CC_HPCC_ECN, + ERDMA_CC_HPCC_INT, + ERDMA_CC_METHODS_NUM +}; + +struct erdma_devattr { + u32 fw_version; + + unsigned char peer_addr[ETH_ALEN]; + + int numa_node; + enum erdma_cc_alg cc; + u32 grp_num; + u32 irq_num; + + bool disable_dwqe; + u16 dwqe_pages; + u16 dwqe_entries; + + u32 max_qp; + u32 max_send_wr; + u32 max_recv_wr; + u32 max_ord; + u32 max_ird; + + u32 max_send_sge; + u32 max_recv_sge; + u32 max_sge_rd; + u32 max_cq; + u32 max_cqe; + u64 max_mr_size; + u32 max_mr; + u32 max_pd; + u32 max_mw; + u32 local_dma_key; +}; + +#define ERDMA_IRQNAME_SIZE 50 + +struct erdma_irq { + char name[ERDMA_IRQNAME_SIZE]; + u32 msix_vector; + cpumask_t affinity_hint_mask; +}; + +struct erdma_eq_cb { + bool ready; + void *dev; /* All EQs use this fields to get erdma_dev struct */ + struct erdma_irq irq; + struct erdma_eq eq; + struct tasklet_struct tasklet; +}; + +struct erdma_resource_cb { + unsigned long *bitmap; + spinlock_t lock; + u32 next_alloc_idx; + u32 max_cap; +}; + +enum { + ERDMA_RES_TYPE_PD = 0, + ERDMA_RES_TYPE_STAG_IDX = 1, + ERDMA_RES_CNT = 2, +}; + +#define ERDMA_EXTRA_BUFFER_SIZE ERDMA_DB_SIZE +#define WARPPED_BUFSIZE(size) ((size) + ERDMA_EXTRA_BUFFER_SIZE) + +struct erdma_dev { + struct ib_device ibdev; + struct net_device *netdev; + struct pci_dev *pdev; + struct notifier_block netdev_nb; + + resource_size_t func_bar_addr; + resource_size_t func_bar_len; + u8 __iomem *func_bar; + + struct erdma_devattr attrs; + /* physical port state (only one port per device) */ + enum ib_port_state state; + + /* cmdq and aeq use the same msix vector */ + struct erdma_irq comm_irq; + struct erdma_cmdq cmdq; + struct erdma_eq aeq; + struct erdma_eq_cb ceqs[ERDMA_NUM_MSIX_VEC - 1]; + + spinlock_t lock; + struct erdma_resource_cb res_cb[ERDMA_RES_CNT]; + struct xarray qp_xa; + struct xarray cq_xa; + + u32 next_alloc_qpn; + u32 next_alloc_cqn; + + spinlock_t db_bitmap_lock; + /* We provide max 64 uContexts that each has one SQ doorbell Page. */ + DECLARE_BITMAP(sdb_page, ERDMA_DWQE_TYPE0_CNT); + /* + * We provide max 496 uContexts that each has one SQ normal Db, + * and one directWQE db。 + */ + DECLARE_BITMAP(sdb_entry, ERDMA_DWQE_TYPE1_CNT); + + atomic_t num_ctx; + struct list_head cep_list; +}; + +static inline void *get_queue_entry(void *qbuf, u32 idx, u32 depth, u32 shift) +{ + idx &= (depth - 1); + + return qbuf + (idx << shift); +} + +static inline struct erdma_dev *to_edev(struct ib_device *ibdev) +{ + return container_of(ibdev, struct erdma_dev, ibdev); +} + +static inline u32 erdma_reg_read32(struct erdma_dev *dev, u32 reg) +{ + return readl(dev->func_bar + reg); +} + +static inline u64 erdma_reg_read64(struct erdma_dev *dev, u32 reg) +{ + return readq(dev->func_bar + reg); +} + +static inline void erdma_reg_write32(struct erdma_dev *dev, u32 reg, u32 value) +{ + writel(value, dev->func_bar + reg); +} + +static inline void erdma_reg_write64(struct erdma_dev *dev, u32 reg, u64 value) +{ + writeq(value, dev->func_bar + reg); +} + +static inline u32 erdma_reg_read32_filed(struct erdma_dev *dev, u32 reg, + u32 filed_mask) +{ + u32 val = erdma_reg_read32(dev, reg); + + return FIELD_GET(filed_mask, val); +} + +int erdma_cmdq_init(struct erdma_dev *dev); +void erdma_finish_cmdq_init(struct erdma_dev *dev); +void erdma_cmdq_destroy(struct erdma_dev *dev); + +void erdma_cmdq_build_reqhdr(u64 *hdr, u32 mod, u32 op); +int erdma_post_cmd_wait(struct erdma_cmdq *cmdq, u64 *req, u32 req_size, + u64 *resp0, u64 *resp1); +void erdma_cmdq_completion_handler(struct erdma_cmdq *cmdq); + +int erdma_ceqs_init(struct erdma_dev *dev); +void erdma_ceqs_uninit(struct erdma_dev *dev); +void notify_eq(struct erdma_eq *eq); +void *get_next_valid_eqe(struct erdma_eq *eq); + +int erdma_aeq_init(struct erdma_dev *dev); +void erdma_aeq_destroy(struct erdma_dev *dev); + +void erdma_aeq_event_handler(struct erdma_dev *dev); +void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb); + +#endif diff --git a/drivers/infiniband/hw/erdma/erdma_cm.c b/drivers/infiniband/hw/erdma/erdma_cm.c new file mode 100644 index 000000000000..f13f16479eca --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_cm.c @@ -0,0 +1,1430 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +/* Authors: Bernard Metzler */ +/* Fredy Neeser */ +/* Greg Joyce */ +/* Copyright (c) 2008-2019, IBM Corporation */ +/* Copyright (c) 2017, Open Grid Computing, Inc. */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "erdma.h" +#include "erdma_cm.h" +#include "erdma_verbs.h" + +static struct workqueue_struct *erdma_cm_wq; + +static void erdma_cm_llp_state_change(struct sock *sk); +static void erdma_cm_llp_data_ready(struct sock *sk); +static void erdma_cm_llp_error_report(struct sock *sk); + +static void erdma_sk_assign_cm_upcalls(struct sock *sk) +{ + write_lock_bh(&sk->sk_callback_lock); + sk->sk_state_change = erdma_cm_llp_state_change; + sk->sk_data_ready = erdma_cm_llp_data_ready; + sk->sk_error_report = erdma_cm_llp_error_report; + write_unlock_bh(&sk->sk_callback_lock); +} + +static void erdma_sk_save_upcalls(struct sock *sk) +{ + struct erdma_cep *cep = sk_to_cep(sk); + + write_lock_bh(&sk->sk_callback_lock); + cep->sk_state_change = sk->sk_state_change; + cep->sk_data_ready = sk->sk_data_ready; + cep->sk_error_report = sk->sk_error_report; + write_unlock_bh(&sk->sk_callback_lock); +} + +static void erdma_sk_restore_upcalls(struct sock *sk, struct erdma_cep *cep) +{ + sk->sk_state_change = cep->sk_state_change; + sk->sk_data_ready = cep->sk_data_ready; + sk->sk_error_report = cep->sk_error_report; + sk->sk_user_data = NULL; +} + +static void erdma_socket_disassoc(struct socket *s) +{ + struct sock *sk = s->sk; + struct erdma_cep *cep; + + if (sk) { + write_lock_bh(&sk->sk_callback_lock); + cep = sk_to_cep(sk); + if (cep) { + erdma_sk_restore_upcalls(sk, cep); + erdma_cep_put(cep); + } else { + WARN_ON_ONCE(1); + } + write_unlock_bh(&sk->sk_callback_lock); + } else { + WARN_ON_ONCE(1); + } +} + +static void erdma_cep_socket_assoc(struct erdma_cep *cep, struct socket *s) +{ + cep->sock = s; + erdma_cep_get(cep); + s->sk->sk_user_data = cep; + + erdma_sk_save_upcalls(s->sk); + erdma_sk_assign_cm_upcalls(s->sk); +} + +static void erdma_disassoc_listen_cep(struct erdma_cep *cep) +{ + if (cep->listen_cep) { + erdma_cep_put(cep->listen_cep); + cep->listen_cep = NULL; + } +} + +static struct erdma_cep *erdma_cep_alloc(struct erdma_dev *dev) +{ + struct erdma_cep *cep = kzalloc(sizeof(*cep), GFP_KERNEL); + unsigned long flags; + + if (!cep) + return NULL; + + INIT_LIST_HEAD(&cep->listenq); + INIT_LIST_HEAD(&cep->devq); + INIT_LIST_HEAD(&cep->work_freelist); + + kref_init(&cep->ref); + cep->state = ERDMA_EPSTATE_IDLE; + init_waitqueue_head(&cep->waitq); + spin_lock_init(&cep->lock); + cep->dev = dev; + + spin_lock_irqsave(&dev->lock, flags); + list_add_tail(&cep->devq, &dev->cep_list); + spin_unlock_irqrestore(&dev->lock, flags); + + return cep; +} + +static void erdma_cm_free_work(struct erdma_cep *cep) +{ + struct list_head *w, *tmp; + struct erdma_cm_work *work; + + list_for_each_safe(w, tmp, &cep->work_freelist) { + work = list_entry(w, struct erdma_cm_work, list); + list_del(&work->list); + kfree(work); + } +} + +static void erdma_cancel_mpatimer(struct erdma_cep *cep) +{ + spin_lock_bh(&cep->lock); + if (cep->mpa_timer) { + if (cancel_delayed_work(&cep->mpa_timer->work)) { + erdma_cep_put(cep); + kfree(cep->mpa_timer); + } + cep->mpa_timer = NULL; + } + spin_unlock_bh(&cep->lock); +} + +static void erdma_put_work(struct erdma_cm_work *work) +{ + INIT_LIST_HEAD(&work->list); + spin_lock_bh(&work->cep->lock); + list_add(&work->list, &work->cep->work_freelist); + spin_unlock_bh(&work->cep->lock); +} + +static void erdma_cep_set_inuse(struct erdma_cep *cep) +{ + unsigned long flags; + + spin_lock_irqsave(&cep->lock, flags); + while (cep->in_use) { + spin_unlock_irqrestore(&cep->lock, flags); + wait_event_interruptible(cep->waitq, !cep->in_use); + if (signal_pending(current)) + flush_signals(current); + + spin_lock_irqsave(&cep->lock, flags); + } + + cep->in_use = 1; + spin_unlock_irqrestore(&cep->lock, flags); +} + +static void erdma_cep_set_free(struct erdma_cep *cep) +{ + unsigned long flags; + + spin_lock_irqsave(&cep->lock, flags); + cep->in_use = 0; + spin_unlock_irqrestore(&cep->lock, flags); + + wake_up(&cep->waitq); +} + +static void __erdma_cep_dealloc(struct kref *ref) +{ + struct erdma_cep *cep = container_of(ref, struct erdma_cep, ref); + struct erdma_dev *dev = cep->dev; + unsigned long flags; + + WARN_ON(cep->listen_cep); + + kfree(cep->private_data); + kfree(cep->mpa.pdata); + spin_lock_bh(&cep->lock); + if (!list_empty(&cep->work_freelist)) + erdma_cm_free_work(cep); + spin_unlock_bh(&cep->lock); + + spin_lock_irqsave(&dev->lock, flags); + list_del(&cep->devq); + spin_unlock_irqrestore(&dev->lock, flags); + kfree(cep); +} + +static struct erdma_cm_work *erdma_get_work(struct erdma_cep *cep) +{ + struct erdma_cm_work *work = NULL; + + spin_lock_bh(&cep->lock); + if (!list_empty(&cep->work_freelist)) { + work = list_entry(cep->work_freelist.next, struct erdma_cm_work, + list); + list_del_init(&work->list); + } + + spin_unlock_bh(&cep->lock); + return work; +} + +static int erdma_cm_alloc_work(struct erdma_cep *cep, int num) +{ + struct erdma_cm_work *work; + + while (num--) { + work = kmalloc(sizeof(*work), GFP_KERNEL); + if (!work) { + if (!(list_empty(&cep->work_freelist))) + erdma_cm_free_work(cep); + return -ENOMEM; + } + work->cep = cep; + INIT_LIST_HEAD(&work->list); + list_add(&work->list, &cep->work_freelist); + } + + return 0; +} + +static int erdma_cm_upcall(struct erdma_cep *cep, enum iw_cm_event_type reason, + int status) +{ + struct iw_cm_event event; + struct iw_cm_id *cm_id; + + memset(&event, 0, sizeof(event)); + event.status = status; + event.event = reason; + + if (reason == IW_CM_EVENT_CONNECT_REQUEST) { + event.provider_data = cep; + cm_id = cep->listen_cep->cm_id; + + event.ird = cep->dev->attrs.max_ird; + event.ord = cep->dev->attrs.max_ord; + } else { + cm_id = cep->cm_id; + } + + if (reason == IW_CM_EVENT_CONNECT_REQUEST || + reason == IW_CM_EVENT_CONNECT_REPLY) { + u16 pd_len = be16_to_cpu(cep->mpa.hdr.params.pd_len); + + if (pd_len && cep->mpa.pdata) { + event.private_data_len = pd_len; + event.private_data = cep->mpa.pdata; + } + + getname_local(cep->sock, &event.local_addr); + getname_peer(cep->sock, &event.remote_addr); + } + + return cm_id->event_handler(cm_id, &event); +} + +void erdma_qp_cm_drop(struct erdma_qp *qp) +{ + struct erdma_cep *cep = qp->cep; + + if (!qp->cep) + return; + + erdma_cep_set_inuse(cep); + + /* already closed. */ + if (cep->state == ERDMA_EPSTATE_CLOSED) + goto out; + + if (cep->cm_id) { + switch (cep->state) { + case ERDMA_EPSTATE_AWAIT_MPAREP: + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, + -EINVAL); + break; + case ERDMA_EPSTATE_RDMA_MODE: + erdma_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0); + break; + case ERDMA_EPSTATE_IDLE: + case ERDMA_EPSTATE_LISTENING: + case ERDMA_EPSTATE_CONNECTING: + case ERDMA_EPSTATE_AWAIT_MPAREQ: + case ERDMA_EPSTATE_RECVD_MPAREQ: + case ERDMA_EPSTATE_CLOSED: + default: + break; + } + cep->cm_id->rem_ref(cep->cm_id); + cep->cm_id = NULL; + erdma_cep_put(cep); + } + cep->state = ERDMA_EPSTATE_CLOSED; + + if (cep->sock) { + erdma_socket_disassoc(cep->sock); + sock_release(cep->sock); + cep->sock = NULL; + } + + if (cep->qp) { + cep->qp = NULL; + erdma_qp_put(qp); + } +out: + erdma_cep_set_free(cep); +} + +void erdma_cep_put(struct erdma_cep *cep) +{ + WARN_ON(kref_read(&cep->ref) < 1); + kref_put(&cep->ref, __erdma_cep_dealloc); +} + +void erdma_cep_get(struct erdma_cep *cep) +{ + kref_get(&cep->ref); +} + +static int erdma_send_mpareqrep(struct erdma_cep *cep, const void *pdata, + u8 pd_len) +{ + struct socket *s = cep->sock; + struct mpa_rr *rr = &cep->mpa.hdr; + struct kvec iov[3]; + struct msghdr msg; + int iovec_num = 0; + int ret; + int mpa_len; + + memset(&msg, 0, sizeof(msg)); + + rr->params.pd_len = cpu_to_be16(pd_len); + + iov[iovec_num].iov_base = rr; + iov[iovec_num].iov_len = sizeof(*rr); + iovec_num++; + mpa_len = sizeof(*rr); + + iov[iovec_num].iov_base = &cep->mpa.ext_data; + iov[iovec_num].iov_len = sizeof(cep->mpa.ext_data); + iovec_num++; + mpa_len += sizeof(cep->mpa.ext_data); + + if (pd_len) { + iov[iovec_num].iov_base = (char *)pdata; + iov[iovec_num].iov_len = pd_len; + mpa_len += pd_len; + iovec_num++; + } + + ret = kernel_sendmsg(s, &msg, iov, iovec_num, mpa_len); + + return ret < 0 ? ret : 0; +} + +static inline int ksock_recv(struct socket *sock, char *buf, size_t size, + int flags) +{ + struct kvec iov = { buf, size }; + struct msghdr msg = { .msg_name = NULL, .msg_flags = flags }; + + return kernel_recvmsg(sock, &msg, &iov, 1, size, flags); +} + +static int __recv_mpa_hdr(struct erdma_cep *cep, int hdr_rcvd, char *hdr, + int hdr_size, int *rcvd_out) +{ + struct socket *s = cep->sock; + int rcvd; + + *rcvd_out = 0; + if (hdr_rcvd < hdr_size) { + rcvd = ksock_recv(s, hdr + hdr_rcvd, hdr_size - hdr_rcvd, + MSG_DONTWAIT); + if (rcvd == -EAGAIN) + return -EAGAIN; + + if (rcvd <= 0) + return -ECONNABORTED; + + hdr_rcvd += rcvd; + *rcvd_out = rcvd; + + if (hdr_rcvd < hdr_size) + return -EAGAIN; + } + + return 0; +} + +static void __mpa_rr_set_revision(__be16 *bits, u8 rev) +{ + *bits = (*bits & ~MPA_RR_MASK_REVISION) | + (cpu_to_be16(rev) & MPA_RR_MASK_REVISION); +} + +static u8 __mpa_rr_revision(__be16 mpa_rr_bits) +{ + __be16 rev = mpa_rr_bits & MPA_RR_MASK_REVISION; + + return (u8)be16_to_cpu(rev); +} + +static void __mpa_ext_set_cc(__be32 *bits, u32 cc) +{ + *bits = (*bits & ~MPA_EXT_FLAG_CC) | + (cpu_to_be32(cc) & MPA_EXT_FLAG_CC); +} + +static u8 __mpa_ext_cc(__be32 mpa_ext_bits) +{ + __be32 cc = mpa_ext_bits & MPA_EXT_FLAG_CC; + + return (u8)be32_to_cpu(cc); +} + +/* + * Receive MPA Request/Reply header. + * + * Returns 0 if complete MPA Request/Reply haeder including + * eventual private data was received. Returns -EAGAIN if + * header was partially received or negative error code otherwise. + * + * Context: May be called in process context only + */ +static int erdma_recv_mpa_rr(struct erdma_cep *cep) +{ + struct mpa_rr *hdr = &cep->mpa.hdr; + struct socket *s = cep->sock; + u16 pd_len; + int rcvd, to_rcv, ret, pd_rcvd; + + if (cep->mpa.bytes_rcvd < sizeof(struct mpa_rr)) { + ret = __recv_mpa_hdr(cep, cep->mpa.bytes_rcvd, + (char *)&cep->mpa.hdr, + sizeof(struct mpa_rr), &rcvd); + cep->mpa.bytes_rcvd += rcvd; + if (ret) + return ret; + } + + if (be16_to_cpu(hdr->params.pd_len) > MPA_MAX_PRIVDATA || + __mpa_rr_revision(hdr->params.bits) != MPA_REVISION_EXT_1) + return -EPROTO; + + if (cep->mpa.bytes_rcvd - sizeof(struct mpa_rr) < + sizeof(struct erdma_mpa_ext)) { + ret = __recv_mpa_hdr( + cep, cep->mpa.bytes_rcvd - sizeof(struct mpa_rr), + (char *)&cep->mpa.ext_data, + sizeof(struct erdma_mpa_ext), &rcvd); + cep->mpa.bytes_rcvd += rcvd; + if (ret) + return ret; + } + + pd_len = be16_to_cpu(hdr->params.pd_len); + pd_rcvd = cep->mpa.bytes_rcvd - sizeof(struct mpa_rr) - + sizeof(struct erdma_mpa_ext); + to_rcv = pd_len - pd_rcvd; + + if (!to_rcv) { + /* + * We have received the whole MPA Request/Reply message. + * Check against peer protocol violation. + */ + u32 word; + + ret = __recv_mpa_hdr(cep, 0, (char *)&word, sizeof(word), + &rcvd); + if (ret == -EAGAIN && rcvd == 0) + return 0; + + if (ret) + return ret; + + return -EPROTO; + } + + /* + * At this point, MPA header has been fully received, and pd_len != 0. + * So, begin to receive private data. + */ + if (!cep->mpa.pdata) { + cep->mpa.pdata = kmalloc(pd_len + 4, GFP_KERNEL); + if (!cep->mpa.pdata) + return -ENOMEM; + } + + rcvd = ksock_recv(s, cep->mpa.pdata + pd_rcvd, to_rcv + 4, + MSG_DONTWAIT); + if (rcvd < 0) + return rcvd; + + if (rcvd > to_rcv) + return -EPROTO; + + cep->mpa.bytes_rcvd += rcvd; + + if (to_rcv == rcvd) + return 0; + + return -EAGAIN; +} + +/* + * erdma_proc_mpareq() + * + * Read MPA Request from socket and signal new connection to IWCM + * if success. Caller must hold lock on corresponding listening CEP. + */ +static int erdma_proc_mpareq(struct erdma_cep *cep) +{ + struct mpa_rr *req; + int ret; + + ret = erdma_recv_mpa_rr(cep); + if (ret) + return ret; + + req = &cep->mpa.hdr; + + if (memcmp(req->key, MPA_KEY_REQ, MPA_KEY_SIZE)) + return -EPROTO; + + memcpy(req->key, MPA_KEY_REP, MPA_KEY_SIZE); + + /* Currently does not support marker and crc. */ + if (req->params.bits & MPA_RR_FLAG_MARKERS || + req->params.bits & MPA_RR_FLAG_CRC) + goto reject_conn; + + cep->state = ERDMA_EPSTATE_RECVD_MPAREQ; + + /* Keep reference until IWCM accepts/rejects */ + erdma_cep_get(cep); + ret = erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REQUEST, 0); + if (ret) + erdma_cep_put(cep); + + return ret; + +reject_conn: + req->params.bits &= ~MPA_RR_FLAG_MARKERS; + req->params.bits |= MPA_RR_FLAG_REJECT; + req->params.bits &= ~MPA_RR_FLAG_CRC; + + kfree(cep->mpa.pdata); + cep->mpa.pdata = NULL; + erdma_send_mpareqrep(cep, NULL, 0); + + return -EOPNOTSUPP; +} + +static int erdma_proc_mpareply(struct erdma_cep *cep) +{ + struct erdma_qp_attrs qp_attrs; + struct erdma_qp *qp = cep->qp; + struct mpa_rr *rep; + int ret; + + ret = erdma_recv_mpa_rr(cep); + if (ret) + goto out_err; + + erdma_cancel_mpatimer(cep); + + rep = &cep->mpa.hdr; + + if (memcmp(rep->key, MPA_KEY_REP, MPA_KEY_SIZE)) { + ret = -EPROTO; + goto out_err; + } + + if (rep->params.bits & MPA_RR_FLAG_REJECT) { + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -ECONNRESET); + return -ECONNRESET; + } + + /* Currently does not support marker and crc. */ + if ((rep->params.bits & MPA_RR_FLAG_MARKERS) || + (rep->params.bits & MPA_RR_FLAG_CRC)) { + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -ECONNREFUSED); + return -EINVAL; + } + + memset(&qp_attrs, 0, sizeof(qp_attrs)); + qp_attrs.irq_size = cep->ird; + qp_attrs.orq_size = cep->ord; + qp_attrs.state = ERDMA_QP_STATE_RTS; + + down_write(&qp->state_lock); + if (qp->attrs.state > ERDMA_QP_STATE_RTR) { + ret = -EINVAL; + up_write(&qp->state_lock); + goto out_err; + } + + qp->attrs.qp_type = ERDMA_QP_ACTIVE; + if (__mpa_ext_cc(cep->mpa.ext_data.bits) != qp->attrs.cc) + qp->attrs.cc = COMPROMISE_CC; + + ret = erdma_modify_qp_internal(qp, &qp_attrs, + ERDMA_QP_ATTR_STATE | + ERDMA_QP_ATTR_LLP_HANDLE | + ERDMA_QP_ATTR_MPA); + + up_write(&qp->state_lock); + + if (!ret) { + ret = erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, 0); + if (!ret) + cep->state = ERDMA_EPSTATE_RDMA_MODE; + + return 0; + } + +out_err: + if (ret != -EAGAIN) + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -EINVAL); + + return ret; +} + +static void erdma_accept_newconn(struct erdma_cep *cep) +{ + struct socket *s = cep->sock; + struct socket *new_s = NULL; + struct erdma_cep *new_cep = NULL; + int ret = 0; + + if (cep->state != ERDMA_EPSTATE_LISTENING) + goto error; + + new_cep = erdma_cep_alloc(cep->dev); + if (!new_cep) + goto error; + + /* + * 4: Allocate a sufficient number of work elements + * to allow concurrent handling of local + peer close + * events, MPA header processing + MPA timeout. + */ + if (erdma_cm_alloc_work(new_cep, 4) != 0) + goto error; + + /* + * Copy saved socket callbacks from listening CEP + * and assign new socket with new CEP + */ + new_cep->sk_state_change = cep->sk_state_change; + new_cep->sk_data_ready = cep->sk_data_ready; + new_cep->sk_error_report = cep->sk_error_report; + + ret = kernel_accept(s, &new_s, O_NONBLOCK); + if (ret != 0) + goto error; + + new_cep->sock = new_s; + erdma_cep_get(new_cep); + new_s->sk->sk_user_data = new_cep; + + tcp_sock_set_nodelay(new_s->sk); + new_cep->state = ERDMA_EPSTATE_AWAIT_MPAREQ; + + ret = erdma_cm_queue_work(new_cep, ERDMA_CM_WORK_MPATIMEOUT); + if (ret) + goto error; + + new_cep->listen_cep = cep; + erdma_cep_get(cep); + + if (atomic_read(&new_s->sk->sk_rmem_alloc)) { + /* MPA REQ already queued */ + erdma_cep_set_inuse(new_cep); + ret = erdma_proc_mpareq(new_cep); + if (ret != -EAGAIN) { + erdma_cep_put(cep); + new_cep->listen_cep = NULL; + if (ret) { + erdma_cep_set_free(new_cep); + goto error; + } + } + erdma_cep_set_free(new_cep); + } + return; + +error: + if (new_cep) { + new_cep->state = ERDMA_EPSTATE_CLOSED; + erdma_cancel_mpatimer(new_cep); + + erdma_cep_put(new_cep); + new_cep->sock = NULL; + } + + if (new_s) { + erdma_socket_disassoc(new_s); + sock_release(new_s); + } +} + +static int erdma_newconn_connected(struct erdma_cep *cep) +{ + int ret = 0; + + cep->mpa.hdr.params.bits = 0; + __mpa_rr_set_revision(&cep->mpa.hdr.params.bits, MPA_REVISION_EXT_1); + + memcpy(cep->mpa.hdr.key, MPA_KEY_REQ, MPA_KEY_SIZE); + cep->mpa.ext_data.cookie = cpu_to_be32(cep->qp->attrs.cookie); + __mpa_ext_set_cc(&cep->mpa.ext_data.bits, cep->qp->attrs.cc); + + ret = erdma_send_mpareqrep(cep, cep->private_data, cep->pd_len); + cep->state = ERDMA_EPSTATE_AWAIT_MPAREP; + cep->mpa.hdr.params.pd_len = 0; + + if (ret >= 0) + ret = erdma_cm_queue_work(cep, ERDMA_CM_WORK_MPATIMEOUT); + + return ret; +} + +static void erdma_cm_work_handler(struct work_struct *w) +{ + struct erdma_cm_work *work; + struct erdma_cep *cep; + int release_cep = 0, ret = 0; + + work = container_of(w, struct erdma_cm_work, work.work); + cep = work->cep; + + erdma_cep_set_inuse(cep); + + switch (work->type) { + case ERDMA_CM_WORK_CONNECTED: + erdma_cancel_mpatimer(cep); + if (cep->state == ERDMA_EPSTATE_CONNECTING) { + ret = erdma_newconn_connected(cep); + if (ret) { + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, + -EIO); + release_cep = 1; + } + } + break; + case ERDMA_CM_WORK_CONNECTTIMEOUT: + if (cep->state == ERDMA_EPSTATE_CONNECTING) { + cep->mpa_timer = NULL; + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, + -ETIMEDOUT); + release_cep = 1; + } + break; + case ERDMA_CM_WORK_ACCEPT: + erdma_accept_newconn(cep); + break; + case ERDMA_CM_WORK_READ_MPAHDR: + if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ) { + if (cep->listen_cep) { + erdma_cep_set_inuse(cep->listen_cep); + + if (cep->listen_cep->state == + ERDMA_EPSTATE_LISTENING) + ret = erdma_proc_mpareq(cep); + else + ret = -EFAULT; + + erdma_cep_set_free(cep->listen_cep); + + if (ret != -EAGAIN) { + erdma_cep_put(cep->listen_cep); + cep->listen_cep = NULL; + if (ret) + erdma_cep_put(cep); + } + } + } else if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) { + ret = erdma_proc_mpareply(cep); + } + + if (ret && ret != -EAGAIN) + release_cep = 1; + break; + case ERDMA_CM_WORK_CLOSE_LLP: + if (cep->cm_id) + erdma_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0); + release_cep = 1; + break; + case ERDMA_CM_WORK_PEER_CLOSE: + if (cep->cm_id) { + if (cep->state == ERDMA_EPSTATE_CONNECTING || + cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) { + /* + * MPA reply not received, but connection drop + */ + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, + -ECONNRESET); + } else if (cep->state == ERDMA_EPSTATE_RDMA_MODE) { + /* + * NOTE: IW_CM_EVENT_DISCONNECT is given just + * to transition IWCM into CLOSING. + */ + erdma_cm_upcall(cep, IW_CM_EVENT_DISCONNECT, 0); + erdma_cm_upcall(cep, IW_CM_EVENT_CLOSE, 0); + } + } else if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ) { + /* Socket close before MPA request received. */ + erdma_disassoc_listen_cep(cep); + erdma_cep_put(cep); + } + release_cep = 1; + break; + case ERDMA_CM_WORK_MPATIMEOUT: + cep->mpa_timer = NULL; + if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) { + /* + * MPA request timed out: + * Hide any partially received private data and signal + * timeout + */ + cep->mpa.hdr.params.pd_len = 0; + + if (cep->cm_id) + erdma_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, + -ETIMEDOUT); + release_cep = 1; + } else if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ) { + /* No MPA req received after peer TCP stream setup. */ + erdma_disassoc_listen_cep(cep); + + erdma_cep_put(cep); + release_cep = 1; + } + break; + default: + WARN(1, "Undefined CM work type: %d\n", work->type); + } + + if (release_cep) { + erdma_cancel_mpatimer(cep); + cep->state = ERDMA_EPSTATE_CLOSED; + if (cep->qp) { + struct erdma_qp *qp = cep->qp; + /* + * Serialize a potential race with application + * closing the QP and calling erdma_qp_cm_drop() + */ + erdma_qp_get(qp); + erdma_cep_set_free(cep); + + erdma_qp_llp_close(qp); + erdma_qp_put(qp); + + erdma_cep_set_inuse(cep); + cep->qp = NULL; + erdma_qp_put(qp); + } + + if (cep->sock) { + erdma_socket_disassoc(cep->sock); + sock_release(cep->sock); + cep->sock = NULL; + } + + if (cep->cm_id) { + cep->cm_id->rem_ref(cep->cm_id); + cep->cm_id = NULL; + if (cep->state != ERDMA_EPSTATE_LISTENING) + erdma_cep_put(cep); + } + } + erdma_cep_set_free(cep); + erdma_put_work(work); + erdma_cep_put(cep); +} + +int erdma_cm_queue_work(struct erdma_cep *cep, enum erdma_work_type type) +{ + struct erdma_cm_work *work = erdma_get_work(cep); + unsigned long delay = 0; + + if (!work) + return -ENOMEM; + + work->type = type; + work->cep = cep; + + erdma_cep_get(cep); + + INIT_DELAYED_WORK(&work->work, erdma_cm_work_handler); + + if (type == ERDMA_CM_WORK_MPATIMEOUT) { + cep->mpa_timer = work; + + if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) + delay = MPAREP_TIMEOUT; + else + delay = MPAREQ_TIMEOUT; + } else if (type == ERDMA_CM_WORK_CONNECTTIMEOUT) { + cep->mpa_timer = work; + + delay = CONNECT_TIMEOUT; + } + + queue_delayed_work(erdma_cm_wq, &work->work, delay); + + return 0; +} + +static void erdma_cm_llp_data_ready(struct sock *sk) +{ + struct erdma_cep *cep; + + read_lock(&sk->sk_callback_lock); + + cep = sk_to_cep(sk); + if (!cep) + goto out; + + if (cep->state == ERDMA_EPSTATE_AWAIT_MPAREQ || + cep->state == ERDMA_EPSTATE_AWAIT_MPAREP) + erdma_cm_queue_work(cep, ERDMA_CM_WORK_READ_MPAHDR); + +out: + read_unlock(&sk->sk_callback_lock); +} + +static void erdma_cm_llp_error_report(struct sock *sk) +{ + struct erdma_cep *cep = sk_to_cep(sk); + + if (cep) + cep->sk_error_report(sk); +} + +static void erdma_cm_llp_state_change(struct sock *sk) +{ + struct erdma_cep *cep; + void (*orig_state_change)(struct sock *sk); + + read_lock(&sk->sk_callback_lock); + + cep = sk_to_cep(sk); + if (!cep) { + read_unlock(&sk->sk_callback_lock); + return; + } + orig_state_change = cep->sk_state_change; + + switch (sk->sk_state) { + case TCP_ESTABLISHED: + if (cep->state == ERDMA_EPSTATE_CONNECTING) + erdma_cm_queue_work(cep, ERDMA_CM_WORK_CONNECTED); + else + erdma_cm_queue_work(cep, ERDMA_CM_WORK_ACCEPT); + break; + case TCP_CLOSE: + case TCP_CLOSE_WAIT: + if (cep->state != ERDMA_EPSTATE_LISTENING) + erdma_cm_queue_work(cep, ERDMA_CM_WORK_PEER_CLOSE); + break; + default: + break; + } + read_unlock(&sk->sk_callback_lock); + orig_state_change(sk); +} + +static int kernel_bindconnect(struct socket *s, struct sockaddr *laddr, + int laddrlen, struct sockaddr *raddr, + int raddrlen, int flags) +{ + int ret; + + sock_set_reuseaddr(s->sk); + ret = s->ops->bind(s, laddr, laddrlen); + if (ret) + return ret; + ret = s->ops->connect(s, raddr, raddrlen, flags); + return ret < 0 ? ret : 0; +} + +int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *params) +{ + struct erdma_dev *dev = to_edev(id->device); + struct erdma_qp *qp; + struct erdma_cep *cep = NULL; + struct socket *s = NULL; + struct sockaddr *laddr = (struct sockaddr *)&id->m_local_addr; + struct sockaddr *raddr = (struct sockaddr *)&id->m_remote_addr; + u16 pd_len = params->private_data_len; + int ret; + + if (pd_len > MPA_MAX_PRIVDATA) + return -EINVAL; + + if (params->ird > dev->attrs.max_ird || + params->ord > dev->attrs.max_ord) + return -EINVAL; + + if (laddr->sa_family != AF_INET || raddr->sa_family != AF_INET) + return -EAFNOSUPPORT; + + qp = find_qp_by_qpn(dev, params->qpn); + if (!qp) + return -ENOENT; + erdma_qp_get(qp); + + ret = sock_create(AF_INET, SOCK_STREAM, IPPROTO_TCP, &s); + if (ret < 0) + goto error_put_qp; + + cep = erdma_cep_alloc(dev); + if (!cep) { + ret = -ENOMEM; + goto error_release_sock; + } + + erdma_cep_set_inuse(cep); + + /* Associate QP with CEP */ + erdma_cep_get(cep); + qp->cep = cep; + cep->qp = qp; + + /* Associate cm_id with CEP */ + id->add_ref(id); + cep->cm_id = id; + + /* + * 6: Allocate a sufficient number of work elements + * to allow concurrent handling of local + peer close + * events, MPA header processing + MPA timeout, connected event + * and connect timeout. + */ + ret = erdma_cm_alloc_work(cep, 6); + if (ret != 0) { + ret = -ENOMEM; + goto error_release_cep; + } + + cep->ird = params->ird; + cep->ord = params->ord; + cep->state = ERDMA_EPSTATE_CONNECTING; + + erdma_cep_socket_assoc(cep, s); + + if (pd_len) { + cep->pd_len = pd_len; + cep->private_data = kmalloc(pd_len, GFP_KERNEL); + if (!cep->private_data) { + ret = -ENOMEM; + goto error_disassoc; + } + + memcpy(cep->private_data, params->private_data, + params->private_data_len); + } + + ret = kernel_bindconnect(s, laddr, sizeof(*laddr), raddr, + sizeof(*raddr), O_NONBLOCK); + if (ret != -EINPROGRESS && ret != 0) { + goto error_disassoc; + } else if (ret == 0) { + ret = erdma_cm_queue_work(cep, ERDMA_CM_WORK_CONNECTED); + if (ret) + goto error_disassoc; + } else { + ret = erdma_cm_queue_work(cep, ERDMA_CM_WORK_CONNECTTIMEOUT); + if (ret) + goto error_disassoc; + } + + erdma_cep_set_free(cep); + return 0; + +error_disassoc: + kfree(cep->private_data); + cep->private_data = NULL; + cep->pd_len = 0; + + erdma_socket_disassoc(s); + +error_release_cep: + /* disassoc with cm_id */ + cep->cm_id = NULL; + id->rem_ref(id); + + /* disassoc with qp */ + qp->cep = NULL; + erdma_cep_put(cep); + cep->qp = NULL; + + cep->state = ERDMA_EPSTATE_CLOSED; + + erdma_cep_set_free(cep); + + /* release the cep. */ + erdma_cep_put(cep); + +error_release_sock: + if (s) + sock_release(s); +error_put_qp: + erdma_qp_put(qp); + + return ret; +} + +int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *params) +{ + struct erdma_dev *dev = to_edev(id->device); + struct erdma_cep *cep = (struct erdma_cep *)id->provider_data; + struct erdma_qp *qp; + struct erdma_qp_attrs qp_attrs; + int ret; + + erdma_cep_set_inuse(cep); + erdma_cep_put(cep); + + /* Free lingering inbound private data */ + if (cep->mpa.hdr.params.pd_len) { + cep->mpa.hdr.params.pd_len = 0; + kfree(cep->mpa.pdata); + cep->mpa.pdata = NULL; + } + erdma_cancel_mpatimer(cep); + + if (cep->state != ERDMA_EPSTATE_RECVD_MPAREQ) { + erdma_cep_set_free(cep); + erdma_cep_put(cep); + + return -ECONNRESET; + } + + qp = find_qp_by_qpn(dev, params->qpn); + if (!qp) + return -ENOENT; + erdma_qp_get(qp); + + down_write(&qp->state_lock); + if (qp->attrs.state > ERDMA_QP_STATE_RTR) { + ret = -EINVAL; + up_write(&qp->state_lock); + goto error; + } + + if (params->ord > dev->attrs.max_ord || + params->ird > dev->attrs.max_ord) { + ret = -EINVAL; + up_write(&qp->state_lock); + goto error; + } + + if (params->private_data_len > MPA_MAX_PRIVDATA) { + ret = -EINVAL; + up_write(&qp->state_lock); + goto error; + } + + cep->ird = params->ird; + cep->ord = params->ord; + + cep->cm_id = id; + id->add_ref(id); + + memset(&qp_attrs, 0, sizeof(qp_attrs)); + qp_attrs.orq_size = params->ord; + qp_attrs.irq_size = params->ird; + + qp_attrs.state = ERDMA_QP_STATE_RTS; + + /* Associate QP with CEP */ + erdma_cep_get(cep); + qp->cep = cep; + cep->qp = qp; + + cep->state = ERDMA_EPSTATE_RDMA_MODE; + + qp->attrs.qp_type = ERDMA_QP_PASSIVE; + qp->attrs.pd_len = params->private_data_len; + + if (qp->attrs.cc != __mpa_ext_cc(cep->mpa.ext_data.bits)) + qp->attrs.cc = COMPROMISE_CC; + + /* move to rts */ + ret = erdma_modify_qp_internal(qp, &qp_attrs, + ERDMA_QP_ATTR_STATE | + ERDMA_QP_ATTR_ORD | + ERDMA_QP_ATTR_LLP_HANDLE | + ERDMA_QP_ATTR_IRD | + ERDMA_QP_ATTR_MPA); + up_write(&qp->state_lock); + + if (ret) + goto error; + + cep->mpa.ext_data.bits = 0; + __mpa_ext_set_cc(&cep->mpa.ext_data.bits, qp->attrs.cc); + cep->mpa.ext_data.cookie = cpu_to_be32(cep->qp->attrs.cookie); + + ret = erdma_send_mpareqrep(cep, params->private_data, + params->private_data_len); + if (!ret) { + ret = erdma_cm_upcall(cep, IW_CM_EVENT_ESTABLISHED, 0); + if (ret) + goto error; + + erdma_cep_set_free(cep); + + return 0; + } + +error: + erdma_socket_disassoc(cep->sock); + sock_release(cep->sock); + cep->sock = NULL; + + cep->state = ERDMA_EPSTATE_CLOSED; + + if (cep->cm_id) { + cep->cm_id->rem_ref(id); + cep->cm_id = NULL; + } + + if (qp->cep) { + erdma_cep_put(cep); + qp->cep = NULL; + } + + cep->qp = NULL; + erdma_qp_put(qp); + + erdma_cep_set_free(cep); + erdma_cep_put(cep); + + return ret; +} + +int erdma_reject(struct iw_cm_id *id, const void *pdata, u8 plen) +{ + struct erdma_cep *cep = (struct erdma_cep *)id->provider_data; + + erdma_cep_set_inuse(cep); + erdma_cep_put(cep); + + erdma_cancel_mpatimer(cep); + + if (cep->state != ERDMA_EPSTATE_RECVD_MPAREQ) { + erdma_cep_set_free(cep); + erdma_cep_put(cep); + + return -ECONNRESET; + } + + if (__mpa_rr_revision(cep->mpa.hdr.params.bits) == MPA_REVISION_EXT_1) { + cep->mpa.hdr.params.bits |= MPA_RR_FLAG_REJECT; /* reject */ + erdma_send_mpareqrep(cep, pdata, plen); + } + + erdma_socket_disassoc(cep->sock); + sock_release(cep->sock); + cep->sock = NULL; + + cep->state = ERDMA_EPSTATE_CLOSED; + + erdma_cep_set_free(cep); + erdma_cep_put(cep); + + return 0; +} + +int erdma_create_listen(struct iw_cm_id *id, int backlog) +{ + struct socket *s; + struct erdma_cep *cep = NULL; + int ret = 0; + struct erdma_dev *dev = to_edev(id->device); + int addr_family = id->local_addr.ss_family; + struct sockaddr_in *laddr = &to_sockaddr_in(id->local_addr); + + if (addr_family != AF_INET) + return -EAFNOSUPPORT; + + ret = sock_create(addr_family, SOCK_STREAM, IPPROTO_TCP, &s); + if (ret < 0) + return ret; + + sock_set_reuseaddr(s->sk); + + /* For wildcard addr, limit binding to current device only */ + if (ipv4_is_zeronet(laddr->sin_addr.s_addr)) + s->sk->sk_bound_dev_if = dev->netdev->ifindex; + + ret = s->ops->bind(s, (struct sockaddr *)laddr, + sizeof(struct sockaddr_in)); + if (ret) + goto error; + + cep = erdma_cep_alloc(dev); + if (!cep) { + ret = -ENOMEM; + goto error; + } + erdma_cep_socket_assoc(cep, s); + + ret = erdma_cm_alloc_work(cep, backlog); + if (ret) + goto error; + + ret = s->ops->listen(s, backlog); + if (ret) + goto error; + + cep->cm_id = id; + id->add_ref(id); + + if (!id->provider_data) { + id->provider_data = + kmalloc(sizeof(struct list_head), GFP_KERNEL); + if (!id->provider_data) { + ret = -ENOMEM; + goto error; + } + INIT_LIST_HEAD((struct list_head *)id->provider_data); + } + + list_add_tail(&cep->listenq, (struct list_head *)id->provider_data); + cep->state = ERDMA_EPSTATE_LISTENING; + + return 0; + +error: + if (cep) { + erdma_cep_set_inuse(cep); + + if (cep->cm_id) { + cep->cm_id->rem_ref(cep->cm_id); + cep->cm_id = NULL; + } + cep->sock = NULL; + erdma_socket_disassoc(s); + cep->state = ERDMA_EPSTATE_CLOSED; + + erdma_cep_set_free(cep); + erdma_cep_put(cep); + } + sock_release(s); + + return ret; +} + +static void erdma_drop_listeners(struct iw_cm_id *id) +{ + struct list_head *p, *tmp; + /* + * In case of a wildcard rdma_listen on a multi-homed device, + * a listener's IWCM id is associated with more than one listening CEP. + */ + list_for_each_safe(p, tmp, (struct list_head *)id->provider_data) { + struct erdma_cep *cep = + list_entry(p, struct erdma_cep, listenq); + + list_del(p); + + erdma_cep_set_inuse(cep); + + if (cep->cm_id) { + cep->cm_id->rem_ref(cep->cm_id); + cep->cm_id = NULL; + } + if (cep->sock) { + erdma_socket_disassoc(cep->sock); + sock_release(cep->sock); + cep->sock = NULL; + } + cep->state = ERDMA_EPSTATE_CLOSED; + erdma_cep_set_free(cep); + erdma_cep_put(cep); + } +} + +int erdma_destroy_listen(struct iw_cm_id *id) +{ + if (!id->provider_data) + return 0; + + erdma_drop_listeners(id); + kfree(id->provider_data); + id->provider_data = NULL; + + return 0; +} + +int erdma_cm_init(void) +{ + erdma_cm_wq = create_singlethread_workqueue("erdma_cm_wq"); + if (!erdma_cm_wq) + return -ENOMEM; + + return 0; +} + +void erdma_cm_exit(void) +{ + if (erdma_cm_wq) + destroy_workqueue(erdma_cm_wq); +} diff --git a/drivers/infiniband/hw/erdma/erdma_cm.h b/drivers/infiniband/hw/erdma/erdma_cm.h new file mode 100644 index 000000000000..8a3f998fec9b --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_cm.h @@ -0,0 +1,167 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +/* Authors: Bernard Metzler */ +/* Greg Joyce */ +/* Copyright (c) 2008-2019, IBM Corporation */ +/* Copyright (c) 2017, Open Grid Computing, Inc. */ + +#ifndef __ERDMA_CM_H__ +#define __ERDMA_CM_H__ + +#include +#include +#include + +/* iWarp MPA protocol defs */ +#define MPA_REVISION_EXT_1 129 +#define MPA_MAX_PRIVDATA RDMA_MAX_PRIVATE_DATA +#define MPA_KEY_REQ "MPA ID Req Frame" +#define MPA_KEY_REP "MPA ID Rep Frame" +#define MPA_KEY_SIZE 16 +#define MPA_DEFAULT_HDR_LEN 28 + +struct mpa_rr_params { + __be16 bits; + __be16 pd_len; +}; + +/* + * MPA request/response Hdr bits & fields + */ +enum { + MPA_RR_FLAG_MARKERS = __cpu_to_be16(0x8000), + MPA_RR_FLAG_CRC = __cpu_to_be16(0x4000), + MPA_RR_FLAG_REJECT = __cpu_to_be16(0x2000), + MPA_RR_RESERVED = __cpu_to_be16(0x1f00), + MPA_RR_MASK_REVISION = __cpu_to_be16(0x00ff) +}; + +/* + * MPA request/reply header + */ +struct mpa_rr { + u8 key[16]; + struct mpa_rr_params params; +}; + +struct erdma_mpa_ext { + __be32 cookie; + __be32 bits; +}; + +enum { + MPA_EXT_FLAG_CC = cpu_to_be32(0x0000000f), +}; + +struct erdma_mpa_info { + struct mpa_rr hdr; /* peer mpa hdr in host byte order */ + struct erdma_mpa_ext ext_data; + char *pdata; + int bytes_rcvd; +}; + +struct erdma_sk_upcalls { + void (*sk_state_change)(struct sock *sk); + void (*sk_data_ready)(struct sock *sk, int bytes); + void (*sk_error_report)(struct sock *sk); +}; + +struct erdma_dev; + +enum erdma_cep_state { + ERDMA_EPSTATE_IDLE = 1, + ERDMA_EPSTATE_LISTENING, + ERDMA_EPSTATE_CONNECTING, + ERDMA_EPSTATE_AWAIT_MPAREQ, + ERDMA_EPSTATE_RECVD_MPAREQ, + ERDMA_EPSTATE_AWAIT_MPAREP, + ERDMA_EPSTATE_RDMA_MODE, + ERDMA_EPSTATE_CLOSED +}; + +struct erdma_cep { + struct iw_cm_id *cm_id; + struct erdma_dev *dev; + struct list_head devq; + spinlock_t lock; + struct kref ref; + int in_use; + wait_queue_head_t waitq; + enum erdma_cep_state state; + + struct list_head listenq; + struct erdma_cep *listen_cep; + + struct erdma_qp *qp; + struct socket *sock; + + struct erdma_cm_work *mpa_timer; + struct list_head work_freelist; + + struct erdma_mpa_info mpa; + int ord; + int ird; + + int pd_len; + /* hold user's private data. */ + void *private_data; + + /* Saved upcalls of socket llp.sock */ + void (*sk_state_change)(struct sock *sk); + void (*sk_data_ready)(struct sock *sk); + void (*sk_error_report)(struct sock *sk); +}; + +#define MPAREQ_TIMEOUT (HZ * 20) +#define MPAREP_TIMEOUT (HZ * 10) +#define CONNECT_TIMEOUT (HZ * 10) + +enum erdma_work_type { + ERDMA_CM_WORK_ACCEPT = 1, + ERDMA_CM_WORK_READ_MPAHDR, + ERDMA_CM_WORK_CLOSE_LLP, /* close socket */ + ERDMA_CM_WORK_PEER_CLOSE, /* socket indicated peer close */ + ERDMA_CM_WORK_MPATIMEOUT, + ERDMA_CM_WORK_CONNECTED, + ERDMA_CM_WORK_CONNECTTIMEOUT +}; + +struct erdma_cm_work { + struct delayed_work work; + struct list_head list; + enum erdma_work_type type; + struct erdma_cep *cep; +}; + +#define to_sockaddr_in(a) (*(struct sockaddr_in *)(&(a))) + +static inline int getname_peer(struct socket *s, struct sockaddr_storage *a) +{ + return s->ops->getname(s, (struct sockaddr *)a, 1); +} + +static inline int getname_local(struct socket *s, struct sockaddr_storage *a) +{ + return s->ops->getname(s, (struct sockaddr *)a, 0); +} + +int erdma_connect(struct iw_cm_id *id, struct iw_cm_conn_param *param); +int erdma_accept(struct iw_cm_id *id, struct iw_cm_conn_param *param); +int erdma_reject(struct iw_cm_id *id, const void *pdata, u8 plen); +int erdma_create_listen(struct iw_cm_id *id, int backlog); +int erdma_destroy_listen(struct iw_cm_id *id); + +void erdma_cep_get(struct erdma_cep *ceq); +void erdma_cep_put(struct erdma_cep *ceq); +int erdma_cm_queue_work(struct erdma_cep *ceq, enum erdma_work_type type); + +int erdma_cm_init(void); +void erdma_cm_exit(void); + +#define sk_to_cep(sk) ((struct erdma_cep *)((sk)->sk_user_data)) + +#endif diff --git a/drivers/infiniband/hw/erdma/erdma_cmdq.c b/drivers/infiniband/hw/erdma/erdma_cmdq.c new file mode 100644 index 000000000000..57da0c670472 --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_cmdq.c @@ -0,0 +1,493 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#include +#include +#include + +#include "erdma.h" +#include "erdma_hw.h" +#include "erdma_verbs.h" + +static void arm_cmdq_cq(struct erdma_cmdq *cmdq) +{ + struct erdma_dev *dev = container_of(cmdq, struct erdma_dev, cmdq); + u64 db_data = FIELD_PREP(ERDMA_CQDB_CI_MASK, cmdq->cq.ci) | + FIELD_PREP(ERDMA_CQDB_ARM_MASK, 1) | + FIELD_PREP(ERDMA_CQDB_CMDSN_MASK, cmdq->cq.cmdsn) | + FIELD_PREP(ERDMA_CQDB_IDX_MASK, cmdq->cq.cmdsn); + + *cmdq->cq.db_record = db_data; + writeq(db_data, dev->func_bar + ERDMA_CMDQ_CQDB_REG); + + atomic64_inc(&cmdq->cq.armed_num); +} + +static void kick_cmdq_db(struct erdma_cmdq *cmdq) +{ + struct erdma_dev *dev = container_of(cmdq, struct erdma_dev, cmdq); + u64 db_data = FIELD_PREP(ERDMA_CMD_HDR_WQEBB_INDEX_MASK, cmdq->sq.pi); + + *cmdq->sq.db_record = db_data; + writeq(db_data, dev->func_bar + ERDMA_CMDQ_SQDB_REG); +} + +static struct erdma_comp_wait *get_comp_wait(struct erdma_cmdq *cmdq) +{ + int comp_idx; + + spin_lock(&cmdq->lock); + comp_idx = find_first_zero_bit(cmdq->comp_wait_bitmap, + cmdq->max_outstandings); + if (comp_idx == cmdq->max_outstandings) { + spin_unlock(&cmdq->lock); + return ERR_PTR(-ENOMEM); + } + + __set_bit(comp_idx, cmdq->comp_wait_bitmap); + spin_unlock(&cmdq->lock); + + return &cmdq->wait_pool[comp_idx]; +} + +static void put_comp_wait(struct erdma_cmdq *cmdq, + struct erdma_comp_wait *comp_wait) +{ + int used; + + cmdq->wait_pool[comp_wait->ctx_id].cmd_status = ERDMA_CMD_STATUS_INIT; + spin_lock(&cmdq->lock); + used = __test_and_clear_bit(comp_wait->ctx_id, cmdq->comp_wait_bitmap); + spin_unlock(&cmdq->lock); + + WARN_ON(!used); +} + +static int erdma_cmdq_wait_res_init(struct erdma_dev *dev, + struct erdma_cmdq *cmdq) +{ + int i; + + cmdq->wait_pool = + devm_kcalloc(&dev->pdev->dev, cmdq->max_outstandings, + sizeof(struct erdma_comp_wait), GFP_KERNEL); + if (!cmdq->wait_pool) + return -ENOMEM; + + spin_lock_init(&cmdq->lock); + cmdq->comp_wait_bitmap = devm_bitmap_zalloc( + &dev->pdev->dev, cmdq->max_outstandings, GFP_KERNEL); + if (!cmdq->comp_wait_bitmap) + return -ENOMEM; + + for (i = 0; i < cmdq->max_outstandings; i++) { + init_completion(&cmdq->wait_pool[i].wait_event); + cmdq->wait_pool[i].ctx_id = i; + } + + return 0; +} + +static int erdma_cmdq_sq_init(struct erdma_dev *dev) +{ + struct erdma_cmdq *cmdq = &dev->cmdq; + struct erdma_cmdq_sq *sq = &cmdq->sq; + u32 buf_size; + + sq->wqebb_cnt = SQEBB_COUNT(ERDMA_CMDQ_SQE_SIZE); + sq->depth = cmdq->max_outstandings * sq->wqebb_cnt; + + buf_size = sq->depth << SQEBB_SHIFT; + + sq->qbuf = + dma_alloc_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(buf_size), + &sq->qbuf_dma_addr, GFP_KERNEL); + if (!sq->qbuf) + return -ENOMEM; + + sq->db_record = (u64 *)(sq->qbuf + buf_size); + + spin_lock_init(&sq->lock); + + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_SQ_ADDR_H_REG, + upper_32_bits(sq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_SQ_ADDR_L_REG, + lower_32_bits(sq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_DEPTH_REG, sq->depth); + erdma_reg_write64(dev, ERDMA_CMDQ_SQ_DB_HOST_ADDR_REG, + sq->qbuf_dma_addr + buf_size); + + return 0; +} + +static int erdma_cmdq_cq_init(struct erdma_dev *dev) +{ + struct erdma_cmdq *cmdq = &dev->cmdq; + struct erdma_cmdq_cq *cq = &cmdq->cq; + u32 buf_size; + + cq->depth = cmdq->sq.depth; + buf_size = cq->depth << CQE_SHIFT; + + cq->qbuf = + dma_alloc_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(buf_size), + &cq->qbuf_dma_addr, GFP_KERNEL | __GFP_ZERO); + if (!cq->qbuf) + return -ENOMEM; + + spin_lock_init(&cq->lock); + + cq->db_record = (u64 *)(cq->qbuf + buf_size); + + atomic64_set(&cq->armed_num, 0); + + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_CQ_ADDR_H_REG, + upper_32_bits(cq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_CQ_ADDR_L_REG, + lower_32_bits(cq->qbuf_dma_addr)); + erdma_reg_write64(dev, ERDMA_CMDQ_CQ_DB_HOST_ADDR_REG, + cq->qbuf_dma_addr + buf_size); + + return 0; +} + +static int erdma_cmdq_eq_init(struct erdma_dev *dev) +{ + struct erdma_cmdq *cmdq = &dev->cmdq; + struct erdma_eq *eq = &cmdq->eq; + u32 buf_size; + + eq->depth = cmdq->max_outstandings; + buf_size = eq->depth << EQE_SHIFT; + + eq->qbuf = + dma_alloc_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(buf_size), + &eq->qbuf_dma_addr, GFP_KERNEL | __GFP_ZERO); + if (!eq->qbuf) + return -ENOMEM; + + spin_lock_init(&eq->lock); + atomic64_set(&eq->event_num, 0); + + eq->db_addr = + (u64 __iomem *)(dev->func_bar + ERDMA_REGS_CEQ_DB_BASE_REG); + eq->db_record = (u64 *)(eq->qbuf + buf_size); + + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_EQ_ADDR_H_REG, + upper_32_bits(eq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_EQ_ADDR_L_REG, + lower_32_bits(eq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_CMDQ_EQ_DEPTH_REG, eq->depth); + erdma_reg_write64(dev, ERDMA_CMDQ_EQ_DB_HOST_ADDR_REG, + eq->qbuf_dma_addr + buf_size); + + return 0; +} + +int erdma_cmdq_init(struct erdma_dev *dev) +{ + int err, i; + struct erdma_cmdq *cmdq = &dev->cmdq; + u32 sts, ctrl; + + cmdq->max_outstandings = ERDMA_CMDQ_MAX_OUTSTANDING; + cmdq->use_event = false; + + sema_init(&cmdq->credits, cmdq->max_outstandings); + + err = erdma_cmdq_wait_res_init(dev, cmdq); + if (err) + return err; + + err = erdma_cmdq_sq_init(dev); + if (err) + return err; + + err = erdma_cmdq_cq_init(dev); + if (err) + goto err_destroy_sq; + + err = erdma_cmdq_eq_init(dev); + if (err) + goto err_destroy_cq; + + ctrl = FIELD_PREP(ERDMA_REG_DEV_CTRL_INIT_MASK, 1); + erdma_reg_write32(dev, ERDMA_REGS_DEV_CTRL_REG, ctrl); + + for (i = 0; i < ERDMA_WAIT_DEV_DONE_CNT; i++) { + sts = erdma_reg_read32_filed(dev, ERDMA_REGS_DEV_ST_REG, + ERDMA_REG_DEV_ST_INIT_DONE_MASK); + if (sts) + break; + + msleep(ERDMA_REG_ACCESS_WAIT_MS); + } + + if (i == ERDMA_WAIT_DEV_DONE_CNT) { + dev_err(&dev->pdev->dev, "wait init done failed.\n"); + err = -ETIMEDOUT; + goto err_destroy_eq; + } + + set_bit(ERDMA_CMDQ_STATE_OK_BIT, &cmdq->state); + + return 0; + +err_destroy_eq: + dma_free_coherent(&dev->pdev->dev, + (cmdq->eq.depth << EQE_SHIFT) + + ERDMA_EXTRA_BUFFER_SIZE, + cmdq->eq.qbuf, cmdq->eq.qbuf_dma_addr); + +err_destroy_cq: + dma_free_coherent(&dev->pdev->dev, + (cmdq->cq.depth << CQE_SHIFT) + + ERDMA_EXTRA_BUFFER_SIZE, + cmdq->cq.qbuf, cmdq->cq.qbuf_dma_addr); + +err_destroy_sq: + dma_free_coherent(&dev->pdev->dev, + (cmdq->sq.depth << SQEBB_SHIFT) + + ERDMA_EXTRA_BUFFER_SIZE, + cmdq->sq.qbuf, cmdq->sq.qbuf_dma_addr); + + return err; +} + +void erdma_finish_cmdq_init(struct erdma_dev *dev) +{ + /* after device init successfully, change cmdq to event mode. */ + dev->cmdq.use_event = true; + arm_cmdq_cq(&dev->cmdq); +} + +void erdma_cmdq_destroy(struct erdma_dev *dev) +{ + struct erdma_cmdq *cmdq = &dev->cmdq; + + clear_bit(ERDMA_CMDQ_STATE_OK_BIT, &cmdq->state); + + dma_free_coherent(&dev->pdev->dev, + (cmdq->eq.depth << EQE_SHIFT) + + ERDMA_EXTRA_BUFFER_SIZE, + cmdq->eq.qbuf, cmdq->eq.qbuf_dma_addr); + dma_free_coherent(&dev->pdev->dev, + (cmdq->sq.depth << SQEBB_SHIFT) + + ERDMA_EXTRA_BUFFER_SIZE, + cmdq->sq.qbuf, cmdq->sq.qbuf_dma_addr); + dma_free_coherent(&dev->pdev->dev, + (cmdq->cq.depth << CQE_SHIFT) + + ERDMA_EXTRA_BUFFER_SIZE, + cmdq->cq.qbuf, cmdq->cq.qbuf_dma_addr); +} + +static void *get_next_valid_cmdq_cqe(struct erdma_cmdq *cmdq) +{ + __be32 *cqe = get_queue_entry(cmdq->cq.qbuf, cmdq->cq.ci, + cmdq->cq.depth, CQE_SHIFT); + u32 owner = FIELD_GET(ERDMA_CQE_HDR_OWNER_MASK, + __be32_to_cpu(READ_ONCE(*cqe))); + + return owner ^ !!(cmdq->cq.ci & cmdq->cq.depth) ? cqe : NULL; +} + +static void push_cmdq_sqe(struct erdma_cmdq *cmdq, u64 *req, size_t req_len, + struct erdma_comp_wait *comp_wait) +{ + __le64 *wqe; + u64 hdr = *req; + + comp_wait->cmd_status = ERDMA_CMD_STATUS_ISSUED; + reinit_completion(&comp_wait->wait_event); + comp_wait->sq_pi = cmdq->sq.pi; + + wqe = get_queue_entry(cmdq->sq.qbuf, cmdq->sq.pi, cmdq->sq.depth, + SQEBB_SHIFT); + memcpy(wqe, req, req_len); + + cmdq->sq.pi += cmdq->sq.wqebb_cnt; + hdr |= FIELD_PREP(ERDMA_CMD_HDR_WQEBB_INDEX_MASK, cmdq->sq.pi) | + FIELD_PREP(ERDMA_CMD_HDR_CONTEXT_COOKIE_MASK, + comp_wait->ctx_id) | + FIELD_PREP(ERDMA_CMD_HDR_WQEBB_CNT_MASK, cmdq->sq.wqebb_cnt - 1); + *wqe = cpu_to_le64(hdr); + + kick_cmdq_db(cmdq); +} + +static int erdma_poll_single_cmd_completion(struct erdma_cmdq *cmdq) +{ + struct erdma_comp_wait *comp_wait; + u32 hdr0, sqe_idx; + __be32 *cqe; + u16 ctx_id; + u64 *sqe; + int i; + + cqe = get_next_valid_cmdq_cqe(cmdq); + if (!cqe) + return -EAGAIN; + + cmdq->cq.ci++; + + dma_rmb(); + hdr0 = __be32_to_cpu(*cqe); + sqe_idx = __be32_to_cpu(*(cqe + 1)); + + sqe = get_queue_entry(cmdq->sq.qbuf, sqe_idx, cmdq->sq.depth, + SQEBB_SHIFT); + ctx_id = FIELD_GET(ERDMA_CMD_HDR_CONTEXT_COOKIE_MASK, *sqe); + comp_wait = &cmdq->wait_pool[ctx_id]; + if (comp_wait->cmd_status != ERDMA_CMD_STATUS_ISSUED) + return -EIO; + + comp_wait->cmd_status = ERDMA_CMD_STATUS_FINISHED; + comp_wait->comp_status = FIELD_GET(ERDMA_CQE_HDR_SYNDROME_MASK, hdr0); + cmdq->sq.ci += cmdq->sq.wqebb_cnt; + + for (i = 0; i < 4; i++) + comp_wait->comp_data[i] = __be32_to_cpu(*(cqe + 2 + i)); + + if (cmdq->use_event) + complete(&comp_wait->wait_event); + + return 0; +} + +static void erdma_polling_cmd_completions(struct erdma_cmdq *cmdq) +{ + unsigned long flags; + u16 comp_num; + + spin_lock_irqsave(&cmdq->cq.lock, flags); + + /* We must have less than # of max_outstandings + * completions at one time. + */ + for (comp_num = 0; comp_num < cmdq->max_outstandings; comp_num++) + if (erdma_poll_single_cmd_completion(cmdq)) + break; + + if (comp_num && cmdq->use_event) + arm_cmdq_cq(cmdq); + + spin_unlock_irqrestore(&cmdq->cq.lock, flags); +} + +void erdma_cmdq_completion_handler(struct erdma_cmdq *cmdq) +{ + int got_event = 0; + + if (!test_bit(ERDMA_CMDQ_STATE_OK_BIT, &cmdq->state) || + !cmdq->use_event) + return; + + while (get_next_valid_eqe(&cmdq->eq)) { + cmdq->eq.ci++; + got_event++; + } + + if (got_event) { + cmdq->cq.cmdsn++; + erdma_polling_cmd_completions(cmdq); + } + + notify_eq(&cmdq->eq); +} + +static int erdma_poll_cmd_completion(struct erdma_comp_wait *comp_ctx, + struct erdma_cmdq *cmdq, u32 timeout) +{ + unsigned long comp_timeout = jiffies + msecs_to_jiffies(timeout); + + while (1) { + erdma_polling_cmd_completions(cmdq); + if (comp_ctx->cmd_status != ERDMA_CMD_STATUS_ISSUED) + break; + + if (time_is_before_jiffies(comp_timeout)) + return -ETIME; + + msleep(20); + } + + return 0; +} + +static int erdma_wait_cmd_completion(struct erdma_comp_wait *comp_ctx, + struct erdma_cmdq *cmdq, u32 timeout) +{ + unsigned long flags = 0; + + wait_for_completion_timeout(&comp_ctx->wait_event, + msecs_to_jiffies(timeout)); + + if (unlikely(comp_ctx->cmd_status != ERDMA_CMD_STATUS_FINISHED)) { + spin_lock_irqsave(&cmdq->cq.lock, flags); + comp_ctx->cmd_status = ERDMA_CMD_STATUS_TIMEOUT; + spin_unlock_irqrestore(&cmdq->cq.lock, flags); + return -ETIME; + } + + return 0; +} + +void erdma_cmdq_build_reqhdr(u64 *hdr, u32 mod, u32 op) +{ + *hdr = FIELD_PREP(ERDMA_CMD_HDR_SUB_MOD_MASK, mod) | + FIELD_PREP(ERDMA_CMD_HDR_OPCODE_MASK, op); +} + +int erdma_post_cmd_wait(struct erdma_cmdq *cmdq, u64 *req, u32 req_size, + u64 *resp0, u64 *resp1) +{ + struct erdma_comp_wait *comp_wait; + int ret; + + if (!test_bit(ERDMA_CMDQ_STATE_OK_BIT, &cmdq->state)) + return -ENODEV; + + down(&cmdq->credits); + + comp_wait = get_comp_wait(cmdq); + if (IS_ERR(comp_wait)) { + clear_bit(ERDMA_CMDQ_STATE_OK_BIT, &cmdq->state); + set_bit(ERDMA_CMDQ_STATE_CTX_ERR_BIT, &cmdq->state); + up(&cmdq->credits); + return PTR_ERR(comp_wait); + } + + spin_lock(&cmdq->sq.lock); + push_cmdq_sqe(cmdq, req, req_size, comp_wait); + spin_unlock(&cmdq->sq.lock); + + if (cmdq->use_event) + ret = erdma_wait_cmd_completion(comp_wait, cmdq, + ERDMA_CMDQ_TIMEOUT_MS); + else + ret = erdma_poll_cmd_completion(comp_wait, cmdq, + ERDMA_CMDQ_TIMEOUT_MS); + + if (ret) { + set_bit(ERDMA_CMDQ_STATE_TIMEOUT_BIT, &cmdq->state); + clear_bit(ERDMA_CMDQ_STATE_OK_BIT, &cmdq->state); + goto out; + } + + if (comp_wait->comp_status) + ret = -EIO; + + if (resp0 && resp1) { + *resp0 = *((u64 *)&comp_wait->comp_data[0]); + *resp1 = *((u64 *)&comp_wait->comp_data[2]); + } + put_comp_wait(cmdq, comp_wait); + +out: + up(&cmdq->credits); + + return ret; +} diff --git a/drivers/infiniband/hw/erdma/erdma_cq.c b/drivers/infiniband/hw/erdma/erdma_cq.c new file mode 100644 index 000000000000..751c7f9f0de7 --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_cq.c @@ -0,0 +1,205 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#include + +#include "erdma_hw.h" +#include "erdma_verbs.h" + +static void *get_next_valid_cqe(struct erdma_cq *cq) +{ + __be32 *cqe = get_queue_entry(cq->kern_cq.qbuf, cq->kern_cq.ci, + cq->depth, CQE_SHIFT); + u32 owner = FIELD_GET(ERDMA_CQE_HDR_OWNER_MASK, + __be32_to_cpu(READ_ONCE(*cqe))); + + return owner ^ !!(cq->kern_cq.ci & cq->depth) ? cqe : NULL; +} + +static void notify_cq(struct erdma_cq *cq, u8 solcitied) +{ + u64 db_data = + FIELD_PREP(ERDMA_CQDB_IDX_MASK, (cq->kern_cq.notify_cnt)) | + FIELD_PREP(ERDMA_CQDB_CQN_MASK, cq->cqn) | + FIELD_PREP(ERDMA_CQDB_ARM_MASK, 1) | + FIELD_PREP(ERDMA_CQDB_SOL_MASK, solcitied) | + FIELD_PREP(ERDMA_CQDB_CMDSN_MASK, cq->kern_cq.cmdsn) | + FIELD_PREP(ERDMA_CQDB_CI_MASK, cq->kern_cq.ci); + + *cq->kern_cq.db_record = db_data; + writeq(db_data, cq->kern_cq.db); +} + +int erdma_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags) +{ + struct erdma_cq *cq = to_ecq(ibcq); + unsigned long irq_flags; + int ret = 0; + + spin_lock_irqsave(&cq->kern_cq.lock, irq_flags); + + notify_cq(cq, (flags & IB_CQ_SOLICITED_MASK) == IB_CQ_SOLICITED); + + if ((flags & IB_CQ_REPORT_MISSED_EVENTS) && get_next_valid_cqe(cq)) + ret = 1; + + cq->kern_cq.notify_cnt++; + + spin_unlock_irqrestore(&cq->kern_cq.lock, irq_flags); + + return ret; +} + +static const enum ib_wc_opcode wc_mapping_table[ERDMA_NUM_OPCODES] = { + [ERDMA_OP_WRITE] = IB_WC_RDMA_WRITE, + [ERDMA_OP_READ] = IB_WC_RDMA_READ, + [ERDMA_OP_SEND] = IB_WC_SEND, + [ERDMA_OP_SEND_WITH_IMM] = IB_WC_SEND, + [ERDMA_OP_RECEIVE] = IB_WC_RECV, + [ERDMA_OP_RECV_IMM] = IB_WC_RECV_RDMA_WITH_IMM, + [ERDMA_OP_RECV_INV] = IB_WC_RECV, + [ERDMA_OP_WRITE_WITH_IMM] = IB_WC_RDMA_WRITE, + [ERDMA_OP_INVALIDATE] = IB_WC_LOCAL_INV, + [ERDMA_OP_RSP_SEND_IMM] = IB_WC_RECV, + [ERDMA_OP_SEND_WITH_INV] = IB_WC_SEND, + [ERDMA_OP_REG_MR] = IB_WC_REG_MR, + [ERDMA_OP_LOCAL_INV] = IB_WC_LOCAL_INV, + [ERDMA_OP_READ_WITH_INV] = IB_WC_RDMA_READ, +}; + +static const struct { + enum erdma_wc_status erdma; + enum ib_wc_status base; + enum erdma_vendor_err vendor; +} map_cqe_status[ERDMA_NUM_WC_STATUS] = { + { ERDMA_WC_SUCCESS, IB_WC_SUCCESS, ERDMA_WC_VENDOR_NO_ERR }, + { ERDMA_WC_GENERAL_ERR, IB_WC_GENERAL_ERR, ERDMA_WC_VENDOR_NO_ERR }, + { ERDMA_WC_RECV_WQE_FORMAT_ERR, IB_WC_GENERAL_ERR, + ERDMA_WC_VENDOR_INVALID_RQE }, + { ERDMA_WC_RECV_STAG_INVALID_ERR, IB_WC_REM_ACCESS_ERR, + ERDMA_WC_VENDOR_RQE_INVALID_STAG }, + { ERDMA_WC_RECV_ADDR_VIOLATION_ERR, IB_WC_REM_ACCESS_ERR, + ERDMA_WC_VENDOR_RQE_ADDR_VIOLATION }, + { ERDMA_WC_RECV_RIGHT_VIOLATION_ERR, IB_WC_REM_ACCESS_ERR, + ERDMA_WC_VENDOR_RQE_ACCESS_RIGHT_ERR }, + { ERDMA_WC_RECV_PDID_ERR, IB_WC_REM_ACCESS_ERR, + ERDMA_WC_VENDOR_RQE_INVALID_PD }, + { ERDMA_WC_RECV_WARRPING_ERR, IB_WC_REM_ACCESS_ERR, + ERDMA_WC_VENDOR_RQE_WRAP_ERR }, + { ERDMA_WC_SEND_WQE_FORMAT_ERR, IB_WC_LOC_QP_OP_ERR, + ERDMA_WC_VENDOR_INVALID_SQE }, + { ERDMA_WC_SEND_WQE_ORD_EXCEED, IB_WC_GENERAL_ERR, + ERDMA_WC_VENDOR_ZERO_ORD }, + { ERDMA_WC_SEND_STAG_INVALID_ERR, IB_WC_LOC_ACCESS_ERR, + ERDMA_WC_VENDOR_SQE_INVALID_STAG }, + { ERDMA_WC_SEND_ADDR_VIOLATION_ERR, IB_WC_LOC_ACCESS_ERR, + ERDMA_WC_VENDOR_SQE_ADDR_VIOLATION }, + { ERDMA_WC_SEND_RIGHT_VIOLATION_ERR, IB_WC_LOC_ACCESS_ERR, + ERDMA_WC_VENDOR_SQE_ACCESS_ERR }, + { ERDMA_WC_SEND_PDID_ERR, IB_WC_LOC_ACCESS_ERR, + ERDMA_WC_VENDOR_SQE_INVALID_PD }, + { ERDMA_WC_SEND_WARRPING_ERR, IB_WC_LOC_ACCESS_ERR, + ERDMA_WC_VENDOR_SQE_WARP_ERR }, + { ERDMA_WC_FLUSH_ERR, IB_WC_WR_FLUSH_ERR, ERDMA_WC_VENDOR_NO_ERR }, + { ERDMA_WC_RETRY_EXC_ERR, IB_WC_RETRY_EXC_ERR, ERDMA_WC_VENDOR_NO_ERR }, +}; + +#define ERDMA_POLLCQ_NO_QP 1 + +static int erdma_poll_one_cqe(struct erdma_cq *cq, struct ib_wc *wc) +{ + struct erdma_dev *dev = to_edev(cq->ibcq.device); + u8 opcode, syndrome, qtype; + struct erdma_kqp *kern_qp; + struct erdma_cqe *cqe; + struct erdma_qp *qp; + u16 wqe_idx, depth; + u32 qpn, cqe_hdr; + u64 *id_table; + u64 *wqe_hdr; + + cqe = get_next_valid_cqe(cq); + if (!cqe) + return -EAGAIN; + + cq->kern_cq.ci++; + + /* cqbuf should be ready when we poll */ + dma_rmb(); + + qpn = be32_to_cpu(cqe->qpn); + wqe_idx = be32_to_cpu(cqe->qe_idx); + cqe_hdr = be32_to_cpu(cqe->hdr); + + qp = find_qp_by_qpn(dev, qpn); + if (!qp) + return ERDMA_POLLCQ_NO_QP; + + kern_qp = &qp->kern_qp; + + qtype = FIELD_GET(ERDMA_CQE_HDR_QTYPE_MASK, cqe_hdr); + syndrome = FIELD_GET(ERDMA_CQE_HDR_SYNDROME_MASK, cqe_hdr); + opcode = FIELD_GET(ERDMA_CQE_HDR_OPCODE_MASK, cqe_hdr); + + if (qtype == ERDMA_CQE_QTYPE_SQ) { + id_table = kern_qp->swr_tbl; + depth = qp->attrs.sq_size; + wqe_hdr = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, + qp->attrs.sq_size, SQEBB_SHIFT); + kern_qp->sq_ci = + FIELD_GET(ERDMA_SQE_HDR_WQEBB_CNT_MASK, *wqe_hdr) + + wqe_idx + 1; + } else { + id_table = kern_qp->rwr_tbl; + depth = qp->attrs.rq_size; + } + wc->wr_id = id_table[wqe_idx & (depth - 1)]; + wc->byte_len = be32_to_cpu(cqe->size); + + wc->wc_flags = 0; + + wc->opcode = wc_mapping_table[opcode]; + if (opcode == ERDMA_OP_RECV_IMM || opcode == ERDMA_OP_RSP_SEND_IMM) { + wc->ex.imm_data = cpu_to_be32(le32_to_cpu(cqe->imm_data)); + wc->wc_flags |= IB_WC_WITH_IMM; + } else if (opcode == ERDMA_OP_RECV_INV) { + wc->ex.invalidate_rkey = be32_to_cpu(cqe->inv_rkey); + wc->wc_flags |= IB_WC_WITH_INVALIDATE; + } + + if (syndrome >= ERDMA_NUM_WC_STATUS) + syndrome = ERDMA_WC_GENERAL_ERR; + + wc->status = map_cqe_status[syndrome].base; + wc->vendor_err = map_cqe_status[syndrome].vendor; + wc->qp = &qp->ibqp; + + return 0; +} + +int erdma_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc) +{ + struct erdma_cq *cq = to_ecq(ibcq); + unsigned long flags; + int npolled, ret; + + spin_lock_irqsave(&cq->kern_cq.lock, flags); + + for (npolled = 0; npolled < num_entries;) { + ret = erdma_poll_one_cqe(cq, wc + npolled); + + if (ret == -EAGAIN) /* no received new CQEs. */ + break; + else if (ret) /* ignore invalid CQEs. */ + continue; + + npolled++; + } + + spin_unlock_irqrestore(&cq->kern_cq.lock, flags); + + return npolled; +} diff --git a/drivers/infiniband/hw/erdma/erdma_eq.c b/drivers/infiniband/hw/erdma/erdma_eq.c new file mode 100644 index 000000000000..8f2d094e0227 --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_eq.c @@ -0,0 +1,329 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#include +#include +#include + +#include "erdma.h" +#include "erdma_hw.h" +#include "erdma_verbs.h" + +#define MAX_POLL_CHUNK_SIZE 16 + +void notify_eq(struct erdma_eq *eq) +{ + u64 db_data = FIELD_PREP(ERDMA_EQDB_CI_MASK, eq->ci) | + FIELD_PREP(ERDMA_EQDB_ARM_MASK, 1); + + *eq->db_record = db_data; + writeq(db_data, eq->db_addr); + + atomic64_inc(&eq->notify_num); +} + +void *get_next_valid_eqe(struct erdma_eq *eq) +{ + u64 *eqe = get_queue_entry(eq->qbuf, eq->ci, eq->depth, EQE_SHIFT); + u32 owner = FIELD_GET(ERDMA_CEQE_HDR_O_MASK, READ_ONCE(*eqe)); + + return owner ^ !!(eq->ci & eq->depth) ? eqe : NULL; +} + +void erdma_aeq_event_handler(struct erdma_dev *dev) +{ + struct erdma_aeqe *aeqe; + u32 cqn, qpn; + struct erdma_qp *qp; + struct erdma_cq *cq; + struct ib_event event; + u32 poll_cnt = 0; + + memset(&event, 0, sizeof(event)); + + while (poll_cnt < MAX_POLL_CHUNK_SIZE) { + aeqe = get_next_valid_eqe(&dev->aeq); + if (!aeqe) + break; + + dma_rmb(); + + dev->aeq.ci++; + atomic64_inc(&dev->aeq.event_num); + poll_cnt++; + + if (FIELD_GET(ERDMA_AEQE_HDR_TYPE_MASK, + le32_to_cpu(aeqe->hdr)) == ERDMA_AE_TYPE_CQ_ERR) { + cqn = le32_to_cpu(aeqe->event_data0); + cq = find_cq_by_cqn(dev, cqn); + if (!cq) + continue; + + event.device = cq->ibcq.device; + event.element.cq = &cq->ibcq; + event.event = IB_EVENT_CQ_ERR; + if (cq->ibcq.event_handler) + cq->ibcq.event_handler(&event, + cq->ibcq.cq_context); + } else { + qpn = le32_to_cpu(aeqe->event_data0); + qp = find_qp_by_qpn(dev, qpn); + if (!qp) + continue; + + event.device = qp->ibqp.device; + event.element.qp = &qp->ibqp; + event.event = IB_EVENT_QP_FATAL; + if (qp->ibqp.event_handler) + qp->ibqp.event_handler(&event, + qp->ibqp.qp_context); + } + } + + notify_eq(&dev->aeq); +} + +int erdma_aeq_init(struct erdma_dev *dev) +{ + struct erdma_eq *eq = &dev->aeq; + u32 buf_size; + + eq->depth = ERDMA_DEFAULT_EQ_DEPTH; + buf_size = eq->depth << EQE_SHIFT; + + eq->qbuf = + dma_alloc_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(buf_size), + &eq->qbuf_dma_addr, GFP_KERNEL | __GFP_ZERO); + if (!eq->qbuf) + return -ENOMEM; + + spin_lock_init(&eq->lock); + atomic64_set(&eq->event_num, 0); + atomic64_set(&eq->notify_num, 0); + + eq->db_addr = (u64 __iomem *)(dev->func_bar + ERDMA_REGS_AEQ_DB_REG); + eq->db_record = (u64 *)(eq->qbuf + buf_size); + + erdma_reg_write32(dev, ERDMA_REGS_AEQ_ADDR_H_REG, + upper_32_bits(eq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_AEQ_ADDR_L_REG, + lower_32_bits(eq->qbuf_dma_addr)); + erdma_reg_write32(dev, ERDMA_REGS_AEQ_DEPTH_REG, eq->depth); + erdma_reg_write64(dev, ERDMA_AEQ_DB_HOST_ADDR_REG, + eq->qbuf_dma_addr + buf_size); + + return 0; +} + +void erdma_aeq_destroy(struct erdma_dev *dev) +{ + struct erdma_eq *eq = &dev->aeq; + + dma_free_coherent(&dev->pdev->dev, + WARPPED_BUFSIZE(eq->depth << EQE_SHIFT), eq->qbuf, + eq->qbuf_dma_addr); +} + +void erdma_ceq_completion_handler(struct erdma_eq_cb *ceq_cb) +{ + struct erdma_dev *dev = ceq_cb->dev; + struct erdma_cq *cq; + u32 poll_cnt = 0; + u64 *ceqe; + int cqn; + + if (!ceq_cb->ready) + return; + + while (poll_cnt < MAX_POLL_CHUNK_SIZE) { + ceqe = get_next_valid_eqe(&ceq_cb->eq); + if (!ceqe) + break; + + dma_rmb(); + ceq_cb->eq.ci++; + poll_cnt++; + cqn = FIELD_GET(ERDMA_CEQE_HDR_CQN_MASK, READ_ONCE(*ceqe)); + + cq = find_cq_by_cqn(dev, cqn); + if (!cq) + continue; + + if (rdma_is_kernel_res(&cq->ibcq.res)) + cq->kern_cq.cmdsn++; + + if (cq->ibcq.comp_handler) + cq->ibcq.comp_handler(&cq->ibcq, cq->ibcq.cq_context); + } + + notify_eq(&ceq_cb->eq); +} + +static irqreturn_t erdma_intr_ceq_handler(int irq, void *data) +{ + struct erdma_eq_cb *ceq_cb = data; + + tasklet_schedule(&ceq_cb->tasklet); + + return IRQ_HANDLED; +} + +static void erdma_intr_ceq_task(unsigned long data) +{ + erdma_ceq_completion_handler((struct erdma_eq_cb *)data); +} + +static int erdma_set_ceq_irq(struct erdma_dev *dev, u16 ceqn) +{ + struct erdma_eq_cb *eqc = &dev->ceqs[ceqn]; + int err; + + snprintf(eqc->irq.name, ERDMA_IRQNAME_SIZE, "erdma-ceq%u@pci:%s", ceqn, + pci_name(dev->pdev)); + eqc->irq.msix_vector = pci_irq_vector(dev->pdev, ceqn + 1); + + tasklet_init(&dev->ceqs[ceqn].tasklet, erdma_intr_ceq_task, + (unsigned long)&dev->ceqs[ceqn]); + + cpumask_set_cpu(cpumask_local_spread(ceqn + 1, dev->attrs.numa_node), + &eqc->irq.affinity_hint_mask); + + err = request_irq(eqc->irq.msix_vector, erdma_intr_ceq_handler, 0, + eqc->irq.name, eqc); + if (err) { + dev_err(&dev->pdev->dev, "failed to request_irq(%d)\n", err); + return err; + } + + irq_set_affinity_hint(eqc->irq.msix_vector, + &eqc->irq.affinity_hint_mask); + + return 0; +} + +static void erdma_free_ceq_irq(struct erdma_dev *dev, u16 ceqn) +{ + struct erdma_eq_cb *eqc = &dev->ceqs[ceqn]; + + irq_set_affinity_hint(eqc->irq.msix_vector, NULL); + free_irq(eqc->irq.msix_vector, eqc); +} + +static int create_eq_cmd(struct erdma_dev *dev, u32 eqn, struct erdma_eq *eq) +{ + struct erdma_cmdq_create_eq_req req; + dma_addr_t db_info_dma_addr; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_COMMON, + CMDQ_OPCODE_CREATE_EQ); + req.eqn = eqn; + req.depth = ilog2(eq->depth); + req.qbuf_addr = eq->qbuf_dma_addr; + req.qtype = ERDMA_EQ_TYPE_CEQ; + /* Vector index is the same as EQN. */ + req.vector_idx = eqn; + db_info_dma_addr = eq->qbuf_dma_addr + (eq->depth << EQE_SHIFT); + req.db_dma_addr_l = lower_32_bits(db_info_dma_addr); + req.db_dma_addr_h = upper_32_bits(db_info_dma_addr); + + return erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, + sizeof(struct erdma_cmdq_create_eq_req), + NULL, NULL); +} + +static int erdma_ceq_init_one(struct erdma_dev *dev, u16 ceqn) +{ + struct erdma_eq *eq = &dev->ceqs[ceqn].eq; + u32 buf_size = ERDMA_DEFAULT_EQ_DEPTH << EQE_SHIFT; + int ret; + + eq->qbuf = + dma_alloc_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(buf_size), + &eq->qbuf_dma_addr, GFP_KERNEL | __GFP_ZERO); + if (!eq->qbuf) + return -ENOMEM; + + spin_lock_init(&eq->lock); + atomic64_set(&eq->event_num, 0); + atomic64_set(&eq->notify_num, 0); + + eq->depth = ERDMA_DEFAULT_EQ_DEPTH; + eq->db_addr = + (u64 __iomem *)(dev->func_bar + ERDMA_REGS_CEQ_DB_BASE_REG + + (ceqn + 1) * ERDMA_DB_SIZE); + eq->db_record = (u64 *)(eq->qbuf + buf_size); + eq->ci = 0; + dev->ceqs[ceqn].dev = dev; + + /* CEQ indexed from 1, 0 rsvd for CMDQ-EQ. */ + ret = create_eq_cmd(dev, ceqn + 1, eq); + dev->ceqs[ceqn].ready = ret ? false : true; + + return ret; +} + +static void erdma_ceq_uninit_one(struct erdma_dev *dev, u16 ceqn) +{ + struct erdma_eq *eq = &dev->ceqs[ceqn].eq; + u32 buf_size = ERDMA_DEFAULT_EQ_DEPTH << EQE_SHIFT; + struct erdma_cmdq_destroy_eq_req req; + int err; + + dev->ceqs[ceqn].ready = 0; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_COMMON, + CMDQ_OPCODE_DESTROY_EQ); + /* CEQ indexed from 1, 0 rsvd for CMDQ-EQ. */ + req.eqn = ceqn + 1; + req.qtype = ERDMA_EQ_TYPE_CEQ; + req.vector_idx = ceqn + 1; + + err = erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); + if (err) + return; + + dma_free_coherent(&dev->pdev->dev, WARPPED_BUFSIZE(buf_size), eq->qbuf, + eq->qbuf_dma_addr); +} + +int erdma_ceqs_init(struct erdma_dev *dev) +{ + u32 i, j; + int err; + + for (i = 0; i < dev->attrs.irq_num - 1; i++) { + err = erdma_ceq_init_one(dev, i); + if (err) + goto out_err; + + err = erdma_set_ceq_irq(dev, i); + if (err) { + erdma_ceq_uninit_one(dev, i); + goto out_err; + } + } + + return 0; + +out_err: + for (j = 0; j < i; j++) { + erdma_free_ceq_irq(dev, j); + erdma_ceq_uninit_one(dev, j); + } + + return err; +} + +void erdma_ceqs_uninit(struct erdma_dev *dev) +{ + u32 i; + + for (i = 0; i < dev->attrs.irq_num - 1; i++) { + erdma_free_ceq_irq(dev, i); + erdma_ceq_uninit_one(dev, i); + } +} diff --git a/drivers/infiniband/hw/erdma/erdma_hw.h b/drivers/infiniband/hw/erdma/erdma_hw.h new file mode 100644 index 000000000000..b210c49c669f --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_hw.h @@ -0,0 +1,508 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#ifndef __ERDMA_HW_H__ +#define __ERDMA_HW_H__ + +#include +#include + +/* PCIe device related definition. */ +#define PCI_VENDOR_ID_ALIBABA 0x1ded + +#define ERDMA_PCI_WIDTH 64 +#define ERDMA_FUNC_BAR 0 +#define ERDMA_MISX_BAR 2 + +#define ERDMA_BAR_MASK (BIT(ERDMA_FUNC_BAR) | BIT(ERDMA_MISX_BAR)) + +/* MSI-X related. */ +#define ERDMA_NUM_MSIX_VEC 32U +#define ERDMA_MSIX_VECTOR_CMDQ 0 + +/* PCIe Bar0 Registers. */ +#define ERDMA_REGS_VERSION_REG 0x0 +#define ERDMA_REGS_DEV_CTRL_REG 0x10 +#define ERDMA_REGS_DEV_ST_REG 0x14 +#define ERDMA_REGS_NETDEV_MAC_L_REG 0x18 +#define ERDMA_REGS_NETDEV_MAC_H_REG 0x1C +#define ERDMA_REGS_CMDQ_SQ_ADDR_L_REG 0x20 +#define ERDMA_REGS_CMDQ_SQ_ADDR_H_REG 0x24 +#define ERDMA_REGS_CMDQ_CQ_ADDR_L_REG 0x28 +#define ERDMA_REGS_CMDQ_CQ_ADDR_H_REG 0x2C +#define ERDMA_REGS_CMDQ_DEPTH_REG 0x30 +#define ERDMA_REGS_CMDQ_EQ_DEPTH_REG 0x34 +#define ERDMA_REGS_CMDQ_EQ_ADDR_L_REG 0x38 +#define ERDMA_REGS_CMDQ_EQ_ADDR_H_REG 0x3C +#define ERDMA_REGS_AEQ_ADDR_L_REG 0x40 +#define ERDMA_REGS_AEQ_ADDR_H_REG 0x44 +#define ERDMA_REGS_AEQ_DEPTH_REG 0x48 +#define ERDMA_REGS_GRP_NUM_REG 0x4c +#define ERDMA_REGS_AEQ_DB_REG 0x50 +#define ERDMA_CMDQ_SQ_DB_HOST_ADDR_REG 0x60 +#define ERDMA_CMDQ_CQ_DB_HOST_ADDR_REG 0x68 +#define ERDMA_CMDQ_EQ_DB_HOST_ADDR_REG 0x70 +#define ERDMA_AEQ_DB_HOST_ADDR_REG 0x78 +#define ERDMA_REGS_STATS_TSO_IN_PKTS_REG 0x80 +#define ERDMA_REGS_STATS_TSO_OUT_PKTS_REG 0x88 +#define ERDMA_REGS_STATS_TSO_OUT_BYTES_REG 0x90 +#define ERDMA_REGS_STATS_TX_DROP_PKTS_REG 0x98 +#define ERDMA_REGS_STATS_TX_BPS_METER_DROP_PKTS_REG 0xa0 +#define ERDMA_REGS_STATS_TX_PPS_METER_DROP_PKTS_REG 0xa8 +#define ERDMA_REGS_STATS_RX_PKTS_REG 0xc0 +#define ERDMA_REGS_STATS_RX_BYTES_REG 0xc8 +#define ERDMA_REGS_STATS_RX_DROP_PKTS_REG 0xd0 +#define ERDMA_REGS_STATS_RX_BPS_METER_DROP_PKTS_REG 0xd8 +#define ERDMA_REGS_STATS_RX_PPS_METER_DROP_PKTS_REG 0xe0 +#define ERDMA_REGS_CEQ_DB_BASE_REG 0x100 +#define ERDMA_CMDQ_SQDB_REG 0x200 +#define ERDMA_CMDQ_CQDB_REG 0x300 + +/* DEV_CTRL_REG details. */ +#define ERDMA_REG_DEV_CTRL_RESET_MASK 0x00000001 +#define ERDMA_REG_DEV_CTRL_INIT_MASK 0x00000002 + +/* DEV_ST_REG details. */ +#define ERDMA_REG_DEV_ST_RESET_DONE_MASK 0x00000001U +#define ERDMA_REG_DEV_ST_INIT_DONE_MASK 0x00000002U + +/* eRDMA PCIe DBs definition. */ +#define ERDMA_BAR_DB_SPACE_BASE 4096 + +#define ERDMA_BAR_SQDB_SPACE_OFFSET ERDMA_BAR_DB_SPACE_BASE +#define ERDMA_BAR_SQDB_SPACE_SIZE (384 * 1024) + +#define ERDMA_BAR_RQDB_SPACE_OFFSET \ + (ERDMA_BAR_SQDB_SPACE_OFFSET + ERDMA_BAR_SQDB_SPACE_SIZE) +#define ERDMA_BAR_RQDB_SPACE_SIZE (96 * 1024) + +#define ERDMA_BAR_CQDB_SPACE_OFFSET \ + (ERDMA_BAR_RQDB_SPACE_OFFSET + ERDMA_BAR_RQDB_SPACE_SIZE) + +/* Doorbell page resources related. */ +/* + * Max # of parallelly issued directSQE is 3072 per device, + * hardware organizes this into 24 group, per group has 128 credits. + */ +#define ERDMA_DWQE_MAX_GRP_CNT 24 +#define ERDMA_DWQE_NUM_PER_GRP 128 + +#define ERDMA_DWQE_TYPE0_CNT 64 +#define ERDMA_DWQE_TYPE1_CNT 496 +/* type1 DB contains 2 DBs, takes 256Byte. */ +#define ERDMA_DWQE_TYPE1_CNT_PER_PAGE 16 + +#define ERDMA_SDB_SHARED_PAGE_INDEX 95 + +/* Doorbell related. */ +#define ERDMA_DB_SIZE 8 + +#define ERDMA_CQDB_IDX_MASK GENMASK_ULL(63, 56) +#define ERDMA_CQDB_CQN_MASK GENMASK_ULL(55, 32) +#define ERDMA_CQDB_ARM_MASK BIT_ULL(31) +#define ERDMA_CQDB_SOL_MASK BIT_ULL(30) +#define ERDMA_CQDB_CMDSN_MASK GENMASK_ULL(29, 28) +#define ERDMA_CQDB_CI_MASK GENMASK_ULL(23, 0) + +#define ERDMA_EQDB_ARM_MASK BIT(31) +#define ERDMA_EQDB_CI_MASK GENMASK_ULL(23, 0) + +#define ERDMA_PAGE_SIZE_SUPPORT 0x7FFFF000 + +/* WQE related. */ +#define EQE_SIZE 16 +#define EQE_SHIFT 4 +#define RQE_SIZE 32 +#define RQE_SHIFT 5 +#define CQE_SIZE 32 +#define CQE_SHIFT 5 +#define SQEBB_SIZE 32 +#define SQEBB_SHIFT 5 +#define SQEBB_MASK (~(SQEBB_SIZE - 1)) +#define SQEBB_ALIGN(size) ((size + SQEBB_SIZE - 1) & SQEBB_MASK) +#define SQEBB_COUNT(size) (SQEBB_ALIGN(size) >> SQEBB_SHIFT) + +#define ERDMA_MAX_SQE_SIZE 128 +#define ERDMA_MAX_WQEBB_PER_SQE 4 + +/* CMDQ related. */ +#define ERDMA_CMDQ_MAX_OUTSTANDING 128 +#define ERDMA_CMDQ_SQE_SIZE 64 + +/* cmdq sub module definition. */ +enum CMDQ_WQE_SUB_MOD { + CMDQ_SUBMOD_RDMA = 0, + CMDQ_SUBMOD_COMMON = 1 +}; + +enum CMDQ_RDMA_OPCODE { + CMDQ_OPCODE_QUERY_DEVICE = 0, + CMDQ_OPCODE_CREATE_QP = 1, + CMDQ_OPCODE_DESTROY_QP = 2, + CMDQ_OPCODE_MODIFY_QP = 3, + CMDQ_OPCODE_CREATE_CQ = 4, + CMDQ_OPCODE_DESTROY_CQ = 5, + CMDQ_OPCODE_REG_MR = 8, + CMDQ_OPCODE_DEREG_MR = 9 +}; + +enum CMDQ_COMMON_OPCODE { + CMDQ_OPCODE_CREATE_EQ = 0, + CMDQ_OPCODE_DESTROY_EQ = 1, + CMDQ_OPCODE_QUERY_FW_INFO = 2, +}; + +/* cmdq-SQE HDR */ +#define ERDMA_CMD_HDR_WQEBB_CNT_MASK GENMASK_ULL(54, 52) +#define ERDMA_CMD_HDR_CONTEXT_COOKIE_MASK GENMASK_ULL(47, 32) +#define ERDMA_CMD_HDR_SUB_MOD_MASK GENMASK_ULL(25, 24) +#define ERDMA_CMD_HDR_OPCODE_MASK GENMASK_ULL(23, 16) +#define ERDMA_CMD_HDR_WQEBB_INDEX_MASK GENMASK_ULL(15, 0) + +struct erdma_cmdq_destroy_cq_req { + u64 hdr; + u32 cqn; +}; + +#define ERDMA_EQ_TYPE_AEQ 0 +#define ERDMA_EQ_TYPE_CEQ 1 + +struct erdma_cmdq_create_eq_req { + u64 hdr; + u64 qbuf_addr; + u8 vector_idx; + u8 eqn; + u8 depth; + u8 qtype; + u32 db_dma_addr_l; + u32 db_dma_addr_h; +}; + +struct erdma_cmdq_destroy_eq_req { + u64 hdr; + u64 rsvd0; + u8 vector_idx; + u8 eqn; + u8 rsvd1; + u8 qtype; +}; + +/* create_cq cfg0 */ +#define ERDMA_CMD_CREATE_CQ_DEPTH_MASK GENMASK(31, 24) +#define ERDMA_CMD_CREATE_CQ_PAGESIZE_MASK GENMASK(23, 20) +#define ERDMA_CMD_CREATE_CQ_CQN_MASK GENMASK(19, 0) + +/* create_cq cfg1 */ +#define ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK GENMASK(31, 16) +#define ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK BIT(15) +#define ERDMA_CMD_CREATE_CQ_EQN_MASK GENMASK(9, 0) + +struct erdma_cmdq_create_cq_req { + u64 hdr; + u32 cfg0; + u32 qbuf_addr_l; + u32 qbuf_addr_h; + u32 cfg1; + u64 cq_db_info_addr; + u32 first_page_offset; +}; + +/* regmr/deregmr cfg0 */ +#define ERDMA_CMD_MR_VALID_MASK BIT(31) +#define ERDMA_CMD_MR_KEY_MASK GENMASK(27, 20) +#define ERDMA_CMD_MR_MPT_IDX_MASK GENMASK(19, 0) + +/* regmr cfg1 */ +#define ERDMA_CMD_REGMR_PD_MASK GENMASK(31, 12) +#define ERDMA_CMD_REGMR_TYPE_MASK GENMASK(7, 6) +#define ERDMA_CMD_REGMR_RIGHT_MASK GENMASK(5, 2) +#define ERDMA_CMD_REGMR_ACC_MODE_MASK GENMASK(1, 0) + +/* regmr cfg2 */ +#define ERDMA_CMD_REGMR_PAGESIZE_MASK GENMASK(31, 27) +#define ERDMA_CMD_REGMR_MTT_TYPE_MASK GENMASK(21, 20) +#define ERDMA_CMD_REGMR_MTT_CNT_MASK GENMASK(19, 0) + +struct erdma_cmdq_reg_mr_req { + u64 hdr; + u32 cfg0; + u32 cfg1; + u64 start_va; + u32 size; + u32 cfg2; + u64 phy_addr[4]; +}; + +struct erdma_cmdq_dereg_mr_req { + u64 hdr; + u32 cfg; +}; + +/* modify qp cfg */ +#define ERDMA_CMD_MODIFY_QP_STATE_MASK GENMASK(31, 24) +#define ERDMA_CMD_MODIFY_QP_CC_MASK GENMASK(23, 20) +#define ERDMA_CMD_MODIFY_QP_QPN_MASK GENMASK(19, 0) + +struct erdma_cmdq_modify_qp_req { + u64 hdr; + u32 cfg; + u32 cookie; + __be32 dip; + __be32 sip; + __be16 sport; + __be16 dport; + u32 send_nxt; + u32 recv_nxt; +}; + +/* create qp cfg0 */ +#define ERDMA_CMD_CREATE_QP_SQ_DEPTH_MASK GENMASK(31, 20) +#define ERDMA_CMD_CREATE_QP_QPN_MASK GENMASK(19, 0) + +/* create qp cfg1 */ +#define ERDMA_CMD_CREATE_QP_RQ_DEPTH_MASK GENMASK(31, 20) +#define ERDMA_CMD_CREATE_QP_PD_MASK GENMASK(19, 0) + +/* create qp cqn_mtt_cfg */ +#define ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK GENMASK(31, 28) +#define ERDMA_CMD_CREATE_QP_CQN_MASK GENMASK(23, 0) + +/* create qp mtt_cfg */ +#define ERDMA_CMD_CREATE_QP_PAGE_OFFSET_MASK GENMASK(31, 12) +#define ERDMA_CMD_CREATE_QP_MTT_CNT_MASK GENMASK(11, 1) +#define ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK BIT(0) + +#define ERDMA_CMDQ_CREATE_QP_RESP_COOKIE_MASK GENMASK_ULL(31, 0) + +struct erdma_cmdq_create_qp_req { + u64 hdr; + u32 cfg0; + u32 cfg1; + u32 sq_cqn_mtt_cfg; + u32 rq_cqn_mtt_cfg; + u64 sq_buf_addr; + u64 rq_buf_addr; + u32 sq_mtt_cfg; + u32 rq_mtt_cfg; + u64 sq_db_info_dma_addr; + u64 rq_db_info_dma_addr; +}; + +struct erdma_cmdq_destroy_qp_req { + u64 hdr; + u32 qpn; +}; + +/* cap qword 0 definition */ +#define ERDMA_CMD_DEV_CAP_MAX_CQE_MASK GENMASK_ULL(47, 40) +#define ERDMA_CMD_DEV_CAP_MAX_RECV_WR_MASK GENMASK_ULL(23, 16) +#define ERDMA_CMD_DEV_CAP_MAX_MR_SIZE_MASK GENMASK_ULL(7, 0) + +/* cap qword 1 definition */ +#define ERDMA_CMD_DEV_CAP_DMA_LOCAL_KEY_MASK GENMASK_ULL(63, 32) +#define ERDMA_CMD_DEV_CAP_DEFAULT_CC_MASK GENMASK_ULL(31, 28) +#define ERDMA_CMD_DEV_CAP_QBLOCK_MASK GENMASK_ULL(27, 16) +#define ERDMA_CMD_DEV_CAP_MAX_MW_MASK GENMASK_ULL(7, 0) + +#define ERDMA_NQP_PER_QBLOCK 1024 + +#define ERDMA_CMD_INFO0_FW_VER_MASK GENMASK_ULL(31, 0) + +/* CQE hdr */ +#define ERDMA_CQE_HDR_OWNER_MASK BIT(31) +#define ERDMA_CQE_HDR_OPCODE_MASK GENMASK(23, 16) +#define ERDMA_CQE_HDR_QTYPE_MASK GENMASK(15, 8) +#define ERDMA_CQE_HDR_SYNDROME_MASK GENMASK(7, 0) + +#define ERDMA_CQE_QTYPE_SQ 0 +#define ERDMA_CQE_QTYPE_RQ 1 +#define ERDMA_CQE_QTYPE_CMDQ 2 + +struct erdma_cqe { + __be32 hdr; + __be32 qe_idx; + __be32 qpn; + union { + __le32 imm_data; + __be32 inv_rkey; + }; + __be32 size; + __be32 rsvd[3]; +}; + +struct erdma_sge { + __aligned_le64 laddr; + __le32 length; + __le32 lkey; +}; + +/* Receive Queue Element */ +struct erdma_rqe { + __le16 qe_idx; + __le16 rsvd0; + __le32 qpn; + __le32 rsvd1; + __le32 rsvd2; + __le64 to; + __le32 length; + __le32 stag; +}; + +/* SQE */ +#define ERDMA_SQE_HDR_SGL_LEN_MASK GENMASK_ULL(63, 56) +#define ERDMA_SQE_HDR_WQEBB_CNT_MASK GENMASK_ULL(54, 52) +#define ERDMA_SQE_HDR_QPN_MASK GENMASK_ULL(51, 32) +#define ERDMA_SQE_HDR_OPCODE_MASK GENMASK_ULL(31, 27) +#define ERDMA_SQE_HDR_DWQE_MASK BIT_ULL(26) +#define ERDMA_SQE_HDR_INLINE_MASK BIT_ULL(25) +#define ERDMA_SQE_HDR_FENCE_MASK BIT_ULL(24) +#define ERDMA_SQE_HDR_SE_MASK BIT_ULL(23) +#define ERDMA_SQE_HDR_CE_MASK BIT_ULL(22) +#define ERDMA_SQE_HDR_WQEBB_INDEX_MASK GENMASK_ULL(15, 0) + +/* REG MR attrs */ +#define ERDMA_SQE_MR_MODE_MASK GENMASK(1, 0) +#define ERDMA_SQE_MR_ACCESS_MASK GENMASK(5, 2) +#define ERDMA_SQE_MR_MTT_TYPE_MASK GENMASK(7, 6) +#define ERDMA_SQE_MR_MTT_CNT_MASK GENMASK(31, 12) + +struct erdma_write_sqe { + __le64 hdr; + __be32 imm_data; + __le32 length; + + __le32 sink_stag; + __le32 sink_to_l; + __le32 sink_to_h; + + __le32 rsvd; + + struct erdma_sge sgl[0]; +}; + +struct erdma_send_sqe { + __le64 hdr; + union { + __be32 imm_data; + __le32 invalid_stag; + }; + + __le32 length; + struct erdma_sge sgl[0]; +}; + +struct erdma_readreq_sqe { + __le64 hdr; + __le32 invalid_stag; + __le32 length; + __le32 sink_stag; + __le32 sink_to_l; + __le32 sink_to_h; + __le32 rsvd; +}; + +struct erdma_reg_mr_sqe { + __le64 hdr; + __le64 addr; + __le32 length; + __le32 stag; + __le32 attrs; + __le32 rsvd; +}; + +/* EQ related. */ +#define ERDMA_DEFAULT_EQ_DEPTH 256 + +/* ceqe */ +#define ERDMA_CEQE_HDR_DB_MASK BIT_ULL(63) +#define ERDMA_CEQE_HDR_PI_MASK GENMASK_ULL(55, 32) +#define ERDMA_CEQE_HDR_O_MASK BIT_ULL(31) +#define ERDMA_CEQE_HDR_CQN_MASK GENMASK_ULL(19, 0) + +/* aeqe */ +#define ERDMA_AEQE_HDR_O_MASK BIT(31) +#define ERDMA_AEQE_HDR_TYPE_MASK GENMASK(23, 16) +#define ERDMA_AEQE_HDR_SUBTYPE_MASK GENMASK(7, 0) + +#define ERDMA_AE_TYPE_QP_FATAL_EVENT 0 +#define ERDMA_AE_TYPE_QP_ERQ_ERR_EVENT 1 +#define ERDMA_AE_TYPE_ACC_ERR_EVENT 2 +#define ERDMA_AE_TYPE_CQ_ERR 3 +#define ERDMA_AE_TYPE_OTHER_ERROR 4 + +struct erdma_aeqe { + __le32 hdr; + __le32 event_data0; + __le32 event_data1; + __le32 rsvd; +}; + +enum erdma_opcode { + ERDMA_OP_WRITE = 0, + ERDMA_OP_READ = 1, + ERDMA_OP_SEND = 2, + ERDMA_OP_SEND_WITH_IMM = 3, + + ERDMA_OP_RECEIVE = 4, + ERDMA_OP_RECV_IMM = 5, + ERDMA_OP_RECV_INV = 6, + + ERDMA_OP_REQ_ERR = 7, + ERDMA_OP_READ_RESPONSE = 8, + ERDMA_OP_WRITE_WITH_IMM = 9, + + ERDMA_OP_RECV_ERR = 10, + + ERDMA_OP_INVALIDATE = 11, + ERDMA_OP_RSP_SEND_IMM = 12, + ERDMA_OP_SEND_WITH_INV = 13, + + ERDMA_OP_REG_MR = 14, + ERDMA_OP_LOCAL_INV = 15, + ERDMA_OP_READ_WITH_INV = 16, + ERDMA_NUM_OPCODES = 17, + ERDMA_OP_INVALID = ERDMA_NUM_OPCODES + 1 +}; + +enum erdma_wc_status { + ERDMA_WC_SUCCESS = 0, + ERDMA_WC_GENERAL_ERR = 1, + ERDMA_WC_RECV_WQE_FORMAT_ERR = 2, + ERDMA_WC_RECV_STAG_INVALID_ERR = 3, + ERDMA_WC_RECV_ADDR_VIOLATION_ERR = 4, + ERDMA_WC_RECV_RIGHT_VIOLATION_ERR = 5, + ERDMA_WC_RECV_PDID_ERR = 6, + ERDMA_WC_RECV_WARRPING_ERR = 7, + ERDMA_WC_SEND_WQE_FORMAT_ERR = 8, + ERDMA_WC_SEND_WQE_ORD_EXCEED = 9, + ERDMA_WC_SEND_STAG_INVALID_ERR = 10, + ERDMA_WC_SEND_ADDR_VIOLATION_ERR = 11, + ERDMA_WC_SEND_RIGHT_VIOLATION_ERR = 12, + ERDMA_WC_SEND_PDID_ERR = 13, + ERDMA_WC_SEND_WARRPING_ERR = 14, + ERDMA_WC_FLUSH_ERR = 15, + ERDMA_WC_RETRY_EXC_ERR = 16, + ERDMA_NUM_WC_STATUS +}; + +enum erdma_vendor_err { + ERDMA_WC_VENDOR_NO_ERR = 0, + ERDMA_WC_VENDOR_INVALID_RQE = 1, + ERDMA_WC_VENDOR_RQE_INVALID_STAG = 2, + ERDMA_WC_VENDOR_RQE_ADDR_VIOLATION = 3, + ERDMA_WC_VENDOR_RQE_ACCESS_RIGHT_ERR = 4, + ERDMA_WC_VENDOR_RQE_INVALID_PD = 5, + ERDMA_WC_VENDOR_RQE_WRAP_ERR = 6, + ERDMA_WC_VENDOR_INVALID_SQE = 0x20, + ERDMA_WC_VENDOR_ZERO_ORD = 0x21, + ERDMA_WC_VENDOR_SQE_INVALID_STAG = 0x30, + ERDMA_WC_VENDOR_SQE_ADDR_VIOLATION = 0x31, + ERDMA_WC_VENDOR_SQE_ACCESS_ERR = 0x32, + ERDMA_WC_VENDOR_SQE_INVALID_PD = 0x33, + ERDMA_WC_VENDOR_SQE_WARP_ERR = 0x34 +}; + +#endif diff --git a/drivers/infiniband/hw/erdma/erdma_main.c b/drivers/infiniband/hw/erdma/erdma_main.c new file mode 100644 index 000000000000..07e743d24847 --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_main.c @@ -0,0 +1,608 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "erdma.h" +#include "erdma_cm.h" +#include "erdma_hw.h" +#include "erdma_verbs.h" + +MODULE_AUTHOR("Cheng Xu "); +MODULE_DESCRIPTION("Alibaba elasticRDMA adapter driver"); +MODULE_LICENSE("Dual BSD/GPL"); + +static int erdma_netdev_event(struct notifier_block *nb, unsigned long event, + void *arg) +{ + struct net_device *netdev = netdev_notifier_info_to_dev(arg); + struct erdma_dev *dev = container_of(nb, struct erdma_dev, netdev_nb); + + if (dev->netdev == NULL || dev->netdev != netdev) + goto done; + + switch (event) { + case NETDEV_UP: + dev->state = IB_PORT_ACTIVE; + erdma_port_event(dev, IB_EVENT_PORT_ACTIVE); + break; + case NETDEV_DOWN: + dev->state = IB_PORT_DOWN; + erdma_port_event(dev, IB_EVENT_PORT_ERR); + break; + case NETDEV_REGISTER: + case NETDEV_UNREGISTER: + case NETDEV_CHANGEADDR: + case NETDEV_CHANGEMTU: + case NETDEV_GOING_DOWN: + case NETDEV_CHANGE: + default: + break; + } + +done: + return NOTIFY_OK; +} + +static int erdma_enum_and_get_netdev(struct erdma_dev *dev) +{ + struct net_device *netdev; + int ret = -ENODEV; + + /* Already binded to a net_device, so we skip. */ + if (dev->netdev) + return 0; + + rtnl_lock(); + for_each_netdev(&init_net, netdev) { + /* + * In erdma, the paired netdev and ibdev should have the same + * MAC address. erdma can get the value from its PCIe bar + * registers. Since erdma can not get the paired netdev + * reference directly, we do a traverse here to get the paired + * netdev. + */ + if (ether_addr_equal_unaligned(netdev->perm_addr, + dev->attrs.peer_addr)) { + ret = ib_device_set_netdev(&dev->ibdev, netdev, 1); + if (ret) { + rtnl_unlock(); + ibdev_warn(&dev->ibdev, + "failed (%d) to link netdev", ret); + return ret; + } + + dev->netdev = netdev; + break; + } + } + + rtnl_unlock(); + + return ret; +} + +static int erdma_device_register(struct erdma_dev *dev) +{ + struct ib_device *ibdev = &dev->ibdev; + int ret; + + ret = erdma_enum_and_get_netdev(dev); + if (ret) + return ret; + + addrconf_addr_eui48((u8 *)&ibdev->node_guid, dev->netdev->dev_addr); + + ret = ib_register_device(ibdev, "erdma_%d", &dev->pdev->dev); + if (ret) { + dev_err(&dev->pdev->dev, + "ib_register_device failed: ret = %d\n", ret); + return ret; + } + + dev->netdev_nb.notifier_call = erdma_netdev_event; + ret = register_netdevice_notifier(&dev->netdev_nb); + if (ret) { + ibdev_err(&dev->ibdev, "failed to register notifier.\n"); + ib_unregister_device(ibdev); + } + + return ret; +} + +static irqreturn_t erdma_comm_irq_handler(int irq, void *data) +{ + struct erdma_dev *dev = data; + + erdma_cmdq_completion_handler(&dev->cmdq); + erdma_aeq_event_handler(dev); + + return IRQ_HANDLED; +} + +static void erdma_dwqe_resource_init(struct erdma_dev *dev) +{ + int total_pages, type0, type1; + + dev->attrs.grp_num = erdma_reg_read32(dev, ERDMA_REGS_GRP_NUM_REG); + + if (dev->attrs.grp_num < 4) + dev->attrs.disable_dwqe = true; + else + dev->attrs.disable_dwqe = false; + + /* One page contains 4 goups. */ + total_pages = dev->attrs.grp_num * 4; + + if (dev->attrs.grp_num >= ERDMA_DWQE_MAX_GRP_CNT) { + dev->attrs.grp_num = ERDMA_DWQE_MAX_GRP_CNT; + type0 = ERDMA_DWQE_TYPE0_CNT; + type1 = ERDMA_DWQE_TYPE1_CNT / ERDMA_DWQE_TYPE1_CNT_PER_PAGE; + } else { + type1 = total_pages / 3; + type0 = total_pages - type1 - 1; + } + + dev->attrs.dwqe_pages = type0; + dev->attrs.dwqe_entries = type1 * ERDMA_DWQE_TYPE1_CNT_PER_PAGE; +} + +static int erdma_request_vectors(struct erdma_dev *dev) +{ + int expect_irq_num = min(num_possible_cpus() + 1, ERDMA_NUM_MSIX_VEC); + int ret; + + ret = pci_alloc_irq_vectors(dev->pdev, 1, expect_irq_num, PCI_IRQ_MSIX); + if (ret < 0) { + dev_err(&dev->pdev->dev, "request irq vectors failed(%d)\n", + ret); + return ret; + } + dev->attrs.irq_num = ret; + + return 0; +} + +static int erdma_comm_irq_init(struct erdma_dev *dev) +{ + snprintf(dev->comm_irq.name, ERDMA_IRQNAME_SIZE, "erdma-common@pci:%s", + pci_name(dev->pdev)); + dev->comm_irq.msix_vector = + pci_irq_vector(dev->pdev, ERDMA_MSIX_VECTOR_CMDQ); + + cpumask_set_cpu(cpumask_first(cpumask_of_pcibus(dev->pdev->bus)), + &dev->comm_irq.affinity_hint_mask); + irq_set_affinity_hint(dev->comm_irq.msix_vector, + &dev->comm_irq.affinity_hint_mask); + + return request_irq(dev->comm_irq.msix_vector, erdma_comm_irq_handler, 0, + dev->comm_irq.name, dev); +} + +static void erdma_comm_irq_uninit(struct erdma_dev *dev) +{ + irq_set_affinity_hint(dev->comm_irq.msix_vector, NULL); + free_irq(dev->comm_irq.msix_vector, dev); +} + +static int erdma_device_init(struct erdma_dev *dev, struct pci_dev *pdev) +{ + int ret; + + erdma_dwqe_resource_init(dev); + + ret = dma_set_mask_and_coherent(&pdev->dev, + DMA_BIT_MASK(ERDMA_PCI_WIDTH)); + if (ret) + return ret; + + dma_set_max_seg_size(&pdev->dev, UINT_MAX); + + return 0; +} + +static void erdma_device_uninit(struct erdma_dev *dev) +{ + u32 ctrl = FIELD_PREP(ERDMA_REG_DEV_CTRL_RESET_MASK, 1); + + erdma_reg_write32(dev, ERDMA_REGS_DEV_CTRL_REG, ctrl); +} + +static const struct pci_device_id erdma_pci_tbl[] = { + { PCI_DEVICE(PCI_VENDOR_ID_ALIBABA, 0x107f) }, + {} +}; + +static int erdma_probe_dev(struct pci_dev *pdev) +{ + struct erdma_dev *dev; + int bars, err; + u32 version; + + err = pci_enable_device(pdev); + if (err) { + dev_err(&pdev->dev, "pci_enable_device failed(%d)\n", err); + return err; + } + + pci_set_master(pdev); + + dev = ib_alloc_device(erdma_dev, ibdev); + if (!dev) { + dev_err(&pdev->dev, "ib_alloc_device failed\n"); + err = -ENOMEM; + goto err_disable_device; + } + + pci_set_drvdata(pdev, dev); + dev->pdev = pdev; + dev->attrs.numa_node = dev_to_node(&pdev->dev); + + bars = pci_select_bars(pdev, IORESOURCE_MEM); + err = pci_request_selected_regions(pdev, bars, DRV_MODULE_NAME); + if (bars != ERDMA_BAR_MASK || err) { + err = err ? err : -EINVAL; + goto err_ib_device_release; + } + + dev->func_bar_addr = pci_resource_start(pdev, ERDMA_FUNC_BAR); + dev->func_bar_len = pci_resource_len(pdev, ERDMA_FUNC_BAR); + + dev->func_bar = + devm_ioremap(&pdev->dev, dev->func_bar_addr, dev->func_bar_len); + if (!dev->func_bar) { + dev_err(&pdev->dev, "devm_ioremap failed.\n"); + err = -EFAULT; + goto err_release_bars; + } + + version = erdma_reg_read32(dev, ERDMA_REGS_VERSION_REG); + if (version == 0) { + /* we knows that it is a non-functional function. */ + err = -ENODEV; + goto err_iounmap_func_bar; + } + + err = erdma_device_init(dev, pdev); + if (err) + goto err_iounmap_func_bar; + + err = erdma_request_vectors(dev); + if (err) + goto err_iounmap_func_bar; + + err = erdma_comm_irq_init(dev); + if (err) + goto err_free_vectors; + + err = erdma_aeq_init(dev); + if (err) + goto err_uninit_comm_irq; + + err = erdma_cmdq_init(dev); + if (err) + goto err_uninit_aeq; + + err = erdma_ceqs_init(dev); + if (err) + goto err_uninit_cmdq; + + erdma_finish_cmdq_init(dev); + + return 0; + +err_uninit_cmdq: + erdma_device_uninit(dev); + erdma_cmdq_destroy(dev); + +err_uninit_aeq: + erdma_aeq_destroy(dev); + +err_uninit_comm_irq: + erdma_comm_irq_uninit(dev); + +err_free_vectors: + pci_free_irq_vectors(dev->pdev); + +err_iounmap_func_bar: + devm_iounmap(&pdev->dev, dev->func_bar); + +err_release_bars: + pci_release_selected_regions(pdev, bars); + +err_ib_device_release: + ib_dealloc_device(&dev->ibdev); + +err_disable_device: + pci_disable_device(pdev); + + return err; +} + +static void erdma_remove_dev(struct pci_dev *pdev) +{ + struct erdma_dev *dev = pci_get_drvdata(pdev); + + erdma_ceqs_uninit(dev); + + erdma_device_uninit(dev); + + erdma_cmdq_destroy(dev); + erdma_aeq_destroy(dev); + erdma_comm_irq_uninit(dev); + pci_free_irq_vectors(dev->pdev); + + devm_iounmap(&pdev->dev, dev->func_bar); + pci_release_selected_regions(pdev, ERDMA_BAR_MASK); + + ib_dealloc_device(&dev->ibdev); + + pci_disable_device(pdev); +} + +#define ERDMA_GET_CAP(name, cap) FIELD_GET(ERDMA_CMD_DEV_CAP_##name##_MASK, cap) + +static int erdma_dev_attrs_init(struct erdma_dev *dev) +{ + int err; + u64 req_hdr, cap0, cap1; + + erdma_cmdq_build_reqhdr(&req_hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_QUERY_DEVICE); + + err = erdma_post_cmd_wait(&dev->cmdq, &req_hdr, sizeof(req_hdr), &cap0, + &cap1); + if (err) + return err; + + dev->attrs.max_cqe = 1 << ERDMA_GET_CAP(MAX_CQE, cap0); + dev->attrs.max_mr_size = 1ULL << ERDMA_GET_CAP(MAX_MR_SIZE, cap0); + dev->attrs.max_mw = 1 << ERDMA_GET_CAP(MAX_MW, cap1); + dev->attrs.max_recv_wr = 1 << ERDMA_GET_CAP(MAX_RECV_WR, cap0); + dev->attrs.local_dma_key = ERDMA_GET_CAP(DMA_LOCAL_KEY, cap1); + dev->attrs.cc = ERDMA_GET_CAP(DEFAULT_CC, cap1); + dev->attrs.max_qp = ERDMA_NQP_PER_QBLOCK * ERDMA_GET_CAP(QBLOCK, cap1); + dev->attrs.max_mr = dev->attrs.max_qp << 1; + dev->attrs.max_cq = dev->attrs.max_qp << 1; + + dev->attrs.max_send_wr = ERDMA_MAX_SEND_WR; + dev->attrs.max_ord = ERDMA_MAX_ORD; + dev->attrs.max_ird = ERDMA_MAX_IRD; + dev->attrs.max_send_sge = ERDMA_MAX_SEND_SGE; + dev->attrs.max_recv_sge = ERDMA_MAX_RECV_SGE; + dev->attrs.max_sge_rd = ERDMA_MAX_SGE_RD; + dev->attrs.max_pd = ERDMA_MAX_PD; + + dev->res_cb[ERDMA_RES_TYPE_PD].max_cap = ERDMA_MAX_PD; + dev->res_cb[ERDMA_RES_TYPE_STAG_IDX].max_cap = dev->attrs.max_mr; + + erdma_cmdq_build_reqhdr(&req_hdr, CMDQ_SUBMOD_COMMON, + CMDQ_OPCODE_QUERY_FW_INFO); + + err = erdma_post_cmd_wait(&dev->cmdq, &req_hdr, sizeof(req_hdr), &cap0, + &cap1); + if (!err) + dev->attrs.fw_version = + FIELD_GET(ERDMA_CMD_INFO0_FW_VER_MASK, cap0); + + return err; +} + +static int erdma_res_cb_init(struct erdma_dev *dev) +{ + int i, j; + + for (i = 0; i < ERDMA_RES_CNT; i++) { + dev->res_cb[i].next_alloc_idx = 1; + spin_lock_init(&dev->res_cb[i].lock); + dev->res_cb[i].bitmap = + bitmap_zalloc(dev->res_cb[i].max_cap, GFP_KERNEL); + if (!dev->res_cb[i].bitmap) + goto err; + } + + return 0; + +err: + for (j = 0; j < i; j++) + bitmap_free(dev->res_cb[j].bitmap); + + return -ENOMEM; +} + +static void erdma_res_cb_free(struct erdma_dev *dev) +{ + int i; + + for (i = 0; i < ERDMA_RES_CNT; i++) + bitmap_free(dev->res_cb[i].bitmap); +} + +static const struct ib_device_ops erdma_device_ops = { + .owner = THIS_MODULE, + .driver_id = RDMA_DRIVER_ERDMA, + .uverbs_abi_ver = ERDMA_ABI_VERSION, + + .alloc_mr = erdma_ib_alloc_mr, + .alloc_pd = erdma_alloc_pd, + .alloc_ucontext = erdma_alloc_ucontext, + .create_cq = erdma_create_cq, + .create_qp = erdma_create_qp, + .dealloc_pd = erdma_dealloc_pd, + .dealloc_ucontext = erdma_dealloc_ucontext, + .dereg_mr = erdma_dereg_mr, + .destroy_cq = erdma_destroy_cq, + .destroy_qp = erdma_destroy_qp, + .get_dma_mr = erdma_get_dma_mr, + .get_port_immutable = erdma_get_port_immutable, + .iw_accept = erdma_accept, + .iw_add_ref = erdma_qp_get_ref, + .iw_connect = erdma_connect, + .iw_create_listen = erdma_create_listen, + .iw_destroy_listen = erdma_destroy_listen, + .iw_get_qp = erdma_get_ibqp, + .iw_reject = erdma_reject, + .iw_rem_ref = erdma_qp_put_ref, + .map_mr_sg = erdma_map_mr_sg, + .mmap = erdma_mmap, + .mmap_free = erdma_mmap_free, + .modify_qp = erdma_modify_qp, + .post_recv = erdma_post_recv, + .post_send = erdma_post_send, + .poll_cq = erdma_poll_cq, + .query_device = erdma_query_device, + .query_gid = erdma_query_gid, + .query_port = erdma_query_port, + .query_qp = erdma_query_qp, + .req_notify_cq = erdma_req_notify_cq, + .reg_user_mr = erdma_reg_user_mr, + + INIT_RDMA_OBJ_SIZE(ib_cq, erdma_cq, ibcq), + INIT_RDMA_OBJ_SIZE(ib_pd, erdma_pd, ibpd), + INIT_RDMA_OBJ_SIZE(ib_ucontext, erdma_ucontext, ibucontext), + INIT_RDMA_OBJ_SIZE(ib_qp, erdma_qp, ibqp), +}; + +static int erdma_ib_device_add(struct pci_dev *pdev) +{ + struct erdma_dev *dev = pci_get_drvdata(pdev); + struct ib_device *ibdev = &dev->ibdev; + u64 mac; + int ret; + + ret = erdma_dev_attrs_init(dev); + if (ret) + return ret; + + ibdev->node_type = RDMA_NODE_RNIC; + memcpy(ibdev->node_desc, ERDMA_NODE_DESC, sizeof(ERDMA_NODE_DESC)); + + /* + * Current model (one-to-one device association): + * One ERDMA device per net_device or, equivalently, + * per physical port. + */ + ibdev->phys_port_cnt = 1; + ibdev->num_comp_vectors = dev->attrs.irq_num - 1; + + ib_set_device_ops(ibdev, &erdma_device_ops); + + INIT_LIST_HEAD(&dev->cep_list); + + spin_lock_init(&dev->lock); + xa_init_flags(&dev->qp_xa, XA_FLAGS_ALLOC1); + xa_init_flags(&dev->cq_xa, XA_FLAGS_ALLOC1); + dev->next_alloc_cqn = 1; + dev->next_alloc_qpn = 1; + + ret = erdma_res_cb_init(dev); + if (ret) + return ret; + + spin_lock_init(&dev->db_bitmap_lock); + bitmap_zero(dev->sdb_page, ERDMA_DWQE_TYPE0_CNT); + bitmap_zero(dev->sdb_entry, ERDMA_DWQE_TYPE1_CNT); + + atomic_set(&dev->num_ctx, 0); + + mac = erdma_reg_read32(dev, ERDMA_REGS_NETDEV_MAC_L_REG); + mac |= (u64)erdma_reg_read32(dev, ERDMA_REGS_NETDEV_MAC_H_REG) << 32; + + u64_to_ether_addr(mac, dev->attrs.peer_addr); + + ret = erdma_device_register(dev); + if (ret) + goto err_out; + + return 0; + +err_out: + xa_destroy(&dev->qp_xa); + xa_destroy(&dev->cq_xa); + + erdma_res_cb_free(dev); + + return ret; +} + +static void erdma_ib_device_remove(struct pci_dev *pdev) +{ + struct erdma_dev *dev = pci_get_drvdata(pdev); + + unregister_netdevice_notifier(&dev->netdev_nb); + ib_unregister_device(&dev->ibdev); + + erdma_res_cb_free(dev); + xa_destroy(&dev->qp_xa); + xa_destroy(&dev->cq_xa); +} + +static int erdma_probe(struct pci_dev *pdev, const struct pci_device_id *ent) +{ + int ret; + + ret = erdma_probe_dev(pdev); + if (ret) + return ret; + + ret = erdma_ib_device_add(pdev); + if (ret) { + erdma_remove_dev(pdev); + return ret; + } + + return 0; +} + +static void erdma_remove(struct pci_dev *pdev) +{ + erdma_ib_device_remove(pdev); + erdma_remove_dev(pdev); +} + +static struct pci_driver erdma_pci_driver = { + .name = DRV_MODULE_NAME, + .id_table = erdma_pci_tbl, + .probe = erdma_probe, + .remove = erdma_remove +}; + +MODULE_DEVICE_TABLE(pci, erdma_pci_tbl); + +static __init int erdma_init_module(void) +{ + int ret; + + ret = erdma_cm_init(); + if (ret) + return ret; + + ret = pci_register_driver(&erdma_pci_driver); + if (ret) + erdma_cm_exit(); + + return ret; +} + +static void __exit erdma_exit_module(void) +{ + pci_unregister_driver(&erdma_pci_driver); + + erdma_cm_exit(); +} + +module_init(erdma_init_module); +module_exit(erdma_exit_module); diff --git a/drivers/infiniband/hw/erdma/erdma_qp.c b/drivers/infiniband/hw/erdma/erdma_qp.c new file mode 100644 index 000000000000..72f08171a28a --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_qp.c @@ -0,0 +1,566 @@ +// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2021, Alibaba Group */ +/* Authors: Bernard Metzler */ +/* Copyright (c) 2008-2019, IBM Corporation */ + +#include +#include +#include +#include + +#include +#include + +#include "erdma.h" +#include "erdma_cm.h" +#include "erdma_verbs.h" + +void erdma_qp_llp_close(struct erdma_qp *qp) +{ + struct erdma_qp_attrs qp_attrs; + + down_write(&qp->state_lock); + + switch (qp->attrs.state) { + case ERDMA_QP_STATE_RTS: + case ERDMA_QP_STATE_RTR: + case ERDMA_QP_STATE_IDLE: + case ERDMA_QP_STATE_TERMINATE: + qp_attrs.state = ERDMA_QP_STATE_CLOSING; + erdma_modify_qp_internal(qp, &qp_attrs, ERDMA_QP_ATTR_STATE); + break; + case ERDMA_QP_STATE_CLOSING: + qp->attrs.state = ERDMA_QP_STATE_IDLE; + break; + default: + break; + } + + if (qp->cep) { + erdma_cep_put(qp->cep); + qp->cep = NULL; + } + + up_write(&qp->state_lock); +} + +struct ib_qp *erdma_get_ibqp(struct ib_device *ibdev, int id) +{ + struct erdma_qp *qp = find_qp_by_qpn(to_edev(ibdev), id); + + if (qp) + return &qp->ibqp; + + return NULL; +} + +static int erdma_modify_qp_state_to_rts(struct erdma_qp *qp, + struct erdma_qp_attrs *attrs, + enum erdma_qp_attr_mask mask) +{ + int ret; + struct erdma_dev *dev = qp->dev; + struct erdma_cmdq_modify_qp_req req; + struct tcp_sock *tp; + struct erdma_cep *cep = qp->cep; + struct sockaddr_storage local_addr, remote_addr; + + if (!(mask & ERDMA_QP_ATTR_LLP_HANDLE)) + return -EINVAL; + + if (!(mask & ERDMA_QP_ATTR_MPA)) + return -EINVAL; + + ret = getname_local(cep->sock, &local_addr); + if (ret < 0) + return ret; + + ret = getname_peer(cep->sock, &remote_addr); + if (ret < 0) + return ret; + + qp->attrs.state = ERDMA_QP_STATE_RTS; + + tp = tcp_sk(qp->cep->sock->sk); + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_MODIFY_QP); + + req.cfg = FIELD_PREP(ERDMA_CMD_MODIFY_QP_STATE_MASK, qp->attrs.state) | + FIELD_PREP(ERDMA_CMD_MODIFY_QP_CC_MASK, qp->attrs.cc) | + FIELD_PREP(ERDMA_CMD_MODIFY_QP_QPN_MASK, QP_ID(qp)); + + req.cookie = be32_to_cpu(qp->cep->mpa.ext_data.cookie); + req.dip = to_sockaddr_in(remote_addr).sin_addr.s_addr; + req.sip = to_sockaddr_in(local_addr).sin_addr.s_addr; + req.dport = to_sockaddr_in(remote_addr).sin_port; + req.sport = to_sockaddr_in(local_addr).sin_port; + + req.send_nxt = tp->snd_nxt; + /* rsvd tcp seq for mpa-rsp in server. */ + if (qp->attrs.qp_type == ERDMA_QP_PASSIVE) + req.send_nxt += MPA_DEFAULT_HDR_LEN + qp->attrs.pd_len; + req.recv_nxt = tp->rcv_nxt; + + return erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); +} + +static int erdma_modify_qp_state_to_stop(struct erdma_qp *qp, + struct erdma_qp_attrs *attrs, + enum erdma_qp_attr_mask mask) +{ + struct erdma_dev *dev = qp->dev; + struct erdma_cmdq_modify_qp_req req; + + qp->attrs.state = attrs->state; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_MODIFY_QP); + + req.cfg = FIELD_PREP(ERDMA_CMD_MODIFY_QP_STATE_MASK, attrs->state) | + FIELD_PREP(ERDMA_CMD_MODIFY_QP_QPN_MASK, QP_ID(qp)); + + return erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); +} + +int erdma_modify_qp_internal(struct erdma_qp *qp, struct erdma_qp_attrs *attrs, + enum erdma_qp_attr_mask mask) +{ + int drop_conn, ret = 0; + + if (!mask) + return 0; + + if (!(mask & ERDMA_QP_ATTR_STATE)) + return 0; + + switch (qp->attrs.state) { + case ERDMA_QP_STATE_IDLE: + case ERDMA_QP_STATE_RTR: + if (attrs->state == ERDMA_QP_STATE_RTS) { + ret = erdma_modify_qp_state_to_rts(qp, attrs, mask); + } else if (attrs->state == ERDMA_QP_STATE_ERROR) { + qp->attrs.state = ERDMA_QP_STATE_ERROR; + if (qp->cep) { + erdma_cep_put(qp->cep); + qp->cep = NULL; + } + ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); + } + break; + case ERDMA_QP_STATE_RTS: + drop_conn = 0; + + if (attrs->state == ERDMA_QP_STATE_CLOSING) { + ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); + drop_conn = 1; + } else if (attrs->state == ERDMA_QP_STATE_TERMINATE) { + qp->attrs.state = ERDMA_QP_STATE_TERMINATE; + ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); + drop_conn = 1; + } else if (attrs->state == ERDMA_QP_STATE_ERROR) { + ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); + qp->attrs.state = ERDMA_QP_STATE_ERROR; + drop_conn = 1; + } + + if (drop_conn) + erdma_qp_cm_drop(qp); + + break; + case ERDMA_QP_STATE_TERMINATE: + if (attrs->state == ERDMA_QP_STATE_ERROR) + qp->attrs.state = ERDMA_QP_STATE_ERROR; + break; + case ERDMA_QP_STATE_CLOSING: + if (attrs->state == ERDMA_QP_STATE_IDLE) { + qp->attrs.state = ERDMA_QP_STATE_IDLE; + } else if (attrs->state == ERDMA_QP_STATE_ERROR) { + ret = erdma_modify_qp_state_to_stop(qp, attrs, mask); + qp->attrs.state = ERDMA_QP_STATE_ERROR; + } else if (attrs->state != ERDMA_QP_STATE_CLOSING) { + return -ECONNABORTED; + } + break; + default: + break; + } + + return ret; +} + +static void erdma_qp_safe_free(struct kref *ref) +{ + struct erdma_qp *qp = container_of(ref, struct erdma_qp, ref); + + complete(&qp->safe_free); +} + +void erdma_qp_put(struct erdma_qp *qp) +{ + WARN_ON(kref_read(&qp->ref) < 1); + kref_put(&qp->ref, erdma_qp_safe_free); +} + +void erdma_qp_get(struct erdma_qp *qp) +{ + kref_get(&qp->ref); +} + +static int fill_inline_data(struct erdma_qp *qp, + const struct ib_send_wr *send_wr, u16 wqe_idx, + u32 sgl_offset, __le32 *length_field) +{ + u32 remain_size, copy_size, data_off, bytes = 0; + char *data; + int i = 0; + + wqe_idx += (sgl_offset >> SQEBB_SHIFT); + sgl_offset &= (SQEBB_SIZE - 1); + data = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, qp->attrs.sq_size, + SQEBB_SHIFT); + + while (i < send_wr->num_sge) { + bytes += send_wr->sg_list[i].length; + if (bytes > (int)ERDMA_MAX_INLINE) + return -EINVAL; + + remain_size = send_wr->sg_list[i].length; + data_off = 0; + + while (1) { + copy_size = min(remain_size, SQEBB_SIZE - sgl_offset); + + memcpy(data + sgl_offset, + (void *)(uintptr_t)send_wr->sg_list[i].addr + + data_off, + copy_size); + remain_size -= copy_size; + data_off += copy_size; + sgl_offset += copy_size; + wqe_idx += (sgl_offset >> SQEBB_SHIFT); + sgl_offset &= (SQEBB_SIZE - 1); + + data = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, + qp->attrs.sq_size, SQEBB_SHIFT); + if (!remain_size) + break; + } + + i++; + } + *length_field = cpu_to_le32(bytes); + + return bytes; +} + +static int fill_sgl(struct erdma_qp *qp, const struct ib_send_wr *send_wr, + u16 wqe_idx, u32 sgl_offset, __le32 *length_field) +{ + int i = 0; + u32 bytes = 0; + char *sgl; + + if (send_wr->num_sge > qp->dev->attrs.max_send_sge) + return -EINVAL; + + if (sgl_offset & 0xF) + return -EINVAL; + + while (i < send_wr->num_sge) { + wqe_idx += (sgl_offset >> SQEBB_SHIFT); + sgl_offset &= (SQEBB_SIZE - 1); + sgl = get_queue_entry(qp->kern_qp.sq_buf, wqe_idx, + qp->attrs.sq_size, SQEBB_SHIFT); + + bytes += send_wr->sg_list[i].length; + memcpy(sgl + sgl_offset, &send_wr->sg_list[i], + sizeof(struct ib_sge)); + + sgl_offset += sizeof(struct ib_sge); + i++; + } + + *length_field = cpu_to_le32(bytes); + return 0; +} + +static int erdma_push_one_sqe(struct erdma_qp *qp, u16 *pi, + const struct ib_send_wr *send_wr) +{ + u32 wqe_size, wqebb_cnt, hw_op, flags, sgl_offset; + u32 idx = *pi & (qp->attrs.sq_size - 1); + enum ib_wr_opcode op = send_wr->opcode; + struct erdma_readreq_sqe *read_sqe; + struct erdma_reg_mr_sqe *regmr_sge; + struct erdma_write_sqe *write_sqe; + struct erdma_send_sqe *send_sqe; + struct ib_rdma_wr *rdma_wr; + struct erdma_mr *mr; + __le32 *length_field; + u64 wqe_hdr, *entry; + struct ib_sge *sge; + u32 attrs; + int ret; + + entry = get_queue_entry(qp->kern_qp.sq_buf, idx, qp->attrs.sq_size, + SQEBB_SHIFT); + + /* Clear the SQE header section. */ + *entry = 0; + + qp->kern_qp.swr_tbl[idx] = send_wr->wr_id; + flags = send_wr->send_flags; + wqe_hdr = FIELD_PREP( + ERDMA_SQE_HDR_CE_MASK, + ((flags & IB_SEND_SIGNALED) || qp->kern_qp.sig_all) ? 1 : 0); + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_SE_MASK, + flags & IB_SEND_SOLICITED ? 1 : 0); + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_FENCE_MASK, + flags & IB_SEND_FENCE ? 1 : 0); + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_INLINE_MASK, + flags & IB_SEND_INLINE ? 1 : 0); + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_QPN_MASK, QP_ID(qp)); + + switch (op) { + case IB_WR_RDMA_WRITE: + case IB_WR_RDMA_WRITE_WITH_IMM: + hw_op = ERDMA_OP_WRITE; + if (op == IB_WR_RDMA_WRITE_WITH_IMM) + hw_op = ERDMA_OP_WRITE_WITH_IMM; + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, hw_op); + rdma_wr = container_of(send_wr, struct ib_rdma_wr, wr); + write_sqe = (struct erdma_write_sqe *)entry; + + write_sqe->imm_data = send_wr->ex.imm_data; + write_sqe->sink_stag = cpu_to_le32(rdma_wr->rkey); + write_sqe->sink_to_h = + cpu_to_le32(upper_32_bits(rdma_wr->remote_addr)); + write_sqe->sink_to_l = + cpu_to_le32(lower_32_bits(rdma_wr->remote_addr)); + + length_field = &write_sqe->length; + wqe_size = sizeof(struct erdma_write_sqe); + sgl_offset = wqe_size; + break; + case IB_WR_RDMA_READ: + case IB_WR_RDMA_READ_WITH_INV: + read_sqe = (struct erdma_readreq_sqe *)entry; + if (unlikely(send_wr->num_sge != 1)) + return -EINVAL; + hw_op = ERDMA_OP_READ; + if (op == IB_WR_RDMA_READ_WITH_INV) { + hw_op = ERDMA_OP_READ_WITH_INV; + read_sqe->invalid_stag = + cpu_to_le32(send_wr->ex.invalidate_rkey); + } + + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, hw_op); + rdma_wr = container_of(send_wr, struct ib_rdma_wr, wr); + read_sqe->length = cpu_to_le32(send_wr->sg_list[0].length); + read_sqe->sink_stag = cpu_to_le32(send_wr->sg_list[0].lkey); + read_sqe->sink_to_l = + cpu_to_le32(lower_32_bits(send_wr->sg_list[0].addr)); + read_sqe->sink_to_h = + cpu_to_le32(upper_32_bits(send_wr->sg_list[0].addr)); + + sge = get_queue_entry(qp->kern_qp.sq_buf, idx + 1, + qp->attrs.sq_size, SQEBB_SHIFT); + sge->addr = rdma_wr->remote_addr; + sge->lkey = rdma_wr->rkey; + sge->length = send_wr->sg_list[0].length; + wqe_size = sizeof(struct erdma_readreq_sqe) + + send_wr->num_sge * sizeof(struct ib_sge); + + goto out; + case IB_WR_SEND: + case IB_WR_SEND_WITH_IMM: + case IB_WR_SEND_WITH_INV: + send_sqe = (struct erdma_send_sqe *)entry; + hw_op = ERDMA_OP_SEND; + if (op == IB_WR_SEND_WITH_IMM) { + hw_op = ERDMA_OP_SEND_WITH_IMM; + send_sqe->imm_data = send_wr->ex.imm_data; + } else if (op == IB_WR_SEND_WITH_INV) { + hw_op = ERDMA_OP_SEND_WITH_INV; + send_sqe->invalid_stag = + cpu_to_le32(send_wr->ex.invalidate_rkey); + } + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, hw_op); + length_field = &send_sqe->length; + wqe_size = sizeof(struct erdma_send_sqe); + sgl_offset = wqe_size; + + break; + case IB_WR_REG_MR: + wqe_hdr |= + FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, ERDMA_OP_REG_MR); + regmr_sge = (struct erdma_reg_mr_sqe *)entry; + mr = to_emr(reg_wr(send_wr)->mr); + + mr->access = ERDMA_MR_ACC_LR | + to_erdma_access_flags(reg_wr(send_wr)->access); + regmr_sge->addr = cpu_to_le64(mr->ibmr.iova); + regmr_sge->length = cpu_to_le32(mr->ibmr.length); + regmr_sge->stag = cpu_to_le32(mr->ibmr.lkey); + attrs = FIELD_PREP(ERDMA_SQE_MR_MODE_MASK, 0) | + FIELD_PREP(ERDMA_SQE_MR_ACCESS_MASK, mr->access) | + FIELD_PREP(ERDMA_SQE_MR_MTT_CNT_MASK, + mr->mem.mtt_nents); + + if (mr->mem.mtt_nents < ERDMA_MAX_INLINE_MTT_ENTRIES) { + attrs |= FIELD_PREP(ERDMA_SQE_MR_MTT_TYPE_MASK, 0); + /* Copy SGLs to SQE content to accelerate */ + memcpy(get_queue_entry(qp->kern_qp.sq_buf, idx + 1, + qp->attrs.sq_size, SQEBB_SHIFT), + mr->mem.mtt_buf, MTT_SIZE(mr->mem.mtt_nents)); + wqe_size = sizeof(struct erdma_reg_mr_sqe) + + MTT_SIZE(mr->mem.mtt_nents); + } else { + attrs |= FIELD_PREP(ERDMA_SQE_MR_MTT_TYPE_MASK, 1); + wqe_size = sizeof(struct erdma_reg_mr_sqe); + } + + regmr_sge->attrs = cpu_to_le32(attrs); + goto out; + case IB_WR_LOCAL_INV: + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_OPCODE_MASK, + ERDMA_OP_LOCAL_INV); + regmr_sge = (struct erdma_reg_mr_sqe *)entry; + regmr_sge->stag = cpu_to_le32(send_wr->ex.invalidate_rkey); + wqe_size = sizeof(struct erdma_reg_mr_sqe); + goto out; + default: + return -EOPNOTSUPP; + } + + if (flags & IB_SEND_INLINE) { + ret = fill_inline_data(qp, send_wr, idx, sgl_offset, + length_field); + if (ret < 0) + return -EINVAL; + wqe_size += ret; + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_SGL_LEN_MASK, ret); + } else { + ret = fill_sgl(qp, send_wr, idx, sgl_offset, length_field); + if (ret) + return -EINVAL; + wqe_size += send_wr->num_sge * sizeof(struct ib_sge); + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_SGL_LEN_MASK, + send_wr->num_sge); + } + +out: + wqebb_cnt = SQEBB_COUNT(wqe_size); + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_WQEBB_CNT_MASK, wqebb_cnt - 1); + *pi += wqebb_cnt; + wqe_hdr |= FIELD_PREP(ERDMA_SQE_HDR_WQEBB_INDEX_MASK, *pi); + + *entry = wqe_hdr; + + return 0; +} + +static void kick_sq_db(struct erdma_qp *qp, u16 pi) +{ + u64 db_data = FIELD_PREP(ERDMA_SQE_HDR_QPN_MASK, QP_ID(qp)) | + FIELD_PREP(ERDMA_SQE_HDR_WQEBB_INDEX_MASK, pi); + + *(u64 *)qp->kern_qp.sq_db_info = db_data; + writeq(db_data, qp->kern_qp.hw_sq_db); +} + +int erdma_post_send(struct ib_qp *ibqp, const struct ib_send_wr *send_wr, + const struct ib_send_wr **bad_send_wr) +{ + struct erdma_qp *qp = to_eqp(ibqp); + int ret = 0; + const struct ib_send_wr *wr = send_wr; + unsigned long flags; + u16 sq_pi; + + if (!send_wr) + return -EINVAL; + + spin_lock_irqsave(&qp->lock, flags); + sq_pi = qp->kern_qp.sq_pi; + + while (wr) { + if ((u16)(sq_pi - qp->kern_qp.sq_ci) >= qp->attrs.sq_size) { + ret = -ENOMEM; + *bad_send_wr = send_wr; + break; + } + + ret = erdma_push_one_sqe(qp, &sq_pi, wr); + if (ret) { + *bad_send_wr = wr; + break; + } + qp->kern_qp.sq_pi = sq_pi; + kick_sq_db(qp, sq_pi); + + wr = wr->next; + } + spin_unlock_irqrestore(&qp->lock, flags); + + return ret; +} + +static int erdma_post_recv_one(struct erdma_qp *qp, + const struct ib_recv_wr *recv_wr) +{ + struct erdma_rqe *rqe = + get_queue_entry(qp->kern_qp.rq_buf, qp->kern_qp.rq_pi, + qp->attrs.rq_size, RQE_SHIFT); + + rqe->qe_idx = cpu_to_le16(qp->kern_qp.rq_pi + 1); + rqe->qpn = cpu_to_le32(QP_ID(qp)); + + if (recv_wr->num_sge == 0) { + rqe->length = 0; + } else if (recv_wr->num_sge == 1) { + rqe->stag = cpu_to_le32(recv_wr->sg_list[0].lkey); + rqe->to = cpu_to_le64(recv_wr->sg_list[0].addr); + rqe->length = cpu_to_le32(recv_wr->sg_list[0].length); + } else { + return -EINVAL; + } + + *(u64 *)qp->kern_qp.rq_db_info = *(u64 *)rqe; + writeq(*(u64 *)rqe, qp->kern_qp.hw_rq_db); + + qp->kern_qp.rwr_tbl[qp->kern_qp.rq_pi & (qp->attrs.rq_size - 1)] = + recv_wr->wr_id; + qp->kern_qp.rq_pi++; + + return 0; +} + +int erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr, + const struct ib_recv_wr **bad_recv_wr) +{ + const struct ib_recv_wr *wr = recv_wr; + struct erdma_qp *qp = to_eqp(ibqp); + unsigned long flags; + int ret; + + spin_lock_irqsave(&qp->lock, flags); + + while (wr) { + ret = erdma_post_recv_one(qp, wr); + if (ret) { + *bad_recv_wr = wr; + break; + } + wr = wr->next; + } + + spin_unlock_irqrestore(&qp->lock, flags); + return ret; +} diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.c b/drivers/infiniband/hw/erdma/erdma_verbs.c new file mode 100644 index 000000000000..a7a3d42e2016 --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_verbs.c @@ -0,0 +1,1460 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +/* Authors: Bernard Metzler */ +/* Copyright (c) 2008-2019, IBM Corporation */ + +/* Copyright (c) 2013-2015, Mellanox Technologies. All rights reserved. */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "erdma.h" +#include "erdma_cm.h" +#include "erdma_hw.h" +#include "erdma_verbs.h" + +static int create_qp_cmd(struct erdma_dev *dev, struct erdma_qp *qp) +{ + struct erdma_cmdq_create_qp_req req; + struct erdma_pd *pd = to_epd(qp->ibqp.pd); + struct erdma_uqp *user_qp; + u64 resp0, resp1; + int err; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_CREATE_QP); + + req.cfg0 = FIELD_PREP(ERDMA_CMD_CREATE_QP_SQ_DEPTH_MASK, + ilog2(qp->attrs.sq_size)) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_QPN_MASK, QP_ID(qp)); + req.cfg1 = FIELD_PREP(ERDMA_CMD_CREATE_QP_RQ_DEPTH_MASK, + ilog2(qp->attrs.rq_size)) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_PD_MASK, pd->pdn); + + if (rdma_is_kernel_res(&qp->ibqp.res)) { + u32 pgsz_range = ilog2(SZ_1M) - PAGE_SHIFT; + + req.sq_cqn_mtt_cfg = + FIELD_PREP(ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, + pgsz_range) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->scq->cqn); + req.rq_cqn_mtt_cfg = + FIELD_PREP(ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, + pgsz_range) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->rcq->cqn); + + req.sq_mtt_cfg = + FIELD_PREP(ERDMA_CMD_CREATE_QP_PAGE_OFFSET_MASK, 0) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, 1) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, + ERDMA_MR_INLINE_MTT); + req.rq_mtt_cfg = req.sq_mtt_cfg; + + req.rq_buf_addr = qp->kern_qp.rq_buf_dma_addr; + req.sq_buf_addr = qp->kern_qp.sq_buf_dma_addr; + req.sq_db_info_dma_addr = qp->kern_qp.sq_buf_dma_addr + + (qp->attrs.sq_size << SQEBB_SHIFT); + req.rq_db_info_dma_addr = qp->kern_qp.rq_buf_dma_addr + + (qp->attrs.rq_size << RQE_SHIFT); + } else { + user_qp = &qp->user_qp; + req.sq_cqn_mtt_cfg = FIELD_PREP( + ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, + ilog2(user_qp->sq_mtt.page_size) - PAGE_SHIFT); + req.sq_cqn_mtt_cfg |= + FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->scq->cqn); + + req.rq_cqn_mtt_cfg = FIELD_PREP( + ERDMA_CMD_CREATE_QP_PAGE_SIZE_MASK, + ilog2(user_qp->rq_mtt.page_size) - PAGE_SHIFT); + req.rq_cqn_mtt_cfg |= + FIELD_PREP(ERDMA_CMD_CREATE_QP_CQN_MASK, qp->rcq->cqn); + + req.sq_mtt_cfg = user_qp->sq_mtt.page_offset; + req.sq_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, + user_qp->sq_mtt.mtt_nents) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, + user_qp->sq_mtt.mtt_type); + + req.rq_mtt_cfg = user_qp->rq_mtt.page_offset; + req.rq_mtt_cfg |= FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_CNT_MASK, + user_qp->rq_mtt.mtt_nents) | + FIELD_PREP(ERDMA_CMD_CREATE_QP_MTT_TYPE_MASK, + user_qp->rq_mtt.mtt_type); + + req.sq_buf_addr = user_qp->sq_mtt.mtt_entry[0]; + req.rq_buf_addr = user_qp->rq_mtt.mtt_entry[0]; + + req.sq_db_info_dma_addr = user_qp->sq_db_info_dma_addr; + req.rq_db_info_dma_addr = user_qp->rq_db_info_dma_addr; + } + + err = erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), &resp0, + &resp1); + if (!err) + qp->attrs.cookie = + FIELD_GET(ERDMA_CMDQ_CREATE_QP_RESP_COOKIE_MASK, resp0); + + return err; +} + +static int regmr_cmd(struct erdma_dev *dev, struct erdma_mr *mr) +{ + struct erdma_cmdq_reg_mr_req req; + struct erdma_pd *pd = to_epd(mr->ibmr.pd); + u64 *phy_addr; + int i; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, CMDQ_OPCODE_REG_MR); + + req.cfg0 = FIELD_PREP(ERDMA_CMD_MR_VALID_MASK, mr->valid) | + FIELD_PREP(ERDMA_CMD_MR_KEY_MASK, mr->ibmr.lkey & 0xFF) | + FIELD_PREP(ERDMA_CMD_MR_MPT_IDX_MASK, mr->ibmr.lkey >> 8); + req.cfg1 = FIELD_PREP(ERDMA_CMD_REGMR_PD_MASK, pd->pdn) | + FIELD_PREP(ERDMA_CMD_REGMR_TYPE_MASK, mr->type) | + FIELD_PREP(ERDMA_CMD_REGMR_RIGHT_MASK, mr->access) | + FIELD_PREP(ERDMA_CMD_REGMR_ACC_MODE_MASK, 0); + req.cfg2 = FIELD_PREP(ERDMA_CMD_REGMR_PAGESIZE_MASK, + ilog2(mr->mem.page_size)) | + FIELD_PREP(ERDMA_CMD_REGMR_MTT_TYPE_MASK, mr->mem.mtt_type) | + FIELD_PREP(ERDMA_CMD_REGMR_MTT_CNT_MASK, mr->mem.page_cnt); + + if (mr->type == ERDMA_MR_TYPE_DMA) + goto post_cmd; + + if (mr->type == ERDMA_MR_TYPE_NORMAL) { + req.start_va = mr->mem.va; + req.size = mr->mem.len; + } + + if (mr->type == ERDMA_MR_TYPE_FRMR || + mr->mem.mtt_type == ERDMA_MR_INDIRECT_MTT) { + phy_addr = req.phy_addr; + *phy_addr = mr->mem.mtt_entry[0]; + } else { + phy_addr = req.phy_addr; + for (i = 0; i < mr->mem.mtt_nents; i++) + *phy_addr++ = mr->mem.mtt_entry[i]; + } + +post_cmd: + return erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); +} + +static int create_cq_cmd(struct erdma_dev *dev, struct erdma_cq *cq) +{ + struct erdma_cmdq_create_cq_req req; + u32 page_size; + struct erdma_mem *mtt; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_CREATE_CQ); + + req.cfg0 = FIELD_PREP(ERDMA_CMD_CREATE_CQ_CQN_MASK, cq->cqn) | + FIELD_PREP(ERDMA_CMD_CREATE_CQ_DEPTH_MASK, ilog2(cq->depth)); + req.cfg1 = FIELD_PREP(ERDMA_CMD_CREATE_CQ_EQN_MASK, cq->assoc_eqn); + + if (rdma_is_kernel_res(&cq->ibcq.res)) { + page_size = SZ_32M; + req.cfg0 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_PAGESIZE_MASK, + ilog2(page_size) - PAGE_SHIFT); + req.qbuf_addr_l = lower_32_bits(cq->kern_cq.qbuf_dma_addr); + req.qbuf_addr_h = upper_32_bits(cq->kern_cq.qbuf_dma_addr); + + req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK, 1) | + FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK, + ERDMA_MR_INLINE_MTT); + + req.first_page_offset = 0; + req.cq_db_info_addr = + cq->kern_cq.qbuf_dma_addr + (cq->depth << CQE_SHIFT); + } else { + mtt = &cq->user_cq.qbuf_mtt; + req.cfg0 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_PAGESIZE_MASK, + ilog2(mtt->page_size) - PAGE_SHIFT); + if (mtt->mtt_nents == 1) { + req.qbuf_addr_l = lower_32_bits(*(u64 *)mtt->mtt_buf); + req.qbuf_addr_h = upper_32_bits(*(u64 *)mtt->mtt_buf); + } else { + req.qbuf_addr_l = lower_32_bits(mtt->mtt_entry[0]); + req.qbuf_addr_h = upper_32_bits(mtt->mtt_entry[0]); + } + req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_CNT_MASK, + mtt->mtt_nents); + req.cfg1 |= FIELD_PREP(ERDMA_CMD_CREATE_CQ_MTT_TYPE_MASK, + mtt->mtt_type); + + req.first_page_offset = mtt->page_offset; + req.cq_db_info_addr = cq->user_cq.db_info_dma_addr; + } + + return erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); +} + +static int erdma_alloc_idx(struct erdma_resource_cb *res_cb) +{ + int idx; + unsigned long flags; + + spin_lock_irqsave(&res_cb->lock, flags); + idx = find_next_zero_bit(res_cb->bitmap, res_cb->max_cap, + res_cb->next_alloc_idx); + if (idx == res_cb->max_cap) { + idx = find_first_zero_bit(res_cb->bitmap, res_cb->max_cap); + if (idx == res_cb->max_cap) { + res_cb->next_alloc_idx = 1; + spin_unlock_irqrestore(&res_cb->lock, flags); + return -ENOSPC; + } + } + + set_bit(idx, res_cb->bitmap); + res_cb->next_alloc_idx = idx + 1; + spin_unlock_irqrestore(&res_cb->lock, flags); + + return idx; +} + +static inline void erdma_free_idx(struct erdma_resource_cb *res_cb, u32 idx) +{ + unsigned long flags; + u32 used; + + spin_lock_irqsave(&res_cb->lock, flags); + used = __test_and_clear_bit(idx, res_cb->bitmap); + spin_unlock_irqrestore(&res_cb->lock, flags); + WARN_ON(!used); +} + +static struct rdma_user_mmap_entry * +erdma_user_mmap_entry_insert(struct erdma_ucontext *uctx, void *address, + u32 size, u8 mmap_flag, u64 *mmap_offset) +{ + struct erdma_user_mmap_entry *entry = + kzalloc(sizeof(*entry), GFP_KERNEL); + int ret; + + if (!entry) + return NULL; + + entry->address = (u64)address; + entry->mmap_flag = mmap_flag; + + size = PAGE_ALIGN(size); + + ret = rdma_user_mmap_entry_insert(&uctx->ibucontext, &entry->rdma_entry, + size); + if (ret) { + kfree(entry); + return NULL; + } + + *mmap_offset = rdma_user_mmap_get_offset(&entry->rdma_entry); + + return &entry->rdma_entry; +} + +int erdma_query_device(struct ib_device *ibdev, struct ib_device_attr *attr, + struct ib_udata *unused) +{ + struct erdma_dev *dev = to_edev(ibdev); + + memset(attr, 0, sizeof(*attr)); + + attr->max_mr_size = dev->attrs.max_mr_size; + attr->vendor_id = PCI_VENDOR_ID_ALIBABA; + attr->vendor_part_id = dev->pdev->device; + attr->hw_ver = dev->pdev->revision; + attr->max_qp = dev->attrs.max_qp; + attr->max_qp_wr = min(dev->attrs.max_send_wr, dev->attrs.max_recv_wr); + attr->max_qp_rd_atom = dev->attrs.max_ord; + attr->max_qp_init_rd_atom = dev->attrs.max_ird; + attr->max_res_rd_atom = dev->attrs.max_qp * dev->attrs.max_ird; + attr->device_cap_flags = IB_DEVICE_MEM_MGT_EXTENSIONS; + attr->kernel_cap_flags = IBK_LOCAL_DMA_LKEY; + ibdev->local_dma_lkey = dev->attrs.local_dma_key; + attr->max_send_sge = dev->attrs.max_send_sge; + attr->max_recv_sge = dev->attrs.max_recv_sge; + attr->max_sge_rd = dev->attrs.max_sge_rd; + attr->max_cq = dev->attrs.max_cq; + attr->max_cqe = dev->attrs.max_cqe; + attr->max_mr = dev->attrs.max_mr; + attr->max_pd = dev->attrs.max_pd; + attr->max_mw = dev->attrs.max_mw; + attr->max_fast_reg_page_list_len = ERDMA_MAX_FRMR_PA; + attr->page_size_cap = ERDMA_PAGE_SIZE_SUPPORT; + attr->fw_ver = dev->attrs.fw_version; + + if (dev->netdev) + addrconf_addr_eui48((u8 *)&attr->sys_image_guid, + dev->netdev->dev_addr); + + return 0; +} + +int erdma_query_gid(struct ib_device *ibdev, u32 port, int idx, + union ib_gid *gid) +{ + struct erdma_dev *dev = to_edev(ibdev); + + memset(gid, 0, sizeof(*gid)); + ether_addr_copy(gid->raw, dev->attrs.peer_addr); + + return 0; +} + +int erdma_query_port(struct ib_device *ibdev, u32 port, + struct ib_port_attr *attr) +{ + struct erdma_dev *dev = to_edev(ibdev); + struct net_device *ndev = dev->netdev; + + memset(attr, 0, sizeof(*attr)); + + attr->gid_tbl_len = 1; + attr->port_cap_flags = IB_PORT_CM_SUP | IB_PORT_DEVICE_MGMT_SUP; + attr->max_msg_sz = -1; + + if (!ndev) + goto out; + + ib_get_eth_speed(ibdev, port, &attr->active_speed, &attr->active_width); + attr->max_mtu = ib_mtu_int_to_enum(ndev->mtu); + attr->active_mtu = ib_mtu_int_to_enum(ndev->mtu); + if (netif_running(ndev) && netif_carrier_ok(ndev)) + dev->state = IB_PORT_ACTIVE; + else + dev->state = IB_PORT_DOWN; + attr->state = dev->state; + +out: + if (dev->state == IB_PORT_ACTIVE) + attr->phys_state = IB_PORT_PHYS_STATE_LINK_UP; + else + attr->phys_state = IB_PORT_PHYS_STATE_DISABLED; + + return 0; +} + +int erdma_get_port_immutable(struct ib_device *ibdev, u32 port, + struct ib_port_immutable *port_immutable) +{ + port_immutable->gid_tbl_len = 1; + port_immutable->core_cap_flags = RDMA_CORE_PORT_IWARP; + + return 0; +} + +int erdma_alloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) +{ + struct erdma_pd *pd = to_epd(ibpd); + struct erdma_dev *dev = to_edev(ibpd->device); + int pdn; + + pdn = erdma_alloc_idx(&dev->res_cb[ERDMA_RES_TYPE_PD]); + if (pdn < 0) + return pdn; + + pd->pdn = pdn; + + return 0; +} + +int erdma_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) +{ + struct erdma_pd *pd = to_epd(ibpd); + struct erdma_dev *dev = to_edev(ibpd->device); + + erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_PD], pd->pdn); + + return 0; +} + +static int erdma_qp_validate_cap(struct erdma_dev *dev, + struct ib_qp_init_attr *attrs) +{ + if ((attrs->cap.max_send_wr > dev->attrs.max_send_wr) || + (attrs->cap.max_recv_wr > dev->attrs.max_recv_wr) || + (attrs->cap.max_send_sge > dev->attrs.max_send_sge) || + (attrs->cap.max_recv_sge > dev->attrs.max_recv_sge) || + (attrs->cap.max_inline_data > ERDMA_MAX_INLINE) || + !attrs->cap.max_send_wr || !attrs->cap.max_recv_wr) { + return -EINVAL; + } + + return 0; +} + +static int erdma_qp_validate_attr(struct erdma_dev *dev, + struct ib_qp_init_attr *attrs) +{ + if (attrs->qp_type != IB_QPT_RC) + return -EOPNOTSUPP; + + if (attrs->srq) + return -EOPNOTSUPP; + + if (!attrs->send_cq || !attrs->recv_cq) + return -EOPNOTSUPP; + + return 0; +} + +static void free_kernel_qp(struct erdma_qp *qp) +{ + struct erdma_dev *dev = qp->dev; + + vfree(qp->kern_qp.swr_tbl); + vfree(qp->kern_qp.rwr_tbl); + + if (qp->kern_qp.sq_buf) + dma_free_coherent( + &dev->pdev->dev, + WARPPED_BUFSIZE(qp->attrs.sq_size << SQEBB_SHIFT), + qp->kern_qp.sq_buf, qp->kern_qp.sq_buf_dma_addr); + + if (qp->kern_qp.rq_buf) + dma_free_coherent( + &dev->pdev->dev, + WARPPED_BUFSIZE(qp->attrs.rq_size << RQE_SHIFT), + qp->kern_qp.rq_buf, qp->kern_qp.rq_buf_dma_addr); +} + +static int init_kernel_qp(struct erdma_dev *dev, struct erdma_qp *qp, + struct ib_qp_init_attr *attrs) +{ + struct erdma_kqp *kqp = &qp->kern_qp; + int size; + + if (attrs->sq_sig_type == IB_SIGNAL_ALL_WR) + kqp->sig_all = 1; + + kqp->sq_pi = 0; + kqp->sq_ci = 0; + kqp->rq_pi = 0; + kqp->rq_ci = 0; + kqp->hw_sq_db = + dev->func_bar + (ERDMA_SDB_SHARED_PAGE_INDEX << PAGE_SHIFT); + kqp->hw_rq_db = dev->func_bar + ERDMA_BAR_RQDB_SPACE_OFFSET; + + kqp->swr_tbl = vmalloc(qp->attrs.sq_size * sizeof(u64)); + kqp->rwr_tbl = vmalloc(qp->attrs.rq_size * sizeof(u64)); + if (!kqp->swr_tbl || !kqp->rwr_tbl) + goto err_out; + + size = (qp->attrs.sq_size << SQEBB_SHIFT) + ERDMA_EXTRA_BUFFER_SIZE; + kqp->sq_buf = dma_alloc_coherent(&dev->pdev->dev, size, + &kqp->sq_buf_dma_addr, GFP_KERNEL); + if (!kqp->sq_buf) + goto err_out; + + size = (qp->attrs.rq_size << RQE_SHIFT) + ERDMA_EXTRA_BUFFER_SIZE; + kqp->rq_buf = dma_alloc_coherent(&dev->pdev->dev, size, + &kqp->rq_buf_dma_addr, GFP_KERNEL); + if (!kqp->rq_buf) + goto err_out; + + kqp->sq_db_info = kqp->sq_buf + (qp->attrs.sq_size << SQEBB_SHIFT); + kqp->rq_db_info = kqp->rq_buf + (qp->attrs.rq_size << RQE_SHIFT); + + return 0; + +err_out: + free_kernel_qp(qp); + return -ENOMEM; +} + +static int get_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem, + u64 start, u64 len, int access, u64 virt, + unsigned long req_page_size, u8 force_indirect_mtt) +{ + struct ib_block_iter biter; + uint64_t *phy_addr = NULL; + int ret = 0; + + mem->umem = ib_umem_get(&dev->ibdev, start, len, access); + if (IS_ERR(mem->umem)) { + ret = PTR_ERR(mem->umem); + mem->umem = NULL; + return ret; + } + + mem->va = virt; + mem->len = len; + mem->page_size = ib_umem_find_best_pgsz(mem->umem, req_page_size, virt); + mem->page_offset = start & (mem->page_size - 1); + mem->mtt_nents = ib_umem_num_dma_blocks(mem->umem, mem->page_size); + mem->page_cnt = mem->mtt_nents; + + if (mem->page_cnt > ERDMA_MAX_INLINE_MTT_ENTRIES || + force_indirect_mtt) { + mem->mtt_type = ERDMA_MR_INDIRECT_MTT; + mem->mtt_buf = + alloc_pages_exact(MTT_SIZE(mem->page_cnt), GFP_KERNEL); + if (!mem->mtt_buf) { + ret = -ENOMEM; + goto error_ret; + } + phy_addr = mem->mtt_buf; + } else { + mem->mtt_type = ERDMA_MR_INLINE_MTT; + phy_addr = mem->mtt_entry; + } + + rdma_umem_for_each_dma_block(mem->umem, &biter, mem->page_size) { + *phy_addr = rdma_block_iter_dma_address(&biter); + phy_addr++; + } + + if (mem->mtt_type == ERDMA_MR_INDIRECT_MTT) { + mem->mtt_entry[0] = + dma_map_single(&dev->pdev->dev, mem->mtt_buf, + MTT_SIZE(mem->page_cnt), DMA_TO_DEVICE); + if (dma_mapping_error(&dev->pdev->dev, mem->mtt_entry[0])) { + free_pages_exact(mem->mtt_buf, MTT_SIZE(mem->page_cnt)); + mem->mtt_buf = NULL; + ret = -ENOMEM; + goto error_ret; + } + } + + return 0; + +error_ret: + if (mem->umem) { + ib_umem_release(mem->umem); + mem->umem = NULL; + } + + return ret; +} + +static void put_mtt_entries(struct erdma_dev *dev, struct erdma_mem *mem) +{ + if (mem->mtt_buf) { + dma_unmap_single(&dev->pdev->dev, mem->mtt_entry[0], + MTT_SIZE(mem->page_cnt), DMA_TO_DEVICE); + free_pages_exact(mem->mtt_buf, MTT_SIZE(mem->page_cnt)); + } + + if (mem->umem) { + ib_umem_release(mem->umem); + mem->umem = NULL; + } +} + +static int erdma_map_user_dbrecords(struct erdma_ucontext *ctx, + u64 dbrecords_va, + struct erdma_user_dbrecords_page **dbr_page, + dma_addr_t *dma_addr) +{ + struct erdma_user_dbrecords_page *page = NULL; + int rv = 0; + + mutex_lock(&ctx->dbrecords_page_mutex); + + list_for_each_entry(page, &ctx->dbrecords_page_list, list) + if (page->va == (dbrecords_va & PAGE_MASK)) + goto found; + + page = kmalloc(sizeof(*page), GFP_KERNEL); + if (!page) { + rv = -ENOMEM; + goto out; + } + + page->va = (dbrecords_va & PAGE_MASK); + page->refcnt = 0; + + page->umem = ib_umem_get(ctx->ibucontext.device, + dbrecords_va & PAGE_MASK, PAGE_SIZE, 0); + if (IS_ERR(page->umem)) { + rv = PTR_ERR(page->umem); + kfree(page); + goto out; + } + + list_add(&page->list, &ctx->dbrecords_page_list); + +found: + *dma_addr = sg_dma_address(page->umem->sgt_append.sgt.sgl) + + (dbrecords_va & ~PAGE_MASK); + *dbr_page = page; + page->refcnt++; + +out: + mutex_unlock(&ctx->dbrecords_page_mutex); + return rv; +} + +static void +erdma_unmap_user_dbrecords(struct erdma_ucontext *ctx, + struct erdma_user_dbrecords_page **dbr_page) +{ + if (!ctx || !(*dbr_page)) + return; + + mutex_lock(&ctx->dbrecords_page_mutex); + if (--(*dbr_page)->refcnt == 0) { + list_del(&(*dbr_page)->list); + ib_umem_release((*dbr_page)->umem); + kfree(*dbr_page); + } + + *dbr_page = NULL; + mutex_unlock(&ctx->dbrecords_page_mutex); +} + +static int init_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx, + u64 va, u32 len, u64 db_info_va) +{ + dma_addr_t db_info_dma_addr; + u32 rq_offset; + int ret; + + if (len < (PAGE_ALIGN(qp->attrs.sq_size * SQEBB_SIZE) + + qp->attrs.rq_size * RQE_SIZE)) + return -EINVAL; + + ret = get_mtt_entries(qp->dev, &qp->user_qp.sq_mtt, va, + qp->attrs.sq_size << SQEBB_SHIFT, 0, va, + (SZ_1M - SZ_4K), 1); + if (ret) + return ret; + + rq_offset = PAGE_ALIGN(qp->attrs.sq_size << SQEBB_SHIFT); + qp->user_qp.rq_offset = rq_offset; + + ret = get_mtt_entries(qp->dev, &qp->user_qp.rq_mtt, va + rq_offset, + qp->attrs.rq_size << RQE_SHIFT, 0, va + rq_offset, + (SZ_1M - SZ_4K), 1); + if (ret) + goto put_sq_mtt; + + ret = erdma_map_user_dbrecords(uctx, db_info_va, + &qp->user_qp.user_dbr_page, + &db_info_dma_addr); + if (ret) + goto put_rq_mtt; + + qp->user_qp.sq_db_info_dma_addr = db_info_dma_addr; + qp->user_qp.rq_db_info_dma_addr = db_info_dma_addr + ERDMA_DB_SIZE; + + return 0; + +put_rq_mtt: + put_mtt_entries(qp->dev, &qp->user_qp.rq_mtt); + +put_sq_mtt: + put_mtt_entries(qp->dev, &qp->user_qp.sq_mtt); + + return ret; +} + +static void free_user_qp(struct erdma_qp *qp, struct erdma_ucontext *uctx) +{ + put_mtt_entries(qp->dev, &qp->user_qp.sq_mtt); + put_mtt_entries(qp->dev, &qp->user_qp.rq_mtt); + erdma_unmap_user_dbrecords(uctx, &qp->user_qp.user_dbr_page); +} + +int erdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attrs, + struct ib_udata *udata) +{ + struct erdma_qp *qp = to_eqp(ibqp); + struct erdma_dev *dev = to_edev(ibqp->device); + struct erdma_ucontext *uctx = rdma_udata_to_drv_context( + udata, struct erdma_ucontext, ibucontext); + struct erdma_ureq_create_qp ureq; + struct erdma_uresp_create_qp uresp; + int ret; + + ret = erdma_qp_validate_cap(dev, attrs); + if (ret) + goto err_out; + + ret = erdma_qp_validate_attr(dev, attrs); + if (ret) + goto err_out; + + qp->scq = to_ecq(attrs->send_cq); + qp->rcq = to_ecq(attrs->recv_cq); + qp->dev = dev; + qp->attrs.cc = dev->attrs.cc; + + init_rwsem(&qp->state_lock); + kref_init(&qp->ref); + init_completion(&qp->safe_free); + + ret = xa_alloc_cyclic(&dev->qp_xa, &qp->ibqp.qp_num, qp, + XA_LIMIT(1, dev->attrs.max_qp - 1), + &dev->next_alloc_qpn, GFP_KERNEL); + if (ret < 0) { + ret = -ENOMEM; + goto err_out; + } + + qp->attrs.sq_size = roundup_pow_of_two(attrs->cap.max_send_wr * + ERDMA_MAX_WQEBB_PER_SQE); + qp->attrs.rq_size = roundup_pow_of_two(attrs->cap.max_recv_wr); + + if (uctx) { + ret = ib_copy_from_udata(&ureq, udata, + min(sizeof(ureq), udata->inlen)); + if (ret) + goto err_out_xa; + + ret = init_user_qp(qp, uctx, ureq.qbuf_va, ureq.qbuf_len, + ureq.db_record_va); + if (ret) + goto err_out_xa; + + memset(&uresp, 0, sizeof(uresp)); + + uresp.num_sqe = qp->attrs.sq_size; + uresp.num_rqe = qp->attrs.rq_size; + uresp.qp_id = QP_ID(qp); + uresp.rq_offset = qp->user_qp.rq_offset; + + ret = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); + if (ret) + goto err_out_cmd; + } else { + init_kernel_qp(dev, qp, attrs); + } + + qp->attrs.max_send_sge = attrs->cap.max_send_sge; + qp->attrs.max_recv_sge = attrs->cap.max_recv_sge; + qp->attrs.state = ERDMA_QP_STATE_IDLE; + + ret = create_qp_cmd(dev, qp); + if (ret) + goto err_out_cmd; + + spin_lock_init(&qp->lock); + + return 0; + +err_out_cmd: + if (uctx) + free_user_qp(qp, uctx); + else + free_kernel_qp(qp); +err_out_xa: + xa_erase(&dev->qp_xa, QP_ID(qp)); +err_out: + return ret; +} + +static int erdma_create_stag(struct erdma_dev *dev, u32 *stag) +{ + int stag_idx; + + stag_idx = erdma_alloc_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX]); + if (stag_idx < 0) + return stag_idx; + + /* For now, we always let key field be zero. */ + *stag = (stag_idx << 8); + + return 0; +} + +struct ib_mr *erdma_get_dma_mr(struct ib_pd *ibpd, int acc) +{ + struct erdma_dev *dev = to_edev(ibpd->device); + struct erdma_mr *mr; + u32 stag; + int ret; + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + ret = erdma_create_stag(dev, &stag); + if (ret) + goto out_free; + + mr->type = ERDMA_MR_TYPE_DMA; + + mr->ibmr.lkey = stag; + mr->ibmr.rkey = stag; + mr->ibmr.pd = ibpd; + mr->access = ERDMA_MR_ACC_LR | to_erdma_access_flags(acc); + ret = regmr_cmd(dev, mr); + if (ret) + goto out_remove_stag; + + return &mr->ibmr; + +out_remove_stag: + erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], + mr->ibmr.lkey >> 8); + +out_free: + kfree(mr); + + return ERR_PTR(ret); +} + +struct ib_mr *erdma_ib_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, + u32 max_num_sg) +{ + struct erdma_mr *mr; + struct erdma_dev *dev = to_edev(ibpd->device); + int ret; + u32 stag; + + if (mr_type != IB_MR_TYPE_MEM_REG) + return ERR_PTR(-EOPNOTSUPP); + + if (max_num_sg > ERDMA_MR_MAX_MTT_CNT) + return ERR_PTR(-EINVAL); + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + ret = erdma_create_stag(dev, &stag); + if (ret) + goto out_free; + + mr->type = ERDMA_MR_TYPE_FRMR; + + mr->ibmr.lkey = stag; + mr->ibmr.rkey = stag; + mr->ibmr.pd = ibpd; + /* update it in FRMR. */ + mr->access = ERDMA_MR_ACC_LR | ERDMA_MR_ACC_LW | ERDMA_MR_ACC_RR | + ERDMA_MR_ACC_RW; + + mr->mem.page_size = PAGE_SIZE; /* update it later. */ + mr->mem.page_cnt = max_num_sg; + mr->mem.mtt_type = ERDMA_MR_INDIRECT_MTT; + mr->mem.mtt_buf = + alloc_pages_exact(MTT_SIZE(mr->mem.page_cnt), GFP_KERNEL); + if (!mr->mem.mtt_buf) { + ret = -ENOMEM; + goto out_remove_stag; + } + + mr->mem.mtt_entry[0] = + dma_map_single(&dev->pdev->dev, mr->mem.mtt_buf, + MTT_SIZE(mr->mem.page_cnt), DMA_TO_DEVICE); + if (dma_mapping_error(&dev->pdev->dev, mr->mem.mtt_entry[0])) { + ret = -ENOMEM; + goto out_free_mtt; + } + + ret = regmr_cmd(dev, mr); + if (ret) + goto out_dma_unmap; + + return &mr->ibmr; + +out_dma_unmap: + dma_unmap_single(&dev->pdev->dev, mr->mem.mtt_entry[0], + MTT_SIZE(mr->mem.page_cnt), DMA_TO_DEVICE); +out_free_mtt: + free_pages_exact(mr->mem.mtt_buf, MTT_SIZE(mr->mem.page_cnt)); + +out_remove_stag: + erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], + mr->ibmr.lkey >> 8); + +out_free: + kfree(mr); + + return ERR_PTR(ret); +} + +static int erdma_set_page(struct ib_mr *ibmr, u64 addr) +{ + struct erdma_mr *mr = to_emr(ibmr); + + if (mr->mem.mtt_nents >= mr->mem.page_cnt) + return -1; + + *((u64 *)mr->mem.mtt_buf + mr->mem.mtt_nents) = addr; + mr->mem.mtt_nents++; + + return 0; +} + +int erdma_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents, + unsigned int *sg_offset) +{ + struct erdma_mr *mr = to_emr(ibmr); + int num; + + mr->mem.mtt_nents = 0; + + num = ib_sg_to_pages(&mr->ibmr, sg, sg_nents, sg_offset, + erdma_set_page); + + return num; +} + +struct ib_mr *erdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, + u64 virt, int access, struct ib_udata *udata) +{ + struct erdma_mr *mr = NULL; + struct erdma_dev *dev = to_edev(ibpd->device); + u32 stag; + int ret; + + if (!len || len > dev->attrs.max_mr_size) + return ERR_PTR(-EINVAL); + + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + ret = get_mtt_entries(dev, &mr->mem, start, len, access, virt, + SZ_2G - SZ_4K, 0); + if (ret) + goto err_out_free; + + ret = erdma_create_stag(dev, &stag); + if (ret) + goto err_out_put_mtt; + + mr->ibmr.lkey = mr->ibmr.rkey = stag; + mr->ibmr.pd = ibpd; + mr->mem.va = virt; + mr->mem.len = len; + mr->access = ERDMA_MR_ACC_LR | to_erdma_access_flags(access); + mr->valid = 1; + mr->type = ERDMA_MR_TYPE_NORMAL; + + ret = regmr_cmd(dev, mr); + if (ret) + goto err_out_mr; + + return &mr->ibmr; + +err_out_mr: + erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], + mr->ibmr.lkey >> 8); + +err_out_put_mtt: + put_mtt_entries(dev, &mr->mem); + +err_out_free: + kfree(mr); + + return ERR_PTR(ret); +} + +int erdma_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) +{ + struct erdma_mr *mr; + struct erdma_dev *dev = to_edev(ibmr->device); + struct erdma_cmdq_dereg_mr_req req; + int ret; + + mr = to_emr(ibmr); + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_DEREG_MR); + + req.cfg = FIELD_PREP(ERDMA_CMD_MR_MPT_IDX_MASK, ibmr->lkey >> 8) | + FIELD_PREP(ERDMA_CMD_MR_KEY_MASK, ibmr->lkey & 0xFF); + + ret = erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); + if (ret) + return ret; + + erdma_free_idx(&dev->res_cb[ERDMA_RES_TYPE_STAG_IDX], ibmr->lkey >> 8); + + put_mtt_entries(dev, &mr->mem); + + kfree(mr); + return 0; +} + +int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) +{ + struct erdma_cq *cq = to_ecq(ibcq); + struct erdma_dev *dev = to_edev(ibcq->device); + struct erdma_ucontext *ctx = rdma_udata_to_drv_context( + udata, struct erdma_ucontext, ibucontext); + int err; + struct erdma_cmdq_destroy_cq_req req; + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_DESTROY_CQ); + req.cqn = cq->cqn; + + err = erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); + if (err) + return err; + + if (rdma_is_kernel_res(&cq->ibcq.res)) { + dma_free_coherent(&dev->pdev->dev, + WARPPED_BUFSIZE(cq->depth << CQE_SHIFT), + cq->kern_cq.qbuf, cq->kern_cq.qbuf_dma_addr); + } else { + erdma_unmap_user_dbrecords(ctx, &cq->user_cq.user_dbr_page); + put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + } + + xa_erase(&dev->cq_xa, cq->cqn); + + return 0; +} + +int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) +{ + struct erdma_qp *qp = to_eqp(ibqp); + struct erdma_dev *dev = to_edev(ibqp->device); + struct erdma_ucontext *ctx = rdma_udata_to_drv_context( + udata, struct erdma_ucontext, ibucontext); + struct erdma_qp_attrs qp_attrs; + int err; + struct erdma_cmdq_destroy_qp_req req; + + down_write(&qp->state_lock); + qp_attrs.state = ERDMA_QP_STATE_ERROR; + erdma_modify_qp_internal(qp, &qp_attrs, ERDMA_QP_ATTR_STATE); + up_write(&qp->state_lock); + + erdma_cmdq_build_reqhdr(&req.hdr, CMDQ_SUBMOD_RDMA, + CMDQ_OPCODE_DESTROY_QP); + req.qpn = QP_ID(qp); + + err = erdma_post_cmd_wait(&dev->cmdq, (u64 *)&req, sizeof(req), NULL, + NULL); + if (err) + return err; + + erdma_qp_put(qp); + wait_for_completion(&qp->safe_free); + + if (rdma_is_kernel_res(&qp->ibqp.res)) { + vfree(qp->kern_qp.swr_tbl); + vfree(qp->kern_qp.rwr_tbl); + dma_free_coherent( + &dev->pdev->dev, + WARPPED_BUFSIZE(qp->attrs.rq_size << RQE_SHIFT), + qp->kern_qp.rq_buf, qp->kern_qp.rq_buf_dma_addr); + dma_free_coherent( + &dev->pdev->dev, + WARPPED_BUFSIZE(qp->attrs.sq_size << SQEBB_SHIFT), + qp->kern_qp.sq_buf, qp->kern_qp.sq_buf_dma_addr); + } else { + put_mtt_entries(dev, &qp->user_qp.sq_mtt); + put_mtt_entries(dev, &qp->user_qp.rq_mtt); + erdma_unmap_user_dbrecords(ctx, &qp->user_qp.user_dbr_page); + } + + if (qp->cep) + erdma_cep_put(qp->cep); + xa_erase(&dev->qp_xa, QP_ID(qp)); + + return 0; +} + +void erdma_qp_get_ref(struct ib_qp *ibqp) +{ + erdma_qp_get(to_eqp(ibqp)); +} + +void erdma_qp_put_ref(struct ib_qp *ibqp) +{ + erdma_qp_put(to_eqp(ibqp)); +} + +int erdma_mmap(struct ib_ucontext *ctx, struct vm_area_struct *vma) +{ + struct rdma_user_mmap_entry *rdma_entry; + struct erdma_user_mmap_entry *entry; + pgprot_t prot; + int err; + + rdma_entry = rdma_user_mmap_entry_get(ctx, vma); + if (!rdma_entry) + return -EINVAL; + + entry = to_emmap(rdma_entry); + + switch (entry->mmap_flag) { + case ERDMA_MMAP_IO_NC: + /* map doorbell. */ + prot = pgprot_device(vma->vm_page_prot); + break; + default: + return -EINVAL; + } + + err = rdma_user_mmap_io(ctx, vma, PFN_DOWN(entry->address), PAGE_SIZE, + prot, rdma_entry); + + rdma_user_mmap_entry_put(rdma_entry); + return err; +} + +void erdma_mmap_free(struct rdma_user_mmap_entry *rdma_entry) +{ + struct erdma_user_mmap_entry *entry = to_emmap(rdma_entry); + + kfree(entry); +} + +#define ERDMA_SDB_PAGE 0 +#define ERDMA_SDB_ENTRY 1 +#define ERDMA_SDB_SHARED 2 + +static void alloc_db_resources(struct erdma_dev *dev, + struct erdma_ucontext *ctx) +{ + u32 bitmap_idx; + struct erdma_devattr *attrs = &dev->attrs; + + if (attrs->disable_dwqe) + goto alloc_normal_db; + + /* Try to alloc independent SDB page. */ + spin_lock(&dev->db_bitmap_lock); + bitmap_idx = find_first_zero_bit(dev->sdb_page, attrs->dwqe_pages); + if (bitmap_idx != attrs->dwqe_pages) { + set_bit(bitmap_idx, dev->sdb_page); + spin_unlock(&dev->db_bitmap_lock); + + ctx->sdb_type = ERDMA_SDB_PAGE; + ctx->sdb_idx = bitmap_idx; + ctx->sdb_page_idx = bitmap_idx; + ctx->sdb = dev->func_bar_addr + ERDMA_BAR_SQDB_SPACE_OFFSET + + (bitmap_idx << PAGE_SHIFT); + ctx->sdb_page_off = 0; + + return; + } + + bitmap_idx = find_first_zero_bit(dev->sdb_entry, attrs->dwqe_entries); + if (bitmap_idx != attrs->dwqe_entries) { + set_bit(bitmap_idx, dev->sdb_entry); + spin_unlock(&dev->db_bitmap_lock); + + ctx->sdb_type = ERDMA_SDB_ENTRY; + ctx->sdb_idx = bitmap_idx; + ctx->sdb_page_idx = attrs->dwqe_pages + + bitmap_idx / ERDMA_DWQE_TYPE1_CNT_PER_PAGE; + ctx->sdb_page_off = bitmap_idx % ERDMA_DWQE_TYPE1_CNT_PER_PAGE; + + ctx->sdb = dev->func_bar_addr + ERDMA_BAR_SQDB_SPACE_OFFSET + + (ctx->sdb_page_idx << PAGE_SHIFT); + + return; + } + + spin_unlock(&dev->db_bitmap_lock); + +alloc_normal_db: + ctx->sdb_type = ERDMA_SDB_SHARED; + ctx->sdb_idx = 0; + ctx->sdb_page_idx = ERDMA_SDB_SHARED_PAGE_INDEX; + ctx->sdb_page_off = 0; + + ctx->sdb = dev->func_bar_addr + (ctx->sdb_page_idx << PAGE_SHIFT); +} + +static void erdma_uctx_user_mmap_entries_remove(struct erdma_ucontext *uctx) +{ + rdma_user_mmap_entry_remove(uctx->sq_db_mmap_entry); + rdma_user_mmap_entry_remove(uctx->rq_db_mmap_entry); + rdma_user_mmap_entry_remove(uctx->cq_db_mmap_entry); +} + +int erdma_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *udata) +{ + struct erdma_ucontext *ctx = to_ectx(ibctx); + struct erdma_dev *dev = to_edev(ibctx->device); + int ret; + struct erdma_uresp_alloc_ctx uresp = {}; + + if (atomic_inc_return(&dev->num_ctx) > ERDMA_MAX_CONTEXT) { + ret = -ENOMEM; + goto err_out; + } + + INIT_LIST_HEAD(&ctx->dbrecords_page_list); + mutex_init(&ctx->dbrecords_page_mutex); + + alloc_db_resources(dev, ctx); + + ctx->rdb = dev->func_bar_addr + ERDMA_BAR_RQDB_SPACE_OFFSET; + ctx->cdb = dev->func_bar_addr + ERDMA_BAR_CQDB_SPACE_OFFSET; + + if (udata->outlen < sizeof(uresp)) { + ret = -EINVAL; + goto err_out; + } + + ctx->sq_db_mmap_entry = erdma_user_mmap_entry_insert( + ctx, (void *)ctx->sdb, PAGE_SIZE, ERDMA_MMAP_IO_NC, &uresp.sdb); + if (!ctx->sq_db_mmap_entry) { + ret = -ENOMEM; + goto err_out; + } + + ctx->rq_db_mmap_entry = erdma_user_mmap_entry_insert( + ctx, (void *)ctx->rdb, PAGE_SIZE, ERDMA_MMAP_IO_NC, &uresp.rdb); + if (!ctx->rq_db_mmap_entry) { + ret = -EINVAL; + goto err_out; + } + + ctx->cq_db_mmap_entry = erdma_user_mmap_entry_insert( + ctx, (void *)ctx->cdb, PAGE_SIZE, ERDMA_MMAP_IO_NC, &uresp.cdb); + if (!ctx->cq_db_mmap_entry) { + ret = -EINVAL; + goto err_out; + } + + uresp.dev_id = dev->pdev->device; + uresp.sdb_type = ctx->sdb_type; + uresp.sdb_offset = ctx->sdb_page_off; + + ret = ib_copy_to_udata(udata, &uresp, sizeof(uresp)); + if (ret) + goto err_out; + + return 0; + +err_out: + erdma_uctx_user_mmap_entries_remove(ctx); + atomic_dec(&dev->num_ctx); + return ret; +} + +void erdma_dealloc_ucontext(struct ib_ucontext *ibctx) +{ + struct erdma_ucontext *ctx = to_ectx(ibctx); + struct erdma_dev *dev = to_edev(ibctx->device); + + spin_lock(&dev->db_bitmap_lock); + if (ctx->sdb_type == ERDMA_SDB_PAGE) + clear_bit(ctx->sdb_idx, dev->sdb_page); + else if (ctx->sdb_type == ERDMA_SDB_ENTRY) + clear_bit(ctx->sdb_idx, dev->sdb_entry); + + erdma_uctx_user_mmap_entries_remove(ctx); + + spin_unlock(&dev->db_bitmap_lock); + + atomic_dec(&dev->num_ctx); +} + +static int ib_qp_state_to_erdma_qp_state[IB_QPS_ERR + 1] = { + [IB_QPS_RESET] = ERDMA_QP_STATE_IDLE, + [IB_QPS_INIT] = ERDMA_QP_STATE_IDLE, + [IB_QPS_RTR] = ERDMA_QP_STATE_RTR, + [IB_QPS_RTS] = ERDMA_QP_STATE_RTS, + [IB_QPS_SQD] = ERDMA_QP_STATE_CLOSING, + [IB_QPS_SQE] = ERDMA_QP_STATE_TERMINATE, + [IB_QPS_ERR] = ERDMA_QP_STATE_ERROR +}; + +int erdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int attr_mask, + struct ib_udata *udata) +{ + struct erdma_qp_attrs new_attrs; + enum erdma_qp_attr_mask erdma_attr_mask = 0; + struct erdma_qp *qp = to_eqp(ibqp); + int ret = 0; + + if (attr_mask & ~IB_QP_ATTR_STANDARD_BITS) + return -EOPNOTSUPP; + + memset(&new_attrs, 0, sizeof(new_attrs)); + + if (attr_mask & IB_QP_STATE) { + new_attrs.state = ib_qp_state_to_erdma_qp_state[attr->qp_state]; + + erdma_attr_mask |= ERDMA_QP_ATTR_STATE; + } + + down_write(&qp->state_lock); + + ret = erdma_modify_qp_internal(qp, &new_attrs, erdma_attr_mask); + + up_write(&qp->state_lock); + + return ret; +} + +int erdma_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *qp_attr, + int qp_attr_mask, struct ib_qp_init_attr *qp_init_attr) +{ + struct erdma_qp *qp; + struct erdma_dev *dev; + + if (ibqp && qp_attr && qp_init_attr) { + qp = to_eqp(ibqp); + dev = to_edev(ibqp->device); + } else { + return -EINVAL; + } + + qp_attr->cap.max_inline_data = ERDMA_MAX_INLINE; + qp_init_attr->cap.max_inline_data = ERDMA_MAX_INLINE; + + qp_attr->cap.max_send_wr = qp->attrs.sq_size; + qp_attr->cap.max_recv_wr = qp->attrs.rq_size; + qp_attr->cap.max_send_sge = qp->attrs.max_send_sge; + qp_attr->cap.max_recv_sge = qp->attrs.max_recv_sge; + + qp_attr->path_mtu = ib_mtu_int_to_enum(dev->netdev->mtu); + qp_attr->max_rd_atomic = qp->attrs.irq_size; + qp_attr->max_dest_rd_atomic = qp->attrs.orq_size; + + qp_attr->qp_access_flags = IB_ACCESS_LOCAL_WRITE | + IB_ACCESS_REMOTE_WRITE | + IB_ACCESS_REMOTE_READ; + + qp_init_attr->cap = qp_attr->cap; + + return 0; +} + +static int erdma_init_user_cq(struct erdma_ucontext *ctx, struct erdma_cq *cq, + struct erdma_ureq_create_cq *ureq) +{ + int ret; + struct erdma_dev *dev = to_edev(cq->ibcq.device); + + ret = get_mtt_entries(dev, &cq->user_cq.qbuf_mtt, ureq->qbuf_va, + ureq->qbuf_len, 0, ureq->qbuf_va, SZ_64M - SZ_4K, + 1); + if (ret) + return ret; + + ret = erdma_map_user_dbrecords(ctx, ureq->db_record_va, + &cq->user_cq.user_dbr_page, + &cq->user_cq.db_info_dma_addr); + if (ret) + put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + + return ret; +} + +static int erdma_init_kernel_cq(struct erdma_cq *cq) +{ + struct erdma_dev *dev = to_edev(cq->ibcq.device); + + cq->kern_cq.qbuf = + dma_alloc_coherent(&dev->pdev->dev, + WARPPED_BUFSIZE(cq->depth << CQE_SHIFT), + &cq->kern_cq.qbuf_dma_addr, GFP_KERNEL); + if (!cq->kern_cq.qbuf) + return -ENOMEM; + + cq->kern_cq.db_record = + (u64 *)(cq->kern_cq.qbuf + (cq->depth << CQE_SHIFT)); + spin_lock_init(&cq->kern_cq.lock); + /* use default cqdb addr */ + cq->kern_cq.db = dev->func_bar + ERDMA_BAR_CQDB_SPACE_OFFSET; + + return 0; +} + +int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct ib_udata *udata) +{ + struct erdma_cq *cq = to_ecq(ibcq); + struct erdma_dev *dev = to_edev(ibcq->device); + unsigned int depth = attr->cqe; + int ret; + struct erdma_ucontext *ctx = rdma_udata_to_drv_context( + udata, struct erdma_ucontext, ibucontext); + + if (depth > dev->attrs.max_cqe) + return -EINVAL; + + depth = roundup_pow_of_two(depth); + cq->ibcq.cqe = depth; + cq->depth = depth; + cq->assoc_eqn = attr->comp_vector + 1; + + ret = xa_alloc_cyclic(&dev->cq_xa, &cq->cqn, cq, + XA_LIMIT(1, dev->attrs.max_cq - 1), + &dev->next_alloc_cqn, GFP_KERNEL); + if (ret < 0) + return ret; + + if (!rdma_is_kernel_res(&ibcq->res)) { + struct erdma_ureq_create_cq ureq; + struct erdma_uresp_create_cq uresp; + + ret = ib_copy_from_udata(&ureq, udata, + min(udata->inlen, sizeof(ureq))); + if (ret) + goto err_out_xa; + + ret = erdma_init_user_cq(ctx, cq, &ureq); + if (ret) + goto err_out_xa; + + uresp.cq_id = cq->cqn; + uresp.num_cqe = depth; + + ret = ib_copy_to_udata(udata, &uresp, + min(sizeof(uresp), udata->outlen)); + if (ret) + goto err_free_res; + } else { + ret = erdma_init_kernel_cq(cq); + if (ret) + goto err_out_xa; + } + + ret = create_cq_cmd(dev, cq); + if (ret) + goto err_free_res; + + return 0; + +err_free_res: + if (!rdma_is_kernel_res(&ibcq->res)) { + erdma_unmap_user_dbrecords(ctx, &cq->user_cq.user_dbr_page); + put_mtt_entries(dev, &cq->user_cq.qbuf_mtt); + } else { + dma_free_coherent(&dev->pdev->dev, + WARPPED_BUFSIZE(depth << CQE_SHIFT), + cq->kern_cq.qbuf, cq->kern_cq.qbuf_dma_addr); + } + +err_out_xa: + xa_erase(&dev->cq_xa, cq->cqn); + + return ret; +} + +void erdma_port_event(struct erdma_dev *dev, enum ib_event_type reason) +{ + struct ib_event event; + + event.device = &dev->ibdev; + event.element.port_num = 1; + event.event = reason; + + ib_dispatch_event(&event); +} diff --git a/drivers/infiniband/hw/erdma/erdma_verbs.h b/drivers/infiniband/hw/erdma/erdma_verbs.h new file mode 100644 index 000000000000..c7baddb1f292 --- /dev/null +++ b/drivers/infiniband/hw/erdma/erdma_verbs.h @@ -0,0 +1,342 @@ +/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ + +/* Authors: Cheng Xu */ +/* Kai Shen */ +/* Copyright (c) 2020-2022, Alibaba Group. */ + +#ifndef __ERDMA_VERBS_H__ +#define __ERDMA_VERBS_H__ + +#include + +#include +#include +#include + +#include "erdma.h" +#include "erdma_cm.h" +#include "erdma_hw.h" + +/* RDMA Capability. */ +#define ERDMA_MAX_PD (128 * 1024) +#define ERDMA_MAX_SEND_WR 4096 +#define ERDMA_MAX_ORD 128 +#define ERDMA_MAX_IRD 128 +#define ERDMA_MAX_SGE_RD 1 +#define ERDMA_MAX_CONTEXT (128 * 1024) +#define ERDMA_MAX_SEND_SGE 6 +#define ERDMA_MAX_RECV_SGE 1 +#define ERDMA_MAX_INLINE (sizeof(struct erdma_sge) * (ERDMA_MAX_SEND_SGE)) +#define ERDMA_MAX_FRMR_PA 512 + +enum { + ERDMA_MMAP_IO_NC = 0, /* no cache */ +}; + +struct erdma_user_mmap_entry { + struct rdma_user_mmap_entry rdma_entry; + u64 address; + u8 mmap_flag; +}; + +struct erdma_ucontext { + struct ib_ucontext ibucontext; + + u32 sdb_type; + u32 sdb_idx; + u32 sdb_page_idx; + u32 sdb_page_off; + u64 sdb; + u64 rdb; + u64 cdb; + + struct rdma_user_mmap_entry *sq_db_mmap_entry; + struct rdma_user_mmap_entry *rq_db_mmap_entry; + struct rdma_user_mmap_entry *cq_db_mmap_entry; + + /* doorbell records */ + struct list_head dbrecords_page_list; + struct mutex dbrecords_page_mutex; +}; + +struct erdma_pd { + struct ib_pd ibpd; + u32 pdn; +}; + +/* + * MemoryRegion definition. + */ +#define ERDMA_MAX_INLINE_MTT_ENTRIES 4 +#define MTT_SIZE(mtt_cnt) (mtt_cnt << 3) /* per mtt takes 8 Bytes. */ +#define ERDMA_MR_MAX_MTT_CNT 524288 +#define ERDMA_MTT_ENTRY_SIZE 8 + +#define ERDMA_MR_TYPE_NORMAL 0 +#define ERDMA_MR_TYPE_FRMR 1 +#define ERDMA_MR_TYPE_DMA 2 + +#define ERDMA_MR_INLINE_MTT 0 +#define ERDMA_MR_INDIRECT_MTT 1 + +#define ERDMA_MR_ACC_LR BIT(0) +#define ERDMA_MR_ACC_LW BIT(1) +#define ERDMA_MR_ACC_RR BIT(2) +#define ERDMA_MR_ACC_RW BIT(3) + +static inline u8 to_erdma_access_flags(int access) +{ + return (access & IB_ACCESS_REMOTE_READ ? ERDMA_MR_ACC_RR : 0) | + (access & IB_ACCESS_LOCAL_WRITE ? ERDMA_MR_ACC_LW : 0) | + (access & IB_ACCESS_REMOTE_WRITE ? ERDMA_MR_ACC_RW : 0); +} + +struct erdma_mem { + struct ib_umem *umem; + void *mtt_buf; + u32 mtt_type; + u32 page_size; + u32 page_offset; + u32 page_cnt; + u32 mtt_nents; + + u64 va; + u64 len; + + u64 mtt_entry[ERDMA_MAX_INLINE_MTT_ENTRIES]; +}; + +struct erdma_mr { + struct ib_mr ibmr; + struct erdma_mem mem; + u8 type; + u8 access; + u8 valid; +}; + +struct erdma_user_dbrecords_page { + struct list_head list; + struct ib_umem *umem; + u64 va; + int refcnt; +}; + +struct erdma_uqp { + struct erdma_mem sq_mtt; + struct erdma_mem rq_mtt; + + dma_addr_t sq_db_info_dma_addr; + dma_addr_t rq_db_info_dma_addr; + + struct erdma_user_dbrecords_page *user_dbr_page; + + u32 rq_offset; +}; + +struct erdma_kqp { + u16 sq_pi; + u16 sq_ci; + + u16 rq_pi; + u16 rq_ci; + + u64 *swr_tbl; + u64 *rwr_tbl; + + void __iomem *hw_sq_db; + void __iomem *hw_rq_db; + + void *sq_buf; + dma_addr_t sq_buf_dma_addr; + + void *rq_buf; + dma_addr_t rq_buf_dma_addr; + + void *sq_db_info; + void *rq_db_info; + + u8 sig_all; +}; + +enum erdma_qp_state { + ERDMA_QP_STATE_IDLE = 0, + ERDMA_QP_STATE_RTR = 1, + ERDMA_QP_STATE_RTS = 2, + ERDMA_QP_STATE_CLOSING = 3, + ERDMA_QP_STATE_TERMINATE = 4, + ERDMA_QP_STATE_ERROR = 5, + ERDMA_QP_STATE_UNDEF = 7, + ERDMA_QP_STATE_COUNT = 8 +}; + +enum erdma_qp_attr_mask { + ERDMA_QP_ATTR_STATE = (1 << 0), + ERDMA_QP_ATTR_LLP_HANDLE = (1 << 2), + ERDMA_QP_ATTR_ORD = (1 << 3), + ERDMA_QP_ATTR_IRD = (1 << 4), + ERDMA_QP_ATTR_SQ_SIZE = (1 << 5), + ERDMA_QP_ATTR_RQ_SIZE = (1 << 6), + ERDMA_QP_ATTR_MPA = (1 << 7) +}; + +struct erdma_qp_attrs { + enum erdma_qp_state state; + enum erdma_cc_alg cc; /* Congestion control algorithm */ + u32 sq_size; + u32 rq_size; + u32 orq_size; + u32 irq_size; + u32 max_send_sge; + u32 max_recv_sge; + u32 cookie; +#define ERDMA_QP_ACTIVE 0 +#define ERDMA_QP_PASSIVE 1 + u8 qp_type; + u8 pd_len; +}; + +struct erdma_qp { + struct ib_qp ibqp; + struct kref ref; + struct completion safe_free; + struct erdma_dev *dev; + struct erdma_cep *cep; + struct rw_semaphore state_lock; + + union { + struct erdma_kqp kern_qp; + struct erdma_uqp user_qp; + }; + + struct erdma_cq *scq; + struct erdma_cq *rcq; + + struct erdma_qp_attrs attrs; + spinlock_t lock; +}; + +struct erdma_kcq_info { + void *qbuf; + dma_addr_t qbuf_dma_addr; + u32 ci; + u32 cmdsn; + u32 notify_cnt; + + spinlock_t lock; + u8 __iomem *db; + u64 *db_record; +}; + +struct erdma_ucq_info { + struct erdma_mem qbuf_mtt; + struct erdma_user_dbrecords_page *user_dbr_page; + dma_addr_t db_info_dma_addr; +}; + +struct erdma_cq { + struct ib_cq ibcq; + u32 cqn; + + u32 depth; + u32 assoc_eqn; + + union { + struct erdma_kcq_info kern_cq; + struct erdma_ucq_info user_cq; + }; +}; + +#define QP_ID(qp) ((qp)->ibqp.qp_num) + +static inline struct erdma_qp *find_qp_by_qpn(struct erdma_dev *dev, int id) +{ + return (struct erdma_qp *)xa_load(&dev->qp_xa, id); +} + +static inline struct erdma_cq *find_cq_by_cqn(struct erdma_dev *dev, int id) +{ + return (struct erdma_cq *)xa_load(&dev->cq_xa, id); +} + +void erdma_qp_get(struct erdma_qp *qp); +void erdma_qp_put(struct erdma_qp *qp); +int erdma_modify_qp_internal(struct erdma_qp *qp, struct erdma_qp_attrs *attrs, + enum erdma_qp_attr_mask mask); +void erdma_qp_llp_close(struct erdma_qp *qp); +void erdma_qp_cm_drop(struct erdma_qp *qp); + +static inline struct erdma_ucontext *to_ectx(struct ib_ucontext *ibctx) +{ + return container_of(ibctx, struct erdma_ucontext, ibucontext); +} + +static inline struct erdma_pd *to_epd(struct ib_pd *pd) +{ + return container_of(pd, struct erdma_pd, ibpd); +} + +static inline struct erdma_mr *to_emr(struct ib_mr *ibmr) +{ + return container_of(ibmr, struct erdma_mr, ibmr); +} + +static inline struct erdma_qp *to_eqp(struct ib_qp *qp) +{ + return container_of(qp, struct erdma_qp, ibqp); +} + +static inline struct erdma_cq *to_ecq(struct ib_cq *ibcq) +{ + return container_of(ibcq, struct erdma_cq, ibcq); +} + +static inline struct erdma_user_mmap_entry * +to_emmap(struct rdma_user_mmap_entry *ibmmap) +{ + return container_of(ibmmap, struct erdma_user_mmap_entry, rdma_entry); +} + +int erdma_alloc_ucontext(struct ib_ucontext *ibctx, struct ib_udata *data); +void erdma_dealloc_ucontext(struct ib_ucontext *ibctx); +int erdma_query_device(struct ib_device *dev, struct ib_device_attr *attr, + struct ib_udata *data); +int erdma_get_port_immutable(struct ib_device *dev, u32 port, + struct ib_port_immutable *ib_port_immutable); +int erdma_create_cq(struct ib_cq *ibcq, const struct ib_cq_init_attr *attr, + struct ib_udata *data); +int erdma_query_port(struct ib_device *dev, u32 port, + struct ib_port_attr *attr); +int erdma_query_gid(struct ib_device *dev, u32 port, int idx, + union ib_gid *gid); +int erdma_alloc_pd(struct ib_pd *ibpd, struct ib_udata *data); +int erdma_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata); +int erdma_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *attr, + struct ib_udata *data); +int erdma_query_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask, + struct ib_qp_init_attr *init_attr); +int erdma_modify_qp(struct ib_qp *ibqp, struct ib_qp_attr *attr, int mask, + struct ib_udata *data); +int erdma_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata); +int erdma_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata); +int erdma_req_notify_cq(struct ib_cq *ibcq, enum ib_cq_notify_flags flags); +struct ib_mr *erdma_reg_user_mr(struct ib_pd *ibpd, u64 start, u64 len, + u64 virt, int access, struct ib_udata *udata); +struct ib_mr *erdma_get_dma_mr(struct ib_pd *ibpd, int rights); +int erdma_dereg_mr(struct ib_mr *ibmr, struct ib_udata *data); +int erdma_mmap(struct ib_ucontext *ctx, struct vm_area_struct *vma); +void erdma_mmap_free(struct rdma_user_mmap_entry *rdma_entry); +void erdma_qp_get_ref(struct ib_qp *ibqp); +void erdma_qp_put_ref(struct ib_qp *ibqp); +struct ib_qp *erdma_get_ibqp(struct ib_device *dev, int id); +int erdma_post_send(struct ib_qp *ibqp, const struct ib_send_wr *send_wr, + const struct ib_send_wr **bad_send_wr); +int erdma_post_recv(struct ib_qp *ibqp, const struct ib_recv_wr *recv_wr, + const struct ib_recv_wr **bad_recv_wr); +int erdma_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *wc); +struct ib_mr *erdma_ib_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, + u32 max_num_sg); +int erdma_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents, + unsigned int *sg_offset); +void erdma_port_event(struct erdma_dev *dev, enum ib_event_type reason); + +#endif diff --git a/drivers/infiniband/hw/hfi1/Kconfig b/drivers/infiniband/hw/hfi1/Kconfig index 6eb739052121..14b92e12bf29 100644 --- a/drivers/infiniband/hw/hfi1/Kconfig +++ b/drivers/infiniband/hw/hfi1/Kconfig @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only config INFINIBAND_HFI1 tristate "Cornelis OPX Gen1 support" - depends on X86_64 && INFINIBAND_RDMAVT && I2C + depends on X86_64 && INFINIBAND_RDMAVT && I2C && !UML select MMU_NOTIFIER select CRC32 select I2C_ALGOBIT diff --git a/drivers/infiniband/hw/hfi1/file_ops.c b/drivers/infiniband/hw/hfi1/file_ops.c index 2e4cf2b11653..629beff053ad 100644 --- a/drivers/infiniband/hw/hfi1/file_ops.c +++ b/drivers/infiniband/hw/hfi1/file_ops.c @@ -1179,8 +1179,10 @@ static int setup_base_ctxt(struct hfi1_filedata *fd, goto done; ret = init_user_ctxt(fd, uctxt); - if (ret) + if (ret) { + hfi1_free_ctxt_rcv_groups(uctxt); goto done; + } user_init(uctxt); diff --git a/drivers/infiniband/hw/hfi1/ipoib_tx.c b/drivers/infiniband/hw/hfi1/ipoib_tx.c index d6bbdb8fcb50..5d9a7b09ca37 100644 --- a/drivers/infiniband/hw/hfi1/ipoib_tx.c +++ b/drivers/infiniband/hw/hfi1/ipoib_tx.c @@ -742,9 +742,7 @@ int hfi1_ipoib_txreq_init(struct hfi1_ipoib_dev_priv *priv) kzalloc_node(sizeof(*tx->sdma_hdr), GFP_KERNEL, priv->dd->node); - netif_tx_napi_add(dev, &txq->napi, - hfi1_ipoib_poll_tx_ring, - NAPI_POLL_WEIGHT); + netif_napi_add_tx(dev, &txq->napi, hfi1_ipoib_poll_tx_ring); } return 0; diff --git a/drivers/infiniband/hw/hfi1/netdev_rx.c b/drivers/infiniband/hw/hfi1/netdev_rx.c index 03b098a494b5..3dfa5aff2512 100644 --- a/drivers/infiniband/hw/hfi1/netdev_rx.c +++ b/drivers/infiniband/hw/hfi1/netdev_rx.c @@ -216,7 +216,7 @@ static int hfi1_netdev_rxq_init(struct hfi1_netdev_rx *rx) * right now. */ set_bit(NAPI_STATE_NO_BUSY_POLL, &rxq->napi.state); - netif_napi_add(dev, &rxq->napi, hfi1_netdev_rx_napi, 64); + netif_napi_add_weight(dev, &rxq->napi, hfi1_netdev_rx_napi, 64); rc = msix_netdev_request_rcd_irq(rxq->rcd); if (rc) goto bail_context_irq_failure; diff --git a/drivers/infiniband/hw/hfi1/pio_copy.c b/drivers/infiniband/hw/hfi1/pio_copy.c index 136f9a99e1e0..7690f996d5e3 100644 --- a/drivers/infiniband/hw/hfi1/pio_copy.c +++ b/drivers/infiniband/hw/hfi1/pio_copy.c @@ -172,7 +172,7 @@ static inline void jcopy(u8 *dest, const u8 *src, u32 n) } /* - * Read nbytes from "from" and and place them in the low bytes + * Read nbytes from "from" and place them in the low bytes * of pbuf->carry. Other bytes are left as-is. Any previous * value in pbuf->carry is lost. * diff --git a/drivers/infiniband/hw/hns/hns_roce_device.h b/drivers/infiniband/hw/hns/hns_roce_device.h index 2855e9ad4b32..f848eedc6a23 100644 --- a/drivers/infiniband/hw/hns/hns_roce_device.h +++ b/drivers/infiniband/hw/hns/hns_roce_device.h @@ -959,6 +959,7 @@ struct hns_roce_dev { const struct hns_roce_hw *hw; void *priv; struct workqueue_struct *irq_workq; + struct work_struct ecc_work; const struct hns_roce_dfx_hw *dfx; u32 func_num; u32 is_vf; diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c index ba3c742258ef..cbdafaac678a 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.c +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.c @@ -55,6 +55,42 @@ enum { CMD_RST_PRC_EBUSY, }; +enum ecc_resource_type { + ECC_RESOURCE_QPC, + ECC_RESOURCE_CQC, + ECC_RESOURCE_MPT, + ECC_RESOURCE_SRQC, + ECC_RESOURCE_GMV, + ECC_RESOURCE_QPC_TIMER, + ECC_RESOURCE_CQC_TIMER, + ECC_RESOURCE_SCCC, + ECC_RESOURCE_COUNT, +}; + +static const struct { + const char *name; + u8 read_bt0_op; + u8 write_bt0_op; +} fmea_ram_res[] = { + { "ECC_RESOURCE_QPC", + HNS_ROCE_CMD_READ_QPC_BT0, HNS_ROCE_CMD_WRITE_QPC_BT0 }, + { "ECC_RESOURCE_CQC", + HNS_ROCE_CMD_READ_CQC_BT0, HNS_ROCE_CMD_WRITE_CQC_BT0 }, + { "ECC_RESOURCE_MPT", + HNS_ROCE_CMD_READ_MPT_BT0, HNS_ROCE_CMD_WRITE_MPT_BT0 }, + { "ECC_RESOURCE_SRQC", + HNS_ROCE_CMD_READ_SRQC_BT0, HNS_ROCE_CMD_WRITE_SRQC_BT0 }, + /* ECC_RESOURCE_GMV is handled by cmdq, not mailbox */ + { "ECC_RESOURCE_GMV", + 0, 0 }, + { "ECC_RESOURCE_QPC_TIMER", + HNS_ROCE_CMD_READ_QPC_TIMER_BT0, HNS_ROCE_CMD_WRITE_QPC_TIMER_BT0 }, + { "ECC_RESOURCE_CQC_TIMER", + HNS_ROCE_CMD_READ_CQC_TIMER_BT0, HNS_ROCE_CMD_WRITE_CQC_TIMER_BT0 }, + { "ECC_RESOURCE_SCCC", + HNS_ROCE_CMD_READ_SCCC_BT0, HNS_ROCE_CMD_WRITE_SCCC_BT0 }, +}; + static inline void set_data_seg_v2(struct hns_roce_v2_wqe_data_seg *dseg, struct ib_sge *sg) { @@ -5855,12 +5891,12 @@ static struct hns_roce_aeqe *next_aeqe_sw_v2(struct hns_roce_eq *eq) !!(eq->cons_index & eq->entries)) ? aeqe : NULL; } -static int hns_roce_v2_aeq_int(struct hns_roce_dev *hr_dev, - struct hns_roce_eq *eq) +static irqreturn_t hns_roce_v2_aeq_int(struct hns_roce_dev *hr_dev, + struct hns_roce_eq *eq) { struct device *dev = hr_dev->dev; struct hns_roce_aeqe *aeqe = next_aeqe_sw_v2(eq); - int aeqe_found = 0; + irqreturn_t aeqe_found = IRQ_NONE; int event_type; u32 queue_num; int sub_type; @@ -5914,7 +5950,7 @@ static int hns_roce_v2_aeq_int(struct hns_roce_dev *hr_dev, eq->event_type = event_type; eq->sub_type = sub_type; ++eq->cons_index; - aeqe_found = 1; + aeqe_found = IRQ_HANDLED; hns_roce_v2_init_irq_work(hr_dev, eq, queue_num); @@ -5922,7 +5958,8 @@ static int hns_roce_v2_aeq_int(struct hns_roce_dev *hr_dev, } update_eq_db(eq); - return aeqe_found; + + return IRQ_RETVAL(aeqe_found); } static struct hns_roce_ceqe *next_ceqe_sw_v2(struct hns_roce_eq *eq) @@ -5937,11 +5974,11 @@ static struct hns_roce_ceqe *next_ceqe_sw_v2(struct hns_roce_eq *eq) !!(eq->cons_index & eq->entries)) ? ceqe : NULL; } -static int hns_roce_v2_ceq_int(struct hns_roce_dev *hr_dev, - struct hns_roce_eq *eq) +static irqreturn_t hns_roce_v2_ceq_int(struct hns_roce_dev *hr_dev, + struct hns_roce_eq *eq) { struct hns_roce_ceqe *ceqe = next_ceqe_sw_v2(eq); - int ceqe_found = 0; + irqreturn_t ceqe_found = IRQ_NONE; u32 cqn; while (ceqe) { @@ -5955,21 +5992,21 @@ static int hns_roce_v2_ceq_int(struct hns_roce_dev *hr_dev, hns_roce_cq_completion(hr_dev, cqn); ++eq->cons_index; - ceqe_found = 1; + ceqe_found = IRQ_HANDLED; ceqe = next_ceqe_sw_v2(eq); } update_eq_db(eq); - return ceqe_found; + return IRQ_RETVAL(ceqe_found); } static irqreturn_t hns_roce_v2_msix_interrupt_eq(int irq, void *eq_ptr) { struct hns_roce_eq *eq = eq_ptr; struct hns_roce_dev *hr_dev = eq->hr_dev; - int int_work; + irqreturn_t int_work; if (eq->type_flag == HNS_ROCE_CEQ) /* Completion event interrupt */ @@ -5981,27 +6018,22 @@ static irqreturn_t hns_roce_v2_msix_interrupt_eq(int irq, void *eq_ptr) return IRQ_RETVAL(int_work); } -static irqreturn_t hns_roce_v2_msix_interrupt_abn(int irq, void *dev_id) +static irqreturn_t abnormal_interrupt_basic(struct hns_roce_dev *hr_dev, + u32 int_st) { - struct hns_roce_dev *hr_dev = dev_id; - struct device *dev = hr_dev->dev; - int int_work = 0; - u32 int_st; + struct pci_dev *pdev = hr_dev->pci_dev; + struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); + const struct hnae3_ae_ops *ops = ae_dev->ops; + irqreturn_t int_work = IRQ_NONE; u32 int_en; - /* Abnormal interrupt */ - int_st = roce_read(hr_dev, ROCEE_VF_ABN_INT_ST_REG); int_en = roce_read(hr_dev, ROCEE_VF_ABN_INT_EN_REG); if (int_st & BIT(HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S)) { - struct pci_dev *pdev = hr_dev->pci_dev; - struct hnae3_ae_dev *ae_dev = pci_get_drvdata(pdev); - const struct hnae3_ae_ops *ops = ae_dev->ops; + dev_err(hr_dev->dev, "AEQ overflow!\n"); - dev_err(dev, "AEQ overflow!\n"); - - int_st |= 1 << HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S; - roce_write(hr_dev, ROCEE_VF_ABN_INT_ST_REG, int_st); + roce_write(hr_dev, ROCEE_VF_ABN_INT_ST_REG, + 1 << HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S); /* Set reset level for reset_event() */ if (ops->set_default_reset_request) @@ -6013,19 +6045,165 @@ static irqreturn_t hns_roce_v2_msix_interrupt_abn(int irq, void *dev_id) int_en |= 1 << HNS_ROCE_V2_VF_ABN_INT_EN_S; roce_write(hr_dev, ROCEE_VF_ABN_INT_EN_REG, int_en); - int_work = 1; - } else if (int_st & BIT(HNS_ROCE_V2_VF_INT_ST_RAS_INT_S)) { - dev_err(dev, "RAS interrupt!\n"); - - int_st |= 1 << HNS_ROCE_V2_VF_INT_ST_RAS_INT_S; - roce_write(hr_dev, ROCEE_VF_ABN_INT_ST_REG, int_st); - - int_en |= 1 << HNS_ROCE_V2_VF_ABN_INT_EN_S; - roce_write(hr_dev, ROCEE_VF_ABN_INT_EN_REG, int_en); - - int_work = 1; + int_work = IRQ_HANDLED; } else { - dev_err(dev, "There is no abnormal irq found!\n"); + dev_err(hr_dev->dev, "there is no basic abn irq found.\n"); + } + + return IRQ_RETVAL(int_work); +} + +static int fmea_ram_ecc_query(struct hns_roce_dev *hr_dev, + struct fmea_ram_ecc *ecc_info) +{ + struct hns_roce_cmq_desc desc; + struct hns_roce_cmq_req *req = (struct hns_roce_cmq_req *)desc.data; + int ret; + + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_QUERY_RAM_ECC, true); + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) + return ret; + + ecc_info->is_ecc_err = hr_reg_read(req, QUERY_RAM_ECC_1BIT_ERR); + ecc_info->res_type = hr_reg_read(req, QUERY_RAM_ECC_RES_TYPE); + ecc_info->index = hr_reg_read(req, QUERY_RAM_ECC_TAG); + + return 0; +} + +static int fmea_recover_gmv(struct hns_roce_dev *hr_dev, u32 idx) +{ + struct hns_roce_cmq_desc desc; + struct hns_roce_cmq_req *req = (struct hns_roce_cmq_req *)desc.data; + u32 addr_upper; + u32 addr_low; + int ret; + + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_CFG_GMV_BT, true); + hr_reg_write(req, CFG_GMV_BT_IDX, idx); + + ret = hns_roce_cmq_send(hr_dev, &desc, 1); + if (ret) { + dev_err(hr_dev->dev, + "failed to execute cmd to read gmv, ret = %d.\n", ret); + return ret; + } + + addr_low = hr_reg_read(req, CFG_GMV_BT_BA_L); + addr_upper = hr_reg_read(req, CFG_GMV_BT_BA_H); + + hns_roce_cmq_setup_basic_desc(&desc, HNS_ROCE_OPC_CFG_GMV_BT, false); + hr_reg_write(req, CFG_GMV_BT_BA_L, addr_low); + hr_reg_write(req, CFG_GMV_BT_BA_H, addr_upper); + hr_reg_write(req, CFG_GMV_BT_IDX, idx); + + return hns_roce_cmq_send(hr_dev, &desc, 1); +} + +static u64 fmea_get_ram_res_addr(u32 res_type, __le64 *data) +{ + if (res_type == ECC_RESOURCE_QPC_TIMER || + res_type == ECC_RESOURCE_CQC_TIMER || + res_type == ECC_RESOURCE_SCCC) + return le64_to_cpu(*data); + + return le64_to_cpu(*data) << PAGE_SHIFT; +} + +static int fmea_recover_others(struct hns_roce_dev *hr_dev, u32 res_type, + u32 index) +{ + u8 write_bt0_op = fmea_ram_res[res_type].write_bt0_op; + u8 read_bt0_op = fmea_ram_res[res_type].read_bt0_op; + struct hns_roce_cmd_mailbox *mailbox; + u64 addr; + int ret; + + mailbox = hns_roce_alloc_cmd_mailbox(hr_dev); + if (IS_ERR(mailbox)) + return PTR_ERR(mailbox); + + ret = hns_roce_cmd_mbox(hr_dev, 0, mailbox->dma, read_bt0_op, index); + if (ret) { + dev_err(hr_dev->dev, + "failed to execute cmd to read fmea ram, ret = %d.\n", + ret); + goto out; + } + + addr = fmea_get_ram_res_addr(res_type, mailbox->buf); + + ret = hns_roce_cmd_mbox(hr_dev, addr, 0, write_bt0_op, index); + if (ret) + dev_err(hr_dev->dev, + "failed to execute cmd to write fmea ram, ret = %d.\n", + ret); + +out: + hns_roce_free_cmd_mailbox(hr_dev, mailbox); + return ret; +} + +static void fmea_ram_ecc_recover(struct hns_roce_dev *hr_dev, + struct fmea_ram_ecc *ecc_info) +{ + u32 res_type = ecc_info->res_type; + u32 index = ecc_info->index; + int ret; + + BUILD_BUG_ON(ARRAY_SIZE(fmea_ram_res) != ECC_RESOURCE_COUNT); + + if (res_type >= ECC_RESOURCE_COUNT) { + dev_err(hr_dev->dev, "unsupported fmea ram ecc type %u.\n", + res_type); + return; + } + + if (res_type == ECC_RESOURCE_GMV) + ret = fmea_recover_gmv(hr_dev, index); + else + ret = fmea_recover_others(hr_dev, res_type, index); + if (ret) + dev_err(hr_dev->dev, + "failed to recover %s, index = %u, ret = %d.\n", + fmea_ram_res[res_type].name, index, ret); +} + +static void fmea_ram_ecc_work(struct work_struct *ecc_work) +{ + struct hns_roce_dev *hr_dev = + container_of(ecc_work, struct hns_roce_dev, ecc_work); + struct fmea_ram_ecc ecc_info = {}; + + if (fmea_ram_ecc_query(hr_dev, &ecc_info)) { + dev_err(hr_dev->dev, "failed to query fmea ram ecc.\n"); + return; + } + + if (!ecc_info.is_ecc_err) { + dev_err(hr_dev->dev, "there is no fmea ram ecc err found.\n"); + return; + } + + fmea_ram_ecc_recover(hr_dev, &ecc_info); +} + +static irqreturn_t hns_roce_v2_msix_interrupt_abn(int irq, void *dev_id) +{ + struct hns_roce_dev *hr_dev = dev_id; + irqreturn_t int_work = IRQ_NONE; + u32 int_st; + + int_st = roce_read(hr_dev, ROCEE_VF_ABN_INT_ST_REG); + + if (int_st) { + int_work = abnormal_interrupt_basic(hr_dev, int_st); + } else if (hr_dev->pci_dev->revision >= PCI_REVISION_ID_HIP09) { + queue_work(hr_dev->irq_workq, &hr_dev->ecc_work); + int_work = IRQ_HANDLED; + } else { + dev_err(hr_dev->dev, "there is no abnormal irq found.\n"); } return IRQ_RETVAL(int_work); @@ -6342,6 +6520,8 @@ static int hns_roce_v2_init_eq_table(struct hns_roce_dev *hr_dev) } } + INIT_WORK(&hr_dev->ecc_work, fmea_ram_ecc_work); + hr_dev->irq_workq = alloc_ordered_workqueue("hns_roce_irq_workq", 0); if (!hr_dev->irq_workq) { dev_err(dev, "failed to create irq workqueue.\n"); diff --git a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h index 7ffb7824d268..f96debac30fe 100644 --- a/drivers/infiniband/hw/hns/hns_roce_hw_v2.h +++ b/drivers/infiniband/hw/hns/hns_roce_hw_v2.h @@ -250,6 +250,7 @@ enum hns_roce_opcode_type { HNS_ROCE_OPC_CFG_GMV_TBL = 0x850f, HNS_ROCE_OPC_CFG_GMV_BT = 0x8510, HNS_ROCE_OPC_EXT_CFG = 0x8512, + HNS_ROCE_QUERY_RAM_ECC = 0x8513, HNS_SWITCH_PARAMETER_CFG = 0x1033, }; @@ -1107,6 +1108,11 @@ enum { #define CFG_GMV_BT_BA_H CMQ_REQ_FIELD_LOC(51, 32) #define CFG_GMV_BT_IDX CMQ_REQ_FIELD_LOC(95, 64) +/* Fields of HNS_ROCE_QUERY_RAM_ECC */ +#define QUERY_RAM_ECC_1BIT_ERR CMQ_REQ_FIELD_LOC(31, 0) +#define QUERY_RAM_ECC_RES_TYPE CMQ_REQ_FIELD_LOC(63, 32) +#define QUERY_RAM_ECC_TAG CMQ_REQ_FIELD_LOC(95, 64) + struct hns_roce_cfg_sgid_tb { __le32 table_idx_rsv; __le32 vf_sgid_l; @@ -1343,6 +1349,12 @@ struct hns_roce_dip { struct list_head node; /* all dips are on a list */ }; +struct fmea_ram_ecc { + u32 is_ecc_err; + u32 res_type; + u32 index; +}; + /* only for RNR timeout issue of HIP08 */ #define HNS_ROCE_CLOCK_ADJUST 1000 #define HNS_ROCE_MAX_CQ_PERIOD 65 @@ -1382,7 +1394,6 @@ struct hns_roce_dip { #define HNS_ROCE_V2_ASYNC_EQE_NUM 0x1000 #define HNS_ROCE_V2_VF_INT_ST_AEQ_OVERFLOW_S 0 -#define HNS_ROCE_V2_VF_INT_ST_RAS_INT_S 1 #define HNS_ROCE_EQ_DB_CMD_AEQ 0x0 #define HNS_ROCE_EQ_DB_CMD_AEQ_ARMED 0x1 diff --git a/drivers/infiniband/hw/irdma/cm.c b/drivers/infiniband/hw/irdma/cm.c index 646fa8677490..7b086fe63a24 100644 --- a/drivers/infiniband/hw/irdma/cm.c +++ b/drivers/infiniband/hw/irdma/cm.c @@ -1477,12 +1477,13 @@ irdma_find_listener(struct irdma_cm_core *cm_core, u32 *dst_addr, u16 dst_port, list_for_each_entry (listen_node, &cm_core->listen_list, list) { memcpy(listen_addr, listen_node->loc_addr, sizeof(listen_addr)); listen_port = listen_node->loc_port; + if (listen_port != dst_port || + !(listener_state & listen_node->listener_state)) + continue; /* compare node pair, return node handle if a match */ - if ((!memcmp(listen_addr, dst_addr, sizeof(listen_addr)) || - !memcmp(listen_addr, ip_zero, sizeof(listen_addr))) && - listen_port == dst_port && - vlan_id == listen_node->vlan_id && - (listener_state & listen_node->listener_state)) { + if (!memcmp(listen_addr, ip_zero, sizeof(listen_addr)) || + (!memcmp(listen_addr, dst_addr, sizeof(listen_addr)) && + vlan_id == listen_node->vlan_id)) { refcount_inc(&listen_node->refcnt); spin_unlock_irqrestore(&cm_core->listen_list_lock, flags); diff --git a/drivers/infiniband/hw/irdma/ctrl.c b/drivers/infiniband/hw/irdma/ctrl.c index 58c0e181ca2b..a41e0d21143a 100644 --- a/drivers/infiniband/hw/irdma/ctrl.c +++ b/drivers/infiniband/hw/irdma/ctrl.c @@ -4872,10 +4872,12 @@ int irdma_cfg_fpm_val(struct irdma_sc_dev *dev, u32 qp_count) sd_diff = sd_needed - hmc_fpm_misc->max_sds; if (sd_diff > 128) { - if (qpwanted > 128 && sd_diff > 144) + if (!(loop_count % 2) && qpwanted > 128) { qpwanted /= 2; - mrwanted /= 2; - pblewanted /= 2; + } else { + mrwanted /= 2; + pblewanted /= 2; + } continue; } if (dev->cqp->hmc_profile != IRDMA_HMC_PROFILE_FAVOR_VF && diff --git a/drivers/infiniband/hw/irdma/hw.c b/drivers/infiniband/hw/irdma/hw.c index dd3943d22dc6..4f132c6fb653 100644 --- a/drivers/infiniband/hw/irdma/hw.c +++ b/drivers/infiniband/hw/irdma/hw.c @@ -257,10 +257,6 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) iwqp->last_aeq = info->ae_id; spin_unlock_irqrestore(&iwqp->lock, flags); ctx_info = &iwqp->ctx_info; - if (rdma_protocol_roce(&iwqp->iwdev->ibdev, 1)) - ctx_info->roce_info->err_rq_idx_valid = true; - else - ctx_info->iwarp_info->err_rq_idx_valid = true; } else { if (info->ae_id != IRDMA_AE_CQ_OPERATION_ERROR) continue; @@ -370,16 +366,12 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) case IRDMA_AE_LCE_FUNCTION_CATASTROPHIC: case IRDMA_AE_LCE_CQ_CATASTROPHIC: case IRDMA_AE_UDA_XMIT_DGRAM_TOO_LONG: - if (rdma_protocol_roce(&iwdev->ibdev, 1)) - ctx_info->roce_info->err_rq_idx_valid = false; - else - ctx_info->iwarp_info->err_rq_idx_valid = false; - fallthrough; default: - ibdev_err(&iwdev->ibdev, "abnormal ae_id = 0x%x bool qp=%d qp_id = %d\n", - info->ae_id, info->qp, info->qp_cq_id); + ibdev_err(&iwdev->ibdev, "abnormal ae_id = 0x%x bool qp=%d qp_id = %d, ae_src=%d\n", + info->ae_id, info->qp, info->qp_cq_id, info->ae_src); if (rdma_protocol_roce(&iwdev->ibdev, 1)) { - if (!info->sq && ctx_info->roce_info->err_rq_idx_valid) { + ctx_info->roce_info->err_rq_idx_valid = info->rq; + if (info->rq) { ctx_info->roce_info->err_rq_idx = info->wqe_idx; irdma_sc_qp_setctx_roce(&iwqp->sc_qp, iwqp->host_ctx.va, ctx_info); @@ -388,7 +380,8 @@ static void irdma_process_aeq(struct irdma_pci_f *rf) irdma_cm_disconn(iwqp); break; } - if (!info->sq && ctx_info->iwarp_info->err_rq_idx_valid) { + ctx_info->iwarp_info->err_rq_idx_valid = info->rq; + if (info->rq) { ctx_info->iwarp_info->err_rq_idx = info->wqe_idx; ctx_info->tcp_info_valid = false; ctx_info->iwarp_info_valid = true; @@ -1512,10 +1505,7 @@ static int irdma_hmc_setup(struct irdma_pci_f *rf) int status; u32 qpcnt; - if (rf->rdma_ver == IRDMA_GEN_1) - qpcnt = rsrc_limits_table[rf->limits_sel].qplimit * 2; - else - qpcnt = rsrc_limits_table[rf->limits_sel].qplimit; + qpcnt = rsrc_limits_table[rf->limits_sel].qplimit; rf->sd_type = IRDMA_SD_TYPE_DIRECT; status = irdma_cfg_fpm_val(&rf->sc_dev, qpcnt); @@ -1543,7 +1533,7 @@ static void irdma_del_init_mem(struct irdma_pci_f *rf) rf->obj_mem.pa); rf->obj_mem.va = NULL; if (rf->rdma_ver != IRDMA_GEN_1) { - kfree(rf->allocated_ws_nodes); + bitmap_free(rf->allocated_ws_nodes); rf->allocated_ws_nodes = NULL; } kfree(rf->ceqlist); @@ -1972,9 +1962,8 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) u32 ret; if (rf->rdma_ver != IRDMA_GEN_1) { - rf->allocated_ws_nodes = - kcalloc(BITS_TO_LONGS(IRDMA_MAX_WS_NODES), - sizeof(unsigned long), GFP_KERNEL); + rf->allocated_ws_nodes = bitmap_zalloc(IRDMA_MAX_WS_NODES, + GFP_KERNEL); if (!rf->allocated_ws_nodes) return -ENOMEM; @@ -2023,7 +2012,7 @@ u32 irdma_initialize_hw_rsrc(struct irdma_pci_f *rf) return 0; mem_rsrc_kzalloc_fail: - kfree(rf->allocated_ws_nodes); + bitmap_free(rf->allocated_ws_nodes); rf->allocated_ws_nodes = NULL; return ret; diff --git a/drivers/infiniband/hw/irdma/main.h b/drivers/infiniband/hw/irdma/main.h index ef862bced20f..65e966ad3453 100644 --- a/drivers/infiniband/hw/irdma/main.h +++ b/drivers/infiniband/hw/irdma/main.h @@ -85,7 +85,7 @@ extern struct auxiliary_driver i40iw_auxiliary_drv; #define IRDMA_NO_QSET 0xffff #define IW_CFG_FPM_QP_COUNT 32768 -#define IRDMA_MAX_PAGES_PER_FMR 512 +#define IRDMA_MAX_PAGES_PER_FMR 262144 #define IRDMA_MIN_PAGES_PER_FMR 1 #define IRDMA_CQP_COMPL_RQ_WQE_FLUSHED 2 #define IRDMA_CQP_COMPL_SQ_WQE_FLUSHED 3 diff --git a/drivers/infiniband/hw/irdma/utils.c b/drivers/infiniband/hw/irdma/utils.c index ab3c5208a123..fdf4cc88cb91 100644 --- a/drivers/infiniband/hw/irdma/utils.c +++ b/drivers/infiniband/hw/irdma/utils.c @@ -652,6 +652,7 @@ static const char *const irdma_cqp_cmd_names[IRDMA_MAX_CQP_OPS] = { }; static const struct irdma_cqp_err_info irdma_noncrit_err_list[] = { + {0xffff, 0x8002, "Invalid State"}, {0xffff, 0x8006, "Flush No Wqe Pending"}, {0xffff, 0x8007, "Modify QP Bad Close"}, {0xffff, 0x8009, "LLP Closed"}, diff --git a/drivers/infiniband/hw/irdma/verbs.c b/drivers/infiniband/hw/irdma/verbs.c index 96135a228f26..9b07b8af2997 100644 --- a/drivers/infiniband/hw/irdma/verbs.c +++ b/drivers/infiniband/hw/irdma/verbs.c @@ -1776,11 +1776,11 @@ static int irdma_destroy_cq(struct ib_cq *ib_cq, struct ib_udata *udata) spin_unlock_irqrestore(&iwcq->lock, flags); irdma_cq_wq_destroy(iwdev->rf, cq); - irdma_cq_free_rsrc(iwdev->rf, iwcq); spin_lock_irqsave(&iwceq->ce_lock, flags); irdma_sc_cleanup_ceqes(cq, ceq); spin_unlock_irqrestore(&iwceq->ce_lock, flags); + irdma_cq_free_rsrc(iwdev->rf, iwcq); return 0; } @@ -2605,7 +2605,7 @@ static struct ib_mr *irdma_alloc_mr(struct ib_pd *pd, enum ib_mr_type mr_type, palloc = &iwpbl->pble_alloc; iwmr->page_cnt = max_num_sg; err_code = irdma_get_pble(iwdev->rf->pble_rsrc, palloc, iwmr->page_cnt, - true); + false); if (err_code) goto err_get_pble; @@ -2641,8 +2641,16 @@ static int irdma_set_page(struct ib_mr *ibmr, u64 addr) if (unlikely(iwmr->npages == iwmr->page_cnt)) return -ENOMEM; - pbl = palloc->level1.addr; - pbl[iwmr->npages++] = addr; + if (palloc->level == PBLE_LEVEL_2) { + struct irdma_pble_info *palloc_info = + palloc->level2.leaf + (iwmr->npages >> PBLE_512_SHIFT); + + palloc_info->addr[iwmr->npages & (PBLE_PER_PAGE - 1)] = addr; + } else { + pbl = palloc->level1.addr; + pbl[iwmr->npages] = addr; + } + iwmr->npages++; return 0; } diff --git a/drivers/infiniband/hw/mlx5/cq.c b/drivers/infiniband/hw/mlx5/cq.c index 08371a80fdc2..be189e0525de 100644 --- a/drivers/infiniband/hw/mlx5/cq.c +++ b/drivers/infiniband/hw/mlx5/cq.c @@ -523,6 +523,10 @@ repoll: "Requestor" : "Responder", cq->mcq.cqn); mlx5_ib_dbg(dev, "syndrome 0x%x, vendor syndrome 0x%x\n", err_cqe->syndrome, err_cqe->vendor_err_synd); + if (wc->status != IB_WC_WR_FLUSH_ERR && + (*cur_qp)->type == MLX5_IB_QPT_REG_UMR) + dev->umrc.state = MLX5_UMR_STATE_RECOVER; + if (opcode == MLX5_CQE_REQ_ERR) { wq = &(*cur_qp)->sq; wqe_ctr = be16_to_cpu(cqe64->wqe_counter); diff --git a/drivers/infiniband/hw/mlx5/fs.c b/drivers/infiniband/hw/mlx5/fs.c index 39ffb363ba0c..490ec308e309 100644 --- a/drivers/infiniband/hw/mlx5/fs.c +++ b/drivers/infiniband/hw/mlx5/fs.c @@ -679,7 +679,15 @@ enum flow_table_type { #define MLX5_FS_MAX_TYPES 6 #define MLX5_FS_MAX_ENTRIES BIT(16) -static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_flow_namespace *ns, +static bool mlx5_ib_shared_ft_allowed(struct ib_device *device) +{ + struct mlx5_ib_dev *dev = to_mdev(device); + + return MLX5_CAP_GEN(dev->mdev, shared_object_to_user_object_allowed); +} + +static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_ib_dev *dev, + struct mlx5_flow_namespace *ns, struct mlx5_ib_flow_prio *prio, int priority, int num_entries, int num_groups, @@ -688,6 +696,8 @@ static struct mlx5_ib_flow_prio *_get_prio(struct mlx5_flow_namespace *ns, struct mlx5_flow_table_attr ft_attr = {}; struct mlx5_flow_table *ft; + if (mlx5_ib_shared_ft_allowed(&dev->ib_dev)) + ft_attr.uid = MLX5_SHARED_RESOURCE_UID; ft_attr.prio = priority; ft_attr.max_fte = num_entries; ft_attr.flags = flags; @@ -784,8 +794,8 @@ static struct mlx5_ib_flow_prio *get_flow_table(struct mlx5_ib_dev *dev, ft = prio->flow_table; if (!ft) - return _get_prio(ns, prio, priority, max_table_size, num_groups, - flags); + return _get_prio(dev, ns, prio, priority, max_table_size, + num_groups, flags); return prio; } @@ -927,7 +937,7 @@ int mlx5_ib_fs_add_op_fc(struct mlx5_ib_dev *dev, u32 port_num, prio = &dev->flow_db->opfcs[type]; if (!prio->flow_table) { - prio = _get_prio(ns, prio, priority, + prio = _get_prio(dev, ns, prio, priority, dev->num_ports * MAX_OPFC_RULES, 1, 0); if (IS_ERR(prio)) { err = PTR_ERR(prio); @@ -1407,8 +1417,8 @@ free_ucmd: } static struct mlx5_ib_flow_prio * -_get_flow_table(struct mlx5_ib_dev *dev, - struct mlx5_ib_flow_matcher *fs_matcher, +_get_flow_table(struct mlx5_ib_dev *dev, u16 user_priority, + enum mlx5_flow_namespace_type ns_type, bool mcast) { struct mlx5_flow_namespace *ns = NULL; @@ -1421,11 +1431,11 @@ _get_flow_table(struct mlx5_ib_dev *dev, if (mcast) priority = MLX5_IB_FLOW_MCAST_PRIO; else - priority = ib_prio_to_core_prio(fs_matcher->priority, false); + priority = ib_prio_to_core_prio(user_priority, false); esw_encap = mlx5_eswitch_get_encap_mode(dev->mdev) != DEVLINK_ESWITCH_ENCAP_MODE_NONE; - switch (fs_matcher->ns_type) { + switch (ns_type) { case MLX5_FLOW_NAMESPACE_BYPASS: max_table_size = BIT( MLX5_CAP_FLOWTABLE_NIC_RX(dev->mdev, log_max_ft_size)); @@ -1452,17 +1462,17 @@ _get_flow_table(struct mlx5_ib_dev *dev, reformat_l3_tunnel_to_l2) && esw_encap) flags |= MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT; - priority = fs_matcher->priority; + priority = user_priority; break; case MLX5_FLOW_NAMESPACE_RDMA_RX: max_table_size = BIT( MLX5_CAP_FLOWTABLE_RDMA_RX(dev->mdev, log_max_ft_size)); - priority = fs_matcher->priority; + priority = user_priority; break; case MLX5_FLOW_NAMESPACE_RDMA_TX: max_table_size = BIT( MLX5_CAP_FLOWTABLE_RDMA_TX(dev->mdev, log_max_ft_size)); - priority = fs_matcher->priority; + priority = user_priority; break; default: break; @@ -1470,11 +1480,11 @@ _get_flow_table(struct mlx5_ib_dev *dev, max_table_size = min_t(int, max_table_size, MLX5_FS_MAX_ENTRIES); - ns = mlx5_get_flow_namespace(dev->mdev, fs_matcher->ns_type); + ns = mlx5_get_flow_namespace(dev->mdev, ns_type); if (!ns) return ERR_PTR(-EOPNOTSUPP); - switch (fs_matcher->ns_type) { + switch (ns_type) { case MLX5_FLOW_NAMESPACE_BYPASS: prio = &dev->flow_db->prios[priority]; break; @@ -1499,7 +1509,7 @@ _get_flow_table(struct mlx5_ib_dev *dev, if (prio->flow_table) return prio; - return _get_prio(ns, prio, priority, max_table_size, + return _get_prio(dev, ns, prio, priority, max_table_size, MLX5_FS_MAX_TYPES, flags); } @@ -1618,7 +1628,8 @@ static struct mlx5_ib_flow_handler *raw_fs_rule_add( mcast = raw_fs_is_multicast(fs_matcher, cmd_in); mutex_lock(&dev->flow_db->lock); - ft_prio = _get_flow_table(dev, fs_matcher, mcast); + ft_prio = _get_flow_table(dev, fs_matcher->priority, + fs_matcher->ns_type, mcast); if (IS_ERR(ft_prio)) { err = PTR_ERR(ft_prio); goto unlock; @@ -2015,6 +2026,23 @@ static int flow_matcher_cleanup(struct ib_uobject *uobject, return 0; } +static int steering_anchor_cleanup(struct ib_uobject *uobject, + enum rdma_remove_reason why, + struct uverbs_attr_bundle *attrs) +{ + struct mlx5_ib_steering_anchor *obj = uobject->object; + + if (atomic_read(&obj->usecnt)) + return -EBUSY; + + mutex_lock(&obj->dev->flow_db->lock); + put_flow_table(obj->dev, obj->ft_prio, true); + mutex_unlock(&obj->dev->flow_db->lock); + + kfree(obj); + return 0; +} + static int mlx5_ib_matcher_ns(struct uverbs_attr_bundle *attrs, struct mlx5_ib_flow_matcher *obj) { @@ -2050,12 +2078,10 @@ static int mlx5_ib_matcher_ns(struct uverbs_attr_bundle *attrs, if (err) return err; - if (flags) { - mlx5_ib_ft_type_to_namespace( + if (flags) + return mlx5_ib_ft_type_to_namespace( MLX5_IB_UAPI_FLOW_TABLE_TYPE_NIC_TX, &obj->ns_type); - return 0; - } } obj->ns_type = MLX5_FLOW_NAMESPACE_BYPASS; @@ -2121,6 +2147,75 @@ end: return err; } +static int UVERBS_HANDLER(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE)( + struct uverbs_attr_bundle *attrs) +{ + struct ib_uobject *uobj = uverbs_attr_get_uobject( + attrs, MLX5_IB_ATTR_STEERING_ANCHOR_CREATE_HANDLE); + struct mlx5_ib_dev *dev = mlx5_udata_to_mdev(&attrs->driver_udata); + enum mlx5_ib_uapi_flow_table_type ib_uapi_ft_type; + enum mlx5_flow_namespace_type ns_type; + struct mlx5_ib_steering_anchor *obj; + struct mlx5_ib_flow_prio *ft_prio; + u16 priority; + u32 ft_id; + int err; + + if (!capable(CAP_NET_RAW)) + return -EPERM; + + err = uverbs_get_const(&ib_uapi_ft_type, attrs, + MLX5_IB_ATTR_STEERING_ANCHOR_FT_TYPE); + if (err) + return err; + + err = mlx5_ib_ft_type_to_namespace(ib_uapi_ft_type, &ns_type); + if (err) + return err; + + err = uverbs_copy_from(&priority, attrs, + MLX5_IB_ATTR_STEERING_ANCHOR_PRIORITY); + if (err) + return err; + + obj = kzalloc(sizeof(*obj), GFP_KERNEL); + if (!obj) + return -ENOMEM; + + mutex_lock(&dev->flow_db->lock); + ft_prio = _get_flow_table(dev, priority, ns_type, 0); + if (IS_ERR(ft_prio)) { + mutex_unlock(&dev->flow_db->lock); + err = PTR_ERR(ft_prio); + goto free_obj; + } + + ft_prio->refcount++; + ft_id = mlx5_flow_table_id(ft_prio->flow_table); + mutex_unlock(&dev->flow_db->lock); + + err = uverbs_copy_to(attrs, MLX5_IB_ATTR_STEERING_ANCHOR_FT_ID, + &ft_id, sizeof(ft_id)); + if (err) + goto put_flow_table; + + uobj->object = obj; + obj->dev = dev; + obj->ft_prio = ft_prio; + atomic_set(&obj->usecnt, 0); + + return 0; + +put_flow_table: + mutex_lock(&dev->flow_db->lock); + put_flow_table(dev, ft_prio, true); + mutex_unlock(&dev->flow_db->lock); +free_obj: + kfree(obj); + + return err; +} + static struct ib_flow_action * mlx5_ib_create_modify_header(struct mlx5_ib_dev *dev, enum mlx5_ib_uapi_flow_table_type ft_type, @@ -2477,6 +2572,35 @@ DECLARE_UVERBS_NAMED_OBJECT(MLX5_IB_OBJECT_FLOW_MATCHER, &UVERBS_METHOD(MLX5_IB_METHOD_FLOW_MATCHER_CREATE), &UVERBS_METHOD(MLX5_IB_METHOD_FLOW_MATCHER_DESTROY)); +DECLARE_UVERBS_NAMED_METHOD( + MLX5_IB_METHOD_STEERING_ANCHOR_CREATE, + UVERBS_ATTR_IDR(MLX5_IB_ATTR_STEERING_ANCHOR_CREATE_HANDLE, + MLX5_IB_OBJECT_STEERING_ANCHOR, + UVERBS_ACCESS_NEW, + UA_MANDATORY), + UVERBS_ATTR_CONST_IN(MLX5_IB_ATTR_STEERING_ANCHOR_FT_TYPE, + enum mlx5_ib_uapi_flow_table_type, + UA_MANDATORY), + UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_STEERING_ANCHOR_PRIORITY, + UVERBS_ATTR_TYPE(u16), + UA_MANDATORY), + UVERBS_ATTR_PTR_IN(MLX5_IB_ATTR_STEERING_ANCHOR_FT_ID, + UVERBS_ATTR_TYPE(u32), + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_METHOD_DESTROY( + MLX5_IB_METHOD_STEERING_ANCHOR_DESTROY, + UVERBS_ATTR_IDR(MLX5_IB_ATTR_STEERING_ANCHOR_DESTROY_HANDLE, + MLX5_IB_OBJECT_STEERING_ANCHOR, + UVERBS_ACCESS_DESTROY, + UA_MANDATORY)); + +DECLARE_UVERBS_NAMED_OBJECT( + MLX5_IB_OBJECT_STEERING_ANCHOR, + UVERBS_TYPE_ALLOC_IDR(steering_anchor_cleanup), + &UVERBS_METHOD(MLX5_IB_METHOD_STEERING_ANCHOR_CREATE), + &UVERBS_METHOD(MLX5_IB_METHOD_STEERING_ANCHOR_DESTROY)); + const struct uapi_definition mlx5_ib_flow_defs[] = { UAPI_DEF_CHAIN_OBJ_TREE_NAMED( MLX5_IB_OBJECT_FLOW_MATCHER), @@ -2485,6 +2609,9 @@ const struct uapi_definition mlx5_ib_flow_defs[] = { &mlx5_ib_fs), UAPI_DEF_CHAIN_OBJ_TREE(UVERBS_OBJECT_FLOW_ACTION, &mlx5_ib_flow_actions), + UAPI_DEF_CHAIN_OBJ_TREE_NAMED( + MLX5_IB_OBJECT_STEERING_ANCHOR, + UAPI_DEF_IS_OBJ_SUPPORTED(mlx5_ib_shared_ft_allowed)), {}, }; diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c index b68fddeac0f1..a174a0eee8dc 100644 --- a/drivers/infiniband/hw/mlx5/main.c +++ b/drivers/infiniband/hw/mlx5/main.c @@ -4002,7 +4002,7 @@ static void mlx5_ib_stage_pre_ib_reg_umr_cleanup(struct mlx5_ib_dev *dev) { int err; - err = mlx5_mr_cache_cleanup(dev); + err = mlx5_mkey_cache_cleanup(dev); if (err) mlx5_ib_warn(dev, "mr cache cleanup failed\n"); @@ -4022,7 +4022,7 @@ static int mlx5_ib_stage_post_ib_reg_umr_init(struct mlx5_ib_dev *dev) if (ret) return ret; - ret = mlx5_mr_cache_init(dev); + ret = mlx5_mkey_cache_init(dev); if (ret) { mlx5_ib_warn(dev, "mr cache init failed %d\n", ret); mlx5r_umr_resource_cleanup(dev); diff --git a/drivers/infiniband/hw/mlx5/mlx5_ib.h b/drivers/infiniband/hw/mlx5/mlx5_ib.h index 998b67509a53..2e2ad3918385 100644 --- a/drivers/infiniband/hw/mlx5/mlx5_ib.h +++ b/drivers/infiniband/hw/mlx5/mlx5_ib.h @@ -259,6 +259,12 @@ struct mlx5_ib_flow_matcher { u8 match_criteria_enable; }; +struct mlx5_ib_steering_anchor { + struct mlx5_ib_flow_prio *ft_prio; + struct mlx5_ib_dev *dev; + atomic_t usecnt; +}; + struct mlx5_ib_pp { u16 index; struct mlx5_core_dev *mdev; @@ -613,6 +619,7 @@ struct mlx5_ib_mkey { unsigned int ndescs; struct wait_queue_head wait; refcount_t usecount; + struct mlx5_cache_ent *cache_ent; }; #define MLX5_IB_MTT_PRESENT (MLX5_IB_MTT_READ | MLX5_IB_MTT_WRITE) @@ -635,20 +642,9 @@ struct mlx5_ib_mr { struct ib_mr ibmr; struct mlx5_ib_mkey mmkey; - /* User MR data */ - struct mlx5_cache_ent *cache_ent; - /* Everything after cache_ent is zero'd when MR allocated */ struct ib_umem *umem; union { - /* Used only while the MR is in the cache */ - struct { - u32 out[MLX5_ST_SZ_DW(create_mkey_out)]; - struct mlx5_async_work cb_work; - /* Cache list element */ - struct list_head list; - }; - /* Used only by kernel MRs (umem == NULL) */ struct { void *descs; @@ -688,12 +684,6 @@ struct mlx5_ib_mr { }; }; -/* Zero the fields in the mr that are variant depending on usage */ -static inline void mlx5_clear_mr(struct mlx5_ib_mr *mr) -{ - memset_after(mr, 0, cache_ent); -} - static inline bool is_odp_mr(struct mlx5_ib_mr *mr) { return IS_ENABLED(CONFIG_INFINIBAND_ON_DEMAND_PAGING) && mr->umem && @@ -717,21 +707,29 @@ struct mlx5_ib_umr_context { struct completion done; }; +enum { + MLX5_UMR_STATE_ACTIVE, + MLX5_UMR_STATE_RECOVER, + MLX5_UMR_STATE_ERR, +}; + struct umr_common { struct ib_pd *pd; struct ib_cq *cq; struct ib_qp *qp; - /* control access to UMR QP + /* Protects from UMR QP overflow */ struct semaphore sem; + /* Protects from using UMR while the UMR is not active + */ + struct mutex lock; + unsigned int state; }; struct mlx5_cache_ent { - struct list_head head; - /* sync access to the cahce entry - */ - spinlock_t lock; - + struct xarray mkeys; + unsigned long stored; + unsigned long reserved; char name[4]; u32 order; @@ -743,18 +741,11 @@ struct mlx5_cache_ent { u8 fill_to_high_water:1; /* - * - available_mrs is the length of list head, ie the number of MRs - * available for immediate allocation. - * - total_mrs is available_mrs plus all in use MRs that could be - * returned to the cache. - * - limit is the low water mark for available_mrs, 2* limit is the + * - limit is the low water mark for stored mkeys, 2* limit is the * upper water mark. - * - pending is the number of MRs currently being created */ - u32 total_mrs; - u32 available_mrs; + u32 in_use; u32 limit; - u32 pending; /* Statistics */ u32 miss; @@ -763,9 +754,19 @@ struct mlx5_cache_ent { struct delayed_work dwork; }; -struct mlx5_mr_cache { +struct mlx5r_async_create_mkey { + union { + u32 in[MLX5_ST_SZ_BYTES(create_mkey_in)]; + u32 out[MLX5_ST_SZ_DW(create_mkey_out)]; + }; + struct mlx5_async_work cb_work; + struct mlx5_cache_ent *ent; + u32 mkey; +}; + +struct mlx5_mkey_cache { struct workqueue_struct *wq; - struct mlx5_cache_ent ent[MAX_MR_CACHE_ENTRIES]; + struct mlx5_cache_ent ent[MAX_MKEY_CACHE_ENTRIES]; struct dentry *root; unsigned long last_add; }; @@ -1064,7 +1065,7 @@ struct mlx5_ib_dev { struct mlx5_ib_resources devr; atomic_t mkey_var; - struct mlx5_mr_cache cache; + struct mlx5_mkey_cache cache; struct timer_list delay_timer; /* Prevents soft lock on massive reg MRs */ struct mutex slow_path_mutex; @@ -1309,8 +1310,8 @@ void mlx5_ib_populate_pas(struct ib_umem *umem, size_t page_size, __be64 *pas, u64 access_flags); void mlx5_ib_copy_pas(u64 *old, u64 *new, int step, int num); int mlx5_ib_get_cqe_size(struct ib_cq *ibcq); -int mlx5_mr_cache_init(struct mlx5_ib_dev *dev); -int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev); +int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev); +int mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev); struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, struct mlx5_cache_ent *ent, @@ -1338,7 +1339,7 @@ int mlx5r_odp_create_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq); void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *ibdev); int __init mlx5_ib_odp_init(void); void mlx5_ib_odp_cleanup(void); -void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent); +void mlx5_odp_init_mkey_cache_entry(struct mlx5_cache_ent *ent); void mlx5_odp_populate_xlt(void *xlt, size_t idx, size_t nentries, struct mlx5_ib_mr *mr, int flags); @@ -1357,7 +1358,7 @@ static inline int mlx5r_odp_create_eq(struct mlx5_ib_dev *dev, static inline void mlx5_ib_odp_cleanup_one(struct mlx5_ib_dev *ibdev) {} static inline int mlx5_ib_odp_init(void) { return 0; } static inline void mlx5_ib_odp_cleanup(void) {} -static inline void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent) {} +static inline void mlx5_odp_init_mkey_cache_entry(struct mlx5_cache_ent *ent) {} static inline void mlx5_odp_populate_xlt(void *xlt, size_t idx, size_t nentries, struct mlx5_ib_mr *mr, int flags) {} diff --git a/drivers/infiniband/hw/mlx5/mr.c b/drivers/infiniband/hw/mlx5/mr.c index aedfd7ff4846..129d531bd01b 100644 --- a/drivers/infiniband/hw/mlx5/mr.c +++ b/drivers/infiniband/hw/mlx5/mr.c @@ -82,15 +82,14 @@ static void set_mkc_access_pd_addr_fields(void *mkc, int acc, u64 start_addr, MLX5_SET64(mkc, mkc, start_addr, start_addr); } -static void assign_mkey_variant(struct mlx5_ib_dev *dev, - struct mlx5_ib_mkey *mkey, u32 *in) +static void assign_mkey_variant(struct mlx5_ib_dev *dev, u32 *mkey, u32 *in) { u8 key = atomic_inc_return(&dev->mkey_var); void *mkc; mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); MLX5_SET(mkc, mkc, mkey_7_0, key); - mkey->key = key; + *mkey = key; } static int mlx5_ib_create_mkey(struct mlx5_ib_dev *dev, @@ -98,7 +97,7 @@ static int mlx5_ib_create_mkey(struct mlx5_ib_dev *dev, { int ret; - assign_mkey_variant(dev, mkey, in); + assign_mkey_variant(dev, &mkey->key, in); ret = mlx5_core_create_mkey(dev->mdev, &mkey->key, in, inlen); if (!ret) init_waitqueue_head(&mkey->wait); @@ -106,20 +105,21 @@ static int mlx5_ib_create_mkey(struct mlx5_ib_dev *dev, return ret; } -static int -mlx5_ib_create_mkey_cb(struct mlx5_ib_dev *dev, - struct mlx5_ib_mkey *mkey, - struct mlx5_async_ctx *async_ctx, - u32 *in, int inlen, u32 *out, int outlen, - struct mlx5_async_work *context) +static int mlx5_ib_create_mkey_cb(struct mlx5r_async_create_mkey *async_create) { - MLX5_SET(create_mkey_in, in, opcode, MLX5_CMD_OP_CREATE_MKEY); - assign_mkey_variant(dev, mkey, in); - return mlx5_cmd_exec_cb(async_ctx, in, inlen, out, outlen, - create_mkey_callback, context); + struct mlx5_ib_dev *dev = async_create->ent->dev; + size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); + size_t outlen = MLX5_ST_SZ_BYTES(create_mkey_out); + + MLX5_SET(create_mkey_in, async_create->in, opcode, + MLX5_CMD_OP_CREATE_MKEY); + assign_mkey_variant(dev, &async_create->mkey, async_create->in); + return mlx5_cmd_exec_cb(&dev->async_ctx, async_create->in, inlen, + async_create->out, outlen, create_mkey_callback, + &async_create->cb_work); } -static int mr_cache_max_order(struct mlx5_ib_dev *dev); +static int mkey_cache_max_order(struct mlx5_ib_dev *dev); static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent); static int destroy_mkey(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) @@ -142,40 +142,132 @@ static void create_mkey_warn(struct mlx5_ib_dev *dev, int status, void *out) mlx5_cmd_out_err(dev->mdev, MLX5_CMD_OP_CREATE_MKEY, 0, out); } + +static int push_mkey(struct mlx5_cache_ent *ent, bool limit_pendings, + void *to_store) +{ + XA_STATE(xas, &ent->mkeys, 0); + void *curr; + + xa_lock_irq(&ent->mkeys); + if (limit_pendings && + (ent->reserved - ent->stored) > MAX_PENDING_REG_MR) { + xa_unlock_irq(&ent->mkeys); + return -EAGAIN; + } + while (1) { + /* + * This is cmpxchg (NULL, XA_ZERO_ENTRY) however this version + * doesn't transparently unlock. Instead we set the xas index to + * the current value of reserved every iteration. + */ + xas_set(&xas, ent->reserved); + curr = xas_load(&xas); + if (!curr) { + if (to_store && ent->stored == ent->reserved) + xas_store(&xas, to_store); + else + xas_store(&xas, XA_ZERO_ENTRY); + if (xas_valid(&xas)) { + ent->reserved++; + if (to_store) { + if (ent->stored != ent->reserved) + __xa_store(&ent->mkeys, + ent->stored, + to_store, + GFP_KERNEL); + ent->stored++; + queue_adjust_cache_locked(ent); + WRITE_ONCE(ent->dev->cache.last_add, + jiffies); + } + } + } + xa_unlock_irq(&ent->mkeys); + + /* + * Notice xas_nomem() must always be called as it cleans + * up any cached allocation. + */ + if (!xas_nomem(&xas, GFP_KERNEL)) + break; + xa_lock_irq(&ent->mkeys); + } + if (xas_error(&xas)) + return xas_error(&xas); + if (WARN_ON(curr)) + return -EINVAL; + return 0; +} + +static void undo_push_reserve_mkey(struct mlx5_cache_ent *ent) +{ + void *old; + + ent->reserved--; + old = __xa_erase(&ent->mkeys, ent->reserved); + WARN_ON(old); +} + +static void push_to_reserved(struct mlx5_cache_ent *ent, u32 mkey) +{ + void *old; + + old = __xa_store(&ent->mkeys, ent->stored, xa_mk_value(mkey), 0); + WARN_ON(old); + ent->stored++; +} + +static u32 pop_stored_mkey(struct mlx5_cache_ent *ent) +{ + void *old, *xa_mkey; + + ent->stored--; + ent->reserved--; + + if (ent->stored == ent->reserved) { + xa_mkey = __xa_erase(&ent->mkeys, ent->stored); + WARN_ON(!xa_mkey); + return (u32)xa_to_value(xa_mkey); + } + + xa_mkey = __xa_store(&ent->mkeys, ent->stored, XA_ZERO_ENTRY, + GFP_KERNEL); + WARN_ON(!xa_mkey || xa_is_err(xa_mkey)); + old = __xa_erase(&ent->mkeys, ent->reserved); + WARN_ON(old); + return (u32)xa_to_value(xa_mkey); +} + static void create_mkey_callback(int status, struct mlx5_async_work *context) { - struct mlx5_ib_mr *mr = - container_of(context, struct mlx5_ib_mr, cb_work); - struct mlx5_cache_ent *ent = mr->cache_ent; + struct mlx5r_async_create_mkey *mkey_out = + container_of(context, struct mlx5r_async_create_mkey, cb_work); + struct mlx5_cache_ent *ent = mkey_out->ent; struct mlx5_ib_dev *dev = ent->dev; unsigned long flags; if (status) { - create_mkey_warn(dev, status, mr->out); - kfree(mr); - spin_lock_irqsave(&ent->lock, flags); - ent->pending--; + create_mkey_warn(dev, status, mkey_out->out); + kfree(mkey_out); + xa_lock_irqsave(&ent->mkeys, flags); + undo_push_reserve_mkey(ent); WRITE_ONCE(dev->fill_delay, 1); - spin_unlock_irqrestore(&ent->lock, flags); + xa_unlock_irqrestore(&ent->mkeys, flags); mod_timer(&dev->delay_timer, jiffies + HZ); return; } - mr->mmkey.type = MLX5_MKEY_MR; - mr->mmkey.key |= mlx5_idx_to_mkey( - MLX5_GET(create_mkey_out, mr->out, mkey_index)); - init_waitqueue_head(&mr->mmkey.wait); - + mkey_out->mkey |= mlx5_idx_to_mkey( + MLX5_GET(create_mkey_out, mkey_out->out, mkey_index)); WRITE_ONCE(dev->cache.last_add, jiffies); - spin_lock_irqsave(&ent->lock, flags); - list_add_tail(&mr->list, &ent->head); - ent->available_mrs++; - ent->total_mrs++; + xa_lock_irqsave(&ent->mkeys, flags); + push_to_reserved(ent, mkey_out->mkey); /* If we are doing fill_to_high_water then keep going. */ queue_adjust_cache_locked(ent); - ent->pending--; - spin_unlock_irqrestore(&ent->lock, flags); + xa_unlock_irqrestore(&ent->mkeys, flags); + kfree(mkey_out); } static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) @@ -197,15 +289,8 @@ static int get_mkc_octo_size(unsigned int access_mode, unsigned int ndescs) return ret; } -static struct mlx5_ib_mr *alloc_cache_mr(struct mlx5_cache_ent *ent, void *mkc) +static void set_cache_mkc(struct mlx5_cache_ent *ent, void *mkc) { - struct mlx5_ib_mr *mr; - - mr = kzalloc(sizeof(*mr), GFP_KERNEL); - if (!mr) - return NULL; - mr->cache_ent = ent; - set_mkc_access_pd_addr_fields(mkc, 0, 0, ent->dev->umrc.pd); MLX5_SET(mkc, mkc, free, 1); MLX5_SET(mkc, mkc, umr_en, 1); @@ -215,133 +300,106 @@ static struct mlx5_ib_mr *alloc_cache_mr(struct mlx5_cache_ent *ent, void *mkc) MLX5_SET(mkc, mkc, translations_octword_size, get_mkc_octo_size(ent->access_mode, ent->ndescs)); MLX5_SET(mkc, mkc, log_page_size, ent->page); - return mr; } /* Asynchronously schedule new MRs to be populated in the cache. */ static int add_keys(struct mlx5_cache_ent *ent, unsigned int num) { - size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - struct mlx5_ib_mr *mr; + struct mlx5r_async_create_mkey *async_create; void *mkc; - u32 *in; int err = 0; int i; - in = kzalloc(inlen, GFP_KERNEL); - if (!in) - return -ENOMEM; - - mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); for (i = 0; i < num; i++) { - mr = alloc_cache_mr(ent, mkc); - if (!mr) { - err = -ENOMEM; - break; - } - spin_lock_irq(&ent->lock); - if (ent->pending >= MAX_PENDING_REG_MR) { - err = -EAGAIN; - spin_unlock_irq(&ent->lock); - kfree(mr); - break; - } - ent->pending++; - spin_unlock_irq(&ent->lock); - err = mlx5_ib_create_mkey_cb(ent->dev, &mr->mmkey, - &ent->dev->async_ctx, in, inlen, - mr->out, sizeof(mr->out), - &mr->cb_work); + async_create = kzalloc(sizeof(struct mlx5r_async_create_mkey), + GFP_KERNEL); + if (!async_create) + return -ENOMEM; + mkc = MLX5_ADDR_OF(create_mkey_in, async_create->in, + memory_key_mkey_entry); + set_cache_mkc(ent, mkc); + async_create->ent = ent; + + err = push_mkey(ent, true, NULL); + if (err) + goto free_async_create; + + err = mlx5_ib_create_mkey_cb(async_create); if (err) { - spin_lock_irq(&ent->lock); - ent->pending--; - spin_unlock_irq(&ent->lock); mlx5_ib_warn(ent->dev, "create mkey failed %d\n", err); - kfree(mr); - break; + goto err_undo_reserve; } } - kfree(in); + return 0; + +err_undo_reserve: + xa_lock_irq(&ent->mkeys); + undo_push_reserve_mkey(ent); + xa_unlock_irq(&ent->mkeys); +free_async_create: + kfree(async_create); return err; } /* Synchronously create a MR in the cache */ -static struct mlx5_ib_mr *create_cache_mr(struct mlx5_cache_ent *ent) +static int create_cache_mkey(struct mlx5_cache_ent *ent, u32 *mkey) { size_t inlen = MLX5_ST_SZ_BYTES(create_mkey_in); - struct mlx5_ib_mr *mr; void *mkc; u32 *in; int err; in = kzalloc(inlen, GFP_KERNEL); if (!in) - return ERR_PTR(-ENOMEM); + return -ENOMEM; mkc = MLX5_ADDR_OF(create_mkey_in, in, memory_key_mkey_entry); + set_cache_mkc(ent, mkc); - mr = alloc_cache_mr(ent, mkc); - if (!mr) { - err = -ENOMEM; - goto free_in; - } - - err = mlx5_core_create_mkey(ent->dev->mdev, &mr->mmkey.key, in, inlen); + err = mlx5_core_create_mkey(ent->dev->mdev, mkey, in, inlen); if (err) - goto free_mr; + goto free_in; - init_waitqueue_head(&mr->mmkey.wait); - mr->mmkey.type = MLX5_MKEY_MR; WRITE_ONCE(ent->dev->cache.last_add, jiffies); - spin_lock_irq(&ent->lock); - ent->total_mrs++; - spin_unlock_irq(&ent->lock); - kfree(in); - return mr; -free_mr: - kfree(mr); free_in: kfree(in); - return ERR_PTR(err); + return err; } static void remove_cache_mr_locked(struct mlx5_cache_ent *ent) { - struct mlx5_ib_mr *mr; + u32 mkey; - lockdep_assert_held(&ent->lock); - if (list_empty(&ent->head)) + lockdep_assert_held(&ent->mkeys.xa_lock); + if (!ent->stored) return; - mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list); - list_del(&mr->list); - ent->available_mrs--; - ent->total_mrs--; - spin_unlock_irq(&ent->lock); - mlx5_core_destroy_mkey(ent->dev->mdev, mr->mmkey.key); - kfree(mr); - spin_lock_irq(&ent->lock); + mkey = pop_stored_mkey(ent); + xa_unlock_irq(&ent->mkeys); + mlx5_core_destroy_mkey(ent->dev->mdev, mkey); + xa_lock_irq(&ent->mkeys); } static int resize_available_mrs(struct mlx5_cache_ent *ent, unsigned int target, bool limit_fill) + __acquires(&ent->mkeys) __releases(&ent->mkeys) { int err; - lockdep_assert_held(&ent->lock); + lockdep_assert_held(&ent->mkeys.xa_lock); while (true) { if (limit_fill) target = ent->limit * 2; - if (target == ent->available_mrs + ent->pending) + if (target == ent->reserved) return 0; - if (target > ent->available_mrs + ent->pending) { - u32 todo = target - (ent->available_mrs + ent->pending); + if (target > ent->reserved) { + u32 todo = target - ent->reserved; - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); err = add_keys(ent, todo); if (err == -EAGAIN) usleep_range(3000, 5000); - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); if (err) { if (err != -EAGAIN) return err; @@ -366,15 +424,15 @@ static ssize_t size_write(struct file *filp, const char __user *buf, /* * Target is the new value of total_mrs the user requests, however we - * cannot free MRs that are in use. Compute the target value for - * available_mrs. + * cannot free MRs that are in use. Compute the target value for stored + * mkeys. */ - spin_lock_irq(&ent->lock); - if (target < ent->total_mrs - ent->available_mrs) { + xa_lock_irq(&ent->mkeys); + if (target < ent->in_use) { err = -EINVAL; goto err_unlock; } - target = target - (ent->total_mrs - ent->available_mrs); + target = target - ent->in_use; if (target < ent->limit || target > ent->limit*2) { err = -EINVAL; goto err_unlock; @@ -382,12 +440,12 @@ static ssize_t size_write(struct file *filp, const char __user *buf, err = resize_available_mrs(ent, target, false); if (err) goto err_unlock; - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); return count; err_unlock: - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); return err; } @@ -398,7 +456,7 @@ static ssize_t size_read(struct file *filp, char __user *buf, size_t count, char lbuf[20]; int err; - err = snprintf(lbuf, sizeof(lbuf), "%d\n", ent->total_mrs); + err = snprintf(lbuf, sizeof(lbuf), "%ld\n", ent->stored + ent->in_use); if (err < 0) return err; @@ -427,10 +485,10 @@ static ssize_t limit_write(struct file *filp, const char __user *buf, * Upon set we immediately fill the cache to high water mark implied by * the limit. */ - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); ent->limit = var; err = resize_available_mrs(ent, 0, true); - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); if (err) return err; return count; @@ -457,17 +515,17 @@ static const struct file_operations limit_fops = { .read = limit_read, }; -static bool someone_adding(struct mlx5_mr_cache *cache) +static bool someone_adding(struct mlx5_mkey_cache *cache) { unsigned int i; - for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + for (i = 0; i < MAX_MKEY_CACHE_ENTRIES; i++) { struct mlx5_cache_ent *ent = &cache->ent[i]; bool ret; - spin_lock_irq(&ent->lock); - ret = ent->available_mrs < ent->limit; - spin_unlock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); + ret = ent->stored < ent->limit; + xa_unlock_irq(&ent->mkeys); if (ret) return true; } @@ -481,26 +539,26 @@ static bool someone_adding(struct mlx5_mr_cache *cache) */ static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent) { - lockdep_assert_held(&ent->lock); + lockdep_assert_held(&ent->mkeys.xa_lock); if (ent->disabled || READ_ONCE(ent->dev->fill_delay)) return; - if (ent->available_mrs < ent->limit) { + if (ent->stored < ent->limit) { ent->fill_to_high_water = true; mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); } else if (ent->fill_to_high_water && - ent->available_mrs + ent->pending < 2 * ent->limit) { + ent->reserved < 2 * ent->limit) { /* * Once we start populating due to hitting a low water mark * continue until we pass the high water mark. */ mod_delayed_work(ent->dev->cache.wq, &ent->dwork, 0); - } else if (ent->available_mrs == 2 * ent->limit) { + } else if (ent->stored == 2 * ent->limit) { ent->fill_to_high_water = false; - } else if (ent->available_mrs > 2 * ent->limit) { + } else if (ent->stored > 2 * ent->limit) { /* Queue deletion of excess entries */ ent->fill_to_high_water = false; - if (ent->pending) + if (ent->stored != ent->reserved) queue_delayed_work(ent->dev->cache.wq, &ent->dwork, msecs_to_jiffies(1000)); else @@ -511,25 +569,24 @@ static void queue_adjust_cache_locked(struct mlx5_cache_ent *ent) static void __cache_work_func(struct mlx5_cache_ent *ent) { struct mlx5_ib_dev *dev = ent->dev; - struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_mkey_cache *cache = &dev->cache; int err; - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); if (ent->disabled) goto out; - if (ent->fill_to_high_water && - ent->available_mrs + ent->pending < 2 * ent->limit && + if (ent->fill_to_high_water && ent->reserved < 2 * ent->limit && !READ_ONCE(dev->fill_delay)) { - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); err = add_keys(ent, 1); - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); if (ent->disabled) goto out; if (err) { /* - * EAGAIN only happens if pending is positive, so we - * will be rescheduled from reg_mr_callback(). The only + * EAGAIN only happens if there are pending MRs, so we + * will be rescheduled when storing them. The only * failure path here is ENOMEM. */ if (err != -EAGAIN) { @@ -541,7 +598,7 @@ static void __cache_work_func(struct mlx5_cache_ent *ent) msecs_to_jiffies(1000)); } } - } else if (ent->available_mrs > 2 * ent->limit) { + } else if (ent->stored > 2 * ent->limit) { bool need_delay; /* @@ -556,11 +613,11 @@ static void __cache_work_func(struct mlx5_cache_ent *ent) * the garbage collection work to try to run in next cycle, in * order to free CPU resources to other tasks. */ - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); need_delay = need_resched() || someone_adding(cache) || !time_after(jiffies, READ_ONCE(cache->last_add) + 300 * HZ); - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); if (ent->disabled) goto out; if (need_delay) { @@ -571,7 +628,7 @@ static void __cache_work_func(struct mlx5_cache_ent *ent) queue_adjust_cache_locked(ent); } out: - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); } static void delayed_cache_work_func(struct work_struct *work) @@ -587,73 +644,59 @@ struct mlx5_ib_mr *mlx5_mr_cache_alloc(struct mlx5_ib_dev *dev, int access_flags) { struct mlx5_ib_mr *mr; + int err; - /* Matches access in alloc_cache_mr() */ if (!mlx5r_umr_can_reconfig(dev, 0, access_flags)) return ERR_PTR(-EOPNOTSUPP); - spin_lock_irq(&ent->lock); - if (list_empty(&ent->head)) { + mr = kzalloc(sizeof(*mr), GFP_KERNEL); + if (!mr) + return ERR_PTR(-ENOMEM); + + xa_lock_irq(&ent->mkeys); + ent->in_use++; + + if (!ent->stored) { queue_adjust_cache_locked(ent); ent->miss++; - spin_unlock_irq(&ent->lock); - mr = create_cache_mr(ent); - if (IS_ERR(mr)) - return mr; + xa_unlock_irq(&ent->mkeys); + err = create_cache_mkey(ent, &mr->mmkey.key); + if (err) { + xa_lock_irq(&ent->mkeys); + ent->in_use--; + xa_unlock_irq(&ent->mkeys); + kfree(mr); + return ERR_PTR(err); + } } else { - mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list); - list_del(&mr->list); - ent->available_mrs--; + mr->mmkey.key = pop_stored_mkey(ent); queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->lock); - - mlx5_clear_mr(mr); + xa_unlock_irq(&ent->mkeys); } + mr->mmkey.cache_ent = ent; + mr->mmkey.type = MLX5_MKEY_MR; + init_waitqueue_head(&mr->mmkey.wait); return mr; } -static void mlx5_mr_cache_free(struct mlx5_ib_dev *dev, struct mlx5_ib_mr *mr) -{ - struct mlx5_cache_ent *ent = mr->cache_ent; - - WRITE_ONCE(dev->cache.last_add, jiffies); - spin_lock_irq(&ent->lock); - list_add_tail(&mr->list, &ent->head); - ent->available_mrs++; - queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->lock); -} - static void clean_keys(struct mlx5_ib_dev *dev, int c) { - struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_mkey_cache *cache = &dev->cache; struct mlx5_cache_ent *ent = &cache->ent[c]; - struct mlx5_ib_mr *tmp_mr; - struct mlx5_ib_mr *mr; - LIST_HEAD(del_list); + u32 mkey; cancel_delayed_work(&ent->dwork); - while (1) { - spin_lock_irq(&ent->lock); - if (list_empty(&ent->head)) { - spin_unlock_irq(&ent->lock); - break; - } - mr = list_first_entry(&ent->head, struct mlx5_ib_mr, list); - list_move(&mr->list, &del_list); - ent->available_mrs--; - ent->total_mrs--; - spin_unlock_irq(&ent->lock); - mlx5_core_destroy_mkey(dev->mdev, mr->mmkey.key); - } - - list_for_each_entry_safe(mr, tmp_mr, &del_list, list) { - list_del(&mr->list); - kfree(mr); + xa_lock_irq(&ent->mkeys); + while (ent->stored) { + mkey = pop_stored_mkey(ent); + xa_unlock_irq(&ent->mkeys); + mlx5_core_destroy_mkey(dev->mdev, mkey); + xa_lock_irq(&ent->mkeys); } + xa_unlock_irq(&ent->mkeys); } -static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) +static void mlx5_mkey_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) { if (!mlx5_debugfs_root || dev->is_rep) return; @@ -662,9 +705,9 @@ static void mlx5_mr_cache_debugfs_cleanup(struct mlx5_ib_dev *dev) dev->cache.root = NULL; } -static void mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) +static void mlx5_mkey_cache_debugfs_init(struct mlx5_ib_dev *dev) { - struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_mkey_cache *cache = &dev->cache; struct mlx5_cache_ent *ent; struct dentry *dir; int i; @@ -674,13 +717,13 @@ static void mlx5_mr_cache_debugfs_init(struct mlx5_ib_dev *dev) cache->root = debugfs_create_dir("mr_cache", mlx5_debugfs_get_dev_root(dev->mdev)); - for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + for (i = 0; i < MAX_MKEY_CACHE_ENTRIES; i++) { ent = &cache->ent[i]; sprintf(ent->name, "%d", ent->order); dir = debugfs_create_dir(ent->name, cache->root); debugfs_create_file("size", 0600, dir, ent, &size_fops); debugfs_create_file("limit", 0600, dir, ent, &limit_fops); - debugfs_create_u32("cur", 0400, dir, &ent->available_mrs); + debugfs_create_ulong("cur", 0400, dir, &ent->stored); debugfs_create_u32("miss", 0600, dir, &ent->miss); } } @@ -692,9 +735,9 @@ static void delay_time_func(struct timer_list *t) WRITE_ONCE(dev->fill_delay, 0); } -int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) +int mlx5_mkey_cache_init(struct mlx5_ib_dev *dev) { - struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_mkey_cache *cache = &dev->cache; struct mlx5_cache_ent *ent; int i; @@ -707,22 +750,21 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) mlx5_cmd_init_async_ctx(dev->mdev, &dev->async_ctx); timer_setup(&dev->delay_timer, delay_time_func, 0); - for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + for (i = 0; i < MAX_MKEY_CACHE_ENTRIES; i++) { ent = &cache->ent[i]; - INIT_LIST_HEAD(&ent->head); - spin_lock_init(&ent->lock); + xa_init_flags(&ent->mkeys, XA_FLAGS_LOCK_IRQ); ent->order = i + 2; ent->dev = dev; ent->limit = 0; INIT_DELAYED_WORK(&ent->dwork, delayed_cache_work_func); - if (i > MR_CACHE_LAST_STD_ENTRY) { - mlx5_odp_init_mr_cache_entry(ent); + if (i > MKEY_CACHE_LAST_STD_ENTRY) { + mlx5_odp_init_mkey_cache_entry(ent); continue; } - if (ent->order > mr_cache_max_order(dev)) + if (ent->order > mkey_cache_max_order(dev)) continue; ent->page = PAGE_SHIFT; @@ -734,36 +776,36 @@ int mlx5_mr_cache_init(struct mlx5_ib_dev *dev) ent->limit = dev->mdev->profile.mr_cache[i].limit; else ent->limit = 0; - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); queue_adjust_cache_locked(ent); - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); } - mlx5_mr_cache_debugfs_init(dev); + mlx5_mkey_cache_debugfs_init(dev); return 0; } -int mlx5_mr_cache_cleanup(struct mlx5_ib_dev *dev) +int mlx5_mkey_cache_cleanup(struct mlx5_ib_dev *dev) { unsigned int i; if (!dev->cache.wq) return 0; - for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) { + for (i = 0; i < MAX_MKEY_CACHE_ENTRIES; i++) { struct mlx5_cache_ent *ent = &dev->cache.ent[i]; - spin_lock_irq(&ent->lock); + xa_lock_irq(&ent->mkeys); ent->disabled = true; - spin_unlock_irq(&ent->lock); + xa_unlock_irq(&ent->mkeys); cancel_delayed_work_sync(&ent->dwork); } - mlx5_mr_cache_debugfs_cleanup(dev); + mlx5_mkey_cache_debugfs_cleanup(dev); mlx5_cmd_cleanup_async_ctx(&dev->async_ctx); - for (i = 0; i < MAX_MR_CACHE_ENTRIES; i++) + for (i = 0; i < MAX_MKEY_CACHE_ENTRIES; i++) clean_keys(dev, i); destroy_workqueue(dev->cache.wq); @@ -830,22 +872,22 @@ static int get_octo_len(u64 addr, u64 len, int page_shift) return (npages + 1) / 2; } -static int mr_cache_max_order(struct mlx5_ib_dev *dev) +static int mkey_cache_max_order(struct mlx5_ib_dev *dev) { if (MLX5_CAP_GEN(dev->mdev, umr_extended_translation_offset)) - return MR_CACHE_LAST_STD_ENTRY + 2; + return MKEY_CACHE_LAST_STD_ENTRY + 2; return MLX5_MAX_UMR_SHIFT; } -static struct mlx5_cache_ent *mr_cache_ent_from_order(struct mlx5_ib_dev *dev, - unsigned int order) +static struct mlx5_cache_ent *mkey_cache_ent_from_order(struct mlx5_ib_dev *dev, + unsigned int order) { - struct mlx5_mr_cache *cache = &dev->cache; + struct mlx5_mkey_cache *cache = &dev->cache; if (order < cache->ent[0].order) return &cache->ent[0]; order = order - cache->ent[0].order; - if (order > MR_CACHE_LAST_STD_ENTRY) + if (order > MKEY_CACHE_LAST_STD_ENTRY) return NULL; return &cache->ent[order]; } @@ -888,7 +930,7 @@ static struct mlx5_ib_mr *alloc_cacheable_mr(struct ib_pd *pd, 0, iova); if (WARN_ON(!page_size)) return ERR_PTR(-EINVAL); - ent = mr_cache_ent_from_order( + ent = mkey_cache_ent_from_order( dev, order_base_2(ib_umem_num_dma_blocks(umem, page_size))); /* * Matches access in alloc_cache_mr(). If the MR can't come from the @@ -1320,7 +1362,7 @@ static bool can_use_umr_rereg_pas(struct mlx5_ib_mr *mr, struct mlx5_ib_dev *dev = to_mdev(mr->ibmr.device); /* We only track the allocated sizes of MRs from the cache */ - if (!mr->cache_ent) + if (!mr->mmkey.cache_ent) return false; if (!mlx5r_umr_can_load_pas(dev, new_umem->length)) return false; @@ -1329,7 +1371,7 @@ static bool can_use_umr_rereg_pas(struct mlx5_ib_mr *mr, mlx5_umem_find_best_pgsz(new_umem, mkc, log_page_size, 0, iova); if (WARN_ON(!*page_size)) return false; - return (1ULL << mr->cache_ent->order) >= + return (1ULL << mr->mmkey.cache_ent->order) >= ib_umem_num_dma_blocks(new_umem, *page_size); } @@ -1570,15 +1612,17 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) } /* Stop DMA */ - if (mr->cache_ent) { - if (mlx5r_umr_revoke_mr(mr)) { - spin_lock_irq(&mr->cache_ent->lock); - mr->cache_ent->total_mrs--; - spin_unlock_irq(&mr->cache_ent->lock); - mr->cache_ent = NULL; - } + if (mr->mmkey.cache_ent) { + xa_lock_irq(&mr->mmkey.cache_ent->mkeys); + mr->mmkey.cache_ent->in_use--; + xa_unlock_irq(&mr->mmkey.cache_ent->mkeys); + + if (mlx5r_umr_revoke_mr(mr) || + push_mkey(mr->mmkey.cache_ent, false, + xa_mk_value(mr->mmkey.key))) + mr->mmkey.cache_ent = NULL; } - if (!mr->cache_ent) { + if (!mr->mmkey.cache_ent) { rc = destroy_mkey(to_mdev(mr->ibmr.device), mr); if (rc) return rc; @@ -1595,12 +1639,10 @@ int mlx5_ib_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) mlx5_ib_free_odp_mr(mr); } - if (mr->cache_ent) { - mlx5_mr_cache_free(dev, mr); - } else { + if (!mr->mmkey.cache_ent) mlx5_free_priv_descs(mr); - kfree(mr); - } + + kfree(mr); return 0; } diff --git a/drivers/infiniband/hw/mlx5/odp.c b/drivers/infiniband/hw/mlx5/odp.c index 84da5674e1ab..e305bf1dc6c2 100644 --- a/drivers/infiniband/hw/mlx5/odp.c +++ b/drivers/infiniband/hw/mlx5/odp.c @@ -1588,7 +1588,7 @@ mlx5_ib_odp_destroy_eq(struct mlx5_ib_dev *dev, struct mlx5_ib_pf_eq *eq) return err; } -void mlx5_odp_init_mr_cache_entry(struct mlx5_cache_ent *ent) +void mlx5_odp_init_mkey_cache_entry(struct mlx5_cache_ent *ent) { if (!(ent->dev->odp_caps.general_caps & IB_ODP_SUPPORT_IMPLICIT)) return; diff --git a/drivers/infiniband/hw/mlx5/umr.c b/drivers/infiniband/hw/mlx5/umr.c index 3a48364c0918..e00b94d1b1ea 100644 --- a/drivers/infiniband/hw/mlx5/umr.c +++ b/drivers/infiniband/hw/mlx5/umr.c @@ -176,6 +176,7 @@ int mlx5r_umr_resource_init(struct mlx5_ib_dev *dev) dev->umrc.pd = pd; sema_init(&dev->umrc.sem, MAX_UMR_WR); + mutex_init(&dev->umrc.lock); return 0; @@ -195,6 +196,31 @@ void mlx5r_umr_resource_cleanup(struct mlx5_ib_dev *dev) ib_dealloc_pd(dev->umrc.pd); } +static int mlx5r_umr_recover(struct mlx5_ib_dev *dev) +{ + struct umr_common *umrc = &dev->umrc; + struct ib_qp_attr attr; + int err; + + attr.qp_state = IB_QPS_RESET; + err = ib_modify_qp(umrc->qp, &attr, IB_QP_STATE); + if (err) { + mlx5_ib_dbg(dev, "Couldn't modify UMR QP\n"); + goto err; + } + + err = mlx5r_umr_qp_rst2rts(dev, umrc->qp); + if (err) + goto err; + + umrc->state = MLX5_UMR_STATE_ACTIVE; + return 0; + +err: + umrc->state = MLX5_UMR_STATE_ERR; + return err; +} + static int mlx5r_umr_post_send(struct ib_qp *ibqp, u32 mkey, struct ib_cqe *cqe, struct mlx5r_umr_wqe *wqe, bool with_data) { @@ -231,7 +257,7 @@ static int mlx5r_umr_post_send(struct ib_qp *ibqp, u32 mkey, struct ib_cqe *cqe, id.ib_cqe = cqe; mlx5r_finish_wqe(qp, ctrl, seg, size, cur_edge, idx, id.wr_id, 0, - MLX5_FENCE_MODE_NONE, MLX5_OPCODE_UMR); + MLX5_FENCE_MODE_INITIATOR_SMALL, MLX5_OPCODE_UMR); mlx5r_ring_db(qp, 1, ctrl); @@ -270,17 +296,49 @@ static int mlx5r_umr_post_send_wait(struct mlx5_ib_dev *dev, u32 mkey, mlx5r_umr_init_context(&umr_context); down(&umrc->sem); - err = mlx5r_umr_post_send(umrc->qp, mkey, &umr_context.cqe, wqe, - with_data); - if (err) - mlx5_ib_warn(dev, "UMR post send failed, err %d\n", err); - else { - wait_for_completion(&umr_context.done); - if (umr_context.status != IB_WC_SUCCESS) { - mlx5_ib_warn(dev, "reg umr failed (%u)\n", - umr_context.status); + while (true) { + mutex_lock(&umrc->lock); + if (umrc->state == MLX5_UMR_STATE_ERR) { + mutex_unlock(&umrc->lock); err = -EFAULT; + break; } + + if (umrc->state == MLX5_UMR_STATE_RECOVER) { + mutex_unlock(&umrc->lock); + usleep_range(3000, 5000); + continue; + } + + err = mlx5r_umr_post_send(umrc->qp, mkey, &umr_context.cqe, wqe, + with_data); + mutex_unlock(&umrc->lock); + if (err) { + mlx5_ib_warn(dev, "UMR post send failed, err %d\n", + err); + break; + } + + wait_for_completion(&umr_context.done); + + if (umr_context.status == IB_WC_SUCCESS) + break; + + if (umr_context.status == IB_WC_WR_FLUSH_ERR) + continue; + + WARN_ON_ONCE(1); + mlx5_ib_warn(dev, + "reg umr failed (%u). Trying to recover and resubmit the flushed WQEs\n", + umr_context.status); + mutex_lock(&umrc->lock); + err = mlx5r_umr_recover(dev); + mutex_unlock(&umrc->lock); + if (err) + mlx5_ib_warn(dev, "couldn't recover UMR, err %d\n", + err); + err = -EFAULT; + break; } up(&umrc->sem); return err; diff --git a/drivers/infiniband/hw/qedr/verbs.c b/drivers/infiniband/hw/qedr/verbs.c index 03ed7c0fae50..d745ce9dc88a 100644 --- a/drivers/infiniband/hw/qedr/verbs.c +++ b/drivers/infiniband/hw/qedr/verbs.c @@ -3084,7 +3084,7 @@ static struct qedr_mr *__qedr_alloc_mr(struct ib_pd *ibpd, else DP_ERR(dev, "roce alloc tid returned error %d\n", rc); - goto err0; + goto err1; } /* Index only, 18 bit long, lkey = itid << 8 | key */ @@ -3108,7 +3108,7 @@ static struct qedr_mr *__qedr_alloc_mr(struct ib_pd *ibpd, rc = dev->ops->rdma_register_tid(dev->rdma_ctx, &mr->hw_mr); if (rc) { DP_ERR(dev, "roce register tid returned an error %d\n", rc); - goto err1; + goto err2; } mr->ibmr.lkey = mr->hw_mr.itid << 8 | mr->hw_mr.key; @@ -3117,8 +3117,10 @@ static struct qedr_mr *__qedr_alloc_mr(struct ib_pd *ibpd, DP_DEBUG(dev, QEDR_MSG_MR, "alloc frmr: %x\n", mr->ibmr.lkey); return mr; -err1: +err2: dev->ops->rdma_free_tid(dev->rdma_ctx, mr->hw_mr.itid); +err1: + qedr_free_pbl(dev, &mr->info.pbl_info, mr->info.pbl_table); err0: kfree(mr); return ERR_PTR(rc); diff --git a/drivers/infiniband/hw/qib/qib.h b/drivers/infiniband/hw/qib/qib.h index b37b1c6d35c6..26c615772be3 100644 --- a/drivers/infiniband/hw/qib/qib.h +++ b/drivers/infiniband/hw/qib/qib.h @@ -321,7 +321,7 @@ struct qib_verbs_txreq { * These 7 values (SDR, DDR, and QDR may be ORed for auto-speed * negotiation) are used for the 3rd argument to path_f_set_ib_cfg * with cmd QIB_IB_CFG_SPD_ENB, by direct calls or via sysfs. They - * are also the the possible values for qib_link_speed_enabled and active + * are also the possible values for qib_link_speed_enabled and active * The values were chosen to match values used within the IB spec. */ #define QIB_IB_SDR 1 diff --git a/drivers/infiniband/hw/qib/qib_file_ops.c b/drivers/infiniband/hw/qib/qib_file_ops.c index aa290928cf96..3937144b2ae5 100644 --- a/drivers/infiniband/hw/qib/qib_file_ops.c +++ b/drivers/infiniband/hw/qib/qib_file_ops.c @@ -153,7 +153,7 @@ static int qib_get_base_info(struct file *fp, void __user *ubase, kinfo->spi_tidcnt += dd->rcvtidcnt % subctxt_cnt; /* * for this use, may be cfgctxts summed over all chips that - * are are configured and present + * are configured and present */ kinfo->spi_nctxts = dd->cfgctxts; /* unit (chip/board) our context is on */ @@ -851,7 +851,7 @@ static int mmap_rcvegrbufs(struct vm_area_struct *vma, ret = -EPERM; goto bail; } - /* don't allow them to later change to writeable with mprotect */ + /* don't allow them to later change to writable with mprotect */ vma->vm_flags &= ~VM_MAYWRITE; start = vma->vm_start; @@ -941,7 +941,7 @@ static int mmap_kvaddr(struct vm_area_struct *vma, u64 pgaddr, goto bail; } /* - * Don't allow permission to later change to writeable + * Don't allow permission to later change to writable * with mprotect. */ vma->vm_flags &= ~VM_MAYWRITE; diff --git a/drivers/infiniband/hw/qib/qib_iba7220.c b/drivers/infiniband/hw/qib/qib_iba7220.c index 37b628a162e0..6af57067c32e 100644 --- a/drivers/infiniband/hw/qib/qib_iba7220.c +++ b/drivers/infiniband/hw/qib/qib_iba7220.c @@ -58,7 +58,7 @@ static void qib_set_ib_7220_lstate(struct qib_pportdata *, u16, u16); /* * This file contains almost all the chip-specific register information and * access functions for the QLogic QLogic_IB 7220 PCI-Express chip, with the - * exception of SerDes support, which in in qib_sd7220.c. + * exception of SerDes support, which in qib_sd7220.c. */ /* Below uses machine-generated qib_chipnum_regs.h file */ diff --git a/drivers/infiniband/hw/qib/qib_iba7322.c b/drivers/infiniband/hw/qib/qib_iba7322.c index ceed302cf6a0..6861c6384f18 100644 --- a/drivers/infiniband/hw/qib/qib_iba7322.c +++ b/drivers/infiniband/hw/qib/qib_iba7322.c @@ -2850,9 +2850,9 @@ static void qib_setup_7322_cleanup(struct qib_devdata *dd) qib_7322_free_irq(dd); kfree(dd->cspec->cntrs); - kfree(dd->cspec->sendchkenable); - kfree(dd->cspec->sendgrhchk); - kfree(dd->cspec->sendibchk); + bitmap_free(dd->cspec->sendchkenable); + bitmap_free(dd->cspec->sendgrhchk); + bitmap_free(dd->cspec->sendibchk); kfree(dd->cspec->msix_entries); for (i = 0; i < dd->num_pports; i++) { unsigned long flags; @@ -6383,18 +6383,11 @@ static int qib_init_7322_variables(struct qib_devdata *dd) features = qib_7322_boardname(dd); /* now that piobcnt2k and 4k set, we can allocate these */ - sbufcnt = dd->piobcnt2k + dd->piobcnt4k + - NUM_VL15_BUFS + BITS_PER_LONG - 1; - sbufcnt /= BITS_PER_LONG; - dd->cspec->sendchkenable = - kmalloc_array(sbufcnt, sizeof(*dd->cspec->sendchkenable), - GFP_KERNEL); - dd->cspec->sendgrhchk = - kmalloc_array(sbufcnt, sizeof(*dd->cspec->sendgrhchk), - GFP_KERNEL); - dd->cspec->sendibchk = - kmalloc_array(sbufcnt, sizeof(*dd->cspec->sendibchk), - GFP_KERNEL); + sbufcnt = dd->piobcnt2k + dd->piobcnt4k + NUM_VL15_BUFS; + + dd->cspec->sendchkenable = bitmap_zalloc(sbufcnt, GFP_KERNEL); + dd->cspec->sendgrhchk = bitmap_zalloc(sbufcnt, GFP_KERNEL); + dd->cspec->sendibchk = bitmap_zalloc(sbufcnt, GFP_KERNEL); if (!dd->cspec->sendchkenable || !dd->cspec->sendgrhchk || !dd->cspec->sendibchk) { ret = -ENOMEM; diff --git a/drivers/infiniband/hw/qib/qib_init.c b/drivers/infiniband/hw/qib/qib_init.c index d1a72e89e297..45211008449f 100644 --- a/drivers/infiniband/hw/qib/qib_init.c +++ b/drivers/infiniband/hw/qib/qib_init.c @@ -1106,8 +1106,7 @@ struct qib_devdata *qib_alloc_devdata(struct pci_dev *pdev, size_t extra) if (!qib_cpulist_count) { u32 count = num_online_cpus(); - qib_cpulist = kcalloc(BITS_TO_LONGS(count), sizeof(long), - GFP_KERNEL); + qib_cpulist = bitmap_zalloc(count, GFP_KERNEL); if (qib_cpulist) qib_cpulist_count = count; } @@ -1279,7 +1278,7 @@ static void __exit qib_ib_cleanup(void) #endif qib_cpulist_count = 0; - kfree(qib_cpulist); + bitmap_free(qib_cpulist); WARN_ON(!xa_empty(&qib_dev_table)); qib_dev_cleanup(); diff --git a/drivers/infiniband/hw/qib/qib_sd7220.c b/drivers/infiniband/hw/qib/qib_sd7220.c index 81b810d006c0..1dc3ccf0cf1f 100644 --- a/drivers/infiniband/hw/qib/qib_sd7220.c +++ b/drivers/infiniband/hw/qib/qib_sd7220.c @@ -587,7 +587,7 @@ static int epb_access(struct qib_devdata *dd, int sdnum, int claim) /* Need to release */ u64 pollval; /* - * The only writeable bits are the request and CS. + * The only writable bits are the request and CS. * Both should be clear */ u64 newval = 0; diff --git a/drivers/infiniband/hw/usnic/usnic_uiom.c b/drivers/infiniband/hw/usnic/usnic_uiom.c index e212929369df..67a1b4562dc2 100644 --- a/drivers/infiniband/hw/usnic/usnic_uiom.c +++ b/drivers/infiniband/hw/usnic/usnic_uiom.c @@ -482,7 +482,7 @@ int usnic_uiom_attach_dev_to_pd(struct usnic_uiom_pd *pd, struct device *dev) if (err) goto out_free_dev; - if (!iommu_capable(dev->bus, IOMMU_CAP_CACHE_COHERENCY)) { + if (!device_iommu_capable(dev, IOMMU_CAP_CACHE_COHERENCY)) { usnic_err("IOMMU of %s does not support cache coherency\n", dev_name(dev)); err = -EINVAL; diff --git a/drivers/infiniband/sw/rxe/rxe_comp.c b/drivers/infiniband/sw/rxe/rxe_comp.c index da3a398053b8..fb0c008af78c 100644 --- a/drivers/infiniband/sw/rxe/rxe_comp.c +++ b/drivers/infiniband/sw/rxe/rxe_comp.c @@ -114,6 +114,8 @@ void retransmit_timer(struct timer_list *t) { struct rxe_qp *qp = from_timer(qp, t, retrans_timer); + pr_debug("%s: fired for qp#%d\n", __func__, qp->elem.index); + if (qp->valid) { qp->comp.timeout = 1; rxe_run_task(&qp->comp.task, 1); @@ -560,17 +562,16 @@ int rxe_completer(void *arg) struct sk_buff *skb = NULL; struct rxe_pkt_info *pkt = NULL; enum comp_state state; - int ret = 0; + int ret; if (!rxe_get(qp)) return -EAGAIN; - if (!qp->valid || qp->req.state == QP_STATE_ERROR || - qp->req.state == QP_STATE_RESET) { + if (!qp->valid || qp->comp.state == QP_STATE_ERROR || + qp->comp.state == QP_STATE_RESET) { rxe_drain_resp_pkts(qp, qp->valid && - qp->req.state == QP_STATE_ERROR); - ret = -EAGAIN; - goto done; + qp->comp.state == QP_STATE_ERROR); + goto exit; } if (qp->comp.timeout) { @@ -580,10 +581,8 @@ int rxe_completer(void *arg) qp->comp.timeout_retry = 0; } - if (qp->req.need_retry) { - ret = -EAGAIN; - goto done; - } + if (qp->req.need_retry) + goto exit; state = COMPST_GET_ACK; @@ -676,8 +675,7 @@ int rxe_completer(void *arg) qp->qp_timeout_jiffies) mod_timer(&qp->retrans_timer, jiffies + qp->qp_timeout_jiffies); - ret = -EAGAIN; - goto done; + goto exit; case COMPST_ERROR_RETRY: /* we come here if the retry timer fired and we did @@ -689,10 +687,8 @@ int rxe_completer(void *arg) */ /* there is nothing to retry in this case */ - if (!wqe || (wqe->state == wqe_state_posted)) { - ret = -EAGAIN; - goto done; - } + if (!wqe || (wqe->state == wqe_state_posted)) + goto exit; /* if we've started a retry, don't start another * retry sequence, unless this is a timeout. @@ -730,18 +726,21 @@ int rxe_completer(void *arg) break; case COMPST_RNR_RETRY: + /* we come here if we received an RNR NAK */ if (qp->comp.rnr_retry > 0) { if (qp->comp.rnr_retry != 7) qp->comp.rnr_retry--; - qp->req.need_retry = 1; + /* don't start a retry flow until the + * rnr timer has fired + */ + qp->req.wait_for_rnr_timer = 1; pr_debug("qp#%d set rnr nak timer\n", qp_num(qp)); mod_timer(&qp->rnr_nak_timer, jiffies + rnrnak_jiffies(aeth_syn(pkt) & ~AETH_TYPE_MASK)); - ret = -EAGAIN; - goto done; + goto exit; } else { rxe_counter_inc(rxe, RXE_CNT_RNR_RETRY_EXCEEDED); @@ -754,12 +753,20 @@ int rxe_completer(void *arg) WARN_ON_ONCE(wqe->status == IB_WC_SUCCESS); do_complete(qp, wqe); rxe_qp_error(qp); - ret = -EAGAIN; - goto done; + goto exit; } } + /* A non-zero return value will cause rxe_do_task to + * exit its loop and end the tasklet. A zero return + * will continue looping and return to rxe_completer + */ done: + ret = 0; + goto out; +exit: + ret = -EAGAIN; +out: if (pkt) free_pkt(pkt); rxe_put(qp); diff --git a/drivers/infiniband/sw/rxe/rxe_cq.c b/drivers/infiniband/sw/rxe/rxe_cq.c index 642b52539ac3..b1a0ab3cd4bd 100644 --- a/drivers/infiniband/sw/rxe/rxe_cq.c +++ b/drivers/infiniband/sw/rxe/rxe_cq.c @@ -19,16 +19,16 @@ int rxe_cq_chk_attr(struct rxe_dev *rxe, struct rxe_cq *cq, } if (cqe > rxe->attr.max_cqe) { - pr_warn("cqe(%d) > max_cqe(%d)\n", - cqe, rxe->attr.max_cqe); + pr_debug("cqe(%d) > max_cqe(%d)\n", + cqe, rxe->attr.max_cqe); goto err1; } if (cq) { count = queue_count(cq->queue, QUEUE_TYPE_TO_CLIENT); if (cqe < count) { - pr_warn("cqe(%d) < current # elements in queue (%d)", - cqe, count); + pr_debug("cqe(%d) < current # elements in queue (%d)", + cqe, count); goto err1; } } diff --git a/drivers/infiniband/sw/rxe/rxe_loc.h b/drivers/infiniband/sw/rxe/rxe_loc.h index 0e022ae1b8a5..22f6cc31d1d6 100644 --- a/drivers/infiniband/sw/rxe/rxe_loc.h +++ b/drivers/infiniband/sw/rxe/rxe_loc.h @@ -77,9 +77,8 @@ struct rxe_mr *lookup_mr(struct rxe_pd *pd, int access, u32 key, enum rxe_mr_lookup_type type); int mr_check_range(struct rxe_mr *mr, u64 iova, size_t length); int advance_dma_data(struct rxe_dma_info *dma, unsigned int length); -int rxe_invalidate_mr(struct rxe_qp *qp, u32 rkey); +int rxe_invalidate_mr(struct rxe_qp *qp, u32 key); int rxe_reg_fast_mr(struct rxe_qp *qp, struct rxe_send_wqe *wqe); -int rxe_mr_set_page(struct ib_mr *ibmr, u64 addr); int rxe_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata); void rxe_mr_cleanup(struct rxe_pool_elem *elem); @@ -145,7 +144,7 @@ static inline int rcv_wqe_size(int max_sge) max_sge * sizeof(struct ib_sge); } -void free_rd_atomic_resource(struct rxe_qp *qp, struct resp_res *res); +void free_rd_atomic_resource(struct resp_res *res); static inline void rxe_advance_resp_resource(struct rxe_qp *qp) { diff --git a/drivers/infiniband/sw/rxe/rxe_mr.c b/drivers/infiniband/sw/rxe/rxe_mr.c index fc3942e04a1f..850b80f5ad8b 100644 --- a/drivers/infiniband/sw/rxe/rxe_mr.c +++ b/drivers/infiniband/sw/rxe/rxe_mr.c @@ -24,7 +24,7 @@ u8 rxe_get_next_key(u32 last_key) int mr_check_range(struct rxe_mr *mr, u64 iova, size_t length) { - struct rxe_map_set *set = mr->cur_map_set; + switch (mr->type) { case IB_MR_TYPE_DMA: @@ -32,8 +32,8 @@ int mr_check_range(struct rxe_mr *mr, u64 iova, size_t length) case IB_MR_TYPE_USER: case IB_MR_TYPE_MEM_REG: - if (iova < set->iova || length > set->length || - iova > set->iova + set->length - length) + if (iova < mr->iova || length > mr->length || + iova > mr->iova + mr->length - length) return -EFAULT; return 0; @@ -65,89 +65,41 @@ static void rxe_mr_init(int access, struct rxe_mr *mr) mr->map_shift = ilog2(RXE_BUF_PER_MAP); } -static void rxe_mr_free_map_set(int num_map, struct rxe_map_set *set) +static int rxe_mr_alloc(struct rxe_mr *mr, int num_buf) { int i; + int num_map; + struct rxe_map **map = mr->map; - for (i = 0; i < num_map; i++) - kfree(set->map[i]); + num_map = (num_buf + RXE_BUF_PER_MAP - 1) / RXE_BUF_PER_MAP; - kfree(set->map); - kfree(set); -} - -static int rxe_mr_alloc_map_set(int num_map, struct rxe_map_set **setp) -{ - int i; - struct rxe_map_set *set; - - set = kmalloc(sizeof(*set), GFP_KERNEL); - if (!set) - goto err_out; - - set->map = kmalloc_array(num_map, sizeof(struct rxe_map *), GFP_KERNEL); - if (!set->map) - goto err_free_set; + mr->map = kmalloc_array(num_map, sizeof(*map), GFP_KERNEL); + if (!mr->map) + goto err1; for (i = 0; i < num_map; i++) { - set->map[i] = kmalloc(sizeof(struct rxe_map), GFP_KERNEL); - if (!set->map[i]) - goto err_free_map; + mr->map[i] = kmalloc(sizeof(**map), GFP_KERNEL); + if (!mr->map[i]) + goto err2; } - *setp = set; - - return 0; - -err_free_map: - for (i--; i >= 0; i--) - kfree(set->map[i]); - - kfree(set->map); -err_free_set: - kfree(set); -err_out: - return -ENOMEM; -} - -/** - * rxe_mr_alloc() - Allocate memory map array(s) for MR - * @mr: Memory region - * @num_buf: Number of buffer descriptors to support - * @both: If non zero allocate both mr->map and mr->next_map - * else just allocate mr->map. Used for fast MRs - * - * Return: 0 on success else an error - */ -static int rxe_mr_alloc(struct rxe_mr *mr, int num_buf, int both) -{ - int ret; - int num_map; - BUILD_BUG_ON(!is_power_of_2(RXE_BUF_PER_MAP)); - num_map = (num_buf + RXE_BUF_PER_MAP - 1) / RXE_BUF_PER_MAP; mr->map_shift = ilog2(RXE_BUF_PER_MAP); mr->map_mask = RXE_BUF_PER_MAP - 1; + mr->num_buf = num_buf; - mr->max_buf = num_map * RXE_BUF_PER_MAP; mr->num_map = num_map; - - ret = rxe_mr_alloc_map_set(num_map, &mr->cur_map_set); - if (ret) - return -ENOMEM; - - if (both) { - ret = rxe_mr_alloc_map_set(num_map, &mr->next_map_set); - if (ret) - goto err_free; - } + mr->max_buf = num_map * RXE_BUF_PER_MAP; return 0; -err_free: - rxe_mr_free_map_set(mr->num_map, mr->cur_map_set); - mr->cur_map_set = NULL; +err2: + for (i--; i >= 0; i--) + kfree(mr->map[i]); + + kfree(mr->map); +err1: return -ENOMEM; } @@ -164,7 +116,6 @@ void rxe_mr_init_dma(struct rxe_pd *pd, int access, struct rxe_mr *mr) int rxe_mr_init_user(struct rxe_pd *pd, u64 start, u64 length, u64 iova, int access, struct rxe_mr *mr) { - struct rxe_map_set *set; struct rxe_map **map; struct rxe_phys_buf *buf = NULL; struct ib_umem *umem; @@ -172,6 +123,7 @@ int rxe_mr_init_user(struct rxe_pd *pd, u64 start, u64 length, u64 iova, int num_buf; void *vaddr; int err; + int i; umem = ib_umem_get(pd->ibpd.device, start, length, access); if (IS_ERR(umem)) { @@ -185,20 +137,18 @@ int rxe_mr_init_user(struct rxe_pd *pd, u64 start, u64 length, u64 iova, rxe_mr_init(access, mr); - err = rxe_mr_alloc(mr, num_buf, 0); + err = rxe_mr_alloc(mr, num_buf); if (err) { pr_warn("%s: Unable to allocate memory for map\n", __func__); goto err_release_umem; } - set = mr->cur_map_set; - set->page_shift = PAGE_SHIFT; - set->page_mask = PAGE_SIZE - 1; - - num_buf = 0; - map = set->map; + mr->page_shift = PAGE_SHIFT; + mr->page_mask = PAGE_SIZE - 1; + num_buf = 0; + map = mr->map; if (length > 0) { buf = map[0]->buf; @@ -214,29 +164,33 @@ int rxe_mr_init_user(struct rxe_pd *pd, u64 start, u64 length, u64 iova, pr_warn("%s: Unable to get virtual address\n", __func__); err = -ENOMEM; - goto err_release_umem; + goto err_cleanup_map; } buf->addr = (uintptr_t)vaddr; buf->size = PAGE_SIZE; num_buf++; buf++; + } } mr->ibmr.pd = &pd->ibpd; mr->umem = umem; mr->access = access; + mr->length = length; + mr->iova = iova; + mr->va = start; + mr->offset = ib_umem_offset(umem); mr->state = RXE_MR_STATE_VALID; mr->type = IB_MR_TYPE_USER; - set->length = length; - set->iova = iova; - set->va = start; - set->offset = ib_umem_offset(umem); - return 0; +err_cleanup_map: + for (i = 0; i < mr->num_map; i++) + kfree(mr->map[i]); + kfree(mr->map); err_release_umem: ib_umem_release(umem); err_out: @@ -250,7 +204,7 @@ int rxe_mr_init_fast(struct rxe_pd *pd, int max_pages, struct rxe_mr *mr) /* always allow remote access for FMRs */ rxe_mr_init(IB_ACCESS_REMOTE, mr); - err = rxe_mr_alloc(mr, max_pages, 1); + err = rxe_mr_alloc(mr, max_pages); if (err) goto err1; @@ -268,24 +222,21 @@ err1: static void lookup_iova(struct rxe_mr *mr, u64 iova, int *m_out, int *n_out, size_t *offset_out) { - struct rxe_map_set *set = mr->cur_map_set; - size_t offset = iova - set->iova + set->offset; + size_t offset = iova - mr->iova + mr->offset; int map_index; int buf_index; u64 length; - struct rxe_map *map; - if (likely(set->page_shift)) { - *offset_out = offset & set->page_mask; - offset >>= set->page_shift; + if (likely(mr->page_shift)) { + *offset_out = offset & mr->page_mask; + offset >>= mr->page_shift; *n_out = offset & mr->map_mask; *m_out = offset >> mr->map_shift; } else { map_index = 0; buf_index = 0; - map = set->map[map_index]; - length = map->buf[buf_index].size; + length = mr->map[map_index]->buf[buf_index].size; while (offset >= length) { offset -= length; @@ -295,8 +246,7 @@ static void lookup_iova(struct rxe_mr *mr, u64 iova, int *m_out, int *n_out, map_index++; buf_index = 0; } - map = set->map[map_index]; - length = map->buf[buf_index].size; + length = mr->map[map_index]->buf[buf_index].size; } *m_out = map_index; @@ -317,7 +267,7 @@ void *iova_to_vaddr(struct rxe_mr *mr, u64 iova, int length) goto out; } - if (!mr->cur_map_set) { + if (!mr->map) { addr = (void *)(uintptr_t)iova; goto out; } @@ -330,13 +280,13 @@ void *iova_to_vaddr(struct rxe_mr *mr, u64 iova, int length) lookup_iova(mr, iova, &m, &n, &offset); - if (offset + length > mr->cur_map_set->map[m]->buf[n].size) { + if (offset + length > mr->map[m]->buf[n].size) { pr_warn("crosses page boundary\n"); addr = NULL; goto out; } - addr = (void *)(uintptr_t)mr->cur_map_set->map[m]->buf[n].addr + offset; + addr = (void *)(uintptr_t)mr->map[m]->buf[n].addr + offset; out: return addr; @@ -372,7 +322,7 @@ int rxe_mr_copy(struct rxe_mr *mr, u64 iova, void *addr, int length, return 0; } - WARN_ON_ONCE(!mr->cur_map_set); + WARN_ON_ONCE(!mr->map); err = mr_check_range(mr, iova, length); if (err) { @@ -382,7 +332,7 @@ int rxe_mr_copy(struct rxe_mr *mr, u64 iova, void *addr, int length, lookup_iova(mr, iova, &m, &i, &offset); - map = mr->cur_map_set->map + m; + map = mr->map + m; buf = map[0]->buf + i; while (length > 0) { @@ -576,22 +526,22 @@ struct rxe_mr *lookup_mr(struct rxe_pd *pd, int access, u32 key, return mr; } -int rxe_invalidate_mr(struct rxe_qp *qp, u32 rkey) +int rxe_invalidate_mr(struct rxe_qp *qp, u32 key) { struct rxe_dev *rxe = to_rdev(qp->ibqp.device); struct rxe_mr *mr; int ret; - mr = rxe_pool_get_index(&rxe->mr_pool, rkey >> 8); + mr = rxe_pool_get_index(&rxe->mr_pool, key >> 8); if (!mr) { - pr_err("%s: No MR for rkey %#x\n", __func__, rkey); + pr_err("%s: No MR for key %#x\n", __func__, key); ret = -EINVAL; goto err; } - if (rkey != mr->rkey) { - pr_err("%s: rkey (%#x) doesn't match mr->rkey (%#x)\n", - __func__, rkey, mr->rkey); + if (mr->rkey ? (key != mr->rkey) : (key != mr->lkey)) { + pr_err("%s: wr key (%#x) doesn't match mr key (%#x)\n", + __func__, key, (mr->rkey ? mr->rkey : mr->lkey)); ret = -EINVAL; goto err_drop_ref; } @@ -628,9 +578,8 @@ err: int rxe_reg_fast_mr(struct rxe_qp *qp, struct rxe_send_wqe *wqe) { struct rxe_mr *mr = to_rmr(wqe->wr.wr.reg.mr); - u32 key = wqe->wr.wr.reg.key & 0xff; + u32 key = wqe->wr.wr.reg.key; u32 access = wqe->wr.wr.reg.access; - struct rxe_map_set *set; /* user can only register MR in free state */ if (unlikely(mr->state != RXE_MR_STATE_FREE)) { @@ -646,36 +595,19 @@ int rxe_reg_fast_mr(struct rxe_qp *qp, struct rxe_send_wqe *wqe) return -EINVAL; } + /* user is only allowed to change key portion of l/rkey */ + if (unlikely((mr->lkey & ~0xff) != (key & ~0xff))) { + pr_warn("%s: key = 0x%x has wrong index mr->lkey = 0x%x\n", + __func__, key, mr->lkey); + return -EINVAL; + } + mr->access = access; - mr->lkey = (mr->lkey & ~0xff) | key; - mr->rkey = (access & IB_ACCESS_REMOTE) ? mr->lkey : 0; + mr->lkey = key; + mr->rkey = (access & IB_ACCESS_REMOTE) ? key : 0; + mr->iova = wqe->wr.wr.reg.mr->iova; mr->state = RXE_MR_STATE_VALID; - set = mr->cur_map_set; - mr->cur_map_set = mr->next_map_set; - mr->cur_map_set->iova = wqe->wr.wr.reg.mr->iova; - mr->next_map_set = set; - - return 0; -} - -int rxe_mr_set_page(struct ib_mr *ibmr, u64 addr) -{ - struct rxe_mr *mr = to_rmr(ibmr); - struct rxe_map_set *set = mr->next_map_set; - struct rxe_map *map; - struct rxe_phys_buf *buf; - - if (unlikely(set->nbuf == mr->num_buf)) - return -ENOMEM; - - map = set->map[set->nbuf / RXE_BUF_PER_MAP]; - buf = &map->buf[set->nbuf % RXE_BUF_PER_MAP]; - - buf->addr = addr; - buf->size = ibmr->page_size; - set->nbuf++; - return 0; } @@ -687,7 +619,7 @@ int rxe_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) if (atomic_read(&mr->num_mw) > 0) return -EINVAL; - rxe_put(mr); + rxe_cleanup(mr); return 0; } @@ -695,14 +627,15 @@ int rxe_dereg_mr(struct ib_mr *ibmr, struct ib_udata *udata) void rxe_mr_cleanup(struct rxe_pool_elem *elem) { struct rxe_mr *mr = container_of(elem, typeof(*mr), elem); + int i; rxe_put(mr_pd(mr)); - ib_umem_release(mr->umem); - if (mr->cur_map_set) - rxe_mr_free_map_set(mr->num_map, mr->cur_map_set); + if (mr->map) { + for (i = 0; i < mr->num_map; i++) + kfree(mr->map[i]); - if (mr->next_map_set) - rxe_mr_free_map_set(mr->num_map, mr->next_map_set); + kfree(mr->map); + } } diff --git a/drivers/infiniband/sw/rxe/rxe_mw.c b/drivers/infiniband/sw/rxe/rxe_mw.c index 2e1fa844fabf..104993801a80 100644 --- a/drivers/infiniband/sw/rxe/rxe_mw.c +++ b/drivers/infiniband/sw/rxe/rxe_mw.c @@ -33,6 +33,8 @@ int rxe_alloc_mw(struct ib_mw *ibmw, struct ib_udata *udata) RXE_MW_STATE_FREE : RXE_MW_STATE_VALID; spin_lock_init(&mw->lock); + rxe_finalize(mw); + return 0; } @@ -40,7 +42,7 @@ int rxe_dealloc_mw(struct ib_mw *ibmw) { struct rxe_mw *mw = to_rmw(ibmw); - rxe_put(mw); + rxe_cleanup(mw); return 0; } @@ -48,8 +50,6 @@ int rxe_dealloc_mw(struct ib_mw *ibmw) static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, struct rxe_mw *mw, struct rxe_mr *mr) { - u32 key = wqe->wr.wr.mw.rkey & 0xff; - if (mw->ibmw.type == IB_MW_TYPE_1) { if (unlikely(mw->state != RXE_MW_STATE_VALID)) { pr_err_once( @@ -87,11 +87,6 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, } } - if (unlikely(key == (mw->rkey & 0xff))) { - pr_err_once("attempt to bind MW with same key\n"); - return -EINVAL; - } - /* remaining checks only apply to a nonzero MR */ if (!mr) return 0; @@ -113,21 +108,21 @@ static int rxe_check_bind_mw(struct rxe_qp *qp, struct rxe_send_wqe *wqe, (IB_ACCESS_REMOTE_WRITE | IB_ACCESS_REMOTE_ATOMIC)) && !(mr->access & IB_ACCESS_LOCAL_WRITE))) { pr_err_once( - "attempt to bind an writeable MW to an MR without local write access\n"); + "attempt to bind an Writable MW to an MR without local write access\n"); return -EINVAL; } /* C10-75 */ if (mw->access & IB_ZERO_BASED) { - if (unlikely(wqe->wr.wr.mw.length > mr->cur_map_set->length)) { + if (unlikely(wqe->wr.wr.mw.length > mr->length)) { pr_err_once( "attempt to bind a ZB MW outside of the MR\n"); return -EINVAL; } } else { - if (unlikely((wqe->wr.wr.mw.addr < mr->cur_map_set->iova) || + if (unlikely((wqe->wr.wr.mw.addr < mr->iova) || ((wqe->wr.wr.mw.addr + wqe->wr.wr.mw.length) > - (mr->cur_map_set->iova + mr->cur_map_set->length)))) { + (mr->iova + mr->length)))) { pr_err_once( "attempt to bind a VA MW outside of the MR\n"); return -EINVAL; diff --git a/drivers/infiniband/sw/rxe/rxe_param.h b/drivers/infiniband/sw/rxe/rxe_param.h index 568a7cbd13d4..86c7a8bf3cbb 100644 --- a/drivers/infiniband/sw/rxe/rxe_param.h +++ b/drivers/infiniband/sw/rxe/rxe_param.h @@ -105,6 +105,12 @@ enum rxe_device_param { RXE_INFLIGHT_SKBS_PER_QP_HIGH = 64, RXE_INFLIGHT_SKBS_PER_QP_LOW = 16, + /* Max number of interations of each tasklet + * before yielding the cpu to let other + * work make progress + */ + RXE_MAX_ITERATIONS = 1024, + /* Delay before calling arbiter timer */ RXE_NSEC_ARB_TIMER_DELAY = 200, diff --git a/drivers/infiniband/sw/rxe/rxe_pool.c b/drivers/infiniband/sw/rxe/rxe_pool.c index 19b14826385b..f50620f5a0a1 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.c +++ b/drivers/infiniband/sw/rxe/rxe_pool.c @@ -6,6 +6,7 @@ #include "rxe.h" +#define RXE_POOL_TIMEOUT (200) #define RXE_POOL_ALIGN (16) static const struct rxe_type_info { @@ -136,10 +137,14 @@ void *rxe_alloc(struct rxe_pool *pool) elem->pool = pool; elem->obj = obj; kref_init(&elem->ref_cnt); + init_completion(&elem->complete); - err = xa_alloc_cyclic(&pool->xa, &elem->index, elem, pool->limit, + /* allocate index in array but leave pointer as NULL so it + * can't be looked up until rxe_finalize() is called + */ + err = xa_alloc_cyclic(&pool->xa, &elem->index, NULL, pool->limit, &pool->next, GFP_KERNEL); - if (err) + if (err < 0) goto err_free; return obj; @@ -151,9 +156,11 @@ err_cnt: return NULL; } -int __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem) +int __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem, + bool sleepable) { int err; + gfp_t gfp_flags; if (WARN_ON(pool->type == RXE_TYPE_MR)) return -EINVAL; @@ -164,10 +171,19 @@ int __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem) elem->pool = pool; elem->obj = (u8 *)elem - pool->elem_offset; kref_init(&elem->ref_cnt); + init_completion(&elem->complete); - err = xa_alloc_cyclic(&pool->xa, &elem->index, elem, pool->limit, - &pool->next, GFP_KERNEL); - if (err) + /* AH objects are unique in that the create_ah verb + * can be called in atomic context. If the create_ah + * call is not sleepable use GFP_ATOMIC. + */ + gfp_flags = sleepable ? GFP_KERNEL : GFP_ATOMIC; + + if (sleepable) + might_sleep(); + err = xa_alloc_cyclic(&pool->xa, &elem->index, NULL, pool->limit, + &pool->next, gfp_flags); + if (err < 0) goto err_cnt; return 0; @@ -181,16 +197,15 @@ void *rxe_pool_get_index(struct rxe_pool *pool, u32 index) { struct rxe_pool_elem *elem; struct xarray *xa = &pool->xa; - unsigned long flags; void *obj; - xa_lock_irqsave(xa, flags); + rcu_read_lock(); elem = xa_load(xa, index); if (elem && kref_get_unless_zero(&elem->ref_cnt)) obj = elem->obj; else obj = NULL; - xa_unlock_irqrestore(xa, flags); + rcu_read_unlock(); return obj; } @@ -198,17 +213,74 @@ void *rxe_pool_get_index(struct rxe_pool *pool, u32 index) static void rxe_elem_release(struct kref *kref) { struct rxe_pool_elem *elem = container_of(kref, typeof(*elem), ref_cnt); - struct rxe_pool *pool = elem->pool; - xa_erase(&pool->xa, elem->index); + complete(&elem->complete); +} + +int __rxe_cleanup(struct rxe_pool_elem *elem, bool sleepable) +{ + struct rxe_pool *pool = elem->pool; + struct xarray *xa = &pool->xa; + static int timeout = RXE_POOL_TIMEOUT; + int ret, err = 0; + void *xa_ret; + + if (sleepable) + might_sleep(); + + /* erase xarray entry to prevent looking up + * the pool elem from its index + */ + xa_ret = xa_erase(xa, elem->index); + WARN_ON(xa_err(xa_ret)); + + /* if this is the last call to rxe_put complete the + * object. It is safe to touch obj->elem after this since + * it is freed below + */ + __rxe_put(elem); + + /* wait until all references to the object have been + * dropped before final object specific cleanup and + * return to rdma-core + */ + if (sleepable) { + if (!completion_done(&elem->complete) && timeout) { + ret = wait_for_completion_timeout(&elem->complete, + timeout); + + /* Shouldn't happen. There are still references to + * the object but, rather than deadlock, free the + * object or pass back to rdma-core. + */ + if (WARN_ON(!ret)) + err = -EINVAL; + } + } else { + unsigned long until = jiffies + timeout; + + /* AH objects are unique in that the destroy_ah verb + * can be called in atomic context. This delay + * replaces the wait_for_completion call above + * when the destroy_ah call is not sleepable + */ + while (!completion_done(&elem->complete) && + time_before(jiffies, until)) + mdelay(1); + + if (WARN_ON(!completion_done(&elem->complete))) + err = -EINVAL; + } if (pool->cleanup) pool->cleanup(elem); if (pool->type == RXE_TYPE_MR) - kfree(elem->obj); + kfree_rcu(elem->obj); atomic_dec(&pool->num_elem); + + return err; } int __rxe_get(struct rxe_pool_elem *elem) @@ -220,3 +292,11 @@ int __rxe_put(struct rxe_pool_elem *elem) { return kref_put(&elem->ref_cnt, rxe_elem_release); } + +void __rxe_finalize(struct rxe_pool_elem *elem) +{ + void *xa_ret; + + xa_ret = xa_store(&elem->pool->xa, elem->index, elem, GFP_KERNEL); + WARN_ON(xa_err(xa_ret)); +} diff --git a/drivers/infiniband/sw/rxe/rxe_pool.h b/drivers/infiniband/sw/rxe/rxe_pool.h index 0860660d65ec..9d83cb32092f 100644 --- a/drivers/infiniband/sw/rxe/rxe_pool.h +++ b/drivers/infiniband/sw/rxe/rxe_pool.h @@ -24,6 +24,7 @@ struct rxe_pool_elem { void *obj; struct kref ref_cnt; struct list_head list; + struct completion complete; u32 index; }; @@ -57,21 +58,28 @@ void rxe_pool_cleanup(struct rxe_pool *pool); void *rxe_alloc(struct rxe_pool *pool); /* connect already allocated object to pool */ -int __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem); - -#define rxe_add_to_pool(pool, obj) __rxe_add_to_pool(pool, &(obj)->elem) +int __rxe_add_to_pool(struct rxe_pool *pool, struct rxe_pool_elem *elem, + bool sleepable); +#define rxe_add_to_pool(pool, obj) __rxe_add_to_pool(pool, &(obj)->elem, true) +#define rxe_add_to_pool_ah(pool, obj, sleepable) __rxe_add_to_pool(pool, \ + &(obj)->elem, sleepable) /* lookup an indexed object from index. takes a reference on object */ void *rxe_pool_get_index(struct rxe_pool *pool, u32 index); int __rxe_get(struct rxe_pool_elem *elem); - #define rxe_get(obj) __rxe_get(&(obj)->elem) int __rxe_put(struct rxe_pool_elem *elem); - #define rxe_put(obj) __rxe_put(&(obj)->elem) +int __rxe_cleanup(struct rxe_pool_elem *elem, bool sleepable); +#define rxe_cleanup(obj) __rxe_cleanup(&(obj)->elem, true) +#define rxe_cleanup_ah(obj, sleepable) __rxe_cleanup(&(obj)->elem, sleepable) + #define rxe_read(obj) kref_read(&(obj)->elem.ref_cnt) +void __rxe_finalize(struct rxe_pool_elem *elem); +#define rxe_finalize(obj) __rxe_finalize(&(obj)->elem) + #endif /* RXE_POOL_H */ diff --git a/drivers/infiniband/sw/rxe/rxe_qp.c b/drivers/infiniband/sw/rxe/rxe_qp.c index 22e9b85344c3..516bf9b95e48 100644 --- a/drivers/infiniband/sw/rxe/rxe_qp.c +++ b/drivers/infiniband/sw/rxe/rxe_qp.c @@ -120,17 +120,15 @@ static void free_rd_atomic_resources(struct rxe_qp *qp) for (i = 0; i < qp->attr.max_dest_rd_atomic; i++) { struct resp_res *res = &qp->resp.resources[i]; - free_rd_atomic_resource(qp, res); + free_rd_atomic_resource(res); } kfree(qp->resp.resources); qp->resp.resources = NULL; } } -void free_rd_atomic_resource(struct rxe_qp *qp, struct resp_res *res) +void free_rd_atomic_resource(struct resp_res *res) { - if (res->type == RXE_ATOMIC_MASK) - kfree_skb(res->atomic.skb); res->type = 0; } @@ -142,7 +140,7 @@ static void cleanup_rd_atomic_resources(struct rxe_qp *qp) if (qp->resp.resources) { for (i = 0; i < qp->attr.max_dest_rd_atomic; i++) { res = &qp->resp.resources[i]; - free_rd_atomic_resource(qp, res); + free_rd_atomic_resource(res); } } } @@ -174,6 +172,14 @@ static void rxe_qp_init_misc(struct rxe_dev *rxe, struct rxe_qp *qp, spin_lock_init(&qp->state_lock); + spin_lock_init(&qp->req.task.state_lock); + spin_lock_init(&qp->resp.task.state_lock); + spin_lock_init(&qp->comp.task.state_lock); + + spin_lock_init(&qp->sq.sq_lock); + spin_lock_init(&qp->rq.producer_lock); + spin_lock_init(&qp->rq.consumer_lock); + atomic_set(&qp->ssn, 0); atomic_set(&qp->skb_out, 0); } @@ -230,10 +236,10 @@ static int rxe_qp_init_req(struct rxe_dev *rxe, struct rxe_qp *qp, QUEUE_TYPE_FROM_CLIENT); qp->req.state = QP_STATE_RESET; + qp->comp.state = QP_STATE_RESET; qp->req.opcode = -1; qp->comp.opcode = -1; - spin_lock_init(&qp->sq.sq_lock); skb_queue_head_init(&qp->req_pkts); rxe_init_task(rxe, &qp->req.task, qp, @@ -284,9 +290,6 @@ static int rxe_qp_init_resp(struct rxe_dev *rxe, struct rxe_qp *qp, } } - spin_lock_init(&qp->rq.producer_lock); - spin_lock_init(&qp->rq.consumer_lock); - skb_queue_head_init(&qp->resp_pkts); rxe_init_task(rxe, &qp->resp.task, qp, @@ -490,6 +493,7 @@ static void rxe_qp_reset(struct rxe_qp *qp) /* move qp to the reset state */ qp->req.state = QP_STATE_RESET; + qp->comp.state = QP_STATE_RESET; qp->resp.state = QP_STATE_RESET; /* let state machines reset themselves drain work and packet queues @@ -507,6 +511,7 @@ static void rxe_qp_reset(struct rxe_qp *qp) atomic_set(&qp->ssn, 0); qp->req.opcode = -1; qp->req.need_retry = 0; + qp->req.wait_for_rnr_timer = 0; qp->req.noack_pkts = 0; qp->resp.msn = 0; qp->resp.opcode = -1; @@ -552,6 +557,7 @@ void rxe_qp_error(struct rxe_qp *qp) { qp->req.state = QP_STATE_ERROR; qp->resp.state = QP_STATE_ERROR; + qp->comp.state = QP_STATE_ERROR; qp->attr.qp_state = IB_QPS_ERR; /* drain work and packet queues */ @@ -689,6 +695,7 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, pr_debug("qp#%d state -> INIT\n", qp_num(qp)); qp->req.state = QP_STATE_INIT; qp->resp.state = QP_STATE_INIT; + qp->comp.state = QP_STATE_INIT; break; case IB_QPS_RTR: @@ -699,6 +706,7 @@ int rxe_qp_from_attr(struct rxe_qp *qp, struct ib_qp_attr *attr, int mask, case IB_QPS_RTS: pr_debug("qp#%d state -> RTS\n", qp_num(qp)); qp->req.state = QP_STATE_READY; + qp->comp.state = QP_STATE_READY; break; case IB_QPS_SQD: @@ -804,13 +812,15 @@ static void rxe_qp_do_cleanup(struct work_struct *work) if (qp->rq.queue) rxe_queue_cleanup(qp->rq.queue); - atomic_dec(&qp->scq->num_wq); - if (qp->scq) + if (qp->scq) { + atomic_dec(&qp->scq->num_wq); rxe_put(qp->scq); + } - atomic_dec(&qp->rcq->num_wq); - if (qp->rcq) + if (qp->rcq) { + atomic_dec(&qp->rcq->num_wq); rxe_put(qp->rcq); + } if (qp->pd) rxe_put(qp->pd); diff --git a/drivers/infiniband/sw/rxe/rxe_queue.h b/drivers/infiniband/sw/rxe/rxe_queue.h index 6227112ef7a2..ed44042782fa 100644 --- a/drivers/infiniband/sw/rxe/rxe_queue.h +++ b/drivers/infiniband/sw/rxe/rxe_queue.h @@ -7,9 +7,6 @@ #ifndef RXE_QUEUE_H #define RXE_QUEUE_H -/* for definition of shared struct rxe_queue_buf */ -#include - /* Implements a simple circular buffer that is shared between user * and the driver and can be resized. The requested element size is * rounded up to a power of 2 and the number of elements in the buffer @@ -53,6 +50,8 @@ enum queue_type { QUEUE_TYPE_FROM_DRIVER, }; +struct rxe_queue_buf; + struct rxe_queue { struct rxe_dev *rxe; struct rxe_queue_buf *buf; diff --git a/drivers/infiniband/sw/rxe/rxe_req.c b/drivers/infiniband/sw/rxe/rxe_req.c index 9d98237389cf..f63771207970 100644 --- a/drivers/infiniband/sw/rxe/rxe_req.c +++ b/drivers/infiniband/sw/rxe/rxe_req.c @@ -15,8 +15,7 @@ static int next_opcode(struct rxe_qp *qp, struct rxe_send_wqe *wqe, u32 opcode); static inline void retry_first_write_send(struct rxe_qp *qp, - struct rxe_send_wqe *wqe, - unsigned int mask, int npsn) + struct rxe_send_wqe *wqe, int npsn) { int i; @@ -83,7 +82,7 @@ static void req_retry(struct rxe_qp *qp) if (mask & WR_WRITE_OR_SEND_MASK) { npsn = (qp->comp.psn - wqe->first_psn) & BTH_PSN_MASK; - retry_first_write_send(qp, wqe, mask, npsn); + retry_first_write_send(qp, wqe, npsn); } if (mask & WR_READ_MASK) { @@ -101,7 +100,11 @@ void rnr_nak_timer(struct timer_list *t) { struct rxe_qp *qp = from_timer(qp, t, rnr_nak_timer); - pr_debug("qp#%d rnr nak timer fired\n", qp_num(qp)); + pr_debug("%s: fired for qp#%d\n", __func__, qp_num(qp)); + + /* request a send queue retry */ + qp->req.need_retry = 1; + qp->req.wait_for_rnr_timer = 0; rxe_run_task(&qp->req.task, 1); } @@ -161,16 +164,36 @@ static struct rxe_send_wqe *req_next_wqe(struct rxe_qp *qp) (wqe->state != wqe_state_processing))) return NULL; - if (unlikely((wqe->wr.send_flags & IB_SEND_FENCE) && - (index != cons))) { - qp->req.wait_fence = 1; - return NULL; - } - wqe->mask = wr_opcode_mask(wqe->wr.opcode, qp); return wqe; } +/** + * rxe_wqe_is_fenced - check if next wqe is fenced + * @qp: the queue pair + * @wqe: the next wqe + * + * Returns: 1 if wqe needs to wait + * 0 if wqe is ready to go + */ +static int rxe_wqe_is_fenced(struct rxe_qp *qp, struct rxe_send_wqe *wqe) +{ + /* Local invalidate fence (LIF) see IBA 10.6.5.1 + * Requires ALL previous operations on the send queue + * are complete. Make mandatory for the rxe driver. + */ + if (wqe->wr.opcode == IB_WR_LOCAL_INV) + return qp->req.wqe_index != queue_get_consumer(qp->sq.queue, + QUEUE_TYPE_FROM_CLIENT); + + /* Fence see IBA 10.8.3.3 + * Requires that all previous read and atomic operations + * are complete. + */ + return (wqe->wr.send_flags & IB_SEND_FENCE) && + atomic_read(&qp->req.rd_atomic) != qp->attr.max_rd_atomic; +} + static int next_opcode_rc(struct rxe_qp *qp, u32 opcode, int fits) { switch (opcode) { @@ -581,9 +604,11 @@ static int rxe_do_local_ops(struct rxe_qp *qp, struct rxe_send_wqe *wqe) wqe->status = IB_WC_SUCCESS; qp->req.wqe_index = queue_next_index(qp->sq.queue, qp->req.wqe_index); - if ((wqe->wr.send_flags & IB_SEND_SIGNALED) || - qp->sq_sig_type == IB_SIGNAL_ALL_WR) - rxe_run_task(&qp->comp.task, 1); + /* There is no ack coming for local work requests + * which can lead to a deadlock. So go ahead and complete + * it now. + */ + rxe_run_task(&qp->comp.task, 1); return 0; } @@ -599,6 +624,7 @@ int rxe_requester(void *arg) u32 payload; int mtu; int opcode; + int err; int ret; struct rxe_send_wqe rollback_wqe; u32 rollback_psn; @@ -609,10 +635,20 @@ int rxe_requester(void *arg) if (!rxe_get(qp)) return -EAGAIN; -next_wqe: - if (unlikely(!qp->valid || qp->req.state == QP_STATE_ERROR)) + if (unlikely(!qp->valid)) goto exit; + if (unlikely(qp->req.state == QP_STATE_ERROR)) { + wqe = req_next_wqe(qp); + if (wqe) + /* + * Generate an error completion for error qp state + */ + goto err; + else + goto exit; + } + if (unlikely(qp->req.state == QP_STATE_RESET)) { qp->req.wqe_index = queue_get_consumer(q, QUEUE_TYPE_FROM_CLIENT); @@ -620,10 +656,17 @@ next_wqe: qp->req.need_rd_atomic = 0; qp->req.wait_psn = 0; qp->req.need_retry = 0; + qp->req.wait_for_rnr_timer = 0; goto exit; } - if (unlikely(qp->req.need_retry)) { + /* we come here if the retransmit timer has fired + * or if the rnr timer has fired. If the retransmit + * timer fires while we are processing an RNR NAK wait + * until the rnr timer has fired before starting the + * retry flow + */ + if (unlikely(qp->req.need_retry && !qp->req.wait_for_rnr_timer)) { req_retry(qp); qp->req.need_retry = 0; } @@ -632,12 +675,17 @@ next_wqe: if (unlikely(!wqe)) goto exit; + if (rxe_wqe_is_fenced(qp, wqe)) { + qp->req.wait_fence = 1; + goto exit; + } + if (wqe->mask & WR_LOCAL_OP_MASK) { - ret = rxe_do_local_ops(qp, wqe); - if (unlikely(ret)) + err = rxe_do_local_ops(qp, wqe); + if (unlikely(err)) goto err; else - goto next_wqe; + goto done; } if (unlikely(qp_type(qp) == IB_QPT_RC && @@ -685,9 +733,8 @@ next_wqe: qp->req.wqe_index); wqe->state = wqe_state_done; wqe->status = IB_WC_SUCCESS; - __rxe_do_task(&qp->comp.task); - rxe_put(qp); - return 0; + rxe_run_task(&qp->comp.task, 0); + goto done; } payload = mtu; } @@ -703,25 +750,29 @@ next_wqe: if (unlikely(!av)) { pr_err("qp#%d Failed no address vector\n", qp_num(qp)); wqe->status = IB_WC_LOC_QP_OP_ERR; - goto err_drop_ah; + goto err; } skb = init_req_packet(qp, av, wqe, opcode, payload, &pkt); if (unlikely(!skb)) { pr_err("qp#%d Failed allocating skb\n", qp_num(qp)); wqe->status = IB_WC_LOC_QP_OP_ERR; - goto err_drop_ah; + if (ah) + rxe_put(ah); + goto err; } - ret = finish_packet(qp, av, wqe, &pkt, skb, payload); - if (unlikely(ret)) { + err = finish_packet(qp, av, wqe, &pkt, skb, payload); + if (unlikely(err)) { pr_debug("qp#%d Error during finish packet\n", qp_num(qp)); - if (ret == -EFAULT) + if (err == -EFAULT) wqe->status = IB_WC_LOC_PROT_ERR; else wqe->status = IB_WC_LOC_QP_OP_ERR; kfree_skb(skb); - goto err_drop_ah; + if (ah) + rxe_put(ah); + goto err; } if (ah) @@ -736,13 +787,14 @@ next_wqe: save_state(wqe, qp, &rollback_wqe, &rollback_psn); update_wqe_state(qp, wqe, &pkt); update_wqe_psn(qp, wqe, &pkt, payload); - ret = rxe_xmit_packet(qp, &pkt, skb); - if (ret) { + + err = rxe_xmit_packet(qp, &pkt, skb); + if (err) { qp->need_req_skb = 1; rollback_state(wqe, qp, &rollback_wqe, rollback_psn); - if (ret == -EAGAIN) { + if (err == -EAGAIN) { rxe_run_task(&qp->req.task, 1); goto exit; } @@ -753,16 +805,23 @@ next_wqe: update_state(qp, &pkt); - goto next_wqe; - -err_drop_ah: - if (ah) - rxe_put(ah); + /* A non-zero return value will cause rxe_do_task to + * exit its loop and end the tasklet. A zero return + * will continue looping and return to rxe_requester + */ +done: + ret = 0; + goto out; err: + /* update wqe_index for each wqe completion */ + qp->req.wqe_index = queue_next_index(qp->sq.queue, qp->req.wqe_index); wqe->state = wqe_state_error; - __rxe_do_task(&qp->comp.task); - + qp->req.state = QP_STATE_ERROR; + rxe_run_task(&qp->comp.task, 0); exit: + ret = -EAGAIN; +out: rxe_put(qp); - return -EAGAIN; + + return ret; } diff --git a/drivers/infiniband/sw/rxe/rxe_resp.c b/drivers/infiniband/sw/rxe/rxe_resp.c index f4f6ee5d81fe..b36ec5c4d5e0 100644 --- a/drivers/infiniband/sw/rxe/rxe_resp.c +++ b/drivers/infiniband/sw/rxe/rxe_resp.c @@ -21,6 +21,7 @@ enum resp_states { RESPST_CHK_RKEY, RESPST_EXECUTE, RESPST_READ_REPLY, + RESPST_ATOMIC_REPLY, RESPST_COMPLETE, RESPST_ACKNOWLEDGE, RESPST_CLEANUP, @@ -55,6 +56,7 @@ static char *resp_state_name[] = { [RESPST_CHK_RKEY] = "CHK_RKEY", [RESPST_EXECUTE] = "EXECUTE", [RESPST_READ_REPLY] = "READ_REPLY", + [RESPST_ATOMIC_REPLY] = "ATOMIC_REPLY", [RESPST_COMPLETE] = "COMPLETE", [RESPST_ACKNOWLEDGE] = "ACKNOWLEDGE", [RESPST_CLEANUP] = "CLEANUP", @@ -448,7 +450,8 @@ static enum resp_states check_rkey(struct rxe_qp *qp, if (rkey_is_mw(rkey)) { mw = rxe_lookup_mw(qp, access, rkey); if (!mw) { - pr_err("%s: no MW matches rkey %#x\n", __func__, rkey); + pr_debug("%s: no MW matches rkey %#x\n", + __func__, rkey); state = RESPST_ERR_RKEY_VIOLATION; goto err; } @@ -468,7 +471,8 @@ static enum resp_states check_rkey(struct rxe_qp *qp, } else { mr = lookup_mr(qp->pd, access, rkey, RXE_LOOKUP_REMOTE); if (!mr) { - pr_err("%s: no MR matches rkey %#x\n", __func__, rkey); + pr_debug("%s: no MR matches rkey %#x\n", + __func__, rkey); state = RESPST_ERR_RKEY_VIOLATION; goto err; } @@ -549,49 +553,106 @@ out: return rc; } +static struct resp_res *rxe_prepare_res(struct rxe_qp *qp, + struct rxe_pkt_info *pkt, + int type) +{ + struct resp_res *res; + u32 pkts; + + res = &qp->resp.resources[qp->resp.res_head]; + rxe_advance_resp_resource(qp); + free_rd_atomic_resource(res); + + res->type = type; + res->replay = 0; + + switch (type) { + case RXE_READ_MASK: + res->read.va = qp->resp.va + qp->resp.offset; + res->read.va_org = qp->resp.va + qp->resp.offset; + res->read.resid = qp->resp.resid; + res->read.length = qp->resp.resid; + res->read.rkey = qp->resp.rkey; + + pkts = max_t(u32, (reth_len(pkt) + qp->mtu - 1)/qp->mtu, 1); + res->first_psn = pkt->psn; + res->cur_psn = pkt->psn; + res->last_psn = (pkt->psn + pkts - 1) & BTH_PSN_MASK; + + res->state = rdatm_res_state_new; + break; + case RXE_ATOMIC_MASK: + res->first_psn = pkt->psn; + res->last_psn = pkt->psn; + res->cur_psn = pkt->psn; + break; + } + + return res; +} + /* Guarantee atomicity of atomic operations at the machine level. */ static DEFINE_SPINLOCK(atomic_ops_lock); -static enum resp_states process_atomic(struct rxe_qp *qp, - struct rxe_pkt_info *pkt) +static enum resp_states atomic_reply(struct rxe_qp *qp, + struct rxe_pkt_info *pkt) { u64 *vaddr; enum resp_states ret; struct rxe_mr *mr = qp->resp.mr; + struct resp_res *res = qp->resp.res; + u64 value; - if (mr->state != RXE_MR_STATE_VALID) { - ret = RESPST_ERR_RKEY_VIOLATION; - goto out; + if (!res) { + res = rxe_prepare_res(qp, pkt, RXE_ATOMIC_MASK); + qp->resp.res = res; } - vaddr = iova_to_vaddr(mr, qp->resp.va + qp->resp.offset, sizeof(u64)); + if (!res->replay) { + if (mr->state != RXE_MR_STATE_VALID) { + ret = RESPST_ERR_RKEY_VIOLATION; + goto out; + } - /* check vaddr is 8 bytes aligned. */ - if (!vaddr || (uintptr_t)vaddr & 7) { - ret = RESPST_ERR_MISALIGNED_ATOMIC; - goto out; + vaddr = iova_to_vaddr(mr, qp->resp.va + qp->resp.offset, + sizeof(u64)); + + /* check vaddr is 8 bytes aligned. */ + if (!vaddr || (uintptr_t)vaddr & 7) { + ret = RESPST_ERR_MISALIGNED_ATOMIC; + goto out; + } + + spin_lock_bh(&atomic_ops_lock); + res->atomic.orig_val = value = *vaddr; + + if (pkt->opcode == IB_OPCODE_RC_COMPARE_SWAP) { + if (value == atmeth_comp(pkt)) + value = atmeth_swap_add(pkt); + } else { + value += atmeth_swap_add(pkt); + } + + *vaddr = value; + spin_unlock_bh(&atomic_ops_lock); + + qp->resp.msn++; + + /* next expected psn, read handles this separately */ + qp->resp.psn = (pkt->psn + 1) & BTH_PSN_MASK; + qp->resp.ack_psn = qp->resp.psn; + + qp->resp.opcode = pkt->opcode; + qp->resp.status = IB_WC_SUCCESS; } - spin_lock_bh(&atomic_ops_lock); - - qp->resp.atomic_orig = *vaddr; - - if (pkt->opcode == IB_OPCODE_RC_COMPARE_SWAP) { - if (*vaddr == atmeth_comp(pkt)) - *vaddr = atmeth_swap_add(pkt); - } else { - *vaddr += atmeth_swap_add(pkt); - } - - spin_unlock_bh(&atomic_ops_lock); - - ret = RESPST_NONE; + ret = RESPST_ACKNOWLEDGE; out: return ret; } static struct sk_buff *prepare_ack_packet(struct rxe_qp *qp, - struct rxe_pkt_info *pkt, struct rxe_pkt_info *ack, int opcode, int payload, @@ -629,7 +690,7 @@ static struct sk_buff *prepare_ack_packet(struct rxe_qp *qp, } if (ack->mask & RXE_ATMACK_MASK) - atmack_set_orig(ack, qp->resp.atomic_orig); + atmack_set_orig(ack, qp->resp.res->atomic.orig_val); err = rxe_prepare(&qp->pri_av, ack, skb); if (err) { @@ -640,34 +701,6 @@ static struct sk_buff *prepare_ack_packet(struct rxe_qp *qp, return skb; } -static struct resp_res *rxe_prepare_read_res(struct rxe_qp *qp, - struct rxe_pkt_info *pkt) -{ - struct resp_res *res; - u32 pkts; - - res = &qp->resp.resources[qp->resp.res_head]; - rxe_advance_resp_resource(qp); - free_rd_atomic_resource(qp, res); - - res->type = RXE_READ_MASK; - res->replay = 0; - res->read.va = qp->resp.va + qp->resp.offset; - res->read.va_org = qp->resp.va + qp->resp.offset; - res->read.resid = qp->resp.resid; - res->read.length = qp->resp.resid; - res->read.rkey = qp->resp.rkey; - - pkts = max_t(u32, (reth_len(pkt) + qp->mtu - 1)/qp->mtu, 1); - res->first_psn = pkt->psn; - res->cur_psn = pkt->psn; - res->last_psn = (pkt->psn + pkts - 1) & BTH_PSN_MASK; - - res->state = rdatm_res_state_new; - - return res; -} - /** * rxe_recheck_mr - revalidate MR from rkey and get a reference * @qp: the qp @@ -738,7 +771,7 @@ static enum resp_states read_reply(struct rxe_qp *qp, struct rxe_mr *mr; if (!res) { - res = rxe_prepare_read_res(qp, req_pkt); + res = rxe_prepare_res(qp, req_pkt, RXE_READ_MASK); qp->resp.res = res; } @@ -771,7 +804,7 @@ static enum resp_states read_reply(struct rxe_qp *qp, payload = min_t(int, res->read.resid, mtu); - skb = prepare_ack_packet(qp, req_pkt, &ack_pkt, opcode, payload, + skb = prepare_ack_packet(qp, &ack_pkt, opcode, payload, res->cur_psn, AETH_ACK_UNLIMITED); if (!skb) return RESPST_ERR_RNR; @@ -858,9 +891,7 @@ static enum resp_states execute(struct rxe_qp *qp, struct rxe_pkt_info *pkt) qp->resp.msn++; return RESPST_READ_REPLY; } else if (pkt->mask & RXE_ATOMIC_MASK) { - err = process_atomic(qp, pkt); - if (err) - return err; + return RESPST_ATOMIC_REPLY; } else { /* Unreachable */ WARN_ON_ONCE(1); @@ -997,14 +1028,13 @@ finish: return RESPST_CLEANUP; } -static int send_ack(struct rxe_qp *qp, struct rxe_pkt_info *pkt, - u8 syndrome, u32 psn) +static int send_ack(struct rxe_qp *qp, u8 syndrome, u32 psn) { int err = 0; struct rxe_pkt_info ack_pkt; struct sk_buff *skb; - skb = prepare_ack_packet(qp, pkt, &ack_pkt, IB_OPCODE_RC_ACKNOWLEDGE, + skb = prepare_ack_packet(qp, &ack_pkt, IB_OPCODE_RC_ACKNOWLEDGE, 0, psn, syndrome); if (!skb) { err = -ENOMEM; @@ -1019,40 +1049,29 @@ err1: return err; } -static int send_atomic_ack(struct rxe_qp *qp, struct rxe_pkt_info *pkt, - u8 syndrome) +static int send_atomic_ack(struct rxe_qp *qp, u8 syndrome, u32 psn) { - int rc = 0; + int err = 0; struct rxe_pkt_info ack_pkt; struct sk_buff *skb; - struct resp_res *res; - skb = prepare_ack_packet(qp, pkt, &ack_pkt, - IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE, 0, pkt->psn, - syndrome); + skb = prepare_ack_packet(qp, &ack_pkt, IB_OPCODE_RC_ATOMIC_ACKNOWLEDGE, + 0, psn, syndrome); if (!skb) { - rc = -ENOMEM; + err = -ENOMEM; goto out; } - res = &qp->resp.resources[qp->resp.res_head]; - free_rd_atomic_resource(qp, res); - rxe_advance_resp_resource(qp); + err = rxe_xmit_packet(qp, &ack_pkt, skb); + if (err) + pr_err_ratelimited("Failed sending atomic ack\n"); - skb_get(skb); - res->type = RXE_ATOMIC_MASK; - res->atomic.skb = skb; - res->first_psn = ack_pkt.psn; - res->last_psn = ack_pkt.psn; - res->cur_psn = ack_pkt.psn; - - rc = rxe_xmit_packet(qp, &ack_pkt, skb); - if (rc) { - pr_err_ratelimited("Failed sending ack\n"); - rxe_put(qp); - } + /* have to clear this since it is used to trigger + * long read replies + */ + qp->resp.res = NULL; out: - return rc; + return err; } static enum resp_states acknowledge(struct rxe_qp *qp, @@ -1062,11 +1081,11 @@ static enum resp_states acknowledge(struct rxe_qp *qp, return RESPST_CLEANUP; if (qp->resp.aeth_syndrome != AETH_ACK_UNLIMITED) - send_ack(qp, pkt, qp->resp.aeth_syndrome, pkt->psn); + send_ack(qp, qp->resp.aeth_syndrome, pkt->psn); else if (pkt->mask & RXE_ATOMIC_MASK) - send_atomic_ack(qp, pkt, AETH_ACK_UNLIMITED); + send_atomic_ack(qp, AETH_ACK_UNLIMITED, pkt->psn); else if (bth_ack(pkt)) - send_ack(qp, pkt, AETH_ACK_UNLIMITED, pkt->psn); + send_ack(qp, AETH_ACK_UNLIMITED, pkt->psn); return RESPST_CLEANUP; } @@ -1119,7 +1138,7 @@ static enum resp_states duplicate_request(struct rxe_qp *qp, if (pkt->mask & RXE_SEND_MASK || pkt->mask & RXE_WRITE_MASK) { /* SEND. Ack again and cleanup. C9-105. */ - send_ack(qp, pkt, AETH_ACK_UNLIMITED, prev_psn); + send_ack(qp, AETH_ACK_UNLIMITED, prev_psn); return RESPST_CLEANUP; } else if (pkt->mask & RXE_READ_MASK) { struct resp_res *res; @@ -1173,14 +1192,11 @@ static enum resp_states duplicate_request(struct rxe_qp *qp, /* Find the operation in our list of responder resources. */ res = find_resource(qp, pkt->psn); if (res) { - skb_get(res->atomic.skb); - /* Resend the result. */ - rc = rxe_xmit_packet(qp, pkt, res->atomic.skb); - if (rc) { - pr_err("Failed resending result. This flow is not handled - skb ignored\n"); - rc = RESPST_CLEANUP; - goto out; - } + res->replay = 1; + res->cur_psn = pkt->psn; + qp->resp.res = res; + rc = RESPST_ATOMIC_REPLY; + goto out; } /* Resource not found. Class D error. Drop the request. */ @@ -1260,17 +1276,15 @@ int rxe_responder(void *arg) struct rxe_dev *rxe = to_rdev(qp->ibqp.device); enum resp_states state; struct rxe_pkt_info *pkt = NULL; - int ret = 0; + int ret; if (!rxe_get(qp)) return -EAGAIN; qp->resp.aeth_syndrome = AETH_ACK_UNLIMITED; - if (!qp->valid) { - ret = -EINVAL; - goto done; - } + if (!qp->valid) + goto exit; switch (qp->resp.state) { case QP_STATE_RESET: @@ -1316,6 +1330,9 @@ int rxe_responder(void *arg) case RESPST_READ_REPLY: state = read_reply(qp, pkt); break; + case RESPST_ATOMIC_REPLY: + state = atomic_reply(qp, pkt); + break; case RESPST_ACKNOWLEDGE: state = acknowledge(qp, pkt); break; @@ -1327,7 +1344,7 @@ int rxe_responder(void *arg) break; case RESPST_ERR_PSN_OUT_OF_SEQ: /* RC only - Class B. Drop packet. */ - send_ack(qp, pkt, AETH_NAK_PSN_SEQ_ERROR, qp->resp.psn); + send_ack(qp, AETH_NAK_PSN_SEQ_ERROR, qp->resp.psn); state = RESPST_CLEANUP; break; @@ -1349,7 +1366,7 @@ int rxe_responder(void *arg) if (qp_type(qp) == IB_QPT_RC) { rxe_counter_inc(rxe, RXE_CNT_SND_RNR); /* RC - class B */ - send_ack(qp, pkt, AETH_RNR_NAK | + send_ack(qp, AETH_RNR_NAK | (~AETH_TYPE_MASK & qp->attr.min_rnr_timer), pkt->psn); @@ -1438,7 +1455,7 @@ int rxe_responder(void *arg) case RESPST_ERROR: qp->resp.goto_error = 0; - pr_warn("qp#%d moved to error state\n", qp_num(qp)); + pr_debug("qp#%d moved to error state\n", qp_num(qp)); rxe_qp_error(qp); goto exit; @@ -1447,9 +1464,16 @@ int rxe_responder(void *arg) } } + /* A non-zero return value will cause rxe_do_task to + * exit its loop and end the tasklet. A zero return + * will continue looping and return to rxe_responder + */ +done: + ret = 0; + goto out; exit: ret = -EAGAIN; -done: +out: rxe_put(qp); return ret; } diff --git a/drivers/infiniband/sw/rxe/rxe_task.c b/drivers/infiniband/sw/rxe/rxe_task.c index 0c4db5bb17d7..2248cf33d776 100644 --- a/drivers/infiniband/sw/rxe/rxe_task.c +++ b/drivers/infiniband/sw/rxe/rxe_task.c @@ -8,7 +8,7 @@ #include #include -#include "rxe_task.h" +#include "rxe.h" int __rxe_do_task(struct rxe_task *task) @@ -33,6 +33,7 @@ void rxe_do_task(struct tasklet_struct *t) int cont; int ret; struct rxe_task *task = from_tasklet(task, t, tasklet); + unsigned int iterations = RXE_MAX_ITERATIONS; spin_lock_bh(&task->state_lock); switch (task->state) { @@ -61,13 +62,20 @@ void rxe_do_task(struct tasklet_struct *t) spin_lock_bh(&task->state_lock); switch (task->state) { case TASK_STATE_BUSY: - if (ret) + if (ret) { task->state = TASK_STATE_START; - else + } else if (iterations--) { cont = 1; + } else { + /* reschedule the tasklet and exit + * the loop to give up the cpu + */ + tasklet_schedule(&task->tasklet); + task->state = TASK_STATE_START; + } break; - /* soneone tried to run the task since the last time we called + /* someone tried to run the task since the last time we called * func, so we will call one more time regardless of the * return value */ diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.c b/drivers/infiniband/sw/rxe/rxe_verbs.c index 9d995854a174..e264cf69bf55 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.c +++ b/drivers/infiniband/sw/rxe/rxe_verbs.c @@ -115,7 +115,7 @@ static void rxe_dealloc_ucontext(struct ib_ucontext *ibuc) { struct rxe_ucontext *uc = to_ruc(ibuc); - rxe_put(uc); + rxe_cleanup(uc); } static int rxe_port_immutable(struct ib_device *dev, u32 port_num, @@ -149,7 +149,7 @@ static int rxe_dealloc_pd(struct ib_pd *ibpd, struct ib_udata *udata) { struct rxe_pd *pd = to_rpd(ibpd); - rxe_put(pd); + rxe_cleanup(pd); return 0; } @@ -176,7 +176,8 @@ static int rxe_create_ah(struct ib_ah *ibah, if (err) return err; - err = rxe_add_to_pool(&rxe->ah_pool, ah); + err = rxe_add_to_pool_ah(&rxe->ah_pool, ah, + init_attr->flags & RDMA_CREATE_AH_SLEEPABLE); if (err) return err; @@ -188,7 +189,7 @@ static int rxe_create_ah(struct ib_ah *ibah, err = copy_to_user(&uresp->ah_num, &ah->ah_num, sizeof(uresp->ah_num)); if (err) { - rxe_put(ah); + rxe_cleanup(ah); return -EFAULT; } } else if (ah->is_user) { @@ -197,6 +198,8 @@ static int rxe_create_ah(struct ib_ah *ibah, } rxe_init_av(init_attr->ah_attr, &ah->av); + rxe_finalize(ah); + return 0; } @@ -228,7 +231,8 @@ static int rxe_destroy_ah(struct ib_ah *ibah, u32 flags) { struct rxe_ah *ah = to_rah(ibah); - rxe_put(ah); + rxe_cleanup_ah(ah, flags & RDMA_DESTROY_AH_SLEEPABLE); + return 0; } @@ -308,12 +312,13 @@ static int rxe_create_srq(struct ib_srq *ibsrq, struct ib_srq_init_attr *init, err = rxe_srq_from_init(rxe, srq, init, udata, uresp); if (err) - goto err_put; + goto err_cleanup; return 0; -err_put: - rxe_put(srq); +err_cleanup: + rxe_cleanup(srq); + return err; } @@ -362,7 +367,7 @@ static int rxe_destroy_srq(struct ib_srq *ibsrq, struct ib_udata *udata) { struct rxe_srq *srq = to_rsrq(ibsrq); - rxe_put(srq); + rxe_cleanup(srq); return 0; } @@ -429,10 +434,11 @@ static int rxe_create_qp(struct ib_qp *ibqp, struct ib_qp_init_attr *init, if (err) goto qp_init; + rxe_finalize(qp); return 0; qp_init: - rxe_put(qp); + rxe_cleanup(qp); return err; } @@ -485,7 +491,7 @@ static int rxe_destroy_qp(struct ib_qp *ibqp, struct ib_udata *udata) if (ret) return ret; - rxe_put(qp); + rxe_cleanup(qp); return 0; } @@ -803,7 +809,7 @@ static int rxe_destroy_cq(struct ib_cq *ibcq, struct ib_udata *udata) rxe_cq_disable(cq); - rxe_put(cq); + rxe_cleanup(cq); return 0; } @@ -898,6 +904,7 @@ static struct ib_mr *rxe_get_dma_mr(struct ib_pd *ibpd, int access) rxe_get(pd); rxe_mr_init_dma(pd, access, mr); + rxe_finalize(mr); return &mr->ibmr; } @@ -926,11 +933,13 @@ static struct ib_mr *rxe_reg_user_mr(struct ib_pd *ibpd, if (err) goto err3; + rxe_finalize(mr); + return &mr->ibmr; err3: rxe_put(pd); - rxe_put(mr); + rxe_cleanup(mr); err2: return ERR_PTR(err); } @@ -958,35 +967,52 @@ static struct ib_mr *rxe_alloc_mr(struct ib_pd *ibpd, enum ib_mr_type mr_type, if (err) goto err2; + rxe_finalize(mr); + return &mr->ibmr; err2: rxe_put(pd); - rxe_put(mr); + rxe_cleanup(mr); err1: return ERR_PTR(err); } -/* build next_map_set from scatterlist - * The IB_WR_REG_MR WR will swap map_sets - */ +static int rxe_set_page(struct ib_mr *ibmr, u64 addr) +{ + struct rxe_mr *mr = to_rmr(ibmr); + struct rxe_map *map; + struct rxe_phys_buf *buf; + + if (unlikely(mr->nbuf == mr->num_buf)) + return -ENOMEM; + + map = mr->map[mr->nbuf / RXE_BUF_PER_MAP]; + buf = &map->buf[mr->nbuf % RXE_BUF_PER_MAP]; + + buf->addr = addr; + buf->size = ibmr->page_size; + mr->nbuf++; + + return 0; +} + static int rxe_map_mr_sg(struct ib_mr *ibmr, struct scatterlist *sg, int sg_nents, unsigned int *sg_offset) { struct rxe_mr *mr = to_rmr(ibmr); - struct rxe_map_set *set = mr->next_map_set; int n; - set->nbuf = 0; + mr->nbuf = 0; - n = ib_sg_to_pages(ibmr, sg, sg_nents, sg_offset, rxe_mr_set_page); + n = ib_sg_to_pages(ibmr, sg, sg_nents, sg_offset, rxe_set_page); - set->va = ibmr->iova; - set->iova = ibmr->iova; - set->length = ibmr->length; - set->page_shift = ilog2(ibmr->page_size); - set->page_mask = ibmr->page_size - 1; - set->offset = set->iova & set->page_mask; + mr->va = ibmr->iova; + mr->iova = ibmr->iova; + mr->length = ibmr->length; + mr->page_shift = ilog2(ibmr->page_size); + mr->page_mask = ibmr->page_size - 1; + mr->offset = mr->iova & mr->page_mask; return n; } diff --git a/drivers/infiniband/sw/rxe/rxe_verbs.h b/drivers/infiniband/sw/rxe/rxe_verbs.h index ac464e68c923..96af3e054f4d 100644 --- a/drivers/infiniband/sw/rxe/rxe_verbs.h +++ b/drivers/infiniband/sw/rxe/rxe_verbs.h @@ -9,7 +9,6 @@ #include #include -#include #include "rxe_pool.h" #include "rxe_task.h" #include "rxe_hw_counters.h" @@ -124,11 +123,13 @@ struct rxe_req_info { int need_rd_atomic; int wait_psn; int need_retry; + int wait_for_rnr_timer; int noack_pkts; struct rxe_task task; }; struct rxe_comp_info { + enum rxe_qp_state state; u32 psn; int opcode; int timeout; @@ -155,7 +156,7 @@ struct resp_res { union { struct { - struct sk_buff *skb; + u64 orig_val; } atomic; struct { u64 va_org; @@ -189,7 +190,6 @@ struct rxe_resp_info { u32 resid; u32 rkey; u32 length; - u64 atomic_orig; /* SRQ only */ struct { @@ -288,17 +288,6 @@ struct rxe_map { struct rxe_phys_buf buf[RXE_BUF_PER_MAP]; }; -struct rxe_map_set { - struct rxe_map **map; - u64 va; - u64 iova; - size_t length; - u32 offset; - u32 nbuf; - int page_shift; - int page_mask; -}; - static inline int rkey_is_mw(u32 rkey) { u32 index = rkey >> 8; @@ -316,20 +305,26 @@ struct rxe_mr { u32 rkey; enum rxe_mr_state state; enum ib_mr_type type; + u64 va; + u64 iova; + size_t length; + u32 offset; int access; + int page_shift; + int page_mask; int map_shift; int map_mask; u32 num_buf; + u32 nbuf; u32 max_buf; u32 num_map; atomic_t num_mw; - struct rxe_map_set *cur_map_set; - struct rxe_map_set *next_map_set; + struct rxe_map **map; }; enum rxe_mw_state { diff --git a/drivers/infiniband/sw/siw/siw_cm.c b/drivers/infiniband/sw/siw/siw_cm.c index 17f34d584cd9..f88d2971c2c6 100644 --- a/drivers/infiniband/sw/siw/siw_cm.c +++ b/drivers/infiniband/sw/siw/siw_cm.c @@ -725,11 +725,11 @@ static int siw_proc_mpareply(struct siw_cep *cep) enum mpa_v2_ctrl mpa_p2p_mode = MPA_V2_RDMA_NO_RTR; rv = siw_recv_mpa_rr(cep); - if (rv != -EAGAIN) - siw_cancel_mpatimer(cep); if (rv) goto out_err; + siw_cancel_mpatimer(cep); + rep = &cep->mpa.hdr; if (__mpa_rr_revision(rep->params.bits) > MPA_REVISION_2) { @@ -895,7 +895,8 @@ static int siw_proc_mpareply(struct siw_cep *cep) } out_err: - siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -EINVAL); + if (rv != -EAGAIN) + siw_cm_upcall(cep, IW_CM_EVENT_CONNECT_REPLY, -EINVAL); return rv; } diff --git a/drivers/infiniband/sw/siw/siw_verbs.c b/drivers/infiniband/sw/siw/siw_verbs.c index 09316072b789..8dedae7ae79e 100644 --- a/drivers/infiniband/sw/siw/siw_verbs.c +++ b/drivers/infiniband/sw/siw/siw_verbs.c @@ -1167,7 +1167,7 @@ int siw_create_cq(struct ib_cq *base_cq, const struct ib_cq_init_attr *attr, err_out: siw_dbg(base_cq->device, "CQ creation failed: %d", rv); - if (cq && cq->queue) { + if (cq->queue) { struct siw_ucontext *ctx = rdma_udata_to_drv_context(udata, struct siw_ucontext, base_ucontext); diff --git a/drivers/infiniband/ulp/ipoib/ipoib_ib.c b/drivers/infiniband/ulp/ipoib/ipoib_ib.c index f7995519bbc8..ed25061fac62 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_ib.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_ib.c @@ -1109,7 +1109,7 @@ static bool ipoib_dev_addr_changed_valid(struct ipoib_dev_priv *priv) * if he sets the device address back to be based on GID index 0, * he no longer wishs to control it. * - * If the user doesn't control the the device address, + * If the user doesn't control the device address, * IPOIB_FLAG_DEV_ADDR_SET is set and ib_find_gid failed it means * the port GUID has changed and GID at index 0 has changed * so we need to change priv->local_gid and priv->dev->dev_addr diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index 2a8961b685c2..a4904371e2db 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -1664,8 +1664,10 @@ static void ipoib_napi_add(struct net_device *dev) { struct ipoib_dev_priv *priv = ipoib_priv(dev); - netif_napi_add(dev, &priv->recv_napi, ipoib_rx_poll, IPOIB_NUM_WC); - netif_napi_add(dev, &priv->send_napi, ipoib_tx_poll, MAX_SEND_CQE); + netif_napi_add_weight(dev, &priv->recv_napi, ipoib_rx_poll, + IPOIB_NUM_WC); + netif_napi_add_weight(dev, &priv->send_napi, ipoib_tx_poll, + MAX_SEND_CQE); } static void ipoib_napi_del(struct net_device *dev) diff --git a/drivers/infiniband/ulp/iser/iscsi_iser.c b/drivers/infiniband/ulp/iser/iscsi_iser.c index 321949a570ed..620ae5b2d80d 100644 --- a/drivers/infiniband/ulp/iser/iscsi_iser.c +++ b/drivers/infiniband/ulp/iser/iscsi_iser.c @@ -568,7 +568,7 @@ static void iscsi_iser_session_destroy(struct iscsi_cls_session *cls_session) struct Scsi_Host *shost = iscsi_session_to_shost(cls_session); iscsi_session_teardown(cls_session); - iscsi_host_remove(shost); + iscsi_host_remove(shost, false); iscsi_host_free(shost); } @@ -685,7 +685,7 @@ iscsi_iser_session_create(struct iscsi_endpoint *ep, return cls_session; remove_host: - iscsi_host_remove(shost); + iscsi_host_remove(shost, false); free_host: iscsi_host_free(shost); return NULL; diff --git a/drivers/infiniband/ulp/iser/iser_verbs.c b/drivers/infiniband/ulp/iser/iser_verbs.c index c08f2d9133b6..a00ca117303a 100644 --- a/drivers/infiniband/ulp/iser/iser_verbs.c +++ b/drivers/infiniband/ulp/iser/iser_verbs.c @@ -246,6 +246,7 @@ static int iser_create_ib_conn_res(struct ib_conn *ib_conn) device = ib_conn->device; ib_dev = device->ib_device; + /* +1 for drain */ if (ib_conn->pi_support) max_send_wr = ISER_QP_SIG_MAX_REQ_DTOS + 1; else @@ -267,7 +268,8 @@ static int iser_create_ib_conn_res(struct ib_conn *ib_conn) init_attr.qp_context = (void *)ib_conn; init_attr.send_cq = ib_conn->cq; init_attr.recv_cq = ib_conn->cq; - init_attr.cap.max_recv_wr = ISER_QP_MAX_RECV_DTOS; + /* +1 for drain */ + init_attr.cap.max_recv_wr = ISER_QP_MAX_RECV_DTOS + 1; init_attr.cap.max_send_sge = 2; init_attr.cap.max_recv_sge = 1; init_attr.sq_sig_type = IB_SIGNAL_REQ_WR; @@ -485,7 +487,7 @@ int iser_conn_terminate(struct iser_conn *iser_conn) iser_conn, err); /* block until all flush errors are consumed */ - ib_drain_sq(ib_conn->qp); + ib_drain_qp(ib_conn->qp); } return 1; diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt-stats.c b/drivers/infiniband/ulp/rtrs/rtrs-clt-stats.c index 385a19846c24..1e6ffafa2db3 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt-stats.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt-stats.c @@ -32,11 +32,7 @@ void rtrs_clt_update_wc_stats(struct rtrs_clt_con *con) void rtrs_clt_inc_failover_cnt(struct rtrs_clt_stats *stats) { - struct rtrs_clt_stats_pcpu *s; - - s = get_cpu_ptr(stats->pcpu_stats); - s->rdma.failover_cnt++; - put_cpu_ptr(stats->pcpu_stats); + this_cpu_inc(stats->pcpu_stats->rdma.failover_cnt); } int rtrs_clt_stats_migration_from_cnt_to_str(struct rtrs_clt_stats *stats, char *buf) @@ -169,12 +165,8 @@ int rtrs_clt_reset_all_stats(struct rtrs_clt_stats *s, bool enable) static inline void rtrs_clt_update_rdma_stats(struct rtrs_clt_stats *stats, size_t size, int d) { - struct rtrs_clt_stats_pcpu *s; - - s = get_cpu_ptr(stats->pcpu_stats); - s->rdma.dir[d].cnt++; - s->rdma.dir[d].size_total += size; - put_cpu_ptr(stats->pcpu_stats); + this_cpu_inc(stats->pcpu_stats->rdma.dir[d].cnt); + this_cpu_add(stats->pcpu_stats->rdma.dir[d].size_total, size); } void rtrs_clt_update_all_stats(struct rtrs_clt_io_req *req, int dir) diff --git a/drivers/infiniband/ulp/rtrs/rtrs-clt.c b/drivers/infiniband/ulp/rtrs/rtrs-clt.c index 9809c3883979..baecde41d126 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-clt.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-clt.c @@ -740,25 +740,25 @@ struct path_it { struct rtrs_clt_path *(*next_path)(struct path_it *it); }; -/** - * list_next_or_null_rr_rcu - get next list element in round-robin fashion. +/* + * rtrs_clt_get_next_path_or_null - get clt path from the list or return NULL * @head: the head for the list. - * @ptr: the list head to take the next element from. - * @type: the type of the struct this is embedded in. - * @memb: the name of the list_head within the struct. + * @clt_path: The element to take the next clt_path from. * - * Next element returned in round-robin fashion, i.e. head will be skipped, + * Next clt path returned in round-robin fashion, i.e. head will be skipped, * but if list is observed as empty, NULL will be returned. * - * This primitive may safely run concurrently with the _rcu list-mutation + * This function may safely run concurrently with the _rcu list-mutation * primitives such as list_add_rcu() as long as it's guarded by rcu_read_lock(). */ -#define list_next_or_null_rr_rcu(head, ptr, type, memb) \ -({ \ - list_next_or_null_rcu(head, ptr, type, memb) ?: \ - list_next_or_null_rcu(head, READ_ONCE((ptr)->next), \ - type, memb); \ -}) +static inline struct rtrs_clt_path * +rtrs_clt_get_next_path_or_null(struct list_head *head, struct rtrs_clt_path *clt_path) +{ + return list_next_or_null_rcu(head, &clt_path->s.entry, typeof(*clt_path), s.entry) ?: + list_next_or_null_rcu(head, + READ_ONCE((&clt_path->s.entry)->next), + typeof(*clt_path), s.entry); +} /** * get_next_path_rr() - Returns path in round-robin fashion. @@ -789,10 +789,8 @@ static struct rtrs_clt_path *get_next_path_rr(struct path_it *it) path = list_first_or_null_rcu(&clt->paths_list, typeof(*path), s.entry); else - path = list_next_or_null_rr_rcu(&clt->paths_list, - &path->s.entry, - typeof(*path), - s.entry); + path = rtrs_clt_get_next_path_or_null(&clt->paths_list, path); + rcu_assign_pointer(*ppcpu_path, path); return path; @@ -1403,8 +1401,7 @@ static int alloc_permits(struct rtrs_clt_sess *clt) unsigned int chunk_bits; int err, i; - clt->permits_map = kcalloc(BITS_TO_LONGS(clt->queue_depth), - sizeof(long), GFP_KERNEL); + clt->permits_map = bitmap_zalloc(clt->queue_depth, GFP_KERNEL); if (!clt->permits_map) { err = -ENOMEM; goto out_err; @@ -1426,7 +1423,7 @@ static int alloc_permits(struct rtrs_clt_sess *clt) return 0; err_map: - kfree(clt->permits_map); + bitmap_free(clt->permits_map); clt->permits_map = NULL; out_err: return err; @@ -1434,13 +1431,11 @@ out_err: static void free_permits(struct rtrs_clt_sess *clt) { - if (clt->permits_map) { - size_t sz = clt->queue_depth; - + if (clt->permits_map) wait_event(clt->permits_wait, - find_first_bit(clt->permits_map, sz) >= sz); - } - kfree(clt->permits_map); + bitmap_empty(clt->permits_map, clt->queue_depth)); + + bitmap_free(clt->permits_map); clt->permits_map = NULL; kfree(clt->permits); clt->permits = NULL; @@ -2277,8 +2272,7 @@ static void rtrs_clt_remove_path_from_arr(struct rtrs_clt_path *clt_path) * removed. If @sess is the last element, then @next is NULL. */ rcu_read_lock(); - next = list_next_or_null_rr_rcu(&clt->paths_list, &clt_path->s.entry, - typeof(*next), s.entry); + next = rtrs_clt_get_next_path_or_null(&clt->paths_list, clt_path); rcu_read_unlock(); /* diff --git a/drivers/infiniband/ulp/rtrs/rtrs-pri.h b/drivers/infiniband/ulp/rtrs/rtrs-pri.h index 9a1e5c2ae55c..ac0df734eba8 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-pri.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-pri.h @@ -23,6 +23,17 @@ #define RTRS_PROTO_VER_STRING __stringify(RTRS_PROTO_VER_MAJOR) "." \ __stringify(RTRS_PROTO_VER_MINOR) +/* + * Max IB immediate data size is 2^28 (MAX_IMM_PAYL_BITS) + * and the minimum chunk size is 4096 (2^12). + * So the maximum sess_queue_depth is 65536 (2^16) in theory. + * But mempool_create, create_qp and ib_post_send fail with + * "cannot allocate memory" error if sess_queue_depth is too big. + * Therefore the pratical max value of sess_queue_depth is + * somewhere between 1 and 65534 and it depends on the system. + */ +#define MAX_SESS_QUEUE_DEPTH 65535 + enum rtrs_imm_const { MAX_IMM_TYPE_BITS = 4, MAX_IMM_TYPE_MASK = ((1 << MAX_IMM_TYPE_BITS) - 1), @@ -46,16 +57,6 @@ enum { MAX_PATHS_NUM = 128, - /* - * Max IB immediate data size is 2^28 (MAX_IMM_PAYL_BITS) - * and the minimum chunk size is 4096 (2^12). - * So the maximum sess_queue_depth is 65536 (2^16) in theory. - * But mempool_create, create_qp and ib_post_send fail with - * "cannot allocate memory" error if sess_queue_depth is too big. - * Therefore the pratical max value of sess_queue_depth is - * somewhere between 1 and 65534 and it depends on the system. - */ - MAX_SESS_QUEUE_DEPTH = 65535, MIN_CHUNK_SIZE = 8192, RTRS_HB_INTERVAL_MS = 5000, diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-stats.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-stats.c index 44b1c1652131..2aff1213a19d 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv-stats.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-stats.c @@ -14,9 +14,14 @@ int rtrs_srv_reset_rdma_stats(struct rtrs_srv_stats *stats, bool enable) { if (enable) { - struct rtrs_srv_stats_rdma_stats *r = &stats->rdma_stats; + int cpu; + struct rtrs_srv_stats_rdma_stats *r; + + for_each_possible_cpu(cpu) { + r = per_cpu_ptr(stats->rdma_stats, cpu); + memset(r, 0, sizeof(*r)); + } - memset(r, 0, sizeof(*r)); return 0; } @@ -25,11 +30,22 @@ int rtrs_srv_reset_rdma_stats(struct rtrs_srv_stats *stats, bool enable) ssize_t rtrs_srv_stats_rdma_to_str(struct rtrs_srv_stats *stats, char *page) { - struct rtrs_srv_stats_rdma_stats *r = &stats->rdma_stats; + int cpu; + struct rtrs_srv_stats_rdma_stats sum; + struct rtrs_srv_stats_rdma_stats *r; - return sysfs_emit(page, "%lld %lld %lld %lldn %u\n", - (s64)atomic64_read(&r->dir[READ].cnt), - (s64)atomic64_read(&r->dir[READ].size_total), - (s64)atomic64_read(&r->dir[WRITE].cnt), - (s64)atomic64_read(&r->dir[WRITE].size_total), 0); + memset(&sum, 0, sizeof(sum)); + + for_each_possible_cpu(cpu) { + r = per_cpu_ptr(stats->rdma_stats, cpu); + + sum.dir[READ].cnt += r->dir[READ].cnt; + sum.dir[READ].size_total += r->dir[READ].size_total; + sum.dir[WRITE].cnt += r->dir[WRITE].cnt; + sum.dir[WRITE].size_total += r->dir[WRITE].size_total; + } + + return sysfs_emit(page, "%llu %llu %llu %llu\n", + sum.dir[READ].cnt, sum.dir[READ].size_total, + sum.dir[WRITE].cnt, sum.dir[WRITE].size_total); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c index b94ae12c2795..2a3c9ac64a42 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv-sysfs.c @@ -220,6 +220,8 @@ static void rtrs_srv_path_stats_release(struct kobject *kobj) stats = container_of(kobj, struct rtrs_srv_stats, kobj_stats); + free_percpu(stats->rdma_stats); + kfree(stats); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.c b/drivers/infiniband/ulp/rtrs/rtrs-srv.c index 24024bce2566..34c03bde5064 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.c +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.c @@ -11,7 +11,6 @@ #define pr_fmt(fmt) KBUILD_MODNAME " L" __stringify(__LINE__) ": " fmt #include -#include #include "rtrs-srv.h" #include "rtrs-log.h" @@ -26,11 +25,7 @@ MODULE_LICENSE("GPL"); #define DEFAULT_SESS_QUEUE_DEPTH 512 #define MAX_HDR_SIZE PAGE_SIZE -/* We guarantee to serve 10 paths at least */ -#define CHUNK_POOL_SZ 10 - static struct rtrs_rdma_dev_pd dev_pd; -static mempool_t *chunk_pool; struct class *rtrs_dev_class; static struct rtrs_srv_ib_ctx ib_ctx; @@ -1358,7 +1353,7 @@ static void free_srv(struct rtrs_srv_sess *srv) WARN_ON(refcount_read(&srv->refcount)); for (i = 0; i < srv->queue_depth; i++) - mempool_free(srv->chunks[i], chunk_pool); + __free_pages(srv->chunks[i], get_order(max_chunk_size)); kfree(srv->chunks); mutex_destroy(&srv->paths_mutex); mutex_destroy(&srv->paths_ev_mutex); @@ -1411,7 +1406,8 @@ static struct rtrs_srv_sess *get_or_create_srv(struct rtrs_srv_ctx *ctx, goto err_free_srv; for (i = 0; i < srv->queue_depth; i++) { - srv->chunks[i] = mempool_alloc(chunk_pool, GFP_KERNEL); + srv->chunks[i] = alloc_pages(GFP_KERNEL, + get_order(max_chunk_size)); if (!srv->chunks[i]) goto err_free_chunks; } @@ -1424,7 +1420,7 @@ static struct rtrs_srv_sess *get_or_create_srv(struct rtrs_srv_ctx *ctx, err_free_chunks: while (i--) - mempool_free(srv->chunks[i], chunk_pool); + __free_pages(srv->chunks[i], get_order(max_chunk_size)); kfree(srv->chunks); err_free_srv: @@ -1513,6 +1509,7 @@ static void free_path(struct rtrs_srv_path *srv_path) kobject_del(&srv_path->kobj); kobject_put(&srv_path->kobj); } else { + free_percpu(srv_path->stats->rdma_stats); kfree(srv_path->stats); kfree(srv_path); } @@ -1755,13 +1752,17 @@ static struct rtrs_srv_path *__alloc_path(struct rtrs_srv_sess *srv, if (!srv_path->stats) goto err_free_sess; + srv_path->stats->rdma_stats = alloc_percpu(struct rtrs_srv_stats_rdma_stats); + if (!srv_path->stats->rdma_stats) + goto err_free_stats; + srv_path->stats->srv_path = srv_path; srv_path->dma_addr = kcalloc(srv->queue_depth, sizeof(*srv_path->dma_addr), GFP_KERNEL); if (!srv_path->dma_addr) - goto err_free_stats; + goto err_free_percpu; srv_path->s.con = kcalloc(con_num, sizeof(*srv_path->s.con), GFP_KERNEL); @@ -1813,6 +1814,8 @@ err_free_con: kfree(srv_path->s.con); err_free_dma_addr: kfree(srv_path->dma_addr); +err_free_percpu: + free_percpu(srv_path->stats->rdma_stats); err_free_stats: kfree(srv_path->stats); err_free_sess: @@ -2266,14 +2269,10 @@ static int __init rtrs_server_init(void) err); return err; } - chunk_pool = mempool_create_page_pool(sess_queue_depth * CHUNK_POOL_SZ, - get_order(max_chunk_size)); - if (!chunk_pool) - return -ENOMEM; rtrs_dev_class = class_create(THIS_MODULE, "rtrs-server"); if (IS_ERR(rtrs_dev_class)) { err = PTR_ERR(rtrs_dev_class); - goto out_chunk_pool; + goto out_err; } rtrs_wq = alloc_workqueue("rtrs_server_wq", 0, 0); if (!rtrs_wq) { @@ -2285,9 +2284,7 @@ static int __init rtrs_server_init(void) out_dev_class: class_destroy(rtrs_dev_class); -out_chunk_pool: - mempool_destroy(chunk_pool); - +out_err: return err; } @@ -2295,7 +2292,6 @@ static void __exit rtrs_server_exit(void) { destroy_workqueue(rtrs_wq); class_destroy(rtrs_dev_class); - mempool_destroy(chunk_pool); rtrs_rdma_dev_pd_deinit(&dev_pd); } diff --git a/drivers/infiniband/ulp/rtrs/rtrs-srv.h b/drivers/infiniband/ulp/rtrs/rtrs-srv.h index 6292e87f6afd..186a63c217df 100644 --- a/drivers/infiniband/ulp/rtrs/rtrs-srv.h +++ b/drivers/infiniband/ulp/rtrs/rtrs-srv.h @@ -12,6 +12,7 @@ #include #include +#include #include "rtrs-pri.h" /* @@ -29,15 +30,15 @@ enum rtrs_srv_state { */ struct rtrs_srv_stats_rdma_stats { struct { - atomic64_t cnt; - atomic64_t size_total; + u64 cnt; + u64 size_total; } dir[2]; }; struct rtrs_srv_stats { - struct kobject kobj_stats; - struct rtrs_srv_stats_rdma_stats rdma_stats; - struct rtrs_srv_path *srv_path; + struct kobject kobj_stats; + struct rtrs_srv_stats_rdma_stats __percpu *rdma_stats; + struct rtrs_srv_path *srv_path; }; struct rtrs_srv_con { @@ -130,8 +131,8 @@ void close_path(struct rtrs_srv_path *srv_path); static inline void rtrs_srv_update_rdma_stats(struct rtrs_srv_stats *s, size_t size, int d) { - atomic64_inc(&s->rdma_stats.dir[d].cnt); - atomic64_add(size, &s->rdma_stats.dir[d].size_total); + this_cpu_inc(s->rdma_stats->dir[d].cnt); + this_cpu_add(s->rdma_stats->dir[d].size_total, size); } /* functions which are implemented in rtrs-srv-stats.c */ diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.c b/drivers/infiniband/ulp/srpt/ib_srpt.c index f86ee1c4b970..21cbe30d526f 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.c +++ b/drivers/infiniband/ulp/srpt/ib_srpt.c @@ -565,12 +565,9 @@ static int srpt_refresh_port(struct srpt_port *sport) if (ret) return ret; - sport->port_guid_id.wwn.priv = sport; - srpt_format_guid(sport->port_guid_id.name, - sizeof(sport->port_guid_id.name), + srpt_format_guid(sport->guid_name, ARRAY_SIZE(sport->guid_name), &sport->gid.global.interface_id); - sport->port_gid_id.wwn.priv = sport; - snprintf(sport->port_gid_id.name, sizeof(sport->port_gid_id.name), + snprintf(sport->gid_name, ARRAY_SIZE(sport->gid_name), "0x%016llx%016llx", be64_to_cpu(sport->gid.global.subnet_prefix), be64_to_cpu(sport->gid.global.interface_id)); @@ -2221,13 +2218,13 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, ch->zw_cqe.done = srpt_zerolength_write_done; INIT_WORK(&ch->release_work, srpt_release_channel_work); ch->sport = sport; - if (ib_cm_id) { - ch->ib_cm.cm_id = ib_cm_id; - ib_cm_id->context = ch; - } else { + if (rdma_cm_id) { ch->using_rdma_cm = true; ch->rdma_cm.cm_id = rdma_cm_id; rdma_cm_id->context = ch; + } else { + ch->ib_cm.cm_id = ib_cm_id; + ib_cm_id->context = ch; } /* * ch->rq_size should be at least as large as the initiator queue @@ -2314,31 +2311,35 @@ static int srpt_cm_req_recv(struct srpt_device *const sdev, tag_num = ch->rq_size; tag_size = 1; /* ib_srpt does not use se_sess->sess_cmd_map */ - mutex_lock(&sport->port_guid_id.mutex); - list_for_each_entry(stpg, &sport->port_guid_id.tpg_list, entry) { - if (!IS_ERR_OR_NULL(ch->sess)) - break; - ch->sess = target_setup_session(&stpg->tpg, tag_num, + if (sport->guid_id) { + mutex_lock(&sport->guid_id->mutex); + list_for_each_entry(stpg, &sport->guid_id->tpg_list, entry) { + if (!IS_ERR_OR_NULL(ch->sess)) + break; + ch->sess = target_setup_session(&stpg->tpg, tag_num, tag_size, TARGET_PROT_NORMAL, ch->sess_name, ch, NULL); + } + mutex_unlock(&sport->guid_id->mutex); } - mutex_unlock(&sport->port_guid_id.mutex); - mutex_lock(&sport->port_gid_id.mutex); - list_for_each_entry(stpg, &sport->port_gid_id.tpg_list, entry) { - if (!IS_ERR_OR_NULL(ch->sess)) - break; - ch->sess = target_setup_session(&stpg->tpg, tag_num, + if (sport->gid_id) { + mutex_lock(&sport->gid_id->mutex); + list_for_each_entry(stpg, &sport->gid_id->tpg_list, entry) { + if (!IS_ERR_OR_NULL(ch->sess)) + break; + ch->sess = target_setup_session(&stpg->tpg, tag_num, tag_size, TARGET_PROT_NORMAL, i_port_id, ch, NULL); - if (!IS_ERR_OR_NULL(ch->sess)) - break; - /* Retry without leading "0x" */ - ch->sess = target_setup_session(&stpg->tpg, tag_num, + if (!IS_ERR_OR_NULL(ch->sess)) + break; + /* Retry without leading "0x" */ + ch->sess = target_setup_session(&stpg->tpg, tag_num, tag_size, TARGET_PROT_NORMAL, i_port_id + 2, ch, NULL); + } + mutex_unlock(&sport->gid_id->mutex); } - mutex_unlock(&sport->port_gid_id.mutex); if (IS_ERR_OR_NULL(ch->sess)) { WARN_ON_ONCE(ch->sess == NULL); @@ -2983,7 +2984,12 @@ static int srpt_release_sport(struct srpt_port *sport) return 0; } -static struct se_wwn *__srpt_lookup_wwn(const char *name) +struct port_and_port_id { + struct srpt_port *sport; + struct srpt_port_id **port_id; +}; + +static struct port_and_port_id __srpt_lookup_port(const char *name) { struct ib_device *dev; struct srpt_device *sdev; @@ -2998,25 +3004,38 @@ static struct se_wwn *__srpt_lookup_wwn(const char *name) for (i = 0; i < dev->phys_port_cnt; i++) { sport = &sdev->port[i]; - if (strcmp(sport->port_guid_id.name, name) == 0) - return &sport->port_guid_id.wwn; - if (strcmp(sport->port_gid_id.name, name) == 0) - return &sport->port_gid_id.wwn; + if (strcmp(sport->guid_name, name) == 0) { + kref_get(&sdev->refcnt); + return (struct port_and_port_id){ + sport, &sport->guid_id}; + } + if (strcmp(sport->gid_name, name) == 0) { + kref_get(&sdev->refcnt); + return (struct port_and_port_id){ + sport, &sport->gid_id}; + } } } - return NULL; + return (struct port_and_port_id){}; } -static struct se_wwn *srpt_lookup_wwn(const char *name) +/** + * srpt_lookup_port() - Look up an RDMA port by name + * @name: ASCII port name + * + * Increments the RDMA port reference count if an RDMA port pointer is returned. + * The caller must drop that reference count by calling srpt_port_put_ref(). + */ +static struct port_and_port_id srpt_lookup_port(const char *name) { - struct se_wwn *wwn; + struct port_and_port_id papi; spin_lock(&srpt_dev_lock); - wwn = __srpt_lookup_wwn(name); + papi = __srpt_lookup_port(name); spin_unlock(&srpt_dev_lock); - return wwn; + return papi; } static void srpt_free_srq(struct srpt_device *sdev) @@ -3101,6 +3120,18 @@ static int srpt_use_srq(struct srpt_device *sdev, bool use_srq) return ret; } +static void srpt_free_sdev(struct kref *refcnt) +{ + struct srpt_device *sdev = container_of(refcnt, typeof(*sdev), refcnt); + + kfree(sdev); +} + +static void srpt_sdev_put(struct srpt_device *sdev) +{ + kref_put(&sdev->refcnt, srpt_free_sdev); +} + /** * srpt_add_one - InfiniBand device addition callback function * @device: Describes a HCA. @@ -3119,6 +3150,7 @@ static int srpt_add_one(struct ib_device *device) if (!sdev) return -ENOMEM; + kref_init(&sdev->refcnt); sdev->device = device; mutex_init(&sdev->sdev_mutex); @@ -3182,10 +3214,6 @@ static int srpt_add_one(struct ib_device *device) sport->port_attrib.srp_sq_size = DEF_SRPT_SQ_SIZE; sport->port_attrib.use_srq = false; INIT_WORK(&sport->work, srpt_refresh_port_work); - mutex_init(&sport->port_guid_id.mutex); - INIT_LIST_HEAD(&sport->port_guid_id.tpg_list); - mutex_init(&sport->port_gid_id.mutex); - INIT_LIST_HEAD(&sport->port_gid_id.tpg_list); ret = srpt_refresh_port(sport); if (ret) { @@ -3214,7 +3242,7 @@ err_ring: srpt_free_srq(sdev); ib_dealloc_pd(sdev->pd); free_dev: - kfree(sdev); + srpt_sdev_put(sdev); pr_info("%s(%s) failed.\n", __func__, dev_name(&device->dev)); return ret; } @@ -3258,7 +3286,7 @@ static void srpt_remove_one(struct ib_device *device, void *client_data) ib_dealloc_pd(sdev->pd); - kfree(sdev); + srpt_sdev_put(sdev); } static struct ib_client srpt_client = { @@ -3286,10 +3314,10 @@ static struct srpt_port_id *srpt_wwn_to_sport_id(struct se_wwn *wwn) { struct srpt_port *sport = wwn->priv; - if (wwn == &sport->port_guid_id.wwn) - return &sport->port_guid_id; - if (wwn == &sport->port_gid_id.wwn) - return &sport->port_gid_id; + if (sport->guid_id && &sport->guid_id->wwn == wwn) + return sport->guid_id; + if (sport->gid_id && &sport->gid_id->wwn == wwn) + return sport->gid_id; WARN_ON_ONCE(true); return NULL; } @@ -3774,7 +3802,31 @@ static struct se_wwn *srpt_make_tport(struct target_fabric_configfs *tf, struct config_group *group, const char *name) { - return srpt_lookup_wwn(name) ? : ERR_PTR(-EINVAL); + struct port_and_port_id papi = srpt_lookup_port(name); + struct srpt_port *sport = papi.sport; + struct srpt_port_id *port_id; + + if (!papi.port_id) + return ERR_PTR(-EINVAL); + if (*papi.port_id) { + /* Attempt to create a directory that already exists. */ + WARN_ON_ONCE(true); + return &(*papi.port_id)->wwn; + } + port_id = kzalloc(sizeof(*port_id), GFP_KERNEL); + if (!port_id) { + srpt_sdev_put(sport->sdev); + return ERR_PTR(-ENOMEM); + } + mutex_init(&port_id->mutex); + INIT_LIST_HEAD(&port_id->tpg_list); + port_id->wwn.priv = sport; + memcpy(port_id->name, port_id == sport->guid_id ? sport->guid_name : + sport->gid_name, ARRAY_SIZE(port_id->name)); + + *papi.port_id = port_id; + + return &port_id->wwn; } /** @@ -3783,6 +3835,18 @@ static struct se_wwn *srpt_make_tport(struct target_fabric_configfs *tf, */ static void srpt_drop_tport(struct se_wwn *wwn) { + struct srpt_port_id *port_id = container_of(wwn, typeof(*port_id), wwn); + struct srpt_port *sport = wwn->priv; + + if (sport->guid_id == port_id) + sport->guid_id = NULL; + else if (sport->gid_id == port_id) + sport->gid_id = NULL; + else + WARN_ON_ONCE(true); + + srpt_sdev_put(sport->sdev); + kfree(port_id); } static ssize_t srpt_wwn_version_show(struct config_item *item, char *buf) diff --git a/drivers/infiniband/ulp/srpt/ib_srpt.h b/drivers/infiniband/ulp/srpt/ib_srpt.h index 76e66f630c17..4c46b301eea1 100644 --- a/drivers/infiniband/ulp/srpt/ib_srpt.h +++ b/drivers/infiniband/ulp/srpt/ib_srpt.h @@ -376,7 +376,7 @@ struct srpt_tpg { }; /** - * struct srpt_port_id - information about an RDMA port name + * struct srpt_port_id - LIO RDMA port information * @mutex: Protects @tpg_list changes. * @tpg_list: TPGs associated with the RDMA port name. * @wwn: WWN associated with the RDMA port name. @@ -393,7 +393,7 @@ struct srpt_port_id { }; /** - * struct srpt_port - information associated by SRPT with a single IB port + * struct srpt_port - SRPT RDMA port information * @sdev: backpointer to the HCA information. * @mad_agent: per-port management datagram processing information. * @enabled: Whether or not this target port is enabled. @@ -402,8 +402,10 @@ struct srpt_port_id { * @lid: cached value of the port's lid. * @gid: cached value of the port's gid. * @work: work structure for refreshing the aforementioned cached values. - * @port_guid_id: target port GUID - * @port_gid_id: target port GID + * @guid_name: port name in GUID format. + * @guid_id: LIO target port information for the port name in GUID format. + * @gid_name: port name in GID format. + * @gid_id: LIO target port information for the port name in GID format. * @port_attrib: Port attributes that can be accessed through configfs. * @refcount: Number of objects associated with this port. * @freed_channels: Completion that will be signaled once @refcount becomes 0. @@ -419,8 +421,10 @@ struct srpt_port { u32 lid; union ib_gid gid; struct work_struct work; - struct srpt_port_id port_guid_id; - struct srpt_port_id port_gid_id; + char guid_name[64]; + struct srpt_port_id *guid_id; + char gid_name[64]; + struct srpt_port_id *gid_id; struct srpt_port_attrib port_attrib; atomic_t refcount; struct completion *freed_channels; @@ -430,6 +434,7 @@ struct srpt_port { /** * struct srpt_device - information associated by SRPT with a single HCA + * @refcnt: Reference count for this device. * @device: Backpointer to the struct ib_device managed by the IB core. * @pd: IB protection domain. * @lkey: L_Key (local key) with write access to all local memory. @@ -445,6 +450,7 @@ struct srpt_port { * @port: Information about the ports owned by this HCA. */ struct srpt_device { + struct kref refcnt; struct ib_device *device; struct ib_pd *pd; u32 lkey; diff --git a/drivers/input/keyboard/bcm-keypad.c b/drivers/input/keyboard/bcm-keypad.c index 166d6023a538..56a919ec23b5 100644 --- a/drivers/input/keyboard/bcm-keypad.c +++ b/drivers/input/keyboard/bcm-keypad.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/input/misc/gpio_decoder.c b/drivers/input/misc/gpio_decoder.c index 145826a1a9a1..ee668eba302f 100644 --- a/drivers/input/misc/gpio_decoder.c +++ b/drivers/input/misc/gpio_decoder.c @@ -1,15 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * * A generic driver to read multiple gpio lines and translate the * encoded numeric value into an input event. */ diff --git a/drivers/input/misc/palmas-pwrbutton.c b/drivers/input/misc/palmas-pwrbutton.c index 2213e06b611d..465e6693077a 100644 --- a/drivers/input/misc/palmas-pwrbutton.c +++ b/drivers/input/misc/palmas-pwrbutton.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments' Palmas Power Button Input Driver * * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/ * Girish S Ghongdemath * Nishanth Menon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/input/misc/tps65218-pwrbutton.c b/drivers/input/misc/tps65218-pwrbutton.c index f011447c44fb..fc450fce0932 100644 --- a/drivers/input/misc/tps65218-pwrbutton.c +++ b/drivers/input/misc/tps65218-pwrbutton.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments' TPS65217 and TPS65218 Power Button Input Driver * * Copyright (C) 2014 Texas Instruments Incorporated - http://www.ti.com/ * Author: Felipe Balbi * Author: Marcin Niestroj - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/input/touchscreen/bcm_iproc_tsc.c b/drivers/input/touchscreen/bcm_iproc_tsc.c index 7de1fd24ce36..35e2fe9911a4 100644 --- a/drivers/input/touchscreen/bcm_iproc_tsc.c +++ b/drivers/input/touchscreen/bcm_iproc_tsc.c @@ -1,14 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Broadcom Corporation * -* This program is free software; you can redistribute it and/or -* modify it under the terms of the GNU General Public License as -* published by the Free Software Foundation version 2. -* -* This program is distributed "as is" WITHOUT ANY WARRANTY of any -* kind, whether express or implied; without even the implied warranty -* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. */ #include #include diff --git a/drivers/iommu/of_iommu.c b/drivers/iommu/of_iommu.c index 5696314ae69e..41f4eb005219 100644 --- a/drivers/iommu/of_iommu.c +++ b/drivers/iommu/of_iommu.c @@ -40,7 +40,7 @@ static int of_iommu_xlate(struct device *dev, * a proper probe-ordering dependency mechanism in future. */ if (!ops) - return driver_deferred_probe_check_state(dev); + return -ENODEV; if (!try_module_get(ops->owner)) return -ENODEV; diff --git a/drivers/irqchip/irq-keystone.c b/drivers/irqchip/irq-keystone.c index d47c8041e5bc..ba9792e60329 100644 --- a/drivers/irqchip/irq-keystone.c +++ b/drivers/irqchip/irq-keystone.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments Keystone IRQ controller IP driver * * Copyright (C) 2014 Texas Instruments, Inc. * Author: Sajesh Kumar Saran * Grygorii Strashko - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/leds/simple/Kconfig b/drivers/leds/simple/Kconfig index 9f6a68336659..fd2b8225d926 100644 --- a/drivers/leds/simple/Kconfig +++ b/drivers/leds/simple/Kconfig @@ -1,11 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only config LEDS_SIEMENS_SIMATIC_IPC tristate "LED driver for Siemens Simatic IPCs" - depends on LEDS_CLASS + depends on LEDS_GPIO depends on SIEMENS_SIMATIC_IPC help This option enables support for the LEDs of several Industrial PCs from Siemens. - To compile this driver as a module, choose M here: the module - will be called simatic-ipc-leds. + To compile this driver as a module, choose M here: the modules + will be called simatic-ipc-leds and simatic-ipc-leds-gpio. diff --git a/drivers/leds/simple/Makefile b/drivers/leds/simple/Makefile index 8481f1e9e360..1c7ef5e1324b 100644 --- a/drivers/leds/simple/Makefile +++ b/drivers/leds/simple/Makefile @@ -1,2 +1,3 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds.o +obj-$(CONFIG_LEDS_SIEMENS_SIMATIC_IPC) += simatic-ipc-leds-gpio.o diff --git a/drivers/leds/simple/simatic-ipc-leds-gpio.c b/drivers/leds/simple/simatic-ipc-leds-gpio.c new file mode 100644 index 000000000000..4c9e663a90ba --- /dev/null +++ b/drivers/leds/simple/simatic-ipc-leds-gpio.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Siemens SIMATIC IPC driver for GPIO based LEDs + * + * Copyright (c) Siemens AG, 2022 + * + * Authors: + * Henning Schild + */ + +#include +#include +#include +#include +#include + +static struct gpiod_lookup_table simatic_ipc_led_gpio_table = { + .dev_id = "leds-gpio", + .table = { + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 51, NULL, 0, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 52, NULL, 1, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 53, NULL, 2, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 57, NULL, 3, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 58, NULL, 4, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 60, NULL, 5, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 56, NULL, 6, GPIO_ACTIVE_LOW), + GPIO_LOOKUP_IDX("apollolake-pinctrl.0", 59, NULL, 7, GPIO_ACTIVE_HIGH), + }, +}; + +static const struct gpio_led simatic_ipc_gpio_leds[] = { + { .name = "green:" LED_FUNCTION_STATUS "-3" }, + { .name = "red:" LED_FUNCTION_STATUS "-1" }, + { .name = "green:" LED_FUNCTION_STATUS "-1" }, + { .name = "red:" LED_FUNCTION_STATUS "-2" }, + { .name = "green:" LED_FUNCTION_STATUS "-2" }, + { .name = "red:" LED_FUNCTION_STATUS "-3" }, +}; + +static const struct gpio_led_platform_data simatic_ipc_gpio_leds_pdata = { + .num_leds = ARRAY_SIZE(simatic_ipc_gpio_leds), + .leds = simatic_ipc_gpio_leds, +}; + +static struct platform_device *simatic_leds_pdev; + +static int simatic_ipc_leds_gpio_remove(struct platform_device *pdev) +{ + gpiod_remove_lookup_table(&simatic_ipc_led_gpio_table); + platform_device_unregister(simatic_leds_pdev); + + return 0; +} + +static int simatic_ipc_leds_gpio_probe(struct platform_device *pdev) +{ + struct gpio_desc *gpiod; + int err; + + gpiod_add_lookup_table(&simatic_ipc_led_gpio_table); + simatic_leds_pdev = platform_device_register_resndata(NULL, + "leds-gpio", PLATFORM_DEVID_NONE, NULL, 0, + &simatic_ipc_gpio_leds_pdata, + sizeof(simatic_ipc_gpio_leds_pdata)); + if (IS_ERR(simatic_leds_pdev)) { + err = PTR_ERR(simatic_leds_pdev); + goto out; + } + + /* PM_BIOS_BOOT_N */ + gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 6, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + goto out; + } + gpiod_put(gpiod); + + /* PM_WDT_OUT */ + gpiod = gpiod_get_index(&simatic_leds_pdev->dev, NULL, 7, GPIOD_OUT_LOW); + if (IS_ERR(gpiod)) { + err = PTR_ERR(gpiod); + goto out; + } + gpiod_put(gpiod); + + return 0; +out: + simatic_ipc_leds_gpio_remove(pdev); + + return err; +} + +static struct platform_driver simatic_ipc_led_gpio_driver = { + .probe = simatic_ipc_leds_gpio_probe, + .remove = simatic_ipc_leds_gpio_remove, + .driver = { + .name = KBUILD_MODNAME, + } +}; +module_platform_driver(simatic_ipc_led_gpio_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:" KBUILD_MODNAME); +MODULE_SOFTDEP("pre: platform:leds-gpio"); +MODULE_AUTHOR("Henning Schild "); diff --git a/drivers/leds/simple/simatic-ipc-leds.c b/drivers/leds/simple/simatic-ipc-leds.c index 078d43f5ba38..4894c228c165 100644 --- a/drivers/leds/simple/simatic-ipc-leds.c +++ b/drivers/leds/simple/simatic-ipc-leds.c @@ -23,7 +23,7 @@ #define SIMATIC_IPC_LED_PORT_BASE 0x404E struct simatic_ipc_led { - unsigned int value; /* mask for io and offset for mem */ + unsigned int value; /* mask for io */ char *name; struct led_classdev cdev; }; @@ -38,21 +38,6 @@ static struct simatic_ipc_led simatic_ipc_leds_io[] = { { } }; -/* the actual start will be discovered with PCI, 0 is a placeholder */ -static struct resource simatic_ipc_led_mem_res = DEFINE_RES_MEM_NAMED(0, SZ_4K, KBUILD_MODNAME); - -static void __iomem *simatic_ipc_led_memory; - -static struct simatic_ipc_led simatic_ipc_leds_mem[] = { - {0x500 + 0x1A0, "red:" LED_FUNCTION_STATUS "-1"}, - {0x500 + 0x1A8, "green:" LED_FUNCTION_STATUS "-1"}, - {0x500 + 0x1C8, "red:" LED_FUNCTION_STATUS "-2"}, - {0x500 + 0x1D0, "green:" LED_FUNCTION_STATUS "-2"}, - {0x500 + 0x1E0, "red:" LED_FUNCTION_STATUS "-3"}, - {0x500 + 0x198, "green:" LED_FUNCTION_STATUS "-3"}, - { } -}; - static struct resource simatic_ipc_led_io_res = DEFINE_RES_IO_NAMED(SIMATIC_IPC_LED_PORT_BASE, SZ_2, KBUILD_MODNAME); @@ -88,28 +73,6 @@ static enum led_brightness simatic_ipc_led_get_io(struct led_classdev *led_cd) return inw(SIMATIC_IPC_LED_PORT_BASE) & led->value ? LED_OFF : led_cd->max_brightness; } -static void simatic_ipc_led_set_mem(struct led_classdev *led_cd, - enum led_brightness brightness) -{ - struct simatic_ipc_led *led = cdev_to_led(led_cd); - void __iomem *reg = simatic_ipc_led_memory + led->value; - u32 val; - - val = readl(reg); - val = (val & ~1) | (brightness == LED_OFF); - writel(val, reg); -} - -static enum led_brightness simatic_ipc_led_get_mem(struct led_classdev *led_cd) -{ - struct simatic_ipc_led *led = cdev_to_led(led_cd); - void __iomem *reg = simatic_ipc_led_memory + led->value; - u32 val; - - val = readl(reg); - return (val & 1) ? LED_OFF : led_cd->max_brightness; -} - static int simatic_ipc_leds_probe(struct platform_device *pdev) { const struct simatic_ipc_platform *plat = pdev->dev.platform_data; @@ -117,9 +80,7 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev) struct simatic_ipc_led *ipcled; struct led_classdev *cdev; struct resource *res; - void __iomem *reg; - int err, type; - u32 val; + int err; switch (plat->devmode) { case SIMATIC_IPC_DEVICE_227D: @@ -134,52 +95,19 @@ static int simatic_ipc_leds_probe(struct platform_device *pdev) } ipcled = simatic_ipc_leds_io; } - type = IORESOURCE_IO; if (!devm_request_region(dev, res->start, resource_size(res), KBUILD_MODNAME)) { dev_err(dev, "Unable to register IO resource at %pR\n", res); return -EBUSY; } break; - case SIMATIC_IPC_DEVICE_127E: - res = &simatic_ipc_led_mem_res; - ipcled = simatic_ipc_leds_mem; - type = IORESOURCE_MEM; - - /* get GPIO base from PCI */ - res->start = simatic_ipc_get_membase0(PCI_DEVFN(13, 0)); - if (res->start == 0) - return -ENODEV; - - /* do the final address calculation */ - res->start = res->start + (0xC5 << 16); - res->end += res->start; - - simatic_ipc_led_memory = devm_ioremap_resource(dev, res); - if (IS_ERR(simatic_ipc_led_memory)) - return PTR_ERR(simatic_ipc_led_memory); - - /* initialize power/watchdog LED */ - reg = simatic_ipc_led_memory + 0x500 + 0x1D8; /* PM_WDT_OUT */ - val = readl(reg); - writel(val & ~1, reg); - - reg = simatic_ipc_led_memory + 0x500 + 0x1C0; /* PM_BIOS_BOOT_N */ - val = readl(reg); - writel(val | 1, reg); - break; default: return -ENODEV; } while (ipcled->value) { cdev = &ipcled->cdev; - if (type == IORESOURCE_MEM) { - cdev->brightness_set = simatic_ipc_led_set_mem; - cdev->brightness_get = simatic_ipc_led_get_mem; - } else { - cdev->brightness_set = simatic_ipc_led_set_io; - cdev->brightness_get = simatic_ipc_led_get_io; - } + cdev->brightness_set = simatic_ipc_led_set_io; + cdev->brightness_get = simatic_ipc_led_get_io; cdev->max_brightness = LED_ON; cdev->name = ipcled->name; diff --git a/drivers/mailbox/bcm-flexrm-mailbox.c b/drivers/mailbox/bcm-flexrm-mailbox.c index 22acb51531cb..fda16f76401e 100644 --- a/drivers/mailbox/bcm-flexrm-mailbox.c +++ b/drivers/mailbox/bcm-flexrm-mailbox.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2017 Broadcom /* * Broadcom FlexRM Mailbox Driver diff --git a/drivers/md/bcache/Kconfig b/drivers/md/bcache/Kconfig index cf3e8096942a..529c9d04e9a4 100644 --- a/drivers/md/bcache/Kconfig +++ b/drivers/md/bcache/Kconfig @@ -29,7 +29,7 @@ config BCACHE_CLOSURES_DEBUG operations that get stuck. config BCACHE_ASYNC_REGISTRATION - bool "Asynchronous device registration (EXPERIMENTAL)" + bool "Asynchronous device registration" depends on BCACHE help Add a sysfs file /sys/fs/bcache/register_async. Writing registering diff --git a/drivers/md/dm-raid.c b/drivers/md/dm-raid.c index 1ec17c32867f..c640be453313 100644 --- a/drivers/md/dm-raid.c +++ b/drivers/md/dm-raid.c @@ -3728,6 +3728,7 @@ static int raid_message(struct dm_target *ti, unsigned int argc, char **argv, if (!strcasecmp(argv[0], "idle") || !strcasecmp(argv[0], "frozen")) { if (mddev->sync_thread) { set_bit(MD_RECOVERY_INTR, &mddev->recovery); + md_unregister_thread(&mddev->sync_thread); md_reap_sync_thread(mddev); } } else if (decipher_sync_action(mddev, mddev->recovery) != st_idle) diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 99642f69bfa7..28bd4a35b86b 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -1016,7 +1016,7 @@ static void dm_wq_requeue_work(struct work_struct *work) while (io) { struct dm_io *next = io->next; - dm_io_rewind(io, &md->queue->bio_split); + dm_io_rewind(io, &md->disk->bio_split); io->next = NULL; __dm_io_complete(io, false); @@ -1181,7 +1181,7 @@ static sector_t max_io_len(struct dm_target *ti, sector_t sector) * Does the target need to split IO even further? * - varied (per target) IO splitting is a tenet of DM; this * explains why stacked chunk_sectors based splitting via - * blk_queue_split() isn't possible here. + * bio_split_to_limits() isn't possible here. */ if (!ti->max_io_len) return len; @@ -1751,10 +1751,10 @@ static void dm_split_and_process_bio(struct mapped_device *md, is_abnormal = is_abnormal_io(bio); if (unlikely(is_abnormal)) { /* - * Use blk_queue_split() for abnormal IO (e.g. discard, etc) + * Use bio_split_to_limits() for abnormal IO (e.g. discard, etc) * otherwise associated queue_limits won't be imposed. */ - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); } init_clone_info(&ci, md, map, bio, is_abnormal); diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index 2cf973722f59..91836e6de326 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -125,7 +125,6 @@ static void __init md_setup_drive(struct md_setup_args *args) char *devname = args->device_names; dev_t devices[MD_SB_DISKS + 1], mdev; struct mdu_array_info_s ainfo = { }; - struct block_device *bdev; struct mddev *mddev; int err = 0, i; char name[16]; @@ -169,24 +168,16 @@ static void __init md_setup_drive(struct md_setup_args *args) pr_info("md: Loading %s: %s\n", name, args->device_names); - bdev = blkdev_get_by_dev(mdev, FMODE_READ, NULL); - if (IS_ERR(bdev)) { - pr_err("md: open failed - cannot start array %s\n", name); + mddev = md_alloc(mdev, name); + if (IS_ERR(mddev)) { + pr_err("md: md_alloc failed - cannot start array %s\n", name); return; } - err = -EIO; - if (WARN(bdev->bd_disk->fops != &md_fops, - "Opening block device %x resulted in non-md device\n", - mdev)) - goto out_blkdev_put; - - mddev = bdev->bd_disk->private_data; - err = mddev_lock(mddev); if (err) { pr_err("md: failed to lock array %s\n", name); - goto out_blkdev_put; + goto out_mddev_put; } if (!list_empty(&mddev->disks) || mddev->raid_disks) { @@ -230,8 +221,8 @@ static void __init md_setup_drive(struct md_setup_args *args) pr_warn("md: starting %s failed\n", name); out_unlock: mddev_unlock(mddev); -out_blkdev_put: - blkdev_put(bdev, FMODE_READ); +out_mddev_put: + mddev_put(mddev); } static int __init raid_setup(char *str) diff --git a/drivers/md/md-cluster.c b/drivers/md/md-cluster.c index 37cbcce3cc66..742b2349fea3 100644 --- a/drivers/md/md-cluster.c +++ b/drivers/md/md-cluster.c @@ -40,7 +40,7 @@ struct resync_info { /* Lock the send communication. This is done through * bit manipulation as opposed to a mutex in order to - * accomodate lock and hold. See next comment. + * accommodate lock and hold. See next comment. */ #define MD_CLUSTER_SEND_LOCK 4 /* If cluster operations (such as adding a disk) must lock the @@ -689,7 +689,7 @@ static int lock_comm(struct md_cluster_info *cinfo, bool mddev_locked) /* * If resync thread run after raid1d thread, then process_metadata_update * could not continue if raid1d held reconfig_mutex (and raid1d is blocked - * since another node already got EX on Token and waitting the EX of Ack), + * since another node already got EX on Token and waiting the EX of Ack), * so let resync wake up thread in case flag is set. */ if (mddev_locked && !test_bit(MD_CLUSTER_HOLDING_MUTEX_FOR_RECVD, diff --git a/drivers/md/md.c b/drivers/md/md.c index 4df78e30b76a..afaf36b2f6ab 100644 --- a/drivers/md/md.c +++ b/drivers/md/md.c @@ -368,28 +368,6 @@ EXPORT_SYMBOL_GPL(md_new_event); static LIST_HEAD(all_mddevs); static DEFINE_SPINLOCK(all_mddevs_lock); -/* - * iterates through all used mddevs in the system. - * We take care to grab the all_mddevs_lock whenever navigating - * the list, and to always hold a refcount when unlocked. - * Any code which breaks out of this loop while own - * a reference to the current mddev and must mddev_put it. - */ -#define for_each_mddev(_mddev,_tmp) \ - \ - for (({ spin_lock(&all_mddevs_lock); \ - _tmp = all_mddevs.next; \ - _mddev = NULL;}); \ - ({ if (_tmp != &all_mddevs) \ - mddev_get(list_entry(_tmp, struct mddev, all_mddevs));\ - spin_unlock(&all_mddevs_lock); \ - if (_mddev) mddev_put(_mddev); \ - _mddev = list_entry(_tmp, struct mddev, all_mddevs); \ - _tmp != &all_mddevs;}); \ - ({ spin_lock(&all_mddevs_lock); \ - _tmp = _tmp->next;}) \ - ) - /* Rather than calling directly into the personality make_request function, * IO requests come here first so that we can check if the device is * being suspended pending a reconfiguration. @@ -464,7 +442,7 @@ static void md_submit_bio(struct bio *bio) return; } - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); if (mddev->ro == 1 && unlikely(rw == WRITE)) { if (bio_sectors(bio) != 0) @@ -647,13 +625,17 @@ EXPORT_SYMBOL(md_flush_request); static inline struct mddev *mddev_get(struct mddev *mddev) { + lockdep_assert_held(&all_mddevs_lock); + + if (test_bit(MD_DELETED, &mddev->flags)) + return NULL; atomic_inc(&mddev->active); return mddev; } static void mddev_delayed_delete(struct work_struct *ws); -static void mddev_put(struct mddev *mddev) +void mddev_put(struct mddev *mddev) { if (!atomic_dec_and_lock(&mddev->active, &all_mddevs_lock)) return; @@ -661,7 +643,7 @@ static void mddev_put(struct mddev *mddev) mddev->ctime == 0 && !mddev->hold_active) { /* Array is not configured at all, and not held active, * so destroy it */ - list_del_init(&mddev->all_mddevs); + set_bit(MD_DELETED, &mddev->flags); /* * Call queue_work inside the spinlock so that @@ -678,7 +660,6 @@ static void md_safemode_timeout(struct timer_list *t); void mddev_init(struct mddev *mddev) { - kobject_init(&mddev->kobj, &md_ktype); mutex_init(&mddev->open_mutex); mutex_init(&mddev->reconfig_mutex); mutex_init(&mddev->bitmap_info.mutex); @@ -733,22 +714,6 @@ static dev_t mddev_alloc_unit(void) return dev; } -static struct mddev *mddev_find(dev_t unit) -{ - struct mddev *mddev; - - if (MAJOR(unit) != MD_MAJOR) - unit &= ~((1 << MdpMinorShift) - 1); - - spin_lock(&all_mddevs_lock); - mddev = mddev_find_locked(unit); - if (mddev) - mddev_get(mddev); - spin_unlock(&all_mddevs_lock); - - return mddev; -} - static struct mddev *mddev_alloc(dev_t unit) { struct mddev *new; @@ -791,6 +756,15 @@ out_free_new: return ERR_PTR(error); } +static void mddev_free(struct mddev *mddev) +{ + spin_lock(&all_mddevs_lock); + list_del(&mddev->all_mddevs); + spin_unlock(&all_mddevs_lock); + + kfree(mddev); +} + static const struct attribute_group md_redundancy_group; void mddev_unlock(struct mddev *mddev) @@ -3335,14 +3309,35 @@ rdev_size_show(struct md_rdev *rdev, char *page) return sprintf(page, "%llu\n", (unsigned long long)rdev->sectors / 2); } -static int overlaps(sector_t s1, sector_t l1, sector_t s2, sector_t l2) +static int md_rdevs_overlap(struct md_rdev *a, struct md_rdev *b) { /* check if two start/length pairs overlap */ - if (s1+l1 <= s2) - return 0; - if (s2+l2 <= s1) - return 0; - return 1; + if (a->data_offset + a->sectors <= b->data_offset) + return false; + if (b->data_offset + b->sectors <= a->data_offset) + return false; + return true; +} + +static bool md_rdev_overlaps(struct md_rdev *rdev) +{ + struct mddev *mddev; + struct md_rdev *rdev2; + + spin_lock(&all_mddevs_lock); + list_for_each_entry(mddev, &all_mddevs, all_mddevs) { + if (test_bit(MD_DELETED, &mddev->flags)) + continue; + rdev_for_each(rdev2, mddev) { + if (rdev != rdev2 && rdev->bdev == rdev2->bdev && + md_rdevs_overlap(rdev, rdev2)) { + spin_unlock(&all_mddevs_lock); + return true; + } + } + } + spin_unlock(&all_mddevs_lock); + return false; } static int strict_blocks_to_sectors(const char *buf, sector_t *sectors) @@ -3394,46 +3389,21 @@ rdev_size_store(struct md_rdev *rdev, const char *buf, size_t len) return -EINVAL; /* component must fit device */ rdev->sectors = sectors; - if (sectors > oldsectors && my_mddev->external) { - /* Need to check that all other rdevs with the same - * ->bdev do not overlap. 'rcu' is sufficient to walk - * the rdev lists safely. - * This check does not provide a hard guarantee, it - * just helps avoid dangerous mistakes. + + /* + * Check that all other rdevs with the same bdev do not overlap. This + * check does not provide a hard guarantee, it just helps avoid + * dangerous mistakes. + */ + if (sectors > oldsectors && my_mddev->external && + md_rdev_overlaps(rdev)) { + /* + * Someone else could have slipped in a size change here, but + * doing so is just silly. We put oldsectors back because we + * know it is safe, and trust userspace not to race with itself. */ - struct mddev *mddev; - int overlap = 0; - struct list_head *tmp; - - rcu_read_lock(); - for_each_mddev(mddev, tmp) { - struct md_rdev *rdev2; - - rdev_for_each(rdev2, mddev) - if (rdev->bdev == rdev2->bdev && - rdev != rdev2 && - overlaps(rdev->data_offset, rdev->sectors, - rdev2->data_offset, - rdev2->sectors)) { - overlap = 1; - break; - } - if (overlap) { - mddev_put(mddev); - break; - } - } - rcu_read_unlock(); - if (overlap) { - /* Someone else could have slipped in a size - * change here, but doing so is just silly. - * We put oldsectors back because we *know* it is - * safe, and trust userspace not to race with - * itself - */ - rdev->sectors = oldsectors; - return -EBUSY; - } + rdev->sectors = oldsectors; + return -EBUSY; } return len; } @@ -4830,6 +4800,19 @@ action_store(struct mddev *mddev, const char *page, size_t len) if (work_pending(&mddev->del_work)) flush_workqueue(md_misc_wq); if (mddev->sync_thread) { + sector_t save_rp = mddev->reshape_position; + + mddev_unlock(mddev); + set_bit(MD_RECOVERY_INTR, &mddev->recovery); + md_unregister_thread(&mddev->sync_thread); + mddev_lock_nointr(mddev); + /* + * set RECOVERY_INTR again and restore reshape + * position in case others changed them after + * got lock, eg, reshape_position_store and + * md_check_recovery. + */ + mddev->reshape_position = save_rp; set_bit(MD_RECOVERY_INTR, &mddev->recovery); md_reap_sync_thread(mddev); } @@ -5001,7 +4984,7 @@ static ssize_t sync_speed_show(struct mddev *mddev, char *page) { unsigned long resync, dt, db; - if (mddev->curr_resync == 0) + if (mddev->curr_resync == MD_RESYNC_NONE) return sprintf(page, "none\n"); resync = mddev->curr_mark_cnt - atomic_read(&mddev->recovery_active); dt = (jiffies - mddev->resync_mark) / HZ; @@ -5020,8 +5003,8 @@ sync_completed_show(struct mddev *mddev, char *page) if (!test_bit(MD_RECOVERY_RUNNING, &mddev->recovery)) return sprintf(page, "none\n"); - if (mddev->curr_resync == 1 || - mddev->curr_resync == 2) + if (mddev->curr_resync == MD_RESYNC_YIELDED || + mddev->curr_resync == MD_RESYNC_DELAYED) return sprintf(page, "delayed\n"); if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery) || @@ -5532,11 +5515,10 @@ md_attr_show(struct kobject *kobj, struct attribute *attr, char *page) if (!entry->show) return -EIO; spin_lock(&all_mddevs_lock); - if (list_empty(&mddev->all_mddevs)) { + if (!mddev_get(mddev)) { spin_unlock(&all_mddevs_lock); return -EBUSY; } - mddev_get(mddev); spin_unlock(&all_mddevs_lock); rv = entry->show(mddev, page); @@ -5557,18 +5539,17 @@ md_attr_store(struct kobject *kobj, struct attribute *attr, if (!capable(CAP_SYS_ADMIN)) return -EACCES; spin_lock(&all_mddevs_lock); - if (list_empty(&mddev->all_mddevs)) { + if (!mddev_get(mddev)) { spin_unlock(&all_mddevs_lock); return -EBUSY; } - mddev_get(mddev); spin_unlock(&all_mddevs_lock); rv = entry->store(mddev, page, length); mddev_put(mddev); return rv; } -static void md_free(struct kobject *ko) +static void md_kobj_release(struct kobject *ko) { struct mddev *mddev = container_of(ko, struct mddev, kobj); @@ -5577,15 +5558,8 @@ static void md_free(struct kobject *ko) if (mddev->sysfs_level) sysfs_put(mddev->sysfs_level); - if (mddev->gendisk) { - del_gendisk(mddev->gendisk); - put_disk(mddev->gendisk); - } - percpu_ref_exit(&mddev->writes_pending); - - bioset_exit(&mddev->bio_set); - bioset_exit(&mddev->sync_set); - kfree(mddev); + del_gendisk(mddev->gendisk); + put_disk(mddev->gendisk); } static const struct sysfs_ops md_sysfs_ops = { @@ -5593,7 +5567,7 @@ static const struct sysfs_ops md_sysfs_ops = { .store = md_attr_store, }; static struct kobj_type md_ktype = { - .release = md_free, + .release = md_kobj_release, .sysfs_ops = &md_sysfs_ops, .default_groups = md_attr_groups, }; @@ -5604,7 +5578,6 @@ static void mddev_delayed_delete(struct work_struct *ws) { struct mddev *mddev = container_of(ws, struct mddev, del_work); - kobject_del(&mddev->kobj); kobject_put(&mddev->kobj); } @@ -5623,7 +5596,7 @@ int mddev_init_writes_pending(struct mddev *mddev) } EXPORT_SYMBOL_GPL(mddev_init_writes_pending); -static int md_alloc(dev_t dev, char *name) +struct mddev *md_alloc(dev_t dev, char *name) { /* * If dev is zero, name is the name of a device to allocate with @@ -5651,8 +5624,8 @@ static int md_alloc(dev_t dev, char *name) mutex_lock(&disks_mutex); mddev = mddev_alloc(dev); if (IS_ERR(mddev)) { - mutex_unlock(&disks_mutex); - return PTR_ERR(mddev); + error = PTR_ERR(mddev); + goto out_unlock; } partitioned = (MAJOR(mddev->unit) != MD_MAJOR); @@ -5670,7 +5643,7 @@ static int md_alloc(dev_t dev, char *name) strcmp(mddev2->gendisk->disk_name, name) == 0) { spin_unlock(&all_mddevs_lock); error = -EEXIST; - goto out_unlock_disks_mutex; + goto out_free_mddev; } spin_unlock(&all_mddevs_lock); } @@ -5683,7 +5656,7 @@ static int md_alloc(dev_t dev, char *name) error = -ENOMEM; disk = blk_alloc_disk(NUMA_NO_NODE); if (!disk) - goto out_unlock_disks_mutex; + goto out_free_mddev; disk->major = MAJOR(mddev->unit); disk->first_minor = unit << shift; @@ -5704,25 +5677,45 @@ static int md_alloc(dev_t dev, char *name) mddev->gendisk = disk; error = add_disk(disk); if (error) - goto out_cleanup_disk; + goto out_put_disk; + kobject_init(&mddev->kobj, &md_ktype); error = kobject_add(&mddev->kobj, &disk_to_dev(disk)->kobj, "%s", "md"); - if (error) - goto out_del_gendisk; + if (error) { + /* + * The disk is already live at this point. Clear the hold flag + * and let mddev_put take care of the deletion, as it isn't any + * different from a normal close on last release now. + */ + mddev->hold_active = 0; + mutex_unlock(&disks_mutex); + mddev_put(mddev); + return ERR_PTR(error); + } kobject_uevent(&mddev->kobj, KOBJ_ADD); mddev->sysfs_state = sysfs_get_dirent_safe(mddev->kobj.sd, "array_state"); mddev->sysfs_level = sysfs_get_dirent_safe(mddev->kobj.sd, "level"); - goto out_unlock_disks_mutex; - -out_del_gendisk: - del_gendisk(disk); -out_cleanup_disk: - put_disk(disk); -out_unlock_disks_mutex: mutex_unlock(&disks_mutex); + return mddev; + +out_put_disk: + put_disk(disk); +out_free_mddev: + mddev_free(mddev); +out_unlock: + mutex_unlock(&disks_mutex); + return ERR_PTR(error); +} + +static int md_alloc_and_put(dev_t dev, char *name) +{ + struct mddev *mddev = md_alloc(dev, name); + + if (IS_ERR(mddev)) + return PTR_ERR(mddev); mddev_put(mddev); - return error; + return 0; } static void md_probe(dev_t dev) @@ -5730,7 +5723,7 @@ static void md_probe(dev_t dev) if (MAJOR(dev) == MD_MAJOR && MINOR(dev) >= 512) return; if (create_on_open) - md_alloc(dev, NULL); + md_alloc_and_put(dev, NULL); } static int add_named_array(const char *val, const struct kernel_param *kp) @@ -5752,12 +5745,12 @@ static int add_named_array(const char *val, const struct kernel_param *kp) return -E2BIG; strscpy(buf, val, len+1); if (strncmp(buf, "md_", 3) == 0) - return md_alloc(0, buf); + return md_alloc_and_put(0, buf); if (strncmp(buf, "md", 2) == 0 && isdigit(buf[2]) && kstrtoul(buf+2, 10, &devnum) == 0 && devnum <= MINORMASK) - return md_alloc(MKDEV(MD_MAJOR, devnum), NULL); + return md_alloc_and_put(MKDEV(MD_MAJOR, devnum), NULL); return -EINVAL; } @@ -6197,6 +6190,7 @@ static void __md_stop_writes(struct mddev *mddev) flush_workqueue(md_misc_wq); if (mddev->sync_thread) { set_bit(MD_RECOVERY_INTR, &mddev->recovery); + md_unregister_thread(&mddev->sync_thread); md_reap_sync_thread(mddev); } @@ -6244,11 +6238,11 @@ static void mddev_detach(struct mddev *mddev) static void __md_stop(struct mddev *mddev) { struct md_personality *pers = mddev->pers; - md_bitmap_destroy(mddev); mddev_detach(mddev); /* Ensure ->event_work is done */ if (mddev->event_work.func) flush_workqueue(md_misc_wq); + md_bitmap_destroy(mddev); spin_lock(&mddev->lock); mddev->pers = NULL; spin_unlock(&mddev->lock); @@ -6497,9 +6491,8 @@ static void autorun_devices(int part) break; } - md_probe(dev); - mddev = mddev_find(dev); - if (!mddev) + mddev = md_alloc(dev, NULL); + if (IS_ERR(mddev)) break; if (mddev_lock(mddev)) @@ -7782,45 +7775,33 @@ out_unlock: static int md_open(struct block_device *bdev, fmode_t mode) { - /* - * Succeed if we can lock the mddev, which confirms that - * it isn't being stopped right now. - */ - struct mddev *mddev = mddev_find(bdev->bd_dev); + struct mddev *mddev; int err; + spin_lock(&all_mddevs_lock); + mddev = mddev_get(bdev->bd_disk->private_data); + spin_unlock(&all_mddevs_lock); if (!mddev) return -ENODEV; - if (mddev->gendisk != bdev->bd_disk) { - /* we are racing with mddev_put which is discarding this - * bd_disk. - */ - mddev_put(mddev); - /* Wait until bdev->bd_disk is definitely gone */ - if (work_pending(&mddev->del_work)) - flush_workqueue(md_misc_wq); - return -EBUSY; - } - BUG_ON(mddev != bdev->bd_disk->private_data); - - if ((err = mutex_lock_interruptible(&mddev->open_mutex))) + err = mutex_lock_interruptible(&mddev->open_mutex); + if (err) goto out; - if (test_bit(MD_CLOSING, &mddev->flags)) { - mutex_unlock(&mddev->open_mutex); - err = -ENODEV; - goto out; - } + err = -ENODEV; + if (test_bit(MD_CLOSING, &mddev->flags)) + goto out_unlock; - err = 0; atomic_inc(&mddev->openers); mutex_unlock(&mddev->open_mutex); bdev_check_media_change(bdev); - out: - if (err) - mddev_put(mddev); + return 0; + +out_unlock: + mutex_unlock(&mddev->open_mutex); +out: + mddev_put(mddev); return err; } @@ -7844,6 +7825,17 @@ static unsigned int md_check_events(struct gendisk *disk, unsigned int clearing) return ret; } +static void md_free_disk(struct gendisk *disk) +{ + struct mddev *mddev = disk->private_data; + + percpu_ref_exit(&mddev->writes_pending); + bioset_exit(&mddev->bio_set); + bioset_exit(&mddev->sync_set); + + mddev_free(mddev); +} + const struct block_device_operations md_fops = { .owner = THIS_MODULE, @@ -7857,6 +7849,7 @@ const struct block_device_operations md_fops = .getgeo = md_getgeo, .check_events = md_check_events, .set_read_only = md_set_read_only, + .free_disk = md_free_disk, }; static int md_thread(void *arg) @@ -8018,16 +8011,26 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev) max_sectors = mddev->dev_sectors; resync = mddev->curr_resync; - if (resync <= 3) { + if (resync < MD_RESYNC_ACTIVE) { if (test_bit(MD_RECOVERY_DONE, &mddev->recovery)) /* Still cleaning up */ resync = max_sectors; - } else if (resync > max_sectors) + } else if (resync > max_sectors) { resync = max_sectors; - else + } else { resync -= atomic_read(&mddev->recovery_active); + if (resync < MD_RESYNC_ACTIVE) { + /* + * Resync has started, but the subtraction has + * yielded one of the special values. Force it + * to active to ensure the status reports an + * active resync. + */ + resync = MD_RESYNC_ACTIVE; + } + } - if (resync == 0) { + if (resync == MD_RESYNC_NONE) { if (test_bit(MD_RESYNCING_REMOTE, &mddev->recovery)) { struct md_rdev *rdev; @@ -8051,7 +8054,7 @@ static int status_resync(struct seq_file *seq, struct mddev *mddev) } return 0; } - if (resync < 3) { + if (resync < MD_RESYNC_ACTIVE) { seq_printf(seq, "\tresync=DELAYED"); return 1; } @@ -8152,6 +8155,8 @@ static void *md_seq_start(struct seq_file *seq, loff_t *pos) if (!l--) { mddev = list_entry(tmp, struct mddev, all_mddevs); mddev_get(mddev); + if (!mddev_get(mddev)) + continue; spin_unlock(&all_mddevs_lock); return mddev; } @@ -8165,25 +8170,35 @@ static void *md_seq_next(struct seq_file *seq, void *v, loff_t *pos) { struct list_head *tmp; struct mddev *next_mddev, *mddev = v; + struct mddev *to_put = NULL; ++*pos; if (v == (void*)2) return NULL; spin_lock(&all_mddevs_lock); - if (v == (void*)1) + if (v == (void*)1) { tmp = all_mddevs.next; - else + } else { + to_put = mddev; + tmp = mddev->all_mddevs.next; + } + + for (;;) { + if (tmp == &all_mddevs) { + next_mddev = (void*)2; + *pos = 0x10000; + break; + } + next_mddev = list_entry(tmp, struct mddev, all_mddevs); + if (mddev_get(next_mddev)) + break; + mddev = next_mddev; tmp = mddev->all_mddevs.next; - if (tmp != &all_mddevs) - next_mddev = mddev_get(list_entry(tmp,struct mddev,all_mddevs)); - else { - next_mddev = (void*)2; - *pos = 0x10000; } spin_unlock(&all_mddevs_lock); - if (v != (void*)1) + if (to_put) mddev_put(mddev); return next_mddev; @@ -8682,7 +8697,6 @@ void md_do_sync(struct md_thread *thread) unsigned long update_time; sector_t mark_cnt[SYNC_MARKS]; int last_mark,m; - struct list_head *tmp; sector_t last_check; int skipped = 0; struct md_rdev *rdev; @@ -8729,13 +8743,7 @@ void md_do_sync(struct md_thread *thread) mddev->last_sync_action = action ?: desc; - /* we overload curr_resync somewhat here. - * 0 == not engaged in resync at all - * 2 == checking that there is no conflict with another sync - * 1 == like 2, but have yielded to allow conflicting resync to - * commence - * other == active in resync - this many blocks - * + /* * Before starting a resync we must have set curr_resync to * 2, and then checked that every "conflicting" array has curr_resync * less than ours. When we find one that is the same or higher @@ -8747,24 +8755,29 @@ void md_do_sync(struct md_thread *thread) do { int mddev2_minor = -1; - mddev->curr_resync = 2; + mddev->curr_resync = MD_RESYNC_DELAYED; try_again: if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) goto skip; - for_each_mddev(mddev2, tmp) { + spin_lock(&all_mddevs_lock); + list_for_each_entry(mddev2, &all_mddevs, all_mddevs) { + if (test_bit(MD_DELETED, &mddev2->flags)) + continue; if (mddev2 == mddev) continue; if (!mddev->parallel_resync && mddev2->curr_resync && match_mddev_units(mddev, mddev2)) { DEFINE_WAIT(wq); - if (mddev < mddev2 && mddev->curr_resync == 2) { + if (mddev < mddev2 && + mddev->curr_resync == MD_RESYNC_DELAYED) { /* arbitrarily yield */ - mddev->curr_resync = 1; + mddev->curr_resync = MD_RESYNC_YIELDED; wake_up(&resync_wait); } - if (mddev > mddev2 && mddev->curr_resync == 1) + if (mddev > mddev2 && + mddev->curr_resync == MD_RESYNC_YIELDED) /* no need to wait here, we can wait the next * time 'round when curr_resync == 2 */ @@ -8782,7 +8795,8 @@ void md_do_sync(struct md_thread *thread) desc, mdname(mddev), mdname(mddev2)); } - mddev_put(mddev2); + spin_unlock(&all_mddevs_lock); + if (signal_pending(current)) flush_signals(current); schedule(); @@ -8792,7 +8806,8 @@ void md_do_sync(struct md_thread *thread) finish_wait(&resync_wait, &wq); } } - } while (mddev->curr_resync < 2); + spin_unlock(&all_mddevs_lock); + } while (mddev->curr_resync < MD_RESYNC_DELAYED); j = 0; if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { @@ -8876,7 +8891,7 @@ void md_do_sync(struct md_thread *thread) desc, mdname(mddev)); mddev->curr_resync = j; } else - mddev->curr_resync = 3; /* no longer delayed */ + mddev->curr_resync = MD_RESYNC_ACTIVE; /* no longer delayed */ mddev->curr_resync_completed = j; sysfs_notify_dirent_safe(mddev->sysfs_completed); md_new_event(); @@ -9011,14 +9026,14 @@ void md_do_sync(struct md_thread *thread) if (!test_bit(MD_RECOVERY_RESHAPE, &mddev->recovery) && !test_bit(MD_RECOVERY_INTR, &mddev->recovery) && - mddev->curr_resync > 3) { + mddev->curr_resync >= MD_RESYNC_ACTIVE) { mddev->curr_resync_completed = mddev->curr_resync; sysfs_notify_dirent_safe(mddev->sysfs_completed); } mddev->pers->sync_request(mddev, max_sectors, &skipped); if (!test_bit(MD_RECOVERY_CHECK, &mddev->recovery) && - mddev->curr_resync > 3) { + mddev->curr_resync >= MD_RESYNC_ACTIVE) { if (test_bit(MD_RECOVERY_SYNC, &mddev->recovery)) { if (test_bit(MD_RECOVERY_INTR, &mddev->recovery)) { if (mddev->curr_resync >= mddev->recovery_cp) { @@ -9082,7 +9097,7 @@ void md_do_sync(struct md_thread *thread) } else if (test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery)) mddev->resync_min = mddev->curr_resync_completed; set_bit(MD_RECOVERY_DONE, &mddev->recovery); - mddev->curr_resync = 0; + mddev->curr_resync = MD_RESYNC_NONE; spin_unlock(&mddev->lock); wake_up(&resync_wait); @@ -9303,6 +9318,7 @@ void md_check_recovery(struct mddev *mddev) * ->spare_active and clear saved_raid_disk */ set_bit(MD_RECOVERY_INTR, &mddev->recovery); + md_unregister_thread(&mddev->sync_thread); md_reap_sync_thread(mddev); clear_bit(MD_RECOVERY_RECOVER, &mddev->recovery); clear_bit(MD_RECOVERY_NEEDED, &mddev->recovery); @@ -9338,6 +9354,7 @@ void md_check_recovery(struct mddev *mddev) goto unlock; } if (mddev->sync_thread) { + md_unregister_thread(&mddev->sync_thread); md_reap_sync_thread(mddev); goto unlock; } @@ -9417,8 +9434,7 @@ void md_reap_sync_thread(struct mddev *mddev) sector_t old_dev_sectors = mddev->dev_sectors; bool is_reshaped = false; - /* resync has finished, collect result */ - md_unregister_thread(&mddev->sync_thread); + /* sync_thread should be unregistered, collect result */ if (!test_bit(MD_RECOVERY_INTR, &mddev->recovery) && !test_bit(MD_RECOVERY_REQUESTED, &mddev->recovery) && mddev->degraded != mddev->raid_disks) { @@ -9466,6 +9482,7 @@ void md_reap_sync_thread(struct mddev *mddev) wake_up(&resync_wait); /* flag recovery needed just to double check */ set_bit(MD_RECOVERY_NEEDED, &mddev->recovery); + sysfs_notify_dirent_safe(mddev->sysfs_completed); sysfs_notify_dirent_safe(mddev->sysfs_action); md_new_event(); if (mddev->event_work.func) @@ -9544,11 +9561,14 @@ EXPORT_SYMBOL_GPL(rdev_clear_badblocks); static int md_notify_reboot(struct notifier_block *this, unsigned long code, void *x) { - struct list_head *tmp; - struct mddev *mddev; + struct mddev *mddev, *n; int need_delay = 0; - for_each_mddev(mddev, tmp) { + spin_lock(&all_mddevs_lock); + list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) { + if (!mddev_get(mddev)) + continue; + spin_unlock(&all_mddevs_lock); if (mddev_trylock(mddev)) { if (mddev->pers) __md_stop_writes(mddev); @@ -9557,7 +9577,11 @@ static int md_notify_reboot(struct notifier_block *this, mddev_unlock(mddev); } need_delay = 1; + mddev_put(mddev); + spin_lock(&all_mddevs_lock); } + spin_unlock(&all_mddevs_lock); + /* * certain more exotic SCSI devices are known to be * volatile wrt too early system reboots. While the @@ -9876,8 +9900,7 @@ void md_autostart_arrays(int part) static __exit void md_exit(void) { - struct mddev *mddev; - struct list_head *tmp; + struct mddev *mddev, *n; int delay = 1; unregister_blkdev(MD_MAJOR,"md"); @@ -9897,17 +9920,24 @@ static __exit void md_exit(void) } remove_proc_entry("mdstat", NULL); - for_each_mddev(mddev, tmp) { + spin_lock(&all_mddevs_lock); + list_for_each_entry_safe(mddev, n, &all_mddevs, all_mddevs) { + if (!mddev_get(mddev)) + continue; + spin_unlock(&all_mddevs_lock); export_array(mddev); mddev->ctime = 0; mddev->hold_active = 0; /* - * for_each_mddev() will call mddev_put() at the end of each - * iteration. As the mddev is now fully clear, this will - * schedule the mddev for destruction by a workqueue, and the + * As the mddev is now fully clear, mddev_put will schedule + * the mddev for destruction by a workqueue, and the * destroy_workqueue() below will wait for that to complete. */ + mddev_put(mddev); + spin_lock(&all_mddevs_lock); } + spin_unlock(&all_mddevs_lock); + destroy_workqueue(md_rdev_misc_wq); destroy_workqueue(md_misc_wq); destroy_workqueue(md_wq); diff --git a/drivers/md/md.h b/drivers/md/md.h index b4f84b27bdef..b4e2d8b87b61 100644 --- a/drivers/md/md.h +++ b/drivers/md/md.h @@ -254,6 +254,7 @@ struct md_cluster_info; * @MD_NOT_READY: do_md_run() is active, so 'array_state', ust not report that * array is ready yet. * @MD_BROKEN: This is used to stop writes and mark array as failed. + * @MD_DELETED: This device is being deleted * * change UNSUPPORTED_MDDEV_FLAGS for each array type if new flag is added */ @@ -270,6 +271,7 @@ enum mddev_flags { MD_UPDATING_SB, MD_NOT_READY, MD_BROKEN, + MD_DELETED, }; enum mddev_sb_flags { @@ -288,6 +290,21 @@ struct serial_info { sector_t _subtree_last; /* highest sector in subtree of rb node */ }; +/* + * mddev->curr_resync stores the current sector of the resync but + * also has some overloaded values. + */ +enum { + /* No resync in progress */ + MD_RESYNC_NONE = 0, + /* Yielded to allow another conflicting resync to commence */ + MD_RESYNC_YIELDED = 1, + /* Delayed to check that there is no conflict with another sync */ + MD_RESYNC_DELAYED = 2, + /* Any value greater than or equal to this is in an active resync */ + MD_RESYNC_ACTIVE = 3, +}; + struct mddev { void *private; struct md_personality *pers; @@ -750,6 +767,8 @@ extern int md_integrity_add_rdev(struct md_rdev *rdev, struct mddev *mddev); extern int strict_strtoul_scaled(const char *cp, unsigned long *res, int scale); extern void mddev_init(struct mddev *mddev); +struct mddev *md_alloc(dev_t dev, char *name); +void mddev_put(struct mddev *mddev); extern int md_run(struct mddev *mddev); extern int md_start(struct mddev *mddev); extern void md_stop(struct mddev *mddev); diff --git a/drivers/md/raid10.c b/drivers/md/raid10.c index 26545950ca42..9117fcdee1be 100644 --- a/drivers/md/raid10.c +++ b/drivers/md/raid10.c @@ -2167,9 +2167,12 @@ static int raid10_remove_disk(struct mddev *mddev, struct md_rdev *rdev) int err = 0; int number = rdev->raid_disk; struct md_rdev **rdevp; - struct raid10_info *p = conf->mirrors + number; + struct raid10_info *p; print_conf(conf); + if (unlikely(number >= mddev->raid_disks)) + return 0; + p = conf->mirrors + number; if (rdev == p->rdev) rdevp = &p->rdev; else if (rdev == p->replacement) diff --git a/drivers/md/raid5-cache.c b/drivers/md/raid5-cache.c index 6f2dd73128b0..f4e1cc1ece43 100644 --- a/drivers/md/raid5-cache.c +++ b/drivers/md/raid5-cache.c @@ -1590,18 +1590,13 @@ void r5l_quiesce(struct r5l_log *log, int quiesce) bool r5l_log_disk_error(struct r5conf *conf) { - struct r5l_log *log; - bool ret; - /* don't allow write if journal disk is missing */ - rcu_read_lock(); - log = rcu_dereference(conf->log); + struct r5l_log *log = conf->log; + /* don't allow write if journal disk is missing */ if (!log) - ret = test_bit(MD_HAS_JOURNAL, &conf->mddev->flags); + return test_bit(MD_HAS_JOURNAL, &conf->mddev->flags); else - ret = test_bit(Faulty, &log->rdev->flags); - rcu_read_unlock(); - return ret; + return test_bit(Faulty, &log->rdev->flags); } #define R5L_RECOVERY_PAGE_POOL_SIZE 256 @@ -2534,12 +2529,13 @@ static ssize_t r5c_journal_mode_show(struct mddev *mddev, char *page) struct r5conf *conf; int ret; - spin_lock(&mddev->lock); + ret = mddev_lock(mddev); + if (ret) + return ret; + conf = mddev->private; - if (!conf || !conf->log) { - spin_unlock(&mddev->lock); - return 0; - } + if (!conf || !conf->log) + goto out_unlock; switch (conf->log->r5c_journal_mode) { case R5C_JOURNAL_MODE_WRITE_THROUGH: @@ -2557,7 +2553,9 @@ static ssize_t r5c_journal_mode_show(struct mddev *mddev, char *page) default: ret = 0; } - spin_unlock(&mddev->lock); + +out_unlock: + mddev_unlock(mddev); return ret; } @@ -2639,7 +2637,7 @@ int r5c_try_caching_write(struct r5conf *conf, int i; struct r5dev *dev; int to_cache = 0; - void **pslot; + void __rcu **pslot; sector_t tree_index; int ret; uintptr_t refcount; @@ -2806,7 +2804,7 @@ void r5c_finish_stripe_write_out(struct r5conf *conf, int i; int do_wakeup = 0; sector_t tree_index; - void **pslot; + void __rcu **pslot; uintptr_t refcount; if (!log || !test_bit(R5_InJournal, &sh->dev[sh->pd_idx].flags)) @@ -3145,7 +3143,7 @@ int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev) spin_lock_init(&log->stripe_in_journal_lock); atomic_set(&log->stripe_in_journal_count, 0); - rcu_assign_pointer(conf->log, log); + conf->log = log; set_bit(MD_HAS_JOURNAL, &conf->mddev->flags); return 0; @@ -3167,13 +3165,13 @@ void r5l_exit_log(struct r5conf *conf) { struct r5l_log *log = conf->log; - conf->log = NULL; - synchronize_rcu(); - /* Ensure disable_writeback_work wakes up and exits */ wake_up(&conf->mddev->sb_wait); flush_work(&log->disable_writeback_work); md_unregister_thread(&log->reclaim_thread); + + conf->log = NULL; + mempool_exit(&log->meta_pool); bioset_exit(&log->bs); mempool_exit(&log->io_pool); diff --git a/drivers/md/raid5-log.h b/drivers/md/raid5-log.h index 43c714a8798c..c8332502669e 100644 --- a/drivers/md/raid5-log.h +++ b/drivers/md/raid5-log.h @@ -2,49 +2,46 @@ #ifndef _RAID5_LOG_H #define _RAID5_LOG_H -extern int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev); -extern void r5l_exit_log(struct r5conf *conf); -extern int r5l_write_stripe(struct r5l_log *log, struct stripe_head *head_sh); -extern void r5l_write_stripe_run(struct r5l_log *log); -extern void r5l_flush_stripe_to_raid(struct r5l_log *log); -extern void r5l_stripe_write_finished(struct stripe_head *sh); -extern int r5l_handle_flush_request(struct r5l_log *log, struct bio *bio); -extern void r5l_quiesce(struct r5l_log *log, int quiesce); -extern bool r5l_log_disk_error(struct r5conf *conf); -extern bool r5c_is_writeback(struct r5l_log *log); -extern int -r5c_try_caching_write(struct r5conf *conf, struct stripe_head *sh, - struct stripe_head_state *s, int disks); -extern void -r5c_finish_stripe_write_out(struct r5conf *conf, struct stripe_head *sh, - struct stripe_head_state *s); -extern void r5c_release_extra_page(struct stripe_head *sh); -extern void r5c_use_extra_page(struct stripe_head *sh); -extern void r5l_wake_reclaim(struct r5l_log *log, sector_t space); -extern void r5c_handle_cached_data_endio(struct r5conf *conf, - struct stripe_head *sh, int disks); -extern int r5c_cache_data(struct r5l_log *log, struct stripe_head *sh); -extern void r5c_make_stripe_write_out(struct stripe_head *sh); -extern void r5c_flush_cache(struct r5conf *conf, int num); -extern void r5c_check_stripe_cache_usage(struct r5conf *conf); -extern void r5c_check_cached_full_stripe(struct r5conf *conf); +int r5l_init_log(struct r5conf *conf, struct md_rdev *rdev); +void r5l_exit_log(struct r5conf *conf); +int r5l_write_stripe(struct r5l_log *log, struct stripe_head *head_sh); +void r5l_write_stripe_run(struct r5l_log *log); +void r5l_flush_stripe_to_raid(struct r5l_log *log); +void r5l_stripe_write_finished(struct stripe_head *sh); +int r5l_handle_flush_request(struct r5l_log *log, struct bio *bio); +void r5l_quiesce(struct r5l_log *log, int quiesce); +bool r5l_log_disk_error(struct r5conf *conf); +bool r5c_is_writeback(struct r5l_log *log); +int r5c_try_caching_write(struct r5conf *conf, struct stripe_head *sh, + struct stripe_head_state *s, int disks); +void r5c_finish_stripe_write_out(struct r5conf *conf, struct stripe_head *sh, + struct stripe_head_state *s); +void r5c_release_extra_page(struct stripe_head *sh); +void r5c_use_extra_page(struct stripe_head *sh); +void r5l_wake_reclaim(struct r5l_log *log, sector_t space); +void r5c_handle_cached_data_endio(struct r5conf *conf, + struct stripe_head *sh, int disks); +int r5c_cache_data(struct r5l_log *log, struct stripe_head *sh); +void r5c_make_stripe_write_out(struct stripe_head *sh); +void r5c_flush_cache(struct r5conf *conf, int num); +void r5c_check_stripe_cache_usage(struct r5conf *conf); +void r5c_check_cached_full_stripe(struct r5conf *conf); extern struct md_sysfs_entry r5c_journal_mode; -extern void r5c_update_on_rdev_error(struct mddev *mddev, - struct md_rdev *rdev); -extern bool r5c_big_stripe_cached(struct r5conf *conf, sector_t sect); -extern int r5l_start(struct r5l_log *log); +void r5c_update_on_rdev_error(struct mddev *mddev, struct md_rdev *rdev); +bool r5c_big_stripe_cached(struct r5conf *conf, sector_t sect); +int r5l_start(struct r5l_log *log); -extern struct dma_async_tx_descriptor * +struct dma_async_tx_descriptor * ops_run_partial_parity(struct stripe_head *sh, struct raid5_percpu *percpu, struct dma_async_tx_descriptor *tx); -extern int ppl_init_log(struct r5conf *conf); -extern void ppl_exit_log(struct r5conf *conf); -extern int ppl_write_stripe(struct r5conf *conf, struct stripe_head *sh); -extern void ppl_write_stripe_run(struct r5conf *conf); -extern void ppl_stripe_write_finished(struct stripe_head *sh); -extern int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add); -extern void ppl_quiesce(struct r5conf *conf, int quiesce); -extern int ppl_handle_flush_request(struct r5l_log *log, struct bio *bio); +int ppl_init_log(struct r5conf *conf); +void ppl_exit_log(struct r5conf *conf); +int ppl_write_stripe(struct r5conf *conf, struct stripe_head *sh); +void ppl_write_stripe_run(struct r5conf *conf); +void ppl_stripe_write_finished(struct stripe_head *sh); +int ppl_modify_log(struct r5conf *conf, struct md_rdev *rdev, bool add); +void ppl_quiesce(struct r5conf *conf, int quiesce); +int ppl_handle_flush_request(struct bio *bio); extern struct md_sysfs_entry ppl_write_hint; static inline bool raid5_has_log(struct r5conf *conf) @@ -111,7 +108,7 @@ static inline int log_handle_flush_request(struct r5conf *conf, struct bio *bio) if (conf->log) ret = r5l_handle_flush_request(conf->log, bio); else if (raid5_has_ppl(conf)) - ret = ppl_handle_flush_request(conf->log, bio); + ret = ppl_handle_flush_request(bio); return ret; } diff --git a/drivers/md/raid5-ppl.c b/drivers/md/raid5-ppl.c index 98988cb26295..31b9157bc9ae 100644 --- a/drivers/md/raid5-ppl.c +++ b/drivers/md/raid5-ppl.c @@ -679,7 +679,7 @@ void ppl_quiesce(struct r5conf *conf, int quiesce) } } -int ppl_handle_flush_request(struct r5l_log *log, struct bio *bio) +int ppl_handle_flush_request(struct bio *bio) { if (bio->bi_iter.bi_size == 0) { bio_endio(bio); diff --git a/drivers/md/raid5.c b/drivers/md/raid5.c index 5cabdbbac48b..860c45c10a57 100644 --- a/drivers/md/raid5.c +++ b/drivers/md/raid5.c @@ -61,6 +61,8 @@ #define cpu_to_group(cpu) cpu_to_node(cpu) #define ANY_GROUP NUMA_NO_NODE +#define RAID5_MAX_REQ_STRIPES 256 + static bool devices_handle_discard_safely = false; module_param(devices_handle_discard_safely, bool, 0644); MODULE_PARM_DESC(devices_handle_discard_safely, @@ -624,6 +626,49 @@ static struct stripe_head *__find_stripe(struct r5conf *conf, sector_t sector, return NULL; } +static struct stripe_head *find_get_stripe(struct r5conf *conf, + sector_t sector, short generation, int hash) +{ + int inc_empty_inactive_list_flag; + struct stripe_head *sh; + + sh = __find_stripe(conf, sector, generation); + if (!sh) + return NULL; + + if (atomic_inc_not_zero(&sh->count)) + return sh; + + /* + * Slow path. The reference count is zero which means the stripe must + * be on a list (sh->lru). Must remove the stripe from the list that + * references it with the device_lock held. + */ + + spin_lock(&conf->device_lock); + if (!atomic_read(&sh->count)) { + if (!test_bit(STRIPE_HANDLE, &sh->state)) + atomic_inc(&conf->active_stripes); + BUG_ON(list_empty(&sh->lru) && + !test_bit(STRIPE_EXPANDING, &sh->state)); + inc_empty_inactive_list_flag = 0; + if (!list_empty(conf->inactive_list + hash)) + inc_empty_inactive_list_flag = 1; + list_del_init(&sh->lru); + if (list_empty(conf->inactive_list + hash) && + inc_empty_inactive_list_flag) + atomic_inc(&conf->empty_inactive_list_nr); + if (sh->group) { + sh->group->stripes_cnt--; + sh->group = NULL; + } + } + atomic_inc(&sh->count); + spin_unlock(&conf->device_lock); + + return sh; +} + /* * Need to check if array has failed when deciding whether to: * - start an array @@ -710,80 +755,121 @@ static bool has_failed(struct r5conf *conf) return degraded > conf->max_degraded; } -struct stripe_head * -raid5_get_active_stripe(struct r5conf *conf, sector_t sector, - int previous, int noblock, int noquiesce) +enum stripe_result { + STRIPE_SUCCESS = 0, + STRIPE_RETRY, + STRIPE_SCHEDULE_AND_RETRY, + STRIPE_FAIL, +}; + +struct stripe_request_ctx { + /* a reference to the last stripe_head for batching */ + struct stripe_head *batch_last; + + /* first sector in the request */ + sector_t first_sector; + + /* last sector in the request */ + sector_t last_sector; + + /* + * bitmap to track stripe sectors that have been added to stripes + * add one to account for unaligned requests + */ + DECLARE_BITMAP(sectors_to_do, RAID5_MAX_REQ_STRIPES + 1); + + /* the request had REQ_PREFLUSH, cleared after the first stripe_head */ + bool do_flush; +}; + +/* + * Block until another thread clears R5_INACTIVE_BLOCKED or + * there are fewer than 3/4 the maximum number of active stripes + * and there is an inactive stripe available. + */ +static bool is_inactive_blocked(struct r5conf *conf, int hash) +{ + int active = atomic_read(&conf->active_stripes); + + if (list_empty(conf->inactive_list + hash)) + return false; + + if (!test_bit(R5_INACTIVE_BLOCKED, &conf->cache_state)) + return true; + + return active < (conf->max_nr_stripes * 3 / 4); +} + +static struct stripe_head *__raid5_get_active_stripe(struct r5conf *conf, + struct stripe_request_ctx *ctx, sector_t sector, + bool previous, bool noblock, bool noquiesce) { struct stripe_head *sh; int hash = stripe_hash_locks_hash(conf, sector); - int inc_empty_inactive_list_flag; pr_debug("get_stripe, sector %llu\n", (unsigned long long)sector); spin_lock_irq(conf->hash_locks + hash); - do { - wait_event_lock_irq(conf->wait_for_quiescent, - conf->quiesce == 0 || noquiesce, - *(conf->hash_locks + hash)); - sh = __find_stripe(conf, sector, conf->generation - previous); - if (!sh) { - if (!test_bit(R5_INACTIVE_BLOCKED, &conf->cache_state)) { - sh = get_free_stripe(conf, hash); - if (!sh && !test_bit(R5_DID_ALLOC, - &conf->cache_state)) - set_bit(R5_ALLOC_MORE, - &conf->cache_state); - } - if (noblock && sh == NULL) - break; - - r5c_check_stripe_cache_usage(conf); - if (!sh) { - set_bit(R5_INACTIVE_BLOCKED, - &conf->cache_state); - r5l_wake_reclaim(conf->log, 0); - wait_event_lock_irq( - conf->wait_for_stripe, - !list_empty(conf->inactive_list + hash) && - (atomic_read(&conf->active_stripes) - < (conf->max_nr_stripes * 3 / 4) - || !test_bit(R5_INACTIVE_BLOCKED, - &conf->cache_state)), - *(conf->hash_locks + hash)); - clear_bit(R5_INACTIVE_BLOCKED, - &conf->cache_state); - } else { - init_stripe(sh, sector, previous); - atomic_inc(&sh->count); - } - } else if (!atomic_inc_not_zero(&sh->count)) { - spin_lock(&conf->device_lock); - if (!atomic_read(&sh->count)) { - if (!test_bit(STRIPE_HANDLE, &sh->state)) - atomic_inc(&conf->active_stripes); - BUG_ON(list_empty(&sh->lru) && - !test_bit(STRIPE_EXPANDING, &sh->state)); - inc_empty_inactive_list_flag = 0; - if (!list_empty(conf->inactive_list + hash)) - inc_empty_inactive_list_flag = 1; - list_del_init(&sh->lru); - if (list_empty(conf->inactive_list + hash) && inc_empty_inactive_list_flag) - atomic_inc(&conf->empty_inactive_list_nr); - if (sh->group) { - sh->group->stripes_cnt--; - sh->group = NULL; - } - } - atomic_inc(&sh->count); - spin_unlock(&conf->device_lock); +retry: + if (!noquiesce && conf->quiesce) { + /* + * Must release the reference to batch_last before waiting, + * on quiesce, otherwise the batch_last will hold a reference + * to a stripe and raid5_quiesce() will deadlock waiting for + * active_stripes to go to zero. + */ + if (ctx && ctx->batch_last) { + raid5_release_stripe(ctx->batch_last); + ctx->batch_last = NULL; } - } while (sh == NULL); + wait_event_lock_irq(conf->wait_for_quiescent, !conf->quiesce, + *(conf->hash_locks + hash)); + } + + sh = find_get_stripe(conf, sector, conf->generation - previous, hash); + if (sh) + goto out; + + if (test_bit(R5_INACTIVE_BLOCKED, &conf->cache_state)) + goto wait_for_stripe; + + sh = get_free_stripe(conf, hash); + if (sh) { + r5c_check_stripe_cache_usage(conf); + init_stripe(sh, sector, previous); + atomic_inc(&sh->count); + goto out; + } + + if (!test_bit(R5_DID_ALLOC, &conf->cache_state)) + set_bit(R5_ALLOC_MORE, &conf->cache_state); + +wait_for_stripe: + if (noblock) + goto out; + + set_bit(R5_INACTIVE_BLOCKED, &conf->cache_state); + r5l_wake_reclaim(conf->log, 0); + wait_event_lock_irq(conf->wait_for_stripe, + is_inactive_blocked(conf, hash), + *(conf->hash_locks + hash)); + clear_bit(R5_INACTIVE_BLOCKED, &conf->cache_state); + goto retry; + +out: spin_unlock_irq(conf->hash_locks + hash); return sh; } +struct stripe_head *raid5_get_active_stripe(struct r5conf *conf, + sector_t sector, bool previous, bool noblock, bool noquiesce) +{ + return __raid5_get_active_stripe(conf, NULL, sector, previous, noblock, + noquiesce); +} + static bool is_full_stripe_write(struct stripe_head *sh) { BUG_ON(sh->overwrite_disks > (sh->disks - sh->raid_conf->max_degraded)); @@ -824,13 +910,13 @@ static bool stripe_can_batch(struct stripe_head *sh) } /* we only do back search */ -static void stripe_add_to_batch_list(struct r5conf *conf, struct stripe_head *sh) +static void stripe_add_to_batch_list(struct r5conf *conf, + struct stripe_head *sh, struct stripe_head *last_sh) { struct stripe_head *head; sector_t head_sector, tmp_sec; int hash; int dd_idx; - int inc_empty_inactive_list_flag; /* Don't cross chunks, so stripe pd_idx/qd_idx is the same */ tmp_sec = sh->sector; @@ -838,36 +924,20 @@ static void stripe_add_to_batch_list(struct r5conf *conf, struct stripe_head *sh return; head_sector = sh->sector - RAID5_STRIPE_SECTORS(conf); - hash = stripe_hash_locks_hash(conf, head_sector); - spin_lock_irq(conf->hash_locks + hash); - head = __find_stripe(conf, head_sector, conf->generation); - if (head && !atomic_inc_not_zero(&head->count)) { - spin_lock(&conf->device_lock); - if (!atomic_read(&head->count)) { - if (!test_bit(STRIPE_HANDLE, &head->state)) - atomic_inc(&conf->active_stripes); - BUG_ON(list_empty(&head->lru) && - !test_bit(STRIPE_EXPANDING, &head->state)); - inc_empty_inactive_list_flag = 0; - if (!list_empty(conf->inactive_list + hash)) - inc_empty_inactive_list_flag = 1; - list_del_init(&head->lru); - if (list_empty(conf->inactive_list + hash) && inc_empty_inactive_list_flag) - atomic_inc(&conf->empty_inactive_list_nr); - if (head->group) { - head->group->stripes_cnt--; - head->group = NULL; - } - } + if (last_sh && head_sector == last_sh->sector) { + head = last_sh; atomic_inc(&head->count); - spin_unlock(&conf->device_lock); + } else { + hash = stripe_hash_locks_hash(conf, head_sector); + spin_lock_irq(conf->hash_locks + hash); + head = find_get_stripe(conf, head_sector, conf->generation, + hash); + spin_unlock_irq(conf->hash_locks + hash); + if (!head) + return; + if (!stripe_can_batch(head)) + goto out; } - spin_unlock_irq(conf->hash_locks + hash); - - if (!head) - return; - if (!stripe_can_batch(head)) - goto out; lock_two_stripes(head, sh); /* clear_batch_ready clear the flag */ @@ -2882,10 +2952,10 @@ static void raid5_end_write_request(struct bio *bi) if (!test_and_clear_bit(R5_DOUBLE_LOCKED, &sh->dev[i].flags)) clear_bit(R5_LOCKED, &sh->dev[i].flags); set_bit(STRIPE_HANDLE, &sh->state); - raid5_release_stripe(sh); if (sh->batch_head && sh != sh->batch_head) raid5_release_stripe(sh->batch_head); + raid5_release_stripe(sh); } static void raid5_error(struct mddev *mddev, struct md_rdev *rdev) @@ -3413,39 +3483,32 @@ schedule_reconstruction(struct stripe_head *sh, struct stripe_head_state *s, s->locked, s->ops_request); } -/* - * Each stripe/dev can have one or more bion attached. - * toread/towrite point to the first in a chain. - * The bi_next chain must be in order. - */ -static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, - int forwrite, int previous) +static bool stripe_bio_overlaps(struct stripe_head *sh, struct bio *bi, + int dd_idx, int forwrite) { - struct bio **bip; struct r5conf *conf = sh->raid_conf; - int firstwrite=0; + struct bio **bip; - pr_debug("adding bi b#%llu to stripe s#%llu\n", - (unsigned long long)bi->bi_iter.bi_sector, - (unsigned long long)sh->sector); + pr_debug("checking bi b#%llu to stripe s#%llu\n", + bi->bi_iter.bi_sector, sh->sector); - spin_lock_irq(&sh->stripe_lock); /* Don't allow new IO added to stripes in batch list */ if (sh->batch_head) - goto overlap; - if (forwrite) { + return true; + + if (forwrite) bip = &sh->dev[dd_idx].towrite; - if (*bip == NULL) - firstwrite = 1; - } else + else bip = &sh->dev[dd_idx].toread; + while (*bip && (*bip)->bi_iter.bi_sector < bi->bi_iter.bi_sector) { if (bio_end_sector(*bip) > bi->bi_iter.bi_sector) - goto overlap; - bip = & (*bip)->bi_next; + return true; + bip = &(*bip)->bi_next; } + if (*bip && (*bip)->bi_iter.bi_sector < bio_end_sector(bi)) - goto overlap; + return true; if (forwrite && raid5_has_ppl(conf)) { /* @@ -3474,9 +3537,30 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, } if (first + conf->chunk_sectors * (count - 1) != last) - goto overlap; + return true; } + return false; +} + +static void __add_stripe_bio(struct stripe_head *sh, struct bio *bi, + int dd_idx, int forwrite, int previous) +{ + struct r5conf *conf = sh->raid_conf; + struct bio **bip; + int firstwrite = 0; + + if (forwrite) { + bip = &sh->dev[dd_idx].towrite; + if (!*bip) + firstwrite = 1; + } else { + bip = &sh->dev[dd_idx].toread; + } + + while (*bip && (*bip)->bi_iter.bi_sector < bi->bi_iter.bi_sector) + bip = &(*bip)->bi_next; + if (!forwrite || previous) clear_bit(STRIPE_BATCH_READY, &sh->state); @@ -3502,9 +3586,9 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, sh->overwrite_disks++; } - pr_debug("added bi b#%llu to stripe s#%llu, disk %d.\n", - (unsigned long long)(*bip)->bi_iter.bi_sector, - (unsigned long long)sh->sector, dd_idx); + pr_debug("added bi b#%llu to stripe s#%llu, disk %d, logical %llu\n", + (*bip)->bi_iter.bi_sector, sh->sector, dd_idx, + sh->dev[dd_idx].sector); if (conf->mddev->bitmap && firstwrite) { /* Cannot hold spinlock over bitmap_startwrite, @@ -3512,7 +3596,7 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, * we have added to the bitmap and set bm_seq. * So set STRIPE_BITMAP_PENDING to prevent * batching. - * If multiple add_stripe_bio() calls race here they + * If multiple __add_stripe_bio() calls race here they * much all set STRIPE_BITMAP_PENDING. So only the first one * to complete "bitmap_startwrite" gets to set * STRIPE_BIT_DELAY. This is important as once a stripe @@ -3530,16 +3614,27 @@ static int add_stripe_bio(struct stripe_head *sh, struct bio *bi, int dd_idx, set_bit(STRIPE_BIT_DELAY, &sh->state); } } - spin_unlock_irq(&sh->stripe_lock); +} - if (stripe_can_batch(sh)) - stripe_add_to_batch_list(conf, sh); - return 1; +/* + * Each stripe/dev can have one or more bios attached. + * toread/towrite point to the first in a chain. + * The bi_next chain must be in order. + */ +static bool add_stripe_bio(struct stripe_head *sh, struct bio *bi, + int dd_idx, int forwrite, int previous) +{ + spin_lock_irq(&sh->stripe_lock); - overlap: - set_bit(R5_Overlap, &sh->dev[dd_idx].flags); + if (stripe_bio_overlaps(sh, bi, dd_idx, forwrite)) { + set_bit(R5_Overlap, &sh->dev[dd_idx].flags); + spin_unlock_irq(&sh->stripe_lock); + return false; + } + + __add_stripe_bio(sh, bi, dd_idx, forwrite, previous); spin_unlock_irq(&sh->stripe_lock); - return 0; + return true; } static void end_reshape(struct r5conf *conf); @@ -5785,17 +5880,215 @@ static void make_discard_request(struct mddev *mddev, struct bio *bi) bio_endio(bi); } +static bool ahead_of_reshape(struct mddev *mddev, sector_t sector, + sector_t reshape_sector) +{ + return mddev->reshape_backwards ? sector < reshape_sector : + sector >= reshape_sector; +} + +static bool range_ahead_of_reshape(struct mddev *mddev, sector_t min, + sector_t max, sector_t reshape_sector) +{ + return mddev->reshape_backwards ? max < reshape_sector : + min >= reshape_sector; +} + +static bool stripe_ahead_of_reshape(struct mddev *mddev, struct r5conf *conf, + struct stripe_head *sh) +{ + sector_t max_sector = 0, min_sector = MaxSector; + bool ret = false; + int dd_idx; + + for (dd_idx = 0; dd_idx < sh->disks; dd_idx++) { + if (dd_idx == sh->pd_idx) + continue; + + min_sector = min(min_sector, sh->dev[dd_idx].sector); + max_sector = min(max_sector, sh->dev[dd_idx].sector); + } + + spin_lock_irq(&conf->device_lock); + + if (!range_ahead_of_reshape(mddev, min_sector, max_sector, + conf->reshape_progress)) + /* mismatch, need to try again */ + ret = true; + + spin_unlock_irq(&conf->device_lock); + + return ret; +} + +static int add_all_stripe_bios(struct r5conf *conf, + struct stripe_request_ctx *ctx, struct stripe_head *sh, + struct bio *bi, int forwrite, int previous) +{ + int dd_idx; + int ret = 1; + + spin_lock_irq(&sh->stripe_lock); + + for (dd_idx = 0; dd_idx < sh->disks; dd_idx++) { + struct r5dev *dev = &sh->dev[dd_idx]; + + if (dd_idx == sh->pd_idx || dd_idx == sh->qd_idx) + continue; + + if (dev->sector < ctx->first_sector || + dev->sector >= ctx->last_sector) + continue; + + if (stripe_bio_overlaps(sh, bi, dd_idx, forwrite)) { + set_bit(R5_Overlap, &dev->flags); + ret = 0; + continue; + } + } + + if (!ret) + goto out; + + for (dd_idx = 0; dd_idx < sh->disks; dd_idx++) { + struct r5dev *dev = &sh->dev[dd_idx]; + + if (dd_idx == sh->pd_idx || dd_idx == sh->qd_idx) + continue; + + if (dev->sector < ctx->first_sector || + dev->sector >= ctx->last_sector) + continue; + + __add_stripe_bio(sh, bi, dd_idx, forwrite, previous); + clear_bit((dev->sector - ctx->first_sector) >> + RAID5_STRIPE_SHIFT(conf), ctx->sectors_to_do); + } + +out: + spin_unlock_irq(&sh->stripe_lock); + return ret; +} + +static enum stripe_result make_stripe_request(struct mddev *mddev, + struct r5conf *conf, struct stripe_request_ctx *ctx, + sector_t logical_sector, struct bio *bi) +{ + const int rw = bio_data_dir(bi); + enum stripe_result ret; + struct stripe_head *sh; + sector_t new_sector; + int previous = 0; + int seq, dd_idx; + + seq = read_seqcount_begin(&conf->gen_lock); + + if (unlikely(conf->reshape_progress != MaxSector)) { + /* + * Spinlock is needed as reshape_progress may be + * 64bit on a 32bit platform, and so it might be + * possible to see a half-updated value + * Of course reshape_progress could change after + * the lock is dropped, so once we get a reference + * to the stripe that we think it is, we will have + * to check again. + */ + spin_lock_irq(&conf->device_lock); + if (ahead_of_reshape(mddev, logical_sector, + conf->reshape_progress)) { + previous = 1; + } else { + if (ahead_of_reshape(mddev, logical_sector, + conf->reshape_safe)) { + spin_unlock_irq(&conf->device_lock); + return STRIPE_SCHEDULE_AND_RETRY; + } + } + spin_unlock_irq(&conf->device_lock); + } + + new_sector = raid5_compute_sector(conf, logical_sector, previous, + &dd_idx, NULL); + pr_debug("raid456: %s, sector %llu logical %llu\n", __func__, + new_sector, logical_sector); + + sh = __raid5_get_active_stripe(conf, ctx, new_sector, previous, + (bi->bi_opf & REQ_RAHEAD), 0); + if (unlikely(!sh)) { + /* cannot get stripe, just give-up */ + bi->bi_status = BLK_STS_IOERR; + return STRIPE_FAIL; + } + + if (unlikely(previous) && + stripe_ahead_of_reshape(mddev, conf, sh)) { + /* + * Expansion moved on while waiting for a stripe. + * Expansion could still move past after this + * test, but as we are holding a reference to + * 'sh', we know that if that happens, + * STRIPE_EXPANDING will get set and the expansion + * won't proceed until we finish with the stripe. + */ + ret = STRIPE_SCHEDULE_AND_RETRY; + goto out_release; + } + + if (read_seqcount_retry(&conf->gen_lock, seq)) { + /* Might have got the wrong stripe_head by accident */ + ret = STRIPE_RETRY; + goto out_release; + } + + if (test_bit(STRIPE_EXPANDING, &sh->state) || + !add_all_stripe_bios(conf, ctx, sh, bi, rw, previous)) { + /* + * Stripe is busy expanding or add failed due to + * overlap. Flush everything and wait a while. + */ + md_wakeup_thread(mddev->thread); + ret = STRIPE_SCHEDULE_AND_RETRY; + goto out_release; + } + + if (stripe_can_batch(sh)) { + stripe_add_to_batch_list(conf, sh, ctx->batch_last); + if (ctx->batch_last) + raid5_release_stripe(ctx->batch_last); + atomic_inc(&sh->count); + ctx->batch_last = sh; + } + + if (ctx->do_flush) { + set_bit(STRIPE_R5C_PREFLUSH, &sh->state); + /* we only need flush for one stripe */ + ctx->do_flush = false; + } + + set_bit(STRIPE_HANDLE, &sh->state); + clear_bit(STRIPE_DELAYED, &sh->state); + if ((!sh->batch_head || sh == sh->batch_head) && + (bi->bi_opf & REQ_SYNC) && + !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) + atomic_inc(&conf->preread_active_stripes); + + release_stripe_plug(mddev, sh); + return STRIPE_SUCCESS; + +out_release: + raid5_release_stripe(sh); + return ret; +} + static bool raid5_make_request(struct mddev *mddev, struct bio * bi) { + DEFINE_WAIT_FUNC(wait, woken_wake_function); struct r5conf *conf = mddev->private; - int dd_idx; - sector_t new_sector; - sector_t logical_sector, last_sector; - struct stripe_head *sh; + sector_t logical_sector; + struct stripe_request_ctx ctx = {}; const int rw = bio_data_dir(bi); - DEFINE_WAIT(w); - bool do_prepare; - bool do_flush = false; + enum stripe_result res; + int s, stripe_cnt; if (unlikely(bi->bi_opf & REQ_PREFLUSH)) { int ret = log_handle_flush_request(conf, bi); @@ -5811,7 +6104,7 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi) * if r5l_handle_flush_request() didn't clear REQ_PREFLUSH, * we need to flush journal device */ - do_flush = bi->bi_opf & REQ_PREFLUSH; + ctx.do_flush = bi->bi_opf & REQ_PREFLUSH; } if (!md_write_start(mddev, bi)) @@ -5835,134 +6128,68 @@ static bool raid5_make_request(struct mddev *mddev, struct bio * bi) } logical_sector = bi->bi_iter.bi_sector & ~((sector_t)RAID5_STRIPE_SECTORS(conf)-1); - last_sector = bio_end_sector(bi); + ctx.first_sector = logical_sector; + ctx.last_sector = bio_end_sector(bi); bi->bi_next = NULL; + stripe_cnt = DIV_ROUND_UP_SECTOR_T(ctx.last_sector - logical_sector, + RAID5_STRIPE_SECTORS(conf)); + bitmap_set(ctx.sectors_to_do, 0, stripe_cnt); + + pr_debug("raid456: %s, logical %llu to %llu\n", __func__, + bi->bi_iter.bi_sector, ctx.last_sector); + /* Bail out if conflicts with reshape and REQ_NOWAIT is set */ if ((bi->bi_opf & REQ_NOWAIT) && (conf->reshape_progress != MaxSector) && - (mddev->reshape_backwards - ? (logical_sector > conf->reshape_progress && logical_sector <= conf->reshape_safe) - : (logical_sector >= conf->reshape_safe && logical_sector < conf->reshape_progress))) { + !ahead_of_reshape(mddev, logical_sector, conf->reshape_progress) && + ahead_of_reshape(mddev, logical_sector, conf->reshape_safe)) { bio_wouldblock_error(bi); if (rw == WRITE) md_write_end(mddev); return true; } md_account_bio(mddev, &bi); - prepare_to_wait(&conf->wait_for_overlap, &w, TASK_UNINTERRUPTIBLE); - for (; logical_sector < last_sector; logical_sector += RAID5_STRIPE_SECTORS(conf)) { - int previous; - int seq; - do_prepare = false; - retry: - seq = read_seqcount_begin(&conf->gen_lock); - previous = 0; - if (do_prepare) - prepare_to_wait(&conf->wait_for_overlap, &w, - TASK_UNINTERRUPTIBLE); - if (unlikely(conf->reshape_progress != MaxSector)) { - /* spinlock is needed as reshape_progress may be - * 64bit on a 32bit platform, and so it might be - * possible to see a half-updated value - * Of course reshape_progress could change after - * the lock is dropped, so once we get a reference - * to the stripe that we think it is, we will have - * to check again. - */ - spin_lock_irq(&conf->device_lock); - if (mddev->reshape_backwards - ? logical_sector < conf->reshape_progress - : logical_sector >= conf->reshape_progress) { - previous = 1; - } else { - if (mddev->reshape_backwards - ? logical_sector < conf->reshape_safe - : logical_sector >= conf->reshape_safe) { - spin_unlock_irq(&conf->device_lock); - schedule(); - do_prepare = true; - goto retry; - } - } - spin_unlock_irq(&conf->device_lock); - } - - new_sector = raid5_compute_sector(conf, logical_sector, - previous, - &dd_idx, NULL); - pr_debug("raid456: raid5_make_request, sector %llu logical %llu\n", - (unsigned long long)new_sector, - (unsigned long long)logical_sector); - - sh = raid5_get_active_stripe(conf, new_sector, previous, - (bi->bi_opf & REQ_RAHEAD), 0); - if (sh) { - if (unlikely(previous)) { - /* expansion might have moved on while waiting for a - * stripe, so we must do the range check again. - * Expansion could still move past after this - * test, but as we are holding a reference to - * 'sh', we know that if that happens, - * STRIPE_EXPANDING will get set and the expansion - * won't proceed until we finish with the stripe. - */ - int must_retry = 0; - spin_lock_irq(&conf->device_lock); - if (mddev->reshape_backwards - ? logical_sector >= conf->reshape_progress - : logical_sector < conf->reshape_progress) - /* mismatch, need to try again */ - must_retry = 1; - spin_unlock_irq(&conf->device_lock); - if (must_retry) { - raid5_release_stripe(sh); - schedule(); - do_prepare = true; - goto retry; - } - } - if (read_seqcount_retry(&conf->gen_lock, seq)) { - /* Might have got the wrong stripe_head - * by accident - */ - raid5_release_stripe(sh); - goto retry; - } - - if (test_bit(STRIPE_EXPANDING, &sh->state) || - !add_stripe_bio(sh, bi, dd_idx, rw, previous)) { - /* Stripe is busy expanding or - * add failed due to overlap. Flush everything - * and wait a while - */ - md_wakeup_thread(mddev->thread); - raid5_release_stripe(sh); - schedule(); - do_prepare = true; - goto retry; - } - if (do_flush) { - set_bit(STRIPE_R5C_PREFLUSH, &sh->state); - /* we only need flush for one stripe */ - do_flush = false; - } - - set_bit(STRIPE_HANDLE, &sh->state); - clear_bit(STRIPE_DELAYED, &sh->state); - if ((!sh->batch_head || sh == sh->batch_head) && - (bi->bi_opf & REQ_SYNC) && - !test_and_set_bit(STRIPE_PREREAD_ACTIVE, &sh->state)) - atomic_inc(&conf->preread_active_stripes); - release_stripe_plug(mddev, sh); - } else { - /* cannot get stripe for read-ahead, just give-up */ - bi->bi_status = BLK_STS_IOERR; + add_wait_queue(&conf->wait_for_overlap, &wait); + while (1) { + res = make_stripe_request(mddev, conf, &ctx, logical_sector, + bi); + if (res == STRIPE_FAIL) break; + + if (res == STRIPE_RETRY) + continue; + + if (res == STRIPE_SCHEDULE_AND_RETRY) { + /* + * Must release the reference to batch_last before + * scheduling and waiting for work to be done, + * otherwise the batch_last stripe head could prevent + * raid5_activate_delayed() from making progress + * and thus deadlocking. + */ + if (ctx.batch_last) { + raid5_release_stripe(ctx.batch_last); + ctx.batch_last = NULL; + } + + wait_woken(&wait, TASK_UNINTERRUPTIBLE, + MAX_SCHEDULE_TIMEOUT); + continue; } + + s = find_first_bit(ctx.sectors_to_do, stripe_cnt); + if (s == stripe_cnt) + break; + + logical_sector = ctx.first_sector + + (s << RAID5_STRIPE_SHIFT(conf)); } - finish_wait(&conf->wait_for_overlap, &w); + remove_wait_queue(&conf->wait_for_overlap, &wait); + + if (ctx.batch_last) + raid5_release_stripe(ctx.batch_last); if (rw == WRITE) md_write_end(mddev); @@ -7815,7 +8042,15 @@ static int raid5_run(struct mddev *mddev) mddev->queue->limits.discard_granularity < stripe) blk_queue_max_discard_sectors(mddev->queue, 0); - blk_queue_max_hw_sectors(mddev->queue, UINT_MAX); + /* + * Requests require having a bitmap for each stripe. + * Limit the max sectors based on this. + */ + blk_queue_max_hw_sectors(mddev->queue, + RAID5_MAX_REQ_STRIPES << RAID5_STRIPE_SHIFT(conf)); + + /* No restrictions on the number of segments in the request */ + blk_queue_max_segments(mddev->queue, USHRT_MAX); } if (log_init(conf, journal_dev, raid5_has_ppl(conf))) @@ -8066,8 +8301,7 @@ static int raid5_add_disk(struct mddev *mddev, struct md_rdev *rdev) * find the disk ... but prefer rdev->saved_raid_disk * if possible. */ - if (rdev->saved_raid_disk >= 0 && - rdev->saved_raid_disk >= first && + if (rdev->saved_raid_disk >= first && rdev->saved_raid_disk <= last && conf->disks[rdev->saved_raid_disk].rdev == NULL) first = rdev->saved_raid_disk; @@ -8704,8 +8938,11 @@ static int raid5_change_consistency_policy(struct mddev *mddev, const char *buf) err = log_init(conf, NULL, true); if (!err) { err = resize_stripes(conf, conf->pool_size); - if (err) + if (err) { + mddev_suspend(mddev); log_exit(conf); + mddev_resume(mddev); + } } } else err = -EINVAL; diff --git a/drivers/md/raid5.h b/drivers/md/raid5.h index 638d29863503..a5082bed83c8 100644 --- a/drivers/md/raid5.h +++ b/drivers/md/raid5.h @@ -812,7 +812,7 @@ extern sector_t raid5_compute_sector(struct r5conf *conf, sector_t r_sector, struct stripe_head *sh); extern struct stripe_head * raid5_get_active_stripe(struct r5conf *conf, sector_t sector, - int previous, int noblock, int noquiesce); + bool previous, bool noblock, bool noquiesce); extern int raid5_calc_degraded(struct r5conf *conf); extern int r5c_journal_mode_set(struct mddev *mddev, int journal_mode); #endif diff --git a/drivers/media/i2c/adv7343_regs.h b/drivers/media/i2c/adv7343_regs.h index 2f04ce4b9118..e0357e6272e3 100644 --- a/drivers/media/i2c/adv7343_regs.h +++ b/drivers/media/i2c/adv7343_regs.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ADV7343 encoder related structure and register definitions * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef ADV7343_REGS_H diff --git a/drivers/media/i2c/adv7393_regs.h b/drivers/media/i2c/adv7393_regs.h index 78968330f0be..6eb8732b5324 100644 --- a/drivers/media/i2c/adv7393_regs.h +++ b/drivers/media/i2c/adv7393_regs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ADV7393 encoder related structure and register definitions * @@ -7,15 +8,6 @@ * Based on ADV7343 driver, * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef ADV7393_REGS_H diff --git a/drivers/media/platform/ti/davinci/vpif.h b/drivers/media/platform/ti/davinci/vpif.h index c6d1d890478a..651943e3e375 100644 --- a/drivers/media/platform/ti/davinci/vpif.h +++ b/drivers/media/platform/ti/davinci/vpif.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * VPIF header file * * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef VPIF_H @@ -685,4 +677,3 @@ struct vpif_params { }; #endif /* End of #ifndef VPIF_H */ - diff --git a/drivers/media/platform/ti/davinci/vpif_display.h b/drivers/media/platform/ti/davinci/vpif_display.h index f98062e79167..f27474e0fc36 100644 --- a/drivers/media/platform/ti/davinci/vpif_display.h +++ b/drivers/media/platform/ti/davinci/vpif_display.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * VPIF display header file * * Copyright (C) 2009 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef VPIF_DISPLAY_H diff --git a/drivers/memory/ti-emif-sram-pm.S b/drivers/memory/ti-emif-sram-pm.S index d1c83bd5b98e..9bcac35c3304 100644 --- a/drivers/memory/ti-emif-sram-pm.S +++ b/drivers/memory/ti-emif-sram-pm.S @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Low level PM code for TI EMIF * * Copyright (C) 2016-2017 Texas Instruments Incorporated - http://www.ti.com/ * Dave Gerlach - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/memstick/core/ms_block.c b/drivers/memstick/core/ms_block.c index ed9a683b3ca8..ba8414519515 100644 --- a/drivers/memstick/core/ms_block.c +++ b/drivers/memstick/core/ms_block.c @@ -1341,17 +1341,17 @@ static int msb_ftl_initialize(struct msb_data *msb) msb->zone_count = msb->block_count / MS_BLOCKS_IN_ZONE; msb->logical_block_count = msb->zone_count * 496 - 2; - msb->used_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL); - msb->erased_blocks_bitmap = kzalloc(msb->block_count / 8, GFP_KERNEL); + msb->used_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); + msb->erased_blocks_bitmap = bitmap_zalloc(msb->block_count, GFP_KERNEL); msb->lba_to_pba_table = kmalloc_array(msb->logical_block_count, sizeof(u16), GFP_KERNEL); if (!msb->used_blocks_bitmap || !msb->lba_to_pba_table || !msb->erased_blocks_bitmap) { - kfree(msb->used_blocks_bitmap); + bitmap_free(msb->used_blocks_bitmap); + bitmap_free(msb->erased_blocks_bitmap); kfree(msb->lba_to_pba_table); - kfree(msb->erased_blocks_bitmap); return -ENOMEM; } @@ -1946,7 +1946,8 @@ static DEFINE_MUTEX(msb_disk_lock); /* protects against races in open/release */ static void msb_data_clear(struct msb_data *msb) { kfree(msb->boot_page); - kfree(msb->used_blocks_bitmap); + bitmap_free(msb->used_blocks_bitmap); + bitmap_free(msb->erased_blocks_bitmap); kfree(msb->lba_to_pba_table); kfree(msb->cache); msb->card = NULL; @@ -2243,8 +2244,8 @@ static int msb_resume(struct memstick_dev *card) goto out; if (msb->block_count != new_msb->block_count || - memcmp(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap, - msb->block_count / 8)) + !bitmap_equal(msb->used_blocks_bitmap, new_msb->used_blocks_bitmap, + msb->block_count)) goto out; card_dead = false; diff --git a/drivers/message/fusion/mptspi.c b/drivers/message/fusion/mptspi.c index 388675cc1765..62089a8caa2f 100644 --- a/drivers/message/fusion/mptspi.c +++ b/drivers/message/fusion/mptspi.c @@ -101,7 +101,7 @@ static u8 mptspiInternalCtx = MPT_MAX_PROTOCOL_DRIVERS; /* Used only for interna * @target: per target private data * @sdev: SCSI device * - * Update the target negotiation parameters based on the the Inquiry + * Update the target negotiation parameters based on the Inquiry * data, adapter capabilities, and NVRAM settings. **/ static void diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 3b59456f5545..9566341de470 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -572,6 +572,7 @@ config LPC_ICH tristate "Intel ICH LPC" depends on PCI select MFD_CORE + select P2SB if X86 help The LPC bridge function of the Intel ICH provides support for many functional units. This driver provides needed support for diff --git a/drivers/mfd/atmel-smc.c b/drivers/mfd/atmel-smc.c index d96f1d689e7f..f3bad2b52f17 100644 --- a/drivers/mfd/atmel-smc.c +++ b/drivers/mfd/atmel-smc.c @@ -240,7 +240,7 @@ EXPORT_SYMBOL_GPL(atmel_smc_cs_conf_set_cycle); * @conf: the SMC CS conf to apply * * Applies an SMC CS configuration. - * Only valid on at91sam9/avr32 SoCs. + * Only valid on at91sam9 SoCs. */ void atmel_smc_cs_conf_apply(struct regmap *regmap, int cs, const struct atmel_smc_cs_conf *conf) @@ -281,7 +281,7 @@ EXPORT_SYMBOL_GPL(atmel_hsmc_cs_conf_apply); * @conf: the SMC CS conf object to store the current conf * * Retrieve the SMC CS configuration. - * Only valid on at91sam9/avr32 SoCs. + * Only valid on at91sam9 SoCs. */ void atmel_smc_cs_conf_get(struct regmap *regmap, int cs, struct atmel_smc_cs_conf *conf) diff --git a/drivers/mfd/cros_ec_dev.c b/drivers/mfd/cros_ec_dev.c index 596731caf407..02d4271dfe06 100644 --- a/drivers/mfd/cros_ec_dev.c +++ b/drivers/mfd/cros_ec_dev.c @@ -250,8 +250,8 @@ static int ec_device_probe(struct platform_device *pdev) * The PCHG device cannot be detected by sending EC_FEATURE_GET_CMD, but * it can be detected by querying the number of peripheral chargers. */ - retval = cros_ec_command(ec->ec_dev, 0, EC_CMD_PCHG_COUNT, NULL, 0, - &pchg_count, sizeof(pchg_count)); + retval = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_PCHG_COUNT, NULL, 0, + &pchg_count, sizeof(pchg_count)); if (retval >= 0 && pchg_count.port_count) { retval = mfd_add_hotplug_devices(ec->dev, cros_ec_pchg_cells, diff --git a/drivers/mfd/lp873x.c b/drivers/mfd/lp873x.c index 858c9e0a49a4..b6166dec492d 100644 --- a/drivers/mfd/lp873x.c +++ b/drivers/mfd/lp873x.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ * * Author: Keerthy - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/mfd/lpc_ich.c b/drivers/mfd/lpc_ich.c index 9ffab9aafd81..650951f89f1c 100644 --- a/drivers/mfd/lpc_ich.c +++ b/drivers/mfd/lpc_ich.c @@ -8,7 +8,8 @@ * Configuration Registers. * * This driver is derived from lpc_sch. - + * + * Copyright (c) 2017, 2021-2022 Intel Corporation * Copyright (c) 2011 Extreme Engineering Solution, Inc. * Author: Aaron Sierra * @@ -42,9 +43,11 @@ #include #include #include +#include #include #include #include +#include #define ACPIBASE 0x40 #define ACPIBASE_GPE_OFF 0x28 @@ -71,8 +74,6 @@ #define BCR 0xdc #define BCR_WPD BIT(0) -#define SPIBASE_APL_SZ 4096 - #define GPIOBASE_ICH0 0x58 #define GPIOCTRL_ICH0 0x5C #define GPIOBASE_ICH6 0x48 @@ -143,6 +144,73 @@ static struct mfd_cell lpc_ich_gpio_cell = { .ignore_resource_conflicts = true, }; +#define APL_GPIO_NORTH 0 +#define APL_GPIO_NORTHWEST 1 +#define APL_GPIO_WEST 2 +#define APL_GPIO_SOUTHWEST 3 +#define APL_GPIO_NR_DEVICES 4 + +/* Offset data for Apollo Lake GPIO controllers */ +static resource_size_t apl_gpio_offsets[APL_GPIO_NR_DEVICES] = { + [APL_GPIO_NORTH] = 0xc50000, + [APL_GPIO_NORTHWEST] = 0xc40000, + [APL_GPIO_WEST] = 0xc70000, + [APL_GPIO_SOUTHWEST] = 0xc00000, +}; + +#define APL_GPIO_RESOURCE_SIZE 0x1000 + +#define APL_GPIO_IRQ 14 + +static struct resource apl_gpio_resources[APL_GPIO_NR_DEVICES][2] = { + [APL_GPIO_NORTH] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, + [APL_GPIO_NORTHWEST] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, + [APL_GPIO_WEST] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, + [APL_GPIO_SOUTHWEST] = { + DEFINE_RES_MEM(0, 0), + DEFINE_RES_IRQ(APL_GPIO_IRQ), + }, +}; + +static const struct mfd_cell apl_gpio_devices[APL_GPIO_NR_DEVICES] = { + [APL_GPIO_NORTH] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_NORTH, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTH]), + .resources = apl_gpio_resources[APL_GPIO_NORTH], + .ignore_resource_conflicts = true, + }, + [APL_GPIO_NORTHWEST] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_NORTHWEST, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_NORTHWEST]), + .resources = apl_gpio_resources[APL_GPIO_NORTHWEST], + .ignore_resource_conflicts = true, + }, + [APL_GPIO_WEST] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_WEST, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_WEST]), + .resources = apl_gpio_resources[APL_GPIO_WEST], + .ignore_resource_conflicts = true, + }, + [APL_GPIO_SOUTHWEST] = { + .name = "apollolake-pinctrl", + .id = APL_GPIO_SOUTHWEST, + .num_resources = ARRAY_SIZE(apl_gpio_resources[APL_GPIO_SOUTHWEST]), + .resources = apl_gpio_resources[APL_GPIO_SOUTHWEST], + .ignore_resource_conflicts = true, + }, +}; static struct mfd_cell lpc_ich_spi_cell = { .name = "intel-spi", @@ -1086,6 +1154,34 @@ wdt_done: return ret; } +static int lpc_ich_init_pinctrl(struct pci_dev *dev) +{ + struct resource base; + unsigned int i; + int ret; + + /* Check, if GPIO has been exported as an ACPI device */ + if (acpi_dev_present("INT3452", NULL, -1)) + return -EEXIST; + + ret = p2sb_bar(dev->bus, 0, &base); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(apl_gpio_devices); i++) { + struct resource *mem = &apl_gpio_resources[i][0]; + resource_size_t offset = apl_gpio_offsets[i]; + + /* Fill MEM resource */ + mem->start = base.start + offset; + mem->end = base.start + offset + APL_GPIO_RESOURCE_SIZE - 1; + mem->flags = base.flags; + } + + return mfd_add_devices(&dev->dev, 0, apl_gpio_devices, + ARRAY_SIZE(apl_gpio_devices), NULL, 0, NULL); +} + static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data) { u32 val; @@ -1100,35 +1196,32 @@ static bool lpc_ich_byt_set_writeable(void __iomem *base, void *data) return val & BYT_BCR_WPD; } -static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data) +static bool lpc_ich_set_writeable(struct pci_bus *bus, unsigned int devfn) { - struct pci_dev *pdev = data; u32 bcr; - pci_read_config_dword(pdev, BCR, &bcr); + pci_bus_read_config_dword(bus, devfn, BCR, &bcr); if (!(bcr & BCR_WPD)) { bcr |= BCR_WPD; - pci_write_config_dword(pdev, BCR, bcr); - pci_read_config_dword(pdev, BCR, &bcr); + pci_bus_write_config_dword(bus, devfn, BCR, bcr); + pci_bus_read_config_dword(bus, devfn, BCR, &bcr); } return bcr & BCR_WPD; } +static bool lpc_ich_lpt_set_writeable(void __iomem *base, void *data) +{ + struct pci_dev *pdev = data; + + return lpc_ich_set_writeable(pdev->bus, pdev->devfn); +} + static bool lpc_ich_bxt_set_writeable(void __iomem *base, void *data) { - unsigned int spi = PCI_DEVFN(13, 2); - struct pci_bus *bus = data; - u32 bcr; + struct pci_dev *pdev = data; - pci_bus_read_config_dword(bus, spi, BCR, &bcr); - if (!(bcr & BCR_WPD)) { - bcr |= BCR_WPD; - pci_bus_write_config_dword(bus, spi, BCR, bcr); - pci_bus_read_config_dword(bus, spi, BCR, &bcr); - } - - return bcr & BCR_WPD; + return lpc_ich_set_writeable(pdev->bus, PCI_DEVFN(13, 2)); } static int lpc_ich_init_spi(struct pci_dev *dev) @@ -1137,6 +1230,7 @@ static int lpc_ich_init_spi(struct pci_dev *dev) struct resource *res = &intel_spi_res[0]; struct intel_spi_boardinfo *info; u32 spi_base, rcba; + int ret; info = devm_kzalloc(&dev->dev, sizeof(*info), GFP_KERNEL); if (!info) @@ -1167,30 +1261,19 @@ static int lpc_ich_init_spi(struct pci_dev *dev) } break; - case INTEL_SPI_BXT: { - unsigned int p2sb = PCI_DEVFN(13, 0); - unsigned int spi = PCI_DEVFN(13, 2); - struct pci_bus *bus = dev->bus; - + case INTEL_SPI_BXT: /* * The P2SB is hidden by BIOS and we need to unhide it in * order to read BAR of the SPI flash device. Once that is * done we hide it again. */ - pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x0); - pci_bus_read_config_dword(bus, spi, PCI_BASE_ADDRESS_0, - &spi_base); - if (spi_base != ~0) { - res->start = spi_base & 0xfffffff0; - res->end = res->start + SPIBASE_APL_SZ - 1; + ret = p2sb_bar(dev->bus, PCI_DEVFN(13, 2), res); + if (ret) + return ret; - info->set_writeable = lpc_ich_bxt_set_writeable; - info->data = bus; - } - - pci_bus_write_config_byte(bus, p2sb, 0xe1, 0x1); + info->set_writeable = lpc_ich_bxt_set_writeable; + info->data = dev; break; - } default: return -EINVAL; @@ -1249,6 +1332,12 @@ static int lpc_ich_probe(struct pci_dev *dev, cell_added = true; } + if (priv->chipset == LPC_APL) { + ret = lpc_ich_init_pinctrl(dev); + if (!ret) + cell_added = true; + } + if (lpc_chipset_info[priv->chipset].spi_type) { ret = lpc_ich_init_spi(dev); if (!ret) diff --git a/drivers/mfd/tps65086.c b/drivers/mfd/tps65086.c index 3bd5728844a0..cbae9777a24e 100644 --- a/drivers/mfd/tps65086.c +++ b/drivers/mfd/tps65086.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65912 driver */ diff --git a/drivers/mfd/tps65217.c b/drivers/mfd/tps65217.c index 8027b0a9e14f..8e8da204a02e 100644 --- a/drivers/mfd/tps65217.c +++ b/drivers/mfd/tps65217.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps65217.c * * TPS65217 chip family multi-function driver * * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/mfd/tps65218.c b/drivers/mfd/tps65218.c index 167e9fc308ef..49bb8fd168f8 100644 --- a/drivers/mfd/tps65218.c +++ b/drivers/mfd/tps65218.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Driver for TPS65218 Integrated power management chipsets * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include diff --git a/drivers/mfd/tps65912-core.c b/drivers/mfd/tps65912-core.c index c282a05e7146..7d994b8a5965 100644 --- a/drivers/mfd/tps65912-core.c +++ b/drivers/mfd/tps65912-core.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Core functions for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera */ diff --git a/drivers/mfd/tps65912-i2c.c b/drivers/mfd/tps65912-i2c.c index 06eb2784d322..afb7f7d97dc0 100644 --- a/drivers/mfd/tps65912-i2c.c +++ b/drivers/mfd/tps65912-i2c.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * I2C access driver for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera */ diff --git a/drivers/mfd/tps65912-spi.c b/drivers/mfd/tps65912-spi.c index bba38fbc781d..9e976f9c6bbe 100644 --- a/drivers/mfd/tps65912-spi.c +++ b/drivers/mfd/tps65912-spi.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SPI access driver for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera */ diff --git a/drivers/mfd/ucb1400_core.c b/drivers/mfd/ucb1400_core.c index 8c3832a58ef6..ac1d18039568 100644 --- a/drivers/mfd/ucb1400_core.c +++ b/drivers/mfd/ucb1400_core.c @@ -72,11 +72,9 @@ static int ucb1400_core_probe(struct device *dev) /* GPIO */ ucb_gpio.ac97 = ac97; - if (pdata) { - ucb_gpio.gpio_setup = pdata->gpio_setup; - ucb_gpio.gpio_teardown = pdata->gpio_teardown; + if (pdata) ucb_gpio.gpio_offset = pdata->gpio_offset; - } + ucb->ucb1400_gpio = platform_device_alloc("ucb1400_gpio", -1); if (!ucb->ucb1400_gpio) { err = -ENOMEM; diff --git a/drivers/misc/atmel-ssc.c b/drivers/misc/atmel-ssc.c index 69f9b0336410..7f9f562d6433 100644 --- a/drivers/misc/atmel-ssc.c +++ b/drivers/misc/atmel-ssc.c @@ -276,7 +276,7 @@ static struct platform_driver ssc_driver = { }; module_platform_driver(ssc_driver); -MODULE_AUTHOR("Hans-Christian Egtvedt "); -MODULE_DESCRIPTION("SSC driver for Atmel AVR32 and AT91"); +MODULE_AUTHOR("Hans-Christian Noren Egtvedt "); +MODULE_DESCRIPTION("SSC driver for Atmel AT91"); MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:ssc"); diff --git a/drivers/misc/sram-exec.c b/drivers/misc/sram-exec.c index 6cc31789b38d..a948e95d4375 100644 --- a/drivers/misc/sram-exec.c +++ b/drivers/misc/sram-exec.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * SRAM protect-exec region helper functions * * Copyright (C) 2017 Texas Instruments Incorporated - https://www.ti.com/ * Dave Gerlach - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/mmc/core/block.c b/drivers/mmc/core/block.c index e08e22f0a7c5..ce89611a136e 100644 --- a/drivers/mmc/core/block.c +++ b/drivers/mmc/core/block.c @@ -176,7 +176,7 @@ static inline int mmc_blk_part_switch(struct mmc_card *card, unsigned int part_type); static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, struct mmc_card *card, - int disable_multi, + int recovery_mode, struct mmc_queue *mq); static void mmc_blk_hsq_req_done(struct mmc_request *mrq); @@ -1302,7 +1302,7 @@ static void mmc_blk_eval_resp_error(struct mmc_blk_request *brq) } static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, - int disable_multi, bool *do_rel_wr_p, + int recovery_mode, bool *do_rel_wr_p, bool *do_data_tag_p) { struct mmc_blk_data *md = mq->blkdata; @@ -1368,12 +1368,12 @@ static void mmc_blk_data_prep(struct mmc_queue *mq, struct mmc_queue_req *mqrq, brq->data.blocks--; /* - * After a read error, we redo the request one sector + * After a read error, we redo the request one (native) sector * at a time in order to accurately determine which * sectors can be read successfully. */ - if (disable_multi) - brq->data.blocks = 1; + if (recovery_mode) + brq->data.blocks = queue_physical_block_size(mq->queue) >> 9; /* * Some controllers have HW issues while operating @@ -1590,7 +1590,7 @@ static int mmc_blk_cqe_issue_rw_rq(struct mmc_queue *mq, struct request *req) static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, struct mmc_card *card, - int disable_multi, + int recovery_mode, struct mmc_queue *mq) { u32 readcmd, writecmd; @@ -1599,7 +1599,7 @@ static void mmc_blk_rw_rq_prep(struct mmc_queue_req *mqrq, struct mmc_blk_data *md = mq->blkdata; bool do_rel_wr, do_data_tag; - mmc_blk_data_prep(mq, mqrq, disable_multi, &do_rel_wr, &do_data_tag); + mmc_blk_data_prep(mq, mqrq, recovery_mode, &do_rel_wr, &do_data_tag); brq->mrq.cmd = &brq->cmd; @@ -1690,7 +1690,7 @@ static int mmc_blk_fix_state(struct mmc_card *card, struct request *req) #define MMC_READ_SINGLE_RETRIES 2 -/* Single sector read during recovery */ +/* Single (native) sector read during recovery */ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req) { struct mmc_queue_req *mqrq = req_to_mmc_queue_req(req); @@ -1698,6 +1698,7 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req) struct mmc_card *card = mq->card; struct mmc_host *host = card->host; blk_status_t error = BLK_STS_OK; + size_t bytes_per_read = queue_physical_block_size(mq->queue); do { u32 status; @@ -1732,13 +1733,13 @@ static void mmc_blk_read_single(struct mmc_queue *mq, struct request *req) else error = BLK_STS_OK; - } while (blk_update_request(req, error, 512)); + } while (blk_update_request(req, error, bytes_per_read)); return; error_exit: mrq->data->bytes_xfered = 0; - blk_update_request(req, BLK_STS_IOERR, 512); + blk_update_request(req, BLK_STS_IOERR, bytes_per_read); /* Let it try the remaining request again */ if (mqrq->retries > MMC_MAX_RETRIES - 1) mqrq->retries = MMC_MAX_RETRIES - 1; @@ -1879,10 +1880,9 @@ static void mmc_blk_mq_rw_recovery(struct mmc_queue *mq, struct request *req) return; } - /* FIXME: Missing single sector read for large sector size */ - if (!mmc_large_sector(card) && rq_data_dir(req) == READ && - brq->data.blocks > 1) { - /* Read one sector at a time */ + if (rq_data_dir(req) == READ && brq->data.blocks > + queue_physical_block_size(mq->queue) >> 9) { + /* Read one (native) sector at a time */ mmc_blk_read_single(mq, req); return; } @@ -2988,7 +2988,7 @@ static int mmc_blk_probe(struct mmc_card *card) * Don't enable runtime PM for SD-combo cards here. Leave that * decision to be taken during the SDIO init sequence instead. */ - if (card->type != MMC_TYPE_SD_COMBO) { + if (!mmc_card_sd_combo(card)) { pm_runtime_set_active(&card->dev); pm_runtime_enable(&card->dev); } @@ -3015,7 +3015,7 @@ static void mmc_blk_remove(struct mmc_card *card) mmc_blk_part_switch(card, md->part_type); mmc_release_host(card->host); } - if (card->type != MMC_TYPE_SD_COMBO) + if (!mmc_card_sd_combo(card)) pm_runtime_disable(&card->dev); pm_runtime_put_noidle(&card->dev); mmc_blk_remove_req(md); diff --git a/drivers/mmc/core/bus.c b/drivers/mmc/core/bus.c index 58a60afa650b..d8762fa3d5cd 100644 --- a/drivers/mmc/core/bus.c +++ b/drivers/mmc/core/bus.c @@ -85,7 +85,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) return retval; } - if (card->type == MMC_TYPE_SDIO || card->type == MMC_TYPE_SD_COMBO) { + if (mmc_card_sdio(card) || mmc_card_sd_combo(card)) { retval = add_uevent_var(env, "SDIO_ID=%04X:%04X", card->cis.vendor, card->cis.device); if (retval) @@ -107,7 +107,7 @@ mmc_bus_uevent(struct device *dev, struct kobj_uevent_env *env) * SDIO (non-combo) cards are not handled by mmc_block driver and do not * have accessible CID register which used by mmc_card_name() function. */ - if (card->type == MMC_TYPE_SDIO) + if (mmc_card_sdio(card)) return 0; retval = add_uevent_var(env, "MMC_NAME=%s", mmc_card_name(card)); diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index 4b70cbfc6d5d..ef53a2578824 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -943,9 +943,11 @@ int mmc_execute_tuning(struct mmc_card *card) } /* Only print error when we don't check for card removal */ - if (!host->detect_change) + if (!host->detect_change) { pr_err("%s: tuning execution failed: %d\n", mmc_hostname(host), err); + mmc_debugfs_err_stats_inc(host, MMC_ERR_TUNING); + } return err; } @@ -2244,6 +2246,12 @@ void mmc_rescan(struct work_struct *work) if (freqs[i] <= host->f_min) break; } + + /* + * Ignore the command timeout errors observed during + * the card init as those are excepted. + */ + host->err_stats[MMC_ERR_CMD_TIMEOUT] = 0; mmc_release_host(host); out: diff --git a/drivers/mmc/core/debugfs.c b/drivers/mmc/core/debugfs.c index 3fdbc801e64a..fe6808771bc7 100644 --- a/drivers/mmc/core/debugfs.c +++ b/drivers/mmc/core/debugfs.c @@ -223,6 +223,81 @@ static int mmc_clock_opt_set(void *data, u64 val) DEFINE_DEBUGFS_ATTRIBUTE(mmc_clock_fops, mmc_clock_opt_get, mmc_clock_opt_set, "%llu\n"); +static int mmc_err_state_get(void *data, u64 *val) +{ + struct mmc_host *host = data; + int i; + + if (!host) + return -EINVAL; + + *val = 0; + for (i = 0; i < MMC_ERR_MAX; i++) { + if (host->err_stats[i]) { + *val = 1; + break; + } + } + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(mmc_err_state, mmc_err_state_get, NULL, "%llu\n"); + +static int mmc_err_stats_show(struct seq_file *file, void *data) +{ + struct mmc_host *host = (struct mmc_host *)file->private; + const char *desc[MMC_ERR_MAX] = { + [MMC_ERR_CMD_TIMEOUT] = "Command Timeout Occurred", + [MMC_ERR_CMD_CRC] = "Command CRC Errors Occurred", + [MMC_ERR_DAT_TIMEOUT] = "Data Timeout Occurred", + [MMC_ERR_DAT_CRC] = "Data CRC Errors Occurred", + [MMC_ERR_AUTO_CMD] = "Auto-Cmd Error Occurred", + [MMC_ERR_ADMA] = "ADMA Error Occurred", + [MMC_ERR_TUNING] = "Tuning Error Occurred", + [MMC_ERR_CMDQ_RED] = "CMDQ RED Errors", + [MMC_ERR_CMDQ_GCE] = "CMDQ GCE Errors", + [MMC_ERR_CMDQ_ICCE] = "CMDQ ICCE Errors", + [MMC_ERR_REQ_TIMEOUT] = "Request Timedout", + [MMC_ERR_CMDQ_REQ_TIMEOUT] = "CMDQ Request Timedout", + [MMC_ERR_ICE_CFG] = "ICE Config Errors", + [MMC_ERR_CTRL_TIMEOUT] = "Controller Timedout errors", + [MMC_ERR_UNEXPECTED_IRQ] = "Unexpected IRQ errors", + }; + int i; + + for (i = 0; i < MMC_ERR_MAX; i++) { + if (desc[i]) + seq_printf(file, "# %s:\t %d\n", + desc[i], host->err_stats[i]); + } + + return 0; +} + +static int mmc_err_stats_open(struct inode *inode, struct file *file) +{ + return single_open(file, mmc_err_stats_show, inode->i_private); +} + +static ssize_t mmc_err_stats_write(struct file *filp, const char __user *ubuf, + size_t cnt, loff_t *ppos) +{ + struct mmc_host *host = filp->f_mapping->host->i_private; + + pr_debug("%s: Resetting MMC error statistics\n", __func__); + memset(host->err_stats, 0, sizeof(host->err_stats)); + + return cnt; +} + +static const struct file_operations mmc_err_stats_fops = { + .open = mmc_err_stats_open, + .read = seq_read, + .write = mmc_err_stats_write, + .release = single_release, +}; + void mmc_add_host_debugfs(struct mmc_host *host) { struct dentry *root; @@ -236,6 +311,11 @@ void mmc_add_host_debugfs(struct mmc_host *host) debugfs_create_file_unsafe("clock", S_IRUSR | S_IWUSR, root, host, &mmc_clock_fops); + debugfs_create_file_unsafe("err_state", 0600, root, host, + &mmc_err_state); + debugfs_create_file("err_stats", 0600, root, host, + &mmc_err_stats_fops); + #ifdef CONFIG_FAIL_MMC_REQUEST if (fail_request) setup_fault_attr(&fail_default_attr, fail_request); diff --git a/drivers/mmc/core/host.c b/drivers/mmc/core/host.c index 2ed2b4d5e5a5..0fd91f749b3a 100644 --- a/drivers/mmc/core/host.c +++ b/drivers/mmc/core/host.c @@ -599,7 +599,7 @@ static int mmc_validate_host_caps(struct mmc_host *host) } if (caps2 & (MMC_CAP2_HS400_ES | MMC_CAP2_HS400) && - !(caps & MMC_CAP_8_BIT_DATA)) { + !(caps & MMC_CAP_8_BIT_DATA) && !(caps2 & MMC_CAP2_NO_MMC)) { dev_warn(dev, "drop HS400 support since no 8-bit bus\n"); host->caps2 = caps2 & ~MMC_CAP2_HS400_ES & ~MMC_CAP2_HS400; } diff --git a/drivers/mmc/core/quirks.h b/drivers/mmc/core/quirks.h index f879dc63d936..be4393988086 100644 --- a/drivers/mmc/core/quirks.h +++ b/drivers/mmc/core/quirks.h @@ -163,8 +163,10 @@ static inline bool mmc_fixup_of_compatible_match(struct mmc_card *card, struct device_node *np; for_each_child_of_node(mmc_dev(card->host)->of_node, np) { - if (of_device_is_compatible(np, compatible)) + if (of_device_is_compatible(np, compatible)) { + of_node_put(np); return true; + } } return false; diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c index c5f1df6ce4c0..cee4c0b59f43 100644 --- a/drivers/mmc/core/sd.c +++ b/drivers/mmc/core/sd.c @@ -793,7 +793,7 @@ static umode_t sd_std_is_visible(struct kobject *kobj, struct attribute *attr, attr == &dev_attr_info2.attr || attr == &dev_attr_info3.attr || attr == &dev_attr_info4.attr - ) && card->type != MMC_TYPE_SD_COMBO) + ) &&!mmc_card_sd_combo(card)) return 0; return attr->mode; @@ -870,7 +870,7 @@ try_again: * the CCS bit is set as well. We deliberately deviate from the spec in * regards to this, which allows UHS-I to be supported for SDSC cards. */ - if (!mmc_host_is_spi(host) && rocr && (*rocr & 0x01000000)) { + if (!mmc_host_is_spi(host) && rocr && (*rocr & SD_ROCR_S18A)) { err = mmc_set_uhs_voltage(host, pocr); if (err == -EAGAIN) { retries--; diff --git a/drivers/mmc/core/sdio.c b/drivers/mmc/core/sdio.c index 25799accf8a0..0b682a31cd3e 100644 --- a/drivers/mmc/core/sdio.c +++ b/drivers/mmc/core/sdio.c @@ -226,6 +226,20 @@ static int sdio_read_cccr(struct mmc_card *card, u32 ocr) card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C; if (data & SDIO_DRIVE_SDTD) card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D; + + ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_INTERRUPT_EXT, 0, &data); + if (ret) + goto out; + + if (data & SDIO_INTERRUPT_EXT_SAI) { + data |= SDIO_INTERRUPT_EXT_EAI; + ret = mmc_io_rw_direct(card, 1, 0, SDIO_CCCR_INTERRUPT_EXT, + data, NULL); + if (ret) + goto out; + + card->cccr.enable_async_irq = 1; + } } /* if no uhs mode ensure we check for high speed */ @@ -335,7 +349,7 @@ static int sdio_disable_4bit_bus(struct mmc_card *card) { int err; - if (card->type == MMC_TYPE_SDIO) + if (mmc_card_sdio(card)) goto out; if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) @@ -360,7 +374,7 @@ static int sdio_enable_4bit_bus(struct mmc_card *card) err = sdio_enable_wide(card); if (err <= 0) return err; - if (card->type == MMC_TYPE_SDIO) + if (mmc_card_sdio(card)) goto out; if (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4) { @@ -415,7 +429,7 @@ static int sdio_enable_hs(struct mmc_card *card) int ret; ret = mmc_sdio_switch_hs(card, true); - if (ret <= 0 || card->type == MMC_TYPE_SDIO) + if (ret <= 0 || mmc_card_sdio(card)) return ret; ret = mmc_sd_switch_hs(card); @@ -441,7 +455,7 @@ static unsigned mmc_sdio_get_max_clock(struct mmc_card *card) max_dtr = card->cis.max_dtr; } - if (card->type == MMC_TYPE_SD_COMBO) + if (mmc_card_sd_combo(card)) max_dtr = min(max_dtr, mmc_sd_get_max_clock(card)); return max_dtr; @@ -689,7 +703,7 @@ try_again: mmc_sd_get_cid(host, ocr & rocr, card->raw_cid, NULL) == 0) { card->type = MMC_TYPE_SD_COMBO; - if (oldcard && (oldcard->type != MMC_TYPE_SD_COMBO || + if (oldcard && (!mmc_card_sd_combo(oldcard) || memcmp(card->raw_cid, oldcard->raw_cid, sizeof(card->raw_cid)) != 0)) { err = -ENOENT; goto mismatch; @@ -697,7 +711,7 @@ try_again: } else { card->type = MMC_TYPE_SDIO; - if (oldcard && oldcard->type != MMC_TYPE_SDIO) { + if (oldcard && !mmc_card_sdio(oldcard)) { err = -ENOENT; goto mismatch; } @@ -754,7 +768,7 @@ try_again: /* * Read CSD, before selecting the card */ - if (!oldcard && card->type == MMC_TYPE_SD_COMBO) { + if (!oldcard && mmc_card_sd_combo(card)) { err = mmc_sd_get_csd(card); if (err) goto remove; @@ -827,7 +841,7 @@ try_again: mmc_fixup_device(card, sdio_fixup_methods); - if (card->type == MMC_TYPE_SD_COMBO) { + if (mmc_card_sd_combo(card)) { err = mmc_sd_setup_card(host, card, oldcard != NULL); /* handle as SDIO-only card if memory init failed */ if (err) { diff --git a/drivers/mmc/host/cavium-octeon.c b/drivers/mmc/host/cavium-octeon.c index 2c4b2df52adb..12dca91a8ef6 100644 --- a/drivers/mmc/host/cavium-octeon.c +++ b/drivers/mmc/host/cavium-octeon.c @@ -277,6 +277,7 @@ static int octeon_mmc_probe(struct platform_device *pdev) if (ret) { dev_err(&pdev->dev, "Error populating slots\n"); octeon_mmc_set_shared_power(host, 0); + of_node_put(cn); goto error; } i++; diff --git a/drivers/mmc/host/cavium-thunderx.c b/drivers/mmc/host/cavium-thunderx.c index 76013bbbcff3..202b1d6da678 100644 --- a/drivers/mmc/host/cavium-thunderx.c +++ b/drivers/mmc/host/cavium-thunderx.c @@ -142,8 +142,10 @@ static int thunder_mmc_probe(struct pci_dev *pdev, continue; ret = cvm_mmc_of_slot_probe(&host->slot_pdev[i]->dev, host); - if (ret) + if (ret) { + of_node_put(child_node); goto error; + } } i++; } diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c index b0d30c35c390..b3d7d6d8d654 100644 --- a/drivers/mmc/host/cqhci-core.c +++ b/drivers/mmc/host/cqhci-core.c @@ -822,8 +822,15 @@ irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error, pr_debug("%s: cqhci: IRQ status: 0x%08x\n", mmc_hostname(mmc), status); if ((status & (CQHCI_IS_RED | CQHCI_IS_GCE | CQHCI_IS_ICCE)) || - cmd_error || data_error) + cmd_error || data_error) { + if (status & CQHCI_IS_RED) + mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_RED); + if (status & CQHCI_IS_GCE) + mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_GCE); + if (status & CQHCI_IS_ICCE) + mmc_debugfs_err_stats_inc(mmc, MMC_ERR_CMDQ_ICCE); cqhci_error_irq(mmc, status, cmd_error, data_error); + } if (status & CQHCI_IS_TCC) { /* read TCN and complete the request */ diff --git a/drivers/mmc/host/dw_mmc-exynos.c b/drivers/mmc/host/dw_mmc-exynos.c index ca5be4445ae0..9f20ac524c8b 100644 --- a/drivers/mmc/host/dw_mmc-exynos.c +++ b/drivers/mmc/host/dw_mmc-exynos.c @@ -670,7 +670,9 @@ static int dw_mci_exynos_remove(struct platform_device *pdev) pm_runtime_set_suspended(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - return dw_mci_pltfm_remove(pdev); + dw_mci_pltfm_remove(pdev); + + return 0; } static const struct dev_pm_ops dw_mci_exynos_pmops = { diff --git a/drivers/mmc/host/dw_mmc-hi3798cv200.c b/drivers/mmc/host/dw_mmc-hi3798cv200.c index e9437ef8ef19..6f22fe054087 100644 --- a/drivers/mmc/host/dw_mmc-hi3798cv200.c +++ b/drivers/mmc/host/dw_mmc-hi3798cv200.c @@ -179,7 +179,9 @@ static int dw_mci_hi3798cv200_remove(struct platform_device *pdev) clk_disable_unprepare(priv->drive_clk); clk_disable_unprepare(priv->sample_clk); - return dw_mci_pltfm_remove(pdev); + dw_mci_pltfm_remove(pdev); + + return 0; } static const struct of_device_id dw_mci_hi3798cv200_match[] = { diff --git a/drivers/mmc/host/dw_mmc-rockchip.c b/drivers/mmc/host/dw_mmc-rockchip.c index f825487aa739..2a99f15f527f 100644 --- a/drivers/mmc/host/dw_mmc-rockchip.c +++ b/drivers/mmc/host/dw_mmc-rockchip.c @@ -377,7 +377,9 @@ static int dw_mci_rockchip_remove(struct platform_device *pdev) pm_runtime_disable(&pdev->dev); pm_runtime_put_noidle(&pdev->dev); - return dw_mci_pltfm_remove(pdev); + dw_mci_pltfm_remove(pdev); + + return 0; } static const struct dev_pm_ops dw_mci_rockchip_dev_pm_ops = { diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c index 01159eaf8694..012aa85489d8 100644 --- a/drivers/mmc/host/mmci.c +++ b/drivers/mmc/host/mmci.c @@ -762,7 +762,7 @@ int mmci_dmae_setup(struct mmci_host *host) /* * If only an RX channel is specified, the driver will - * attempt to use it bidirectionally, however if it is + * attempt to use it bidirectionally, however if it * is specified but cannot be located, DMA will be disabled. */ if (dmae->rx_channel && !dmae->tx_channel) diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c index 9da4489dc345..4ff73d1883de 100644 --- a/drivers/mmc/host/mtk-sd.c +++ b/drivers/mmc/host/mtk-sd.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Copyright (c) 2014-2015 MediaTek Inc. + * Copyright (c) 2014-2015, 2022 MediaTek Inc. * Author: Chaotian.Jing */ @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -440,8 +441,10 @@ struct msdc_host { struct pinctrl *pinctrl; struct pinctrl_state *pins_default; struct pinctrl_state *pins_uhs; + struct pinctrl_state *pins_eint; struct delayed_work req_timeout; int irq; /* host interrupt */ + int eint_irq; /* interrupt from sdio device for waking up system */ struct reset_control *reset; struct clk *src_clk; /* msdc source clock */ @@ -1521,17 +1524,46 @@ static void __msdc_enable_sdio_irq(struct msdc_host *host, int enb) static void msdc_enable_sdio_irq(struct mmc_host *mmc, int enb) { - unsigned long flags; struct msdc_host *host = mmc_priv(mmc); + unsigned long flags; + int ret; spin_lock_irqsave(&host->lock, flags); __msdc_enable_sdio_irq(host, enb); spin_unlock_irqrestore(&host->lock, flags); - if (enb) - pm_runtime_get_noresume(host->dev); - else - pm_runtime_put_noidle(host->dev); + if (mmc_card_enable_async_irq(mmc->card) && host->pins_eint) { + if (enb) { + /* + * In dev_pm_set_dedicated_wake_irq_reverse(), eint pin will be set to + * GPIO mode. We need to restore it to SDIO DAT1 mode after that. + * Since the current pinstate is pins_uhs, to ensure pinctrl select take + * affect successfully, we change the pinstate to pins_eint firstly. + */ + pinctrl_select_state(host->pinctrl, host->pins_eint); + ret = dev_pm_set_dedicated_wake_irq_reverse(host->dev, host->eint_irq); + + if (ret) { + dev_err(host->dev, "Failed to register SDIO wakeup irq!\n"); + host->pins_eint = NULL; + pm_runtime_get_noresume(host->dev); + } else { + dev_dbg(host->dev, "SDIO eint irq: %d!\n", host->eint_irq); + } + + pinctrl_select_state(host->pinctrl, host->pins_uhs); + } else { + dev_pm_clear_wake_irq(host->dev); + } + } else { + if (enb) { + /* Ensure host->pins_eint is NULL */ + host->pins_eint = NULL; + pm_runtime_get_noresume(host->dev); + } else { + pm_runtime_put_noidle(host->dev); + } + } } static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts) @@ -2319,7 +2351,7 @@ static int msdc_execute_hs400_tuning(struct mmc_host *mmc, struct mmc_card *card else val = readl(host->base + PAD_DS_TUNE); - dev_info(host->dev, "Fianl PAD_DS_TUNE: 0x%x\n", val); + dev_info(host->dev, "Final PAD_DS_TUNE: 0x%x\n", val); return 0; @@ -2635,6 +2667,20 @@ static int msdc_drv_probe(struct platform_device *pdev) goto host_free; } + /* Support for SDIO eint irq ? */ + if ((mmc->pm_caps & MMC_PM_WAKE_SDIO_IRQ) && (mmc->pm_caps & MMC_PM_KEEP_POWER)) { + host->eint_irq = platform_get_irq_byname(pdev, "sdio_wakeup"); + if (host->eint_irq > 0) { + host->pins_eint = pinctrl_lookup_state(host->pinctrl, "state_eint"); + if (IS_ERR(host->pins_eint)) { + dev_err(&pdev->dev, "Cannot find pinctrl eint!\n"); + host->pins_eint = NULL; + } else { + device_init_wakeup(&pdev->dev, true); + } + } + } + msdc_of_property_parse(pdev, host); host->dev = &pdev->dev; @@ -2849,6 +2895,15 @@ static int __maybe_unused msdc_runtime_suspend(struct device *dev) struct msdc_host *host = mmc_priv(mmc); msdc_save_reg(host); + + if (sdio_irq_claimed(mmc)) { + if (host->pins_eint) { + disable_irq(host->irq); + pinctrl_select_state(host->pinctrl, host->pins_eint); + } + + __msdc_enable_sdio_irq(host, 0); + } msdc_gate_clock(host); return 0; } @@ -2864,12 +2919,18 @@ static int __maybe_unused msdc_runtime_resume(struct device *dev) return ret; msdc_restore_reg(host); + + if (sdio_irq_claimed(mmc) && host->pins_eint) { + pinctrl_select_state(host->pinctrl, host->pins_uhs); + enable_irq(host->irq); + } return 0; } static int __maybe_unused msdc_suspend(struct device *dev) { struct mmc_host *mmc = dev_get_drvdata(dev); + struct msdc_host *host = mmc_priv(mmc); int ret; if (mmc->caps2 & MMC_CAP2_CQE) { @@ -2878,11 +2939,24 @@ static int __maybe_unused msdc_suspend(struct device *dev) return ret; } + /* + * Bump up runtime PM usage counter otherwise dev->power.needs_force_resume will + * not be marked as 1, pm_runtime_force_resume() will go out directly. + */ + if (sdio_irq_claimed(mmc) && host->pins_eint) + pm_runtime_get_noresume(dev); + return pm_runtime_force_suspend(dev); } static int __maybe_unused msdc_resume(struct device *dev) { + struct mmc_host *mmc = dev_get_drvdata(dev); + struct msdc_host *host = mmc_priv(mmc); + + if (sdio_irq_claimed(mmc) && host->pins_eint) + pm_runtime_put_noidle(dev); + return pm_runtime_force_resume(dev); } diff --git a/drivers/mmc/host/mxcmmc.c b/drivers/mmc/host/mxcmmc.c index de04b5afef2e..2cf0413407ea 100644 --- a/drivers/mmc/host/mxcmmc.c +++ b/drivers/mmc/host/mxcmmc.c @@ -923,7 +923,7 @@ static void mxcmci_init_card(struct mmc_host *host, struct mmc_card *card) * One way to prevent this is to only allow 1-bit transfers. */ - if (is_imx31_mmc(mxcmci) && card->type == MMC_TYPE_SDIO) + if (is_imx31_mmc(mxcmci) && mmc_card_sdio(card)) host->caps &= ~MMC_CAP_4_BIT_DATA; else host->caps |= MMC_CAP_4_BIT_DATA; @@ -1025,7 +1025,7 @@ static int mxcmci_probe(struct platform_device *pdev) mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; mmc->max_seg_size = mmc->max_req_size; - host->devtype = (enum mxcmci_type)of_device_get_match_data(&pdev->dev); + host->devtype = (uintptr_t)of_device_get_match_data(&pdev->dev); /* adjust max_segs after devtype detection */ if (!is_mpc512x_mmc(host)) diff --git a/drivers/mmc/host/renesas_sdhi.h b/drivers/mmc/host/renesas_sdhi.h index 1a1e3e020a8c..c4abfee1ebae 100644 --- a/drivers/mmc/host/renesas_sdhi.h +++ b/drivers/mmc/host/renesas_sdhi.h @@ -43,6 +43,7 @@ struct renesas_sdhi_quirks { bool hs400_4taps; bool fixed_addr_mode; bool dma_one_rx_only; + bool manual_tap_correction; u32 hs400_bad_taps; const u8 (*hs400_calib_table)[SDHI_CALIB_TABLE_MAX]; }; diff --git a/drivers/mmc/host/renesas_sdhi_core.c b/drivers/mmc/host/renesas_sdhi_core.c index 4404ca1f98d8..6edbf5c161ab 100644 --- a/drivers/mmc/host/renesas_sdhi_core.c +++ b/drivers/mmc/host/renesas_sdhi_core.c @@ -49,9 +49,6 @@ #define HOST_MODE_GEN3_32BIT (HOST_MODE_GEN3_WMODE | HOST_MODE_GEN3_BUSWIDTH) #define HOST_MODE_GEN3_64BIT 0 -#define CTL_SDIF_MODE 0xe6 -#define SDIF_MODE_HS400 BIT(0) - #define SDHI_VER_GEN2_SDR50 0x490c #define SDHI_VER_RZ_A1 0x820b /* very old datasheets said 0x490c for SDR104, too. They are wrong! */ @@ -383,8 +380,7 @@ static void renesas_sdhi_hs400_complete(struct mmc_host *mmc) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_DT2FF, priv->scc_tappos_hs400); - /* Gen3 can't do automatic tap correction with HS400, so disable it */ - if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC) + if (priv->quirks && priv->quirks->manual_tap_correction) sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL, ~SH_MOBILE_SDHI_SCC_RVSCNTL_RVSEN & sd_scc_read32(host, priv, SH_MOBILE_SDHI_SCC_RVSCNTL)); @@ -562,23 +558,25 @@ static void renesas_sdhi_scc_reset(struct tmio_mmc_host *host, struct renesas_sd } /* only populated for TMIO_MMC_MIN_RCAR2 */ -static void renesas_sdhi_reset(struct tmio_mmc_host *host) +static void renesas_sdhi_reset(struct tmio_mmc_host *host, bool preserve) { struct renesas_sdhi *priv = host_to_priv(host); int ret; u16 val; - if (priv->rstc) { - reset_control_reset(priv->rstc); - /* Unknown why but without polling reset status, it will hang */ - read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100, - false, priv->rstc); - /* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */ - sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); - priv->needs_adjust_hs400 = false; - renesas_sdhi_set_clock(host, host->clk_cache); - } else if (priv->scc_ctl) { - renesas_sdhi_scc_reset(host, priv); + if (!preserve) { + if (priv->rstc) { + reset_control_reset(priv->rstc); + /* Unknown why but without polling reset status, it will hang */ + read_poll_timeout(reset_control_status, ret, ret == 0, 1, 100, + false, priv->rstc); + /* At least SDHI_VER_GEN2_SDR50 needs manual release of reset */ + sd_ctrl_write16(host, CTL_RESET_SD, 0x0001); + priv->needs_adjust_hs400 = false; + renesas_sdhi_set_clock(host, host->clk_cache); + } else if (priv->scc_ctl) { + renesas_sdhi_scc_reset(host, priv); + } } if (sd_ctrl_read16(host, CTL_VERSION) >= SDHI_VER_GEN3_SD) { @@ -719,7 +717,7 @@ static bool renesas_sdhi_manual_correction(struct tmio_mmc_host *host, bool use_ sd_scc_write32(host, priv, SH_MOBILE_SDHI_SCC_RVSREQ, 0); /* Change TAP position according to correction status */ - if (sd_ctrl_read16(host, CTL_VERSION) == SDHI_VER_GEN3_SDMMC && + if (priv->quirks && priv->quirks->manual_tap_correction && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { u32 bad_taps = priv->quirks ? priv->quirks->hs400_bad_taps : 0; /* @@ -938,6 +936,10 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (IS_ERR(priv->clk_cd)) return dev_err_probe(&pdev->dev, PTR_ERR(priv->clk_cd), "cannot get cd clock"); + priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); + if (IS_ERR(priv->rstc)) + return PTR_ERR(priv->rstc); + priv->pinctrl = devm_pinctrl_get(&pdev->dev); if (!IS_ERR(priv->pinctrl)) { priv->pins_default = pinctrl_lookup_state(priv->pinctrl, @@ -1030,10 +1032,6 @@ int renesas_sdhi_probe(struct platform_device *pdev, if (ret) goto efree; - priv->rstc = devm_reset_control_get_optional_exclusive(&pdev->dev, NULL); - if (IS_ERR(priv->rstc)) - return PTR_ERR(priv->rstc); - ver = sd_ctrl_read16(host, CTL_VERSION); /* GEN2_SDR104 is first known SDHI to use 32bit block count */ if (ver < SDHI_VER_GEN2_SDR104 && mmc_data->max_blk_count > U16_MAX) diff --git a/drivers/mmc/host/renesas_sdhi_internal_dmac.c b/drivers/mmc/host/renesas_sdhi_internal_dmac.c index 3084b15ae2cb..42937596c4c4 100644 --- a/drivers/mmc/host/renesas_sdhi_internal_dmac.c +++ b/drivers/mmc/host/renesas_sdhi_internal_dmac.c @@ -170,6 +170,7 @@ static const struct renesas_sdhi_quirks sdhi_quirks_4tap_nohs400_one_rx = { static const struct renesas_sdhi_quirks sdhi_quirks_4tap = { .hs400_4taps = true, .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7), + .manual_tap_correction = true, }; static const struct renesas_sdhi_quirks sdhi_quirks_nohs400 = { @@ -182,25 +183,30 @@ static const struct renesas_sdhi_quirks sdhi_quirks_fixed_addr = { static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps1357 = { .hs400_bad_taps = BIT(1) | BIT(3) | BIT(5) | BIT(7), + .manual_tap_correction = true, }; static const struct renesas_sdhi_quirks sdhi_quirks_bad_taps2367 = { .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7), + .manual_tap_correction = true, }; static const struct renesas_sdhi_quirks sdhi_quirks_r8a7796_es13 = { .hs400_4taps = true, .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7), .hs400_calib_table = r8a7796_es13_calib_table, + .manual_tap_correction = true, }; static const struct renesas_sdhi_quirks sdhi_quirks_r8a77965 = { .hs400_bad_taps = BIT(2) | BIT(3) | BIT(6) | BIT(7), .hs400_calib_table = r8a77965_calib_table, + .manual_tap_correction = true, }; static const struct renesas_sdhi_quirks sdhi_quirks_r8a77990 = { .hs400_calib_table = r8a77990_calib_table, + .manual_tap_correction = true, }; /* @@ -268,6 +274,7 @@ static const struct of_device_id renesas_sdhi_internal_dmac_of_match[] = { { .compatible = "renesas,sdhi-r8a77990", .data = &of_r8a77990_compatible, }, { .compatible = "renesas,sdhi-r8a77995", .data = &of_rcar_gen3_nohs400_compatible, }, { .compatible = "renesas,rcar-gen3-sdhi", .data = &of_rcar_gen3_compatible, }, + { .compatible = "renesas,rcar-gen4-sdhi", .data = &of_rcar_gen3_compatible, }, {}, }; MODULE_DEVICE_TABLE(of, renesas_sdhi_internal_dmac_of_match); @@ -321,7 +328,7 @@ renesas_sdhi_internal_dmac_dataend_dma(struct tmio_mmc_host *host) } /* - * renesas_sdhi_internal_dmac_map() will be called with two difference + * renesas_sdhi_internal_dmac_map() will be called with two different * sg pointers in two mmc_data by .pre_req(), but tmio host can have a single * sg_ptr only. So, renesas_sdhi_internal_dmac_{un}map() should use a sg * pointer in a mmc_data instead of host->sg_ptr. @@ -355,7 +362,7 @@ renesas_sdhi_internal_dmac_map(struct tmio_mmc_host *host, data->host_cookie = cookie; - /* This DMAC cannot handle if buffer is not 128-bytes alignment */ + /* This DMAC needs buffers to be 128-byte aligned */ if (!IS_ALIGNED(sg_dma_address(data->sg), 128)) { renesas_sdhi_internal_dmac_unmap(host, data, cookie); return false; diff --git a/drivers/mmc/host/sdhci-bcm-kona.c b/drivers/mmc/host/sdhci-bcm-kona.c index 4d4aac85cc7a..61a12f2f7f03 100644 --- a/drivers/mmc/host/sdhci-bcm-kona.c +++ b/drivers/mmc/host/sdhci-bcm-kona.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Broadcom Corporation #include #include diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c index 8eb57de48e0c..aff36a933ebe 100644 --- a/drivers/mmc/host/sdhci-brcmstb.c +++ b/drivers/mmc/host/sdhci-brcmstb.c @@ -31,6 +31,8 @@ struct sdhci_brcmstb_priv { void __iomem *cfg_regs; unsigned int flags; + struct clk *base_clk; + u32 base_freq_hz; }; struct brcmstb_match_priv { @@ -250,9 +252,11 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; const struct of_device_id *match; struct sdhci_brcmstb_priv *priv; + u32 actual_clock_mhz; struct sdhci_host *host; struct resource *iomem; struct clk *clk; + struct clk *base_clk = NULL; int res; match = of_match_node(sdhci_brcm_of_match, pdev->dev.of_node); @@ -330,6 +334,35 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) if (match_priv->flags & BRCMSTB_MATCH_FLAGS_BROKEN_TIMEOUT) host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; + /* Change the base clock frequency if the DT property exists */ + if (device_property_read_u32(&pdev->dev, "clock-frequency", + &priv->base_freq_hz) != 0) + goto add_host; + + base_clk = devm_clk_get_optional(&pdev->dev, "sdio_freq"); + if (IS_ERR(base_clk)) { + dev_warn(&pdev->dev, "Clock for \"sdio_freq\" not found\n"); + goto add_host; + } + + res = clk_prepare_enable(base_clk); + if (res) + goto err; + + /* set improved clock rate */ + clk_set_rate(base_clk, priv->base_freq_hz); + actual_clock_mhz = clk_get_rate(base_clk) / 1000000; + + host->caps &= ~SDHCI_CLOCK_V3_BASE_MASK; + host->caps |= (actual_clock_mhz << SDHCI_CLOCK_BASE_SHIFT); + /* Disable presets because they are now incorrect */ + host->quirks2 |= SDHCI_QUIRK2_PRESET_VALUE_BROKEN; + + dev_dbg(&pdev->dev, "Base Clock Frequency changed to %dMHz\n", + actual_clock_mhz); + priv->base_clk = base_clk; + +add_host: res = sdhci_brcmstb_add_host(host, priv); if (res) goto err; @@ -340,6 +373,7 @@ static int sdhci_brcmstb_probe(struct platform_device *pdev) err: sdhci_pltfm_free(pdev); err_clk: + clk_disable_unprepare(base_clk); clk_disable_unprepare(clk); return res; } @@ -351,11 +385,51 @@ static void sdhci_brcmstb_shutdown(struct platform_device *pdev) MODULE_DEVICE_TABLE(of, sdhci_brcm_of_match); +#ifdef CONFIG_PM_SLEEP +static int sdhci_brcmstb_suspend(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + + clk_disable_unprepare(priv->base_clk); + return sdhci_pltfm_suspend(dev); +} + +static int sdhci_brcmstb_resume(struct device *dev) +{ + struct sdhci_host *host = dev_get_drvdata(dev); + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct sdhci_brcmstb_priv *priv = sdhci_pltfm_priv(pltfm_host); + int ret; + + ret = sdhci_pltfm_resume(dev); + if (!ret && priv->base_freq_hz) { + ret = clk_prepare_enable(priv->base_clk); + /* + * Note: using clk_get_rate() below as clk_get_rate() + * honors CLK_GET_RATE_NOCACHE attribute, but clk_set_rate() + * may do implicit get_rate() calls that do not honor + * CLK_GET_RATE_NOCACHE. + */ + if (!ret && + (clk_get_rate(priv->base_clk) != priv->base_freq_hz)) + ret = clk_set_rate(priv->base_clk, priv->base_freq_hz); + } + + return ret; +} +#endif + +static const struct dev_pm_ops sdhci_brcmstb_pmops = { + SET_SYSTEM_SLEEP_PM_OPS(sdhci_brcmstb_suspend, sdhci_brcmstb_resume) +}; + static struct platform_driver sdhci_brcmstb_driver = { .driver = { .name = "sdhci-brcmstb", .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &sdhci_pltfm_pmops, + .pm = &sdhci_brcmstb_pmops, .of_match_table = of_match_ptr(sdhci_brcm_of_match), }, .probe = sdhci_brcmstb_probe, diff --git a/drivers/mmc/host/sdhci-iproc.c b/drivers/mmc/host/sdhci-iproc.c index 032bf852397f..6db35b1b8557 100644 --- a/drivers/mmc/host/sdhci-iproc.c +++ b/drivers/mmc/host/sdhci-iproc.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation /* * iProc SDHCI platform driver diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c index e395411fb6fd..dc2991422a87 100644 --- a/drivers/mmc/host/sdhci-msm.c +++ b/drivers/mmc/host/sdhci-msm.c @@ -2435,33 +2435,12 @@ static const struct sdhci_msm_variant_info sdm845_sdhci_var = { }; static const struct of_device_id sdhci_msm_dt_match[] = { - /* Following two entries are deprecated (kept only for backward compatibility) */ + /* + * Do not add new variants to the driver which are compatible with + * generic ones, unless they need customization. + */ {.compatible = "qcom,sdhci-msm-v4", .data = &sdhci_msm_mci_var}, {.compatible = "qcom,sdhci-msm-v5", .data = &sdhci_msm_v5_var}, - /* Add entries for sdcc versions less than 5.0 here */ - {.compatible = "qcom,apq8084-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8226-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8916-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8953-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8974-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8992-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8994-sdhci", .data = &sdhci_msm_mci_var}, - {.compatible = "qcom,msm8996-sdhci", .data = &sdhci_msm_mci_var}, - /* - * Add entries for sdcc version 5.0 here. For SDCC version 5.0.0, - * MCI registers are removed from SDCC interface and some registers - * are moved to HC. - */ - {.compatible = "qcom,qcs404-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sdx55-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sdx65-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sdm630-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sm6125-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sm6350-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sm8150-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sm8250-sdhci", .data = &sdhci_msm_v5_var}, - {.compatible = "qcom,sc7280-sdhci", .data = &sdhci_msm_v5_var}, - /* Add entries where soc specific handling is required, here */ {.compatible = "qcom,sdm845-sdhci", .data = &sdm845_sdhci_var}, {.compatible = "qcom,sc7180-sdhci", .data = &sdm845_sdhci_var}, {}, diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c index 757801dfc308..3997cad1f793 100644 --- a/drivers/mmc/host/sdhci-of-arasan.c +++ b/drivers/mmc/host/sdhci-of-arasan.c @@ -1733,7 +1733,6 @@ err_pltfm_free: static int sdhci_arasan_remove(struct platform_device *pdev) { - int ret; struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct sdhci_arasan_data *sdhci_arasan = sdhci_pltfm_priv(pltfm_host); @@ -1747,11 +1746,11 @@ static int sdhci_arasan_remove(struct platform_device *pdev) sdhci_arasan_unregister_sdclk(&pdev->dev); - ret = sdhci_pltfm_unregister(pdev); + sdhci_pltfm_unregister(pdev); clk_disable_unprepare(clk_ahb); - return ret; + return 0; } static struct platform_driver sdhci_arasan_driver = { diff --git a/drivers/mmc/host/sdhci-of-at91.c b/drivers/mmc/host/sdhci-of-at91.c index 10fb4cb2c731..cd0134580a90 100644 --- a/drivers/mmc/host/sdhci-of-at91.c +++ b/drivers/mmc/host/sdhci-of-at91.c @@ -100,8 +100,13 @@ static void sdhci_at91_set_clock(struct sdhci_host *host, unsigned int clock) static void sdhci_at91_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { - if (timing == MMC_TIMING_MMC_DDR52) - sdhci_writeb(host, SDMMC_MC1R_DDR, SDMMC_MC1R); + u8 mc1r; + + if (timing == MMC_TIMING_MMC_DDR52) { + mc1r = sdhci_readb(host, SDMMC_MC1R); + mc1r |= SDMMC_MC1R_DDR; + sdhci_writeb(host, mc1r, SDMMC_MC1R); + } sdhci_set_uhs_signaling(host, timing); } diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c index bac874ab0b33..4e904850973c 100644 --- a/drivers/mmc/host/sdhci-of-dwcmshc.c +++ b/drivers/mmc/host/sdhci-of-dwcmshc.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "sdhci-pltfm.h" @@ -30,6 +31,7 @@ /* Offset inside the vendor area 1 */ #define DWCMSHC_HOST_CTRL3 0x8 #define DWCMSHC_EMMC_CONTROL 0x2c +#define DWCMSHC_CARD_IS_EMMC BIT(0) #define DWCMSHC_ENHANCED_STROBE BIT(8) #define DWCMSHC_EMMC_ATCTRL 0x40 @@ -38,7 +40,7 @@ #define DWCMSHC_EMMC_DLL_RXCLK 0x804 #define DWCMSHC_EMMC_DLL_TXCLK 0x808 #define DWCMSHC_EMMC_DLL_STRBIN 0x80c -#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) +#define DECMSHC_EMMC_DLL_CMDOUT 0x810 #define DWCMSHC_EMMC_DLL_STATUS0 0x840 #define DWCMSHC_EMMC_DLL_START BIT(0) #define DWCMSHC_EMMC_DLL_LOCKED BIT(8) @@ -47,22 +49,39 @@ #define DWCMSHC_EMMC_DLL_START_POINT 16 #define DWCMSHC_EMMC_DLL_INC 8 #define DWCMSHC_EMMC_DLL_DLYENA BIT(27) -#define DLL_TXCLK_TAPNUM_DEFAULT 0x8 -#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_TXCLK_TAPNUM_DEFAULT 0x10 +#define DLL_TXCLK_TAPNUM_90_DEGREES 0xA #define DLL_TXCLK_TAPNUM_FROM_SW BIT(24) +#define DLL_STRBIN_TAPNUM_DEFAULT 0x8 +#define DLL_STRBIN_TAPNUM_FROM_SW BIT(24) +#define DLL_STRBIN_DELAY_NUM_SEL BIT(26) +#define DLL_STRBIN_DELAY_NUM_OFFSET 16 +#define DLL_STRBIN_DELAY_NUM_DEFAULT 0x16 #define DLL_RXCLK_NO_INVERTER 1 #define DLL_RXCLK_INVERTER 0 +#define DLL_CMDOUT_TAPNUM_90_DEGREES 0x8 +#define DLL_CMDOUT_TAPNUM_FROM_SW BIT(24) +#define DLL_CMDOUT_SRC_CLK_NEG BIT(28) +#define DLL_CMDOUT_EN_SRC_CLK_NEG BIT(29) + #define DLL_LOCK_WO_TMOUT(x) \ ((((x) & DWCMSHC_EMMC_DLL_LOCKED) == DWCMSHC_EMMC_DLL_LOCKED) && \ (((x) & DWCMSHC_EMMC_DLL_TIMEOUT) == 0)) -#define RK3568_MAX_CLKS 3 +#define RK35xx_MAX_CLKS 3 #define BOUNDARY_OK(addr, len) \ ((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1))) -struct rk3568_priv { +enum dwcmshc_rk_type { + DWCMSHC_RK3568, + DWCMSHC_RK3588, +}; + +struct rk35xx_priv { /* Rockchip specified optional clocks */ - struct clk_bulk_data rockchip_clks[RK3568_MAX_CLKS]; + struct clk_bulk_data rockchip_clks[RK35xx_MAX_CLKS]; + struct reset_control *reset; + enum dwcmshc_rk_type devtype; u8 txclk_tapnum; }; @@ -131,7 +150,9 @@ static void dwcmshc_request(struct mmc_host *mmc, struct mmc_request *mrq) static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, unsigned int timing) { - u16 ctrl_2; + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); + u16 ctrl, ctrl_2; ctrl_2 = sdhci_readw(host, SDHCI_HOST_CONTROL2); /* Select Bus Speed Mode for host */ @@ -149,8 +170,15 @@ static void dwcmshc_set_uhs_signaling(struct sdhci_host *host, else if ((timing == MMC_TIMING_UHS_DDR50) || (timing == MMC_TIMING_MMC_DDR52)) ctrl_2 |= SDHCI_CTRL_UHS_DDR50; - else if (timing == MMC_TIMING_MMC_HS400) + else if (timing == MMC_TIMING_MMC_HS400) { + /* set CARD_IS_EMMC bit to enable Data Strobe for HS400 */ + ctrl = sdhci_readw(host, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + ctrl |= DWCMSHC_CARD_IS_EMMC; + sdhci_writew(host, ctrl, priv->vendor_specific_area1 + DWCMSHC_EMMC_CONTROL); + ctrl_2 |= DWCMSHC_CTRL_HS400; + } + sdhci_writew(host, ctrl_2, SDHCI_HOST_CONTROL2); } @@ -176,24 +204,18 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock { struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); - struct rk3568_priv *priv = dwc_priv->priv; + struct rk35xx_priv *priv = dwc_priv->priv; u8 txclk_tapnum = DLL_TXCLK_TAPNUM_DEFAULT; u32 extra, reg; int err; host->mmc->actual_clock = 0; - /* - * DO NOT TOUCH THIS SETTING. RX clk inverter unit is enabled - * by default, but it shouldn't be enabled. We should anyway - * disable it before issuing any cmds. - */ - extra = DWCMSHC_EMMC_DLL_DLYENA | - DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; - sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); - - if (clock == 0) + if (clock == 0) { + /* Disable interface clock at initial state. */ + sdhci_set_clock(host, clock); return; + } /* Rockchip platform only support 375KHz for identify mode */ if (clock <= 400000) @@ -211,9 +233,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock extra &= ~BIT(0); sdhci_writel(host, extra, reg); - if (clock <= 400000) { - /* Disable DLL to reset sample clock */ + if (clock <= 52000000) { + /* Disable DLL and reset both of sample and drive clock */ sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_CTRL); + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_RXCLK); + sdhci_writel(host, 0, DWCMSHC_EMMC_DLL_TXCLK); + sdhci_writel(host, 0, DECMSHC_EMMC_DLL_CMDOUT); + /* + * Before switching to hs400es mode, the driver will enable + * enhanced strobe first. PHY needs to configure the parameters + * of enhanced strobe first. + */ + extra = DWCMSHC_EMMC_DLL_DLYENA | + DLL_STRBIN_DELAY_NUM_SEL | + DLL_STRBIN_DELAY_NUM_DEFAULT << DLL_STRBIN_DELAY_NUM_OFFSET; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); return; } @@ -222,6 +256,15 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock udelay(1); sdhci_writel(host, 0x0, DWCMSHC_EMMC_DLL_CTRL); + /* + * We shouldn't set DLL_RXCLK_NO_INVERTER for identify mode but + * we must set it in higher speed mode. + */ + extra = DWCMSHC_EMMC_DLL_DLYENA; + if (priv->devtype == DWCMSHC_RK3568) + extra |= DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL; + sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_RXCLK); + /* Init DLL settings */ extra = 0x5 << DWCMSHC_EMMC_DLL_START_POINT | 0x2 << DWCMSHC_EMMC_DLL_INC | @@ -244,8 +287,20 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock host->mmc->ios.timing == MMC_TIMING_MMC_HS400) txclk_tapnum = priv->txclk_tapnum; + if ((priv->devtype == DWCMSHC_RK3588) && host->mmc->ios.timing == MMC_TIMING_MMC_HS400) { + txclk_tapnum = DLL_TXCLK_TAPNUM_90_DEGREES; + + extra = DLL_CMDOUT_SRC_CLK_NEG | + DLL_CMDOUT_EN_SRC_CLK_NEG | + DWCMSHC_EMMC_DLL_DLYENA | + DLL_CMDOUT_TAPNUM_90_DEGREES | + DLL_CMDOUT_TAPNUM_FROM_SW; + sdhci_writel(host, extra, DECMSHC_EMMC_DLL_CMDOUT); + } + extra = DWCMSHC_EMMC_DLL_DLYENA | DLL_TXCLK_TAPNUM_FROM_SW | + DLL_RXCLK_NO_INVERTER << DWCMSHC_EMMC_DLL_RXCLK_SRCSEL | txclk_tapnum; sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_TXCLK); @@ -255,6 +310,21 @@ static void dwcmshc_rk3568_set_clock(struct sdhci_host *host, unsigned int clock sdhci_writel(host, extra, DWCMSHC_EMMC_DLL_STRBIN); } +static void rk35xx_sdhci_reset(struct sdhci_host *host, u8 mask) +{ + struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); + struct dwcmshc_priv *dwc_priv = sdhci_pltfm_priv(pltfm_host); + struct rk35xx_priv *priv = dwc_priv->priv; + + if (mask & SDHCI_RESET_ALL && priv->reset) { + reset_control_assert(priv->reset); + udelay(1); + reset_control_deassert(priv->reset); + } + + sdhci_reset(host, mask); +} + static const struct sdhci_ops sdhci_dwcmshc_ops = { .set_clock = sdhci_set_clock, .set_bus_width = sdhci_set_bus_width, @@ -264,12 +334,12 @@ static const struct sdhci_ops sdhci_dwcmshc_ops = { .adma_write_desc = dwcmshc_adma_write_desc, }; -static const struct sdhci_ops sdhci_dwcmshc_rk3568_ops = { +static const struct sdhci_ops sdhci_dwcmshc_rk35xx_ops = { .set_clock = dwcmshc_rk3568_set_clock, .set_bus_width = sdhci_set_bus_width, .set_uhs_signaling = dwcmshc_set_uhs_signaling, .get_max_clock = sdhci_pltfm_clk_get_max_clock, - .reset = sdhci_reset, + .reset = rk35xx_sdhci_reset, .adma_write_desc = dwcmshc_adma_write_desc, }; @@ -279,30 +349,37 @@ static const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = { .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN, }; -static const struct sdhci_pltfm_data sdhci_dwcmshc_rk3568_pdata = { - .ops = &sdhci_dwcmshc_rk3568_ops, +static const struct sdhci_pltfm_data sdhci_dwcmshc_rk35xx_pdata = { + .ops = &sdhci_dwcmshc_rk35xx_ops, .quirks = SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN | SDHCI_QUIRK_BROKEN_TIMEOUT_VAL, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | SDHCI_QUIRK2_CLOCK_DIV_ZERO_BROKEN, }; -static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +static int dwcmshc_rk35xx_init(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) { int err; - struct rk3568_priv *priv = dwc_priv->priv; + struct rk35xx_priv *priv = dwc_priv->priv; + + priv->reset = devm_reset_control_array_get_optional_exclusive(mmc_dev(host->mmc)); + if (IS_ERR(priv->reset)) { + err = PTR_ERR(priv->reset); + dev_err(mmc_dev(host->mmc), "failed to get reset control %d\n", err); + return err; + } priv->rockchip_clks[0].id = "axi"; priv->rockchip_clks[1].id = "block"; priv->rockchip_clks[2].id = "timer"; - err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK3568_MAX_CLKS, + err = devm_clk_bulk_get_optional(mmc_dev(host->mmc), RK35xx_MAX_CLKS, priv->rockchip_clks); if (err) { dev_err(mmc_dev(host->mmc), "failed to get clocks %d\n", err); return err; } - err = clk_bulk_prepare_enable(RK3568_MAX_CLKS, priv->rockchip_clks); + err = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, priv->rockchip_clks); if (err) { dev_err(mmc_dev(host->mmc), "failed to enable clocks %d\n", err); return err; @@ -321,10 +398,28 @@ static int dwcmshc_rk3568_init(struct sdhci_host *host, struct dwcmshc_priv *dwc return 0; } +static void dwcmshc_rk35xx_postinit(struct sdhci_host *host, struct dwcmshc_priv *dwc_priv) +{ + /* + * Don't support highspeed bus mode with low clk speed as we + * cannot use DLL for this condition. + */ + if (host->mmc->f_max <= 52000000) { + dev_info(mmc_dev(host->mmc), "Disabling HS200/HS400, frequency too low (%d)\n", + host->mmc->f_max); + host->mmc->caps2 &= ~(MMC_CAP2_HS200 | MMC_CAP2_HS400); + host->mmc->caps &= ~(MMC_CAP_3_3V_DDR | MMC_CAP_1_8V_DDR); + } +} + static const struct of_device_id sdhci_dwcmshc_dt_ids[] = { + { + .compatible = "rockchip,rk3588-dwcmshc", + .data = &sdhci_dwcmshc_rk35xx_pdata, + }, { .compatible = "rockchip,rk3568-dwcmshc", - .data = &sdhci_dwcmshc_rk3568_pdata, + .data = &sdhci_dwcmshc_rk35xx_pdata, }, { .compatible = "snps,dwcmshc-sdhci", @@ -347,7 +442,7 @@ static int dwcmshc_probe(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host; struct sdhci_host *host; struct dwcmshc_priv *priv; - struct rk3568_priv *rk_priv = NULL; + struct rk35xx_priv *rk_priv = NULL; const struct sdhci_pltfm_data *pltfm_data; int err; u32 extra; @@ -402,33 +497,47 @@ static int dwcmshc_probe(struct platform_device *pdev) host->mmc_host_ops.request = dwcmshc_request; host->mmc_host_ops.hs400_enhanced_strobe = dwcmshc_hs400_enhanced_strobe; - if (pltfm_data == &sdhci_dwcmshc_rk3568_pdata) { - rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk3568_priv), GFP_KERNEL); + if (pltfm_data == &sdhci_dwcmshc_rk35xx_pdata) { + rk_priv = devm_kzalloc(&pdev->dev, sizeof(struct rk35xx_priv), GFP_KERNEL); if (!rk_priv) { err = -ENOMEM; goto err_clk; } + if (of_device_is_compatible(pdev->dev.of_node, "rockchip,rk3588-dwcmshc")) + rk_priv->devtype = DWCMSHC_RK3588; + else + rk_priv->devtype = DWCMSHC_RK3568; + priv->priv = rk_priv; - err = dwcmshc_rk3568_init(host, priv); + err = dwcmshc_rk35xx_init(host, priv); if (err) goto err_clk; } host->mmc->caps |= MMC_CAP_WAIT_WHILE_BUSY; - err = sdhci_add_host(host); + err = sdhci_setup_host(host); if (err) goto err_clk; + if (rk_priv) + dwcmshc_rk35xx_postinit(host, priv); + + err = __sdhci_add_host(host); + if (err) + goto err_setup_host; + return 0; +err_setup_host: + sdhci_cleanup_host(host); err_clk: clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); if (rk_priv) - clk_bulk_disable_unprepare(RK3568_MAX_CLKS, + clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, rk_priv->rockchip_clks); free_pltfm: sdhci_pltfm_free(pdev); @@ -440,14 +549,14 @@ static int dwcmshc_remove(struct platform_device *pdev) struct sdhci_host *host = platform_get_drvdata(pdev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - struct rk3568_priv *rk_priv = priv->priv; + struct rk35xx_priv *rk_priv = priv->priv; sdhci_remove_host(host, 0); clk_disable_unprepare(pltfm_host->clk); clk_disable_unprepare(priv->bus_clk); if (rk_priv) - clk_bulk_disable_unprepare(RK3568_MAX_CLKS, + clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, rk_priv->rockchip_clks); sdhci_pltfm_free(pdev); @@ -460,7 +569,7 @@ static int dwcmshc_suspend(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - struct rk3568_priv *rk_priv = priv->priv; + struct rk35xx_priv *rk_priv = priv->priv; int ret; ret = sdhci_suspend_host(host); @@ -472,7 +581,7 @@ static int dwcmshc_suspend(struct device *dev) clk_disable_unprepare(priv->bus_clk); if (rk_priv) - clk_bulk_disable_unprepare(RK3568_MAX_CLKS, + clk_bulk_disable_unprepare(RK35xx_MAX_CLKS, rk_priv->rockchip_clks); return ret; @@ -483,7 +592,7 @@ static int dwcmshc_resume(struct device *dev) struct sdhci_host *host = dev_get_drvdata(dev); struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct dwcmshc_priv *priv = sdhci_pltfm_priv(pltfm_host); - struct rk3568_priv *rk_priv = priv->priv; + struct rk35xx_priv *rk_priv = priv->priv; int ret; ret = clk_prepare_enable(pltfm_host->clk); @@ -497,7 +606,7 @@ static int dwcmshc_resume(struct device *dev) } if (rk_priv) { - ret = clk_bulk_prepare_enable(RK3568_MAX_CLKS, + ret = clk_bulk_prepare_enable(RK35xx_MAX_CLKS, rk_priv->rockchip_clks); if (ret) return ret; diff --git a/drivers/mmc/host/sdhci-of-esdhc.c b/drivers/mmc/host/sdhci-of-esdhc.c index d9dc41143bb3..e0266638381d 100644 --- a/drivers/mmc/host/sdhci-of-esdhc.c +++ b/drivers/mmc/host/sdhci-of-esdhc.c @@ -904,6 +904,7 @@ static int esdhc_signal_voltage_switch(struct mmc_host *mmc, scfg_node = of_find_matching_node(NULL, scfg_device_ids); if (scfg_node) scfg_base = of_iomap(scfg_node, 0); + of_node_put(scfg_node); if (scfg_base) { sdhciovselcr = SDHCIOVSELCR_TGLEN | SDHCIOVSELCR_VSELVAL; @@ -1418,7 +1419,7 @@ static int esdhc_hs400_prepare_ddr(struct mmc_host *mmc) static int sdhci_esdhc_probe(struct platform_device *pdev) { struct sdhci_host *host; - struct device_node *np; + struct device_node *np, *tp; struct sdhci_pltfm_host *pltfm_host; struct sdhci_esdhc *esdhc; int ret; @@ -1463,7 +1464,9 @@ static int sdhci_esdhc_probe(struct platform_device *pdev) if (esdhc->vendor_ver > VENDOR_V_22) host->quirks &= ~SDHCI_QUIRK_NO_BUSY_IRQ; - if (of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc")) { + tp = of_find_compatible_node(NULL, NULL, "fsl,p2020-esdhc"); + if (tp) { + of_node_put(tp); host->quirks |= SDHCI_QUIRK_RESET_AFTER_REQUEST; host->quirks |= SDHCI_QUIRK_BROKEN_TIMEOUT_VAL; } diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c index f13c08db3da5..4d509f656188 100644 --- a/drivers/mmc/host/sdhci-pci-gli.c +++ b/drivers/mmc/host/sdhci-pci-gli.c @@ -95,6 +95,9 @@ #define PCIE_GLI_9763E_SCR 0x8E0 #define GLI_9763E_SCR_AXI_REQ BIT(9) +#define PCIE_GLI_9763E_CFG 0x8A0 +#define GLI_9763E_CFG_LPSN_DIS BIT(12) + #define PCIE_GLI_9763E_CFG2 0x8A4 #define GLI_9763E_CFG2_L1DLY GENMASK(28, 19) #define GLI_9763E_CFG2_L1DLY_MID 0x54 @@ -963,12 +966,40 @@ static void gli_set_gl9763e(struct sdhci_pci_slot *slot) } #ifdef CONFIG_PM +static void gl9763e_set_low_power_negotiation(struct sdhci_pci_slot *slot, bool enable) +{ + struct pci_dev *pdev = slot->chip->pdev; + u32 value; + + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); + value &= ~GLI_9763E_VHS_REV; + value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_W); + pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); + + pci_read_config_dword(pdev, PCIE_GLI_9763E_CFG, &value); + + if (enable) + value &= ~GLI_9763E_CFG_LPSN_DIS; + else + value |= GLI_9763E_CFG_LPSN_DIS; + + pci_write_config_dword(pdev, PCIE_GLI_9763E_CFG, value); + + pci_read_config_dword(pdev, PCIE_GLI_9763E_VHS, &value); + value &= ~GLI_9763E_VHS_REV; + value |= FIELD_PREP(GLI_9763E_VHS_REV, GLI_9763E_VHS_REV_R); + pci_write_config_dword(pdev, PCIE_GLI_9763E_VHS, value); +} + static int gl9763e_runtime_suspend(struct sdhci_pci_chip *chip) { struct sdhci_pci_slot *slot = chip->slots[0]; struct sdhci_host *host = slot->host; u16 clock; + /* Enable LPM negotiation to allow entering L1 state */ + gl9763e_set_low_power_negotiation(slot, true); + clock = sdhci_readw(host, SDHCI_CLOCK_CONTROL); clock &= ~(SDHCI_CLOCK_PLL_EN | SDHCI_CLOCK_CARD_EN); sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL); @@ -1002,6 +1033,9 @@ static int gl9763e_runtime_resume(struct sdhci_pci_chip *chip) clock |= SDHCI_CLOCK_CARD_EN; sdhci_writew(host, clock, SDHCI_CLOCK_CONTROL); + /* Disable LPM negotiation to avoid entering L1 state. */ + gl9763e_set_low_power_negotiation(slot, false); + return 0; } #endif diff --git a/drivers/mmc/host/sdhci-st.c b/drivers/mmc/host/sdhci-st.c index d41582c21aa3..6415916fbd91 100644 --- a/drivers/mmc/host/sdhci-st.c +++ b/drivers/mmc/host/sdhci-st.c @@ -440,15 +440,14 @@ static int sdhci_st_remove(struct platform_device *pdev) struct sdhci_pltfm_host *pltfm_host = sdhci_priv(host); struct st_mmc_platform_data *pdata = sdhci_pltfm_priv(pltfm_host); struct reset_control *rstc = pdata->rstc; - int ret; - ret = sdhci_pltfm_unregister(pdev); + sdhci_pltfm_unregister(pdev); clk_disable_unprepare(pdata->icnclk); reset_control_assert(rstc); - return ret; + return 0; } #ifdef CONFIG_PM_SLEEP diff --git a/drivers/mmc/host/sdhci.c b/drivers/mmc/host/sdhci.c index 22152029e14c..7689ffec5ad1 100644 --- a/drivers/mmc/host/sdhci.c +++ b/drivers/mmc/host/sdhci.c @@ -224,6 +224,7 @@ void sdhci_reset(struct sdhci_host *host, u8 mask) if (timedout) { pr_err("%s: Reset 0x%x never completed.\n", mmc_hostname(host->mmc), (int)mask); + sdhci_err_stats_inc(host, CTRL_TIMEOUT); sdhci_dumpregs(host); return; } @@ -1716,6 +1717,7 @@ static bool sdhci_send_command_retry(struct sdhci_host *host, if (!timeout--) { pr_err("%s: Controller never released inhibit bit(s).\n", mmc_hostname(host->mmc)); + sdhci_err_stats_inc(host, CTRL_TIMEOUT); sdhci_dumpregs(host); cmd->error = -EIO; return false; @@ -1965,6 +1967,7 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk) if (timedout) { pr_err("%s: Internal clock never stabilised.\n", mmc_hostname(host->mmc)); + sdhci_err_stats_inc(host, CTRL_TIMEOUT); sdhci_dumpregs(host); return; } @@ -1987,6 +1990,7 @@ void sdhci_enable_clk(struct sdhci_host *host, u16 clk) if (timedout) { pr_err("%s: PLL clock never stabilised.\n", mmc_hostname(host->mmc)); + sdhci_err_stats_inc(host, CTRL_TIMEOUT); sdhci_dumpregs(host); return; } @@ -3161,6 +3165,7 @@ static void sdhci_timeout_timer(struct timer_list *t) if (host->cmd && !sdhci_data_line_cmd(host->cmd)) { pr_err("%s: Timeout waiting for hardware cmd interrupt.\n", mmc_hostname(host->mmc)); + sdhci_err_stats_inc(host, REQ_TIMEOUT); sdhci_dumpregs(host); host->cmd->error = -ETIMEDOUT; @@ -3183,6 +3188,7 @@ static void sdhci_timeout_data_timer(struct timer_list *t) (host->cmd && sdhci_data_line_cmd(host->cmd))) { pr_err("%s: Timeout waiting for hardware interrupt.\n", mmc_hostname(host->mmc)); + sdhci_err_stats_inc(host, REQ_TIMEOUT); sdhci_dumpregs(host); if (host->data) { @@ -3234,17 +3240,21 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p) return; pr_err("%s: Got command interrupt 0x%08x even though no command operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); + sdhci_err_stats_inc(host, UNEXPECTED_IRQ); sdhci_dumpregs(host); return; } if (intmask & (SDHCI_INT_TIMEOUT | SDHCI_INT_CRC | SDHCI_INT_END_BIT | SDHCI_INT_INDEX)) { - if (intmask & SDHCI_INT_TIMEOUT) + if (intmask & SDHCI_INT_TIMEOUT) { host->cmd->error = -ETIMEDOUT; - else + sdhci_err_stats_inc(host, CMD_TIMEOUT); + } else { host->cmd->error = -EILSEQ; - + if (!mmc_op_tuning(host->cmd->opcode)) + sdhci_err_stats_inc(host, CMD_CRC); + } /* Treat data command CRC error the same as data CRC error */ if (host->cmd->data && (intmask & (SDHCI_INT_CRC | SDHCI_INT_TIMEOUT)) == @@ -3266,6 +3276,8 @@ static void sdhci_cmd_irq(struct sdhci_host *host, u32 intmask, u32 *intmask_p) -ETIMEDOUT : -EILSEQ; + sdhci_err_stats_inc(host, AUTO_CMD); + if (sdhci_auto_cmd23(host, mrq)) { mrq->sbc->error = err; __sdhci_finish_mrq(host, mrq); @@ -3342,6 +3354,7 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) if (intmask & SDHCI_INT_DATA_TIMEOUT) { host->data_cmd = NULL; data_cmd->error = -ETIMEDOUT; + sdhci_err_stats_inc(host, CMD_TIMEOUT); __sdhci_finish_mrq(host, data_cmd->mrq); return; } @@ -3370,23 +3383,30 @@ static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) pr_err("%s: Got data interrupt 0x%08x even though no data operation was in progress.\n", mmc_hostname(host->mmc), (unsigned)intmask); + sdhci_err_stats_inc(host, UNEXPECTED_IRQ); sdhci_dumpregs(host); return; } - if (intmask & SDHCI_INT_DATA_TIMEOUT) + if (intmask & SDHCI_INT_DATA_TIMEOUT) { host->data->error = -ETIMEDOUT; - else if (intmask & SDHCI_INT_DATA_END_BIT) + sdhci_err_stats_inc(host, DAT_TIMEOUT); + } else if (intmask & SDHCI_INT_DATA_END_BIT) { host->data->error = -EILSEQ; - else if ((intmask & SDHCI_INT_DATA_CRC) && + if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)))) + sdhci_err_stats_inc(host, DAT_CRC); + } else if ((intmask & SDHCI_INT_DATA_CRC) && SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) - != MMC_BUS_TEST_R) + != MMC_BUS_TEST_R) { host->data->error = -EILSEQ; - else if (intmask & SDHCI_INT_ADMA_ERROR) { + if (!mmc_op_tuning(SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)))) + sdhci_err_stats_inc(host, DAT_CRC); + } else if (intmask & SDHCI_INT_ADMA_ERROR) { pr_err("%s: ADMA error: 0x%08x\n", mmc_hostname(host->mmc), intmask); sdhci_adma_show_error(host); + sdhci_err_stats_inc(host, ADMA); host->data->error = -EIO; if (host->ops->adma_workaround) host->ops->adma_workaround(host, intmask); @@ -3584,6 +3604,7 @@ out: if (unexpected) { pr_err("%s: Unexpected interrupt 0x%08x.\n", mmc_hostname(host->mmc), unexpected); + sdhci_err_stats_inc(host, UNEXPECTED_IRQ); sdhci_dumpregs(host); } @@ -3905,20 +3926,27 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error, if (!host->cqe_on) return false; - if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC)) + if (intmask & (SDHCI_INT_INDEX | SDHCI_INT_END_BIT | SDHCI_INT_CRC)) { *cmd_error = -EILSEQ; - else if (intmask & SDHCI_INT_TIMEOUT) + if (!mmc_op_tuning(host->cmd->opcode)) + sdhci_err_stats_inc(host, CMD_CRC); + } else if (intmask & SDHCI_INT_TIMEOUT) { *cmd_error = -ETIMEDOUT; - else + sdhci_err_stats_inc(host, CMD_TIMEOUT); + } else *cmd_error = 0; - if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) + if (intmask & (SDHCI_INT_DATA_END_BIT | SDHCI_INT_DATA_CRC)) { *data_error = -EILSEQ; - else if (intmask & SDHCI_INT_DATA_TIMEOUT) + if (!mmc_op_tuning(host->cmd->opcode)) + sdhci_err_stats_inc(host, DAT_CRC); + } else if (intmask & SDHCI_INT_DATA_TIMEOUT) { *data_error = -ETIMEDOUT; - else if (intmask & SDHCI_INT_ADMA_ERROR) + sdhci_err_stats_inc(host, DAT_TIMEOUT); + } else if (intmask & SDHCI_INT_ADMA_ERROR) { *data_error = -EIO; - else + sdhci_err_stats_inc(host, ADMA); + } else *data_error = 0; /* Clear selected interrupts. */ @@ -3934,6 +3962,7 @@ bool sdhci_cqe_irq(struct sdhci_host *host, u32 intmask, int *cmd_error, sdhci_writel(host, intmask, SDHCI_INT_STATUS); pr_err("%s: CQE: Unexpected interrupt 0x%08x.\n", mmc_hostname(host->mmc), intmask); + sdhci_err_stats_inc(host, UNEXPECTED_IRQ); sdhci_dumpregs(host); } diff --git a/drivers/mmc/host/sdhci.h b/drivers/mmc/host/sdhci.h index d7929d725730..95a08f09df30 100644 --- a/drivers/mmc/host/sdhci.h +++ b/drivers/mmc/host/sdhci.h @@ -356,6 +356,9 @@ struct sdhci_adma2_64_desc { */ #define MMC_CMD_TRANSFER_TIME (10 * NSEC_PER_MSEC) /* max 10 ms */ +#define sdhci_err_stats_inc(host, err_name) \ + mmc_debugfs_err_stats_inc((host)->mmc, MMC_ERR_##err_name) + enum sdhci_cookie { COOKIE_UNMAPPED, COOKIE_PRE_MAPPED, /* mapped by sdhci_pre_req() */ diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c index b55a29c53d9c..53a2ad9a24b8 100644 --- a/drivers/mmc/host/tmio_mmc.c +++ b/drivers/mmc/host/tmio_mmc.c @@ -75,7 +75,7 @@ static void tmio_mmc_set_clock(struct tmio_mmc_host *host, tmio_mmc_clk_start(host); } -static void tmio_mmc_reset(struct tmio_mmc_host *host) +static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve) { sd_ctrl_write16(host, CTL_RESET_SDIO, 0x0000); usleep_range(10000, 11000); diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h index e754bb3f5c32..501613c74406 100644 --- a/drivers/mmc/host/tmio_mmc.h +++ b/drivers/mmc/host/tmio_mmc.h @@ -42,6 +42,7 @@ #define CTL_DMA_ENABLE 0xd8 #define CTL_RESET_SD 0xe0 #define CTL_VERSION 0xe2 +#define CTL_SDIF_MODE 0xe6 /* only known on R-Car 2+ */ /* Definitions for values the CTL_STOP_INTERNAL_ACTION register can take */ #define TMIO_STOP_STP BIT(0) @@ -98,6 +99,9 @@ /* Definitions for values the CTL_DMA_ENABLE register can take */ #define DMA_ENABLE_DMASDRW BIT(1) +/* Definitions for values the CTL_SDIF_MODE register can take */ +#define SDIF_MODE_HS400 BIT(0) /* only known on R-Car 2+ */ + /* Define some IRQ masks */ /* This is the mask used at reset by the chip */ #define TMIO_MASK_ALL 0x837f031d @@ -181,7 +185,7 @@ struct tmio_mmc_host { int (*multi_io_quirk)(struct mmc_card *card, unsigned int direction, int blk_size); int (*write16_hook)(struct tmio_mmc_host *host, int addr); - void (*reset)(struct tmio_mmc_host *host); + void (*reset)(struct tmio_mmc_host *host, bool preserve); bool (*check_retune)(struct tmio_mmc_host *host, struct mmc_request *mrq); void (*fixup_request)(struct tmio_mmc_host *host, struct mmc_request *mrq); unsigned int (*get_timeout_cycles)(struct tmio_mmc_host *host); diff --git a/drivers/mmc/host/tmio_mmc_core.c b/drivers/mmc/host/tmio_mmc_core.c index a5850d83908b..437048bb8027 100644 --- a/drivers/mmc/host/tmio_mmc_core.c +++ b/drivers/mmc/host/tmio_mmc_core.c @@ -179,8 +179,17 @@ static void tmio_mmc_set_bus_width(struct tmio_mmc_host *host, sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, reg); } -static void tmio_mmc_reset(struct tmio_mmc_host *host) +static void tmio_mmc_reset(struct tmio_mmc_host *host, bool preserve) { + u16 card_opt, clk_ctrl, sdif_mode; + + if (preserve) { + card_opt = sd_ctrl_read16(host, CTL_SD_MEM_CARD_OPT); + clk_ctrl = sd_ctrl_read16(host, CTL_SD_CARD_CLK_CTL); + if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) + sdif_mode = sd_ctrl_read16(host, CTL_SDIF_MODE); + } + /* FIXME - should we set stop clock reg here */ sd_ctrl_write16(host, CTL_RESET_SD, 0x0000); usleep_range(10000, 11000); @@ -190,7 +199,7 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host) tmio_mmc_abort_dma(host); if (host->reset) - host->reset(host); + host->reset(host, preserve); sd_ctrl_write32_as_16_and_16(host, CTL_IRQ_MASK, host->sdcard_irq_mask_all); host->sdcard_irq_mask = host->sdcard_irq_mask_all; @@ -206,6 +215,13 @@ static void tmio_mmc_reset(struct tmio_mmc_host *host) sd_ctrl_write16(host, CTL_TRANSACTION_CTL, 0x0001); } + if (preserve) { + sd_ctrl_write16(host, CTL_SD_MEM_CARD_OPT, card_opt); + sd_ctrl_write16(host, CTL_SD_CARD_CLK_CTL, clk_ctrl); + if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) + sd_ctrl_write16(host, CTL_SDIF_MODE, sdif_mode); + } + if (host->mmc->card) mmc_retune_needed(host->mmc); } @@ -248,7 +264,7 @@ static void tmio_mmc_reset_work(struct work_struct *work) spin_unlock_irqrestore(&host->lock, flags); - tmio_mmc_reset(host); + tmio_mmc_reset(host, true); /* Ready for new calls */ host->mrq = NULL; @@ -961,7 +977,7 @@ static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) tmio_mmc_power_off(host); /* For R-Car Gen2+, we need to reset SDHI specific SCC */ if (host->pdata->flags & TMIO_MMC_MIN_RCAR2) - tmio_mmc_reset(host); + tmio_mmc_reset(host, false); host->set_clock(host, 0); break; @@ -1189,7 +1205,7 @@ int tmio_mmc_host_probe(struct tmio_mmc_host *_host) _host->sdcard_irq_mask_all = TMIO_MASK_ALL; _host->set_clock(_host, 0); - tmio_mmc_reset(_host); + tmio_mmc_reset(_host, false); spin_lock_init(&_host->lock); mutex_init(&_host->ios_lock); @@ -1285,7 +1301,7 @@ int tmio_mmc_host_runtime_resume(struct device *dev) struct tmio_mmc_host *host = dev_get_drvdata(dev); tmio_mmc_clk_enable(host); - tmio_mmc_reset(host); + tmio_mmc_reset(host, false); if (host->clk_cache) host->set_clock(host, host->clk_cache); diff --git a/drivers/net/can/ti_hecc.c b/drivers/net/can/ti_hecc.c index ec0ffeeb2015..b218fb3c6b76 100644 --- a/drivers/net/can/ti_hecc.c +++ b/drivers/net/can/ti_hecc.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI HECC (CAN) device driver * @@ -6,16 +7,6 @@ * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ * Copyright (C) 2019 Jeroen Hofstee - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed as is WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #include diff --git a/drivers/net/ethernet/cadence/macb_main.c b/drivers/net/ethernet/cadence/macb_main.c index 494fe961a49d..66c7d08d376a 100644 --- a/drivers/net/ethernet/cadence/macb_main.c +++ b/drivers/net/ethernet/cadence/macb_main.c @@ -4784,7 +4784,6 @@ static const struct macb_config versal_config = { }; static const struct of_device_id macb_dt_ids[] = { - { .compatible = "cdns,at32ap7000-macb" }, { .compatible = "cdns,at91sam9260-macb", .data = &at91sam9260_config }, { .compatible = "cdns,macb" }, { .compatible = "cdns,np4-macb", .data = &np4_config }, diff --git a/drivers/net/ethernet/chelsio/cxgb/common.h b/drivers/net/ethernet/chelsio/cxgb/common.h index 0321be77366c..e56eff701395 100644 --- a/drivers/net/ethernet/chelsio/cxgb/common.h +++ b/drivers/net/ethernet/chelsio/cxgb/common.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: common.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/cphy.h b/drivers/net/ethernet/chelsio/cxgb/cphy.h index bf43da6c6a63..12639b688ddc 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cphy.h +++ b/drivers/net/ethernet/chelsio/cxgb/cphy.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: cphy.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h b/drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h index 5249686afe71..a30fb407115d 100644 --- a/drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h +++ b/drivers/net/ethernet/chelsio/cxgb/cpl5_cmd.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: cpl5_cmd.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * @@ -635,4 +626,3 @@ struct cpl_mss_change { }; #endif /* _CXGB_CPL5_CMD_H_ */ - diff --git a/drivers/net/ethernet/chelsio/cxgb/elmer0.h b/drivers/net/ethernet/chelsio/cxgb/elmer0.h index 81526ad36339..0427e894c277 100644 --- a/drivers/net/ethernet/chelsio/cxgb/elmer0.h +++ b/drivers/net/ethernet/chelsio/cxgb/elmer0.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: elmer0.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * @@ -154,4 +145,3 @@ enum { #define MI1_OP_INDIRECT_READ 3 #endif /* _CXGB_ELMER0_H_ */ - diff --git a/drivers/net/ethernet/chelsio/cxgb/espi.c b/drivers/net/ethernet/chelsio/cxgb/espi.c index 3e182eee799e..ef70569435be 100644 --- a/drivers/net/ethernet/chelsio/cxgb/espi.c +++ b/drivers/net/ethernet/chelsio/cxgb/espi.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /***************************************************************************** * * * File: espi.c * @@ -7,16 +8,6 @@ * Ethernet SPI functionality. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/espi.h b/drivers/net/ethernet/chelsio/cxgb/espi.h index 162de5259df9..f588e9f3b37a 100644 --- a/drivers/net/ethernet/chelsio/cxgb/espi.h +++ b/drivers/net/ethernet/chelsio/cxgb/espi.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: espi.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/gmac.h b/drivers/net/ethernet/chelsio/cxgb/gmac.h index 5913eaf442b5..96077da1ed5e 100644 --- a/drivers/net/ethernet/chelsio/cxgb/gmac.h +++ b/drivers/net/ethernet/chelsio/cxgb/gmac.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: gmac.h * @@ -7,16 +8,6 @@ * Generic MAC functionality. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c index 7ddb301bcba0..556c8ad68fa8 100644 --- a/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c +++ b/drivers/net/ethernet/chelsio/cxgb/mv88x201x.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /***************************************************************************** * * * File: mv88x201x.c * @@ -7,16 +8,6 @@ * Marvell PHY (mv88x201x) functionality. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/pm3393.c b/drivers/net/ethernet/chelsio/cxgb/pm3393.c index 0bb37e4680c7..cbfa03d5663a 100644 --- a/drivers/net/ethernet/chelsio/cxgb/pm3393.c +++ b/drivers/net/ethernet/chelsio/cxgb/pm3393.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /***************************************************************************** * * * File: pm3393.c * @@ -7,16 +8,6 @@ * PMC/SIERRA (pm3393) MAC-PHY functionality. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/regs.h b/drivers/net/ethernet/chelsio/cxgb/regs.h index 964ce59ee169..f751e680cf7d 100644 --- a/drivers/net/ethernet/chelsio/cxgb/regs.h +++ b/drivers/net/ethernet/chelsio/cxgb/regs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: regs.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/sge.c b/drivers/net/ethernet/chelsio/cxgb/sge.c index 12e76fd0ae91..861edff5ed89 100644 --- a/drivers/net/ethernet/chelsio/cxgb/sge.c +++ b/drivers/net/ethernet/chelsio/cxgb/sge.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /***************************************************************************** * * * File: sge.c * @@ -7,16 +8,6 @@ * DMA engine. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/sge.h b/drivers/net/ethernet/chelsio/cxgb/sge.h index 716705b96f26..f7e6f64040ea 100644 --- a/drivers/net/ethernet/chelsio/cxgb/sge.h +++ b/drivers/net/ethernet/chelsio/cxgb/sge.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: sge.h * @@ -6,16 +7,6 @@ * Description: * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/subr.c b/drivers/net/ethernet/chelsio/cxgb/subr.c index 007c591b8bf5..367a9e4581d5 100644 --- a/drivers/net/ethernet/chelsio/cxgb/subr.c +++ b/drivers/net/ethernet/chelsio/cxgb/subr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /***************************************************************************** * * * File: subr.c * @@ -7,16 +8,6 @@ * Various subroutines (intr,pio,etc.) used by Chelsio 10G Ethernet driver. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * diff --git a/drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h b/drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h index 7f79cc7ceb75..4c883170683b 100644 --- a/drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h +++ b/drivers/net/ethernet/chelsio/cxgb/suni1x10gexp_regs.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /***************************************************************************** * * * File: suni1x10gexp_regs.h * @@ -7,16 +8,6 @@ * PMC/SIERRA (pm3393) MAC-PHY functionality. * * part of the Chelsio 10Gb Ethernet Driver. * * * - * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU General Public License, version 2, as * - * published by the Free Software Foundation. * - * * - * You should have received a copy of the GNU General Public License along * - * with this program; if not, see . * - * * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * - * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * * * * http://www.chelsio.com * * * @@ -1639,4 +1630,3 @@ #define SUNI1x10GEXP_BITMSK_PL4IDU_DIP4I 0x0002 #endif /* _CXGB_SUNI1x10GEXP_REGS_H_ */ - diff --git a/drivers/net/ethernet/cisco/enic/cq_desc.h b/drivers/net/ethernet/cisco/enic/cq_desc.h index d6dd1b4edf6e..462c5435a206 100644 --- a/drivers/net/ethernet/cisco/enic/cq_desc.h +++ b/drivers/net/ethernet/cisco/enic/cq_desc.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _CQ_DESC_H_ diff --git a/drivers/net/ethernet/cisco/enic/cq_enet_desc.h b/drivers/net/ethernet/cisco/enic/cq_enet_desc.h index ac37cacc6136..d25426470a29 100644 --- a/drivers/net/ethernet/cisco/enic/cq_enet_desc.h +++ b/drivers/net/ethernet/cisco/enic/cq_enet_desc.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _CQ_ENET_DESC_H_ diff --git a/drivers/net/ethernet/cisco/enic/enic.h b/drivers/net/ethernet/cisco/enic/enic.h index 52aaf1bb5205..a0964b629ffc 100644 --- a/drivers/net/ethernet/cisco/enic/enic.h +++ b/drivers/net/ethernet/cisco/enic/enic.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _ENIC_H_ diff --git a/drivers/net/ethernet/cisco/enic/enic_api.c b/drivers/net/ethernet/cisco/enic/enic_api.c index 3bdc74fba1e3..e3b700c28bc4 100644 --- a/drivers/net/ethernet/cisco/enic/enic_api.c +++ b/drivers/net/ethernet/cisco/enic/enic_api.c @@ -1,20 +1,5 @@ -/* - * Copyright 2013 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2013 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/net/ethernet/cisco/enic/enic_api.h b/drivers/net/ethernet/cisco/enic/enic_api.h index 6b9f9255af28..e01790fb0415 100644 --- a/drivers/net/ethernet/cisco/enic/enic_api.h +++ b/drivers/net/ethernet/cisco/enic/enic_api.h @@ -1,20 +1,5 @@ -/** - * Copyright 2013 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2013 Cisco Systems, Inc. All rights reserved. */ #ifndef __ENIC_API_H__ #define __ENIC_API_H__ diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.c b/drivers/net/ethernet/cisco/enic/enic_dev.c index f8d2a6a34282..2cbae7c6cc3d 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.c +++ b/drivers/net/ethernet/cisco/enic/enic_dev.c @@ -1,20 +1,5 @@ -/* - * Copyright 2011 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2011 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/net/ethernet/cisco/enic/enic_dev.h b/drivers/net/ethernet/cisco/enic/enic_dev.h index f5bb058b3f96..698d0cb02064 100644 --- a/drivers/net/ethernet/cisco/enic/enic_dev.h +++ b/drivers/net/ethernet/cisco/enic/enic_dev.h @@ -1,20 +1,5 @@ -/* - * Copyright 2011 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2011 Cisco Systems, Inc. All rights reserved. */ #ifndef _ENIC_DEV_H_ #define _ENIC_DEV_H_ diff --git a/drivers/net/ethernet/cisco/enic/enic_ethtool.c b/drivers/net/ethernet/cisco/enic/enic_ethtool.c index 6c11f9d62526..60d8c0fbc037 100644 --- a/drivers/net/ethernet/cisco/enic/enic_ethtool.c +++ b/drivers/net/ethernet/cisco/enic/enic_ethtool.c @@ -1,20 +1,5 @@ -/* - * Copyright 2013 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2013 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/net/ethernet/cisco/enic/enic_pp.c b/drivers/net/ethernet/cisco/enic/enic_pp.c index 80f46dbd5117..4720a952725d 100644 --- a/drivers/net/ethernet/cisco/enic/enic_pp.c +++ b/drivers/net/ethernet/cisco/enic/enic_pp.c @@ -1,20 +1,5 @@ -/* - * Copyright 2011 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2011 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/net/ethernet/cisco/enic/enic_pp.h b/drivers/net/ethernet/cisco/enic/enic_pp.h index a09ff392c1c6..20a2687713ef 100644 --- a/drivers/net/ethernet/cisco/enic/enic_pp.h +++ b/drivers/net/ethernet/cisco/enic/enic_pp.h @@ -1,20 +1,5 @@ -/* - * Copyright 2011 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2011 Cisco Systems, Inc. All rights reserved. */ #ifndef _ENIC_PP_H_ #define _ENIC_PP_H_ diff --git a/drivers/net/ethernet/cisco/enic/enic_res.c b/drivers/net/ethernet/cisco/enic/enic_res.c index 40b20817ddd5..1c48aebdbab0 100644 --- a/drivers/net/ethernet/cisco/enic/enic_res.c +++ b/drivers/net/ethernet/cisco/enic/enic_res.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #include diff --git a/drivers/net/ethernet/cisco/enic/enic_res.h b/drivers/net/ethernet/cisco/enic/enic_res.h index 81f98a8b60e9..b8ee42d297aa 100644 --- a/drivers/net/ethernet/cisco/enic/enic_res.h +++ b/drivers/net/ethernet/cisco/enic/enic_res.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _ENIC_RES_H_ diff --git a/drivers/net/ethernet/cisco/enic/rq_enet_desc.h b/drivers/net/ethernet/cisco/enic/rq_enet_desc.h index e6dd30988d6f..0ab5fd6b8d46 100644 --- a/drivers/net/ethernet/cisco/enic/rq_enet_desc.h +++ b/drivers/net/ethernet/cisco/enic/rq_enet_desc.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _RQ_ENET_DESC_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.c b/drivers/net/ethernet/cisco/enic/vnic_cq.c index 519323460f26..27c885e91552 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_cq.c +++ b/drivers/net/ethernet/cisco/enic/vnic_cq.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #include diff --git a/drivers/net/ethernet/cisco/enic/vnic_cq.h b/drivers/net/ethernet/cisco/enic/vnic_cq.h index 4e6aa65857f7..eed5bf59e5d2 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_cq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_cq.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_CQ_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.c b/drivers/net/ethernet/cisco/enic/vnic_dev.c index 45015931b335..12a83fa1302d 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.c +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #include diff --git a/drivers/net/ethernet/cisco/enic/vnic_dev.h b/drivers/net/ethernet/cisco/enic/vnic_dev.h index 714fc1ed79e3..6273794b923b 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_dev.h +++ b/drivers/net/ethernet/cisco/enic/vnic_dev.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_DEV_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_devcmd.h b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h index fcc4a3ccdd94..db56d778877a 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_devcmd.h +++ b/drivers/net/ethernet/cisco/enic/vnic_devcmd.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_DEVCMD_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_enet.h b/drivers/net/ethernet/cisco/enic/vnic_enet.h index 7d6fbb5635a4..5acc236069de 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_enet.h +++ b/drivers/net/ethernet/cisco/enic/vnic_enet.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_ENIC_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_intr.c b/drivers/net/ethernet/cisco/enic/vnic_intr.c index 23604e3d4455..25319f072a04 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_intr.c +++ b/drivers/net/ethernet/cisco/enic/vnic_intr.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #include diff --git a/drivers/net/ethernet/cisco/enic/vnic_intr.h b/drivers/net/ethernet/cisco/enic/vnic_intr.h index 2b1636392294..33a72aa10b26 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_intr.h +++ b/drivers/net/ethernet/cisco/enic/vnic_intr.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_INTR_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_nic.h b/drivers/net/ethernet/cisco/enic/vnic_nic.h index 84ff8ca17fcb..04fee45b5d39 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_nic.h +++ b/drivers/net/ethernet/cisco/enic/vnic_nic.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_NIC_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_resource.h b/drivers/net/ethernet/cisco/enic/vnic_resource.h index 4e45f88ac1d4..b4776e334d63 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_resource.h +++ b/drivers/net/ethernet/cisco/enic/vnic_resource.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_RESOURCE_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_rq.c b/drivers/net/ethernet/cisco/enic/vnic_rq.c index a3e7b003ada1..5ae80551f17c 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_rq.c +++ b/drivers/net/ethernet/cisco/enic/vnic_rq.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #include @@ -216,4 +203,3 @@ void vnic_rq_clean(struct vnic_rq *rq, vnic_dev_clear_desc_ring(&rq->ring); } - diff --git a/drivers/net/ethernet/cisco/enic/vnic_rq.h b/drivers/net/ethernet/cisco/enic/vnic_rq.h index 0413103ebe94..0bc595abc03b 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_rq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_rq.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_RQ_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_rss.h b/drivers/net/ethernet/cisco/enic/vnic_rss.h index 881fa18542b3..4dcf0e61cb13 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_rss.h +++ b/drivers/net/ethernet/cisco/enic/vnic_rss.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_RSS_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_stats.h b/drivers/net/ethernet/cisco/enic/vnic_stats.h index 74c81ed6fdab..2dd04322d760 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_stats.h +++ b/drivers/net/ethernet/cisco/enic/vnic_stats.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_STATS_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_vic.c b/drivers/net/ethernet/cisco/enic/vnic_vic.c index 24ef8cd40545..20fcb20b42ed 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_vic.c +++ b/drivers/net/ethernet/cisco/enic/vnic_vic.c @@ -1,20 +1,5 @@ -/* - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2010 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/net/ethernet/cisco/enic/vnic_vic.h b/drivers/net/ethernet/cisco/enic/vnic_vic.h index 057776908828..b51c1c52f8bf 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_vic.h +++ b/drivers/net/ethernet/cisco/enic/vnic_vic.h @@ -1,20 +1,5 @@ -/* - * Copyright 2010 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2010 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_VIC_H_ #define _VNIC_VIC_H_ diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.c b/drivers/net/ethernet/cisco/enic/vnic_wq.c index eb75891974df..29c7900349b2 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_wq.c +++ b/drivers/net/ethernet/cisco/enic/vnic_wq.c @@ -1,20 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #include diff --git a/drivers/net/ethernet/cisco/enic/vnic_wq.h b/drivers/net/ethernet/cisco/enic/vnic_wq.h index 01209613d57d..75c526911074 100644 --- a/drivers/net/ethernet/cisco/enic/vnic_wq.h +++ b/drivers/net/ethernet/cisco/enic/vnic_wq.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _VNIC_WQ_H_ diff --git a/drivers/net/ethernet/cisco/enic/wq_enet_desc.h b/drivers/net/ethernet/cisco/enic/wq_enet_desc.h index c7021e3a631f..425e46a804ee 100644 --- a/drivers/net/ethernet/cisco/enic/wq_enet_desc.h +++ b/drivers/net/ethernet/cisco/enic/wq_enet_desc.h @@ -1,20 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008-2010 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * */ #ifndef _WQ_ENET_DESC_H_ diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c index 735dc805dad7..e735e19461ba 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.c @@ -50,10 +50,12 @@ static int mlx5_cmd_stub_update_root_ft(struct mlx5_flow_root_namespace *ns, static int mlx5_cmd_stub_create_flow_table(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft, - unsigned int size, + struct mlx5_flow_table_attr *ft_attr, struct mlx5_flow_table *next_ft) { - ft->max_fte = size ? roundup_pow_of_two(size) : 1; + int max_fte = ft_attr->max_fte; + + ft->max_fte = max_fte ? roundup_pow_of_two(max_fte) : 1; return 0; } @@ -258,7 +260,7 @@ static int mlx5_cmd_update_root_ft(struct mlx5_flow_root_namespace *ns, static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft, - unsigned int size, + struct mlx5_flow_table_attr *ft_attr, struct mlx5_flow_table *next_ft) { int en_encap = !!(ft->flags & MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT); @@ -267,17 +269,19 @@ static int mlx5_cmd_create_flow_table(struct mlx5_flow_root_namespace *ns, u32 out[MLX5_ST_SZ_DW(create_flow_table_out)] = {}; u32 in[MLX5_ST_SZ_DW(create_flow_table_in)] = {}; struct mlx5_core_dev *dev = ns->dev; + unsigned int size; int err; - if (size != POOL_NEXT_SIZE) - size = roundup_pow_of_two(size); - size = mlx5_ft_pool_get_avail_sz(dev, ft->type, size); + if (ft_attr->max_fte != POOL_NEXT_SIZE) + size = roundup_pow_of_two(ft_attr->max_fte); + size = mlx5_ft_pool_get_avail_sz(dev, ft->type, ft_attr->max_fte); if (!size) return -ENOSPC; MLX5_SET(create_flow_table_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_TABLE); + MLX5_SET(create_flow_table_in, in, uid, ft_attr->uid); MLX5_SET(create_flow_table_in, in, table_type, ft->type); MLX5_SET(create_flow_table_in, in, flow_table_context.level, ft->level); MLX5_SET(create_flow_table_in, in, flow_table_context.log_size, size ? ilog2(size) : 0); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h index 274004e80f03..8ef4254b9ea1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_cmd.h @@ -38,7 +38,7 @@ struct mlx5_flow_cmds { int (*create_flow_table)(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft, - unsigned int size, + struct mlx5_flow_table_attr *ft_attr, struct mlx5_flow_table *next_ft); int (*destroy_flow_table)(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c index f1b908d40fa6..e3960cdf5131 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/fs_core.c @@ -1155,7 +1155,7 @@ static struct mlx5_flow_table *__mlx5_create_flow_table(struct mlx5_flow_namespa find_next_chained_ft(fs_prio); ft->def_miss_action = ns->def_miss_action; ft->ns = ns; - err = root->cmds->create_flow_table(root, ft, ft_attr->max_fte, next_ft); + err = root->cmds->create_flow_table(root, ft, ft_attr, next_ft); if (err) goto free_ft; @@ -1195,6 +1195,12 @@ struct mlx5_flow_table *mlx5_create_flow_table(struct mlx5_flow_namespace *ns, } EXPORT_SYMBOL(mlx5_create_flow_table); +u32 mlx5_flow_table_id(struct mlx5_flow_table *ft) +{ + return ft->id; +} +EXPORT_SYMBOL(mlx5_flow_table_id); + struct mlx5_flow_table * mlx5_create_vport_flow_table(struct mlx5_flow_namespace *ns, struct mlx5_flow_table_attr *ft_attr, u16 vport) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c index 223c8741b7ae..16d65fe4f654 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_cmd.c @@ -439,6 +439,7 @@ int mlx5dr_cmd_create_flow_table(struct mlx5_core_dev *mdev, MLX5_SET(create_flow_table_in, in, opcode, MLX5_CMD_OP_CREATE_FLOW_TABLE); MLX5_SET(create_flow_table_in, in, table_type, attr->table_type); + MLX5_SET(create_flow_table_in, in, uid, attr->uid); ft_mdev = MLX5_ADDR_OF(create_flow_table_in, in, flow_table_context); MLX5_SET(flow_table_context, ft_mdev, termination_table, attr->term_tbl); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c index e5f6412baea9..31d443dd8386 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_table.c @@ -214,7 +214,7 @@ static int dr_table_destroy_sw_owned_tbl(struct mlx5dr_table *tbl) tbl->table_type); } -static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl) +static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl, u16 uid) { bool en_encap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT); bool en_decap = !!(tbl->flags & MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); @@ -236,6 +236,7 @@ static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl) ft_attr.sw_owner = true; ft_attr.decap_en = en_decap; ft_attr.reformat_en = en_encap; + ft_attr.uid = uid; ret = mlx5dr_cmd_create_flow_table(tbl->dmn->mdev, &ft_attr, NULL, &tbl->table_id); @@ -243,7 +244,8 @@ static int dr_table_create_sw_owned_tbl(struct mlx5dr_table *tbl) return ret; } -struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level, u32 flags) +struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level, + u32 flags, u16 uid) { struct mlx5dr_table *tbl; int ret; @@ -263,7 +265,7 @@ struct mlx5dr_table *mlx5dr_table_create(struct mlx5dr_domain *dmn, u32 level, u if (ret) goto free_tbl; - ret = dr_table_create_sw_owned_tbl(tbl); + ret = dr_table_create_sw_owned_tbl(tbl, uid); if (ret) goto uninit_tbl; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h index feed227944b7..062c7c74a1f3 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/dr_types.h @@ -1217,6 +1217,7 @@ struct mlx5dr_cmd_query_flow_table_details { struct mlx5dr_cmd_create_flow_table_attr { u32 table_type; + u16 uid; u64 icm_addr_rx; u64 icm_addr_tx; u8 level; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c index 75672663359d..13b6d4721e17 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/fs_dr.c @@ -62,7 +62,7 @@ static int set_miss_action(struct mlx5_flow_root_namespace *ns, static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns, struct mlx5_flow_table *ft, - unsigned int size, + struct mlx5_flow_table_attr *ft_attr, struct mlx5_flow_table *next_ft) { struct mlx5dr_table *tbl; @@ -71,7 +71,7 @@ static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns, if (mlx5_dr_is_fw_table(ft->flags)) return mlx5_fs_cmd_get_fw_cmds()->create_flow_table(ns, ft, - size, + ft_attr, next_ft); flags = ft->flags; /* turn off encap/decap if not supported for sw-str by fw */ @@ -79,7 +79,8 @@ static int mlx5_cmd_dr_create_flow_table(struct mlx5_flow_root_namespace *ns, flags = ft->flags & ~(MLX5_FLOW_TABLE_TUNNEL_EN_REFORMAT | MLX5_FLOW_TABLE_TUNNEL_EN_DECAP); - tbl = mlx5dr_table_create(ns->fs_dr_domain.dr_domain, ft->level, flags); + tbl = mlx5dr_table_create(ns->fs_dr_domain.dr_domain, ft->level, flags, + ft_attr->uid); if (!tbl) { mlx5_core_err(ns->dev, "Failed creating dr flow_table\n"); return -EINVAL; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h index ecffc2cbe6fe..226a0d7bb06d 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/steering/mlx5dr.h @@ -51,7 +51,8 @@ void mlx5dr_domain_set_peer(struct mlx5dr_domain *dmn, struct mlx5dr_domain *peer_dmn); struct mlx5dr_table * -mlx5dr_table_create(struct mlx5dr_domain *domain, u32 level, u32 flags); +mlx5dr_table_create(struct mlx5dr_domain *domain, u32 level, u32 flags, + u16 uid); struct mlx5dr_table * mlx5dr_table_get_from_fs_ft(struct mlx5_flow_table *ft); diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c index 1c1584fca632..3e79c2c51929 100644 --- a/drivers/net/mdio/fwnode_mdio.c +++ b/drivers/net/mdio/fwnode_mdio.c @@ -47,9 +47,7 @@ int fwnode_mdiobus_phy_device_register(struct mii_bus *mdio, * just fall back to poll mode */ if (rc == -EPROBE_DEFER) - rc = driver_deferred_probe_check_state(&phy->mdio.dev); - if (rc == -EPROBE_DEFER) - return rc; + rc = -ENODEV; if (rc > 0) { phy->irq = rc; diff --git a/drivers/net/wireless/marvell/mwifiex/11ac.c b/drivers/net/wireless/marvell/mwifiex/11ac.c index 756f019ef28a..b9278d996c56 100644 --- a/drivers/net/wireless/marvell/mwifiex/11ac.c +++ b/drivers/net/wireless/marvell/mwifiex/11ac.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: 802.11ac * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/11ac.h b/drivers/net/wireless/marvell/mwifiex/11ac.h index 29e83468cf3f..65a88d6d8b88 100644 --- a/drivers/net/wireless/marvell/mwifiex/11ac.h +++ b/drivers/net/wireless/marvell/mwifiex/11ac.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: 802.11ac * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_11AC_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/11h.c b/drivers/net/wireless/marvell/mwifiex/11h.c index 4ca8d0135708..6a9d7bc1f41e 100644 --- a/drivers/net/wireless/marvell/mwifiex/11h.c +++ b/drivers/net/wireless/marvell/mwifiex/11h.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: 802.11h * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "main.h" diff --git a/drivers/net/wireless/marvell/mwifiex/11n.c b/drivers/net/wireless/marvell/mwifiex/11n.c index 9ff2058bcd7e..4af57e6d4393 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.c +++ b/drivers/net/wireless/marvell/mwifiex/11n.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: 802.11n * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/11n.h b/drivers/net/wireless/marvell/mwifiex/11n.h index 83a88eecbda6..94b5e3e4ba08 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n.h +++ b/drivers/net/wireless/marvell/mwifiex/11n.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: 802.11n * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_11N_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c index 46f41dbcf30d..34b4b34276d6 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: 802.11n Aggregation * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/11n_aggr.h b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h index 382c1265c441..69b6888812f1 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_aggr.h +++ b/drivers/net/wireless/marvell/mwifiex/11n_aggr.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: 802.11n Aggregation * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_11N_AGGR_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c index 1046b59647f5..bd835288ce57 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: 802.11n RX Re-ordering * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h index 465f244b3636..c205a3bbc8b3 100644 --- a/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h +++ b/drivers/net/wireless/marvell/mwifiex/11n_rxreorder.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: 802.11n RX Re-ordering * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_11N_RXREORDER_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/Makefile b/drivers/net/wireless/marvell/mwifiex/Makefile index 2bd00f40958e..12d8affced18 100644 --- a/drivers/net/wireless/marvell/mwifiex/Makefile +++ b/drivers/net/wireless/marvell/mwifiex/Makefile @@ -1,18 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0-only # # Copyright 2011-2020 NXP # -# This software file (the "File") is distributed by NXP -# under the terms of the GNU General Public License Version 2, June 1991 -# (the "License"). You may use, redistribute and/or modify this File in -# accordance with the terms and conditions of the License, a copy of which -# is available by writing to the Free Software Foundation, Inc., -# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the -# worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. -# -# THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE -# IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE -# ARE EXPRESSLY DISCLAIMED. The License provides additional details about -# this warranty disclaimer. mwifiex-y += main.o diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.c b/drivers/net/wireless/marvell/mwifiex/cfg80211.c index d68c40e0e122..134114ac1ac0 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.c +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: CFG80211 * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "cfg80211.h" diff --git a/drivers/net/wireless/marvell/mwifiex/cfg80211.h b/drivers/net/wireless/marvell/mwifiex/cfg80211.h index 530a63f13f14..50f7001f5ef0 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfg80211.h +++ b/drivers/net/wireless/marvell/mwifiex/cfg80211.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: CFG80211 * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef __MWIFIEX_CFG80211__ diff --git a/drivers/net/wireless/marvell/mwifiex/cfp.c b/drivers/net/wireless/marvell/mwifiex/cfp.c index fb91ecfc5546..d39092b99212 100644 --- a/drivers/net/wireless/marvell/mwifiex/cfp.c +++ b/drivers/net/wireless/marvell/mwifiex/cfp.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: Channel, Frequence and Power * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/cmdevt.c b/drivers/net/wireless/marvell/mwifiex/cmdevt.c index d6a61f850c6f..d3339d67e7a0 100644 --- a/drivers/net/wireless/marvell/mwifiex/cmdevt.c +++ b/drivers/net/wireless/marvell/mwifiex/cmdevt.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: commands and events * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include diff --git a/drivers/net/wireless/marvell/mwifiex/debugfs.c b/drivers/net/wireless/marvell/mwifiex/debugfs.c index dded92db1f37..bda53cb91f37 100644 --- a/drivers/net/wireless/marvell/mwifiex/debugfs.c +++ b/drivers/net/wireless/marvell/mwifiex/debugfs.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: debugfs * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include diff --git a/drivers/net/wireless/marvell/mwifiex/decl.h b/drivers/net/wireless/marvell/mwifiex/decl.h index 6bd23c9b1eed..88648c062713 100644 --- a/drivers/net/wireless/marvell/mwifiex/decl.h +++ b/drivers/net/wireless/marvell/mwifiex/decl.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: generic data structures and APIs * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_DECL_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/ethtool.c b/drivers/net/wireless/marvell/mwifiex/ethtool.c index 9bdad3f59039..17c6e7fedfc4 100644 --- a/drivers/net/wireless/marvell/mwifiex/ethtool.c +++ b/drivers/net/wireless/marvell/mwifiex/ethtool.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: ethtool * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "main.h" diff --git a/drivers/net/wireless/marvell/mwifiex/fw.h b/drivers/net/wireless/marvell/mwifiex/fw.h index 63c25c69ed2b..26a48d8f49be 100644 --- a/drivers/net/wireless/marvell/mwifiex/fw.h +++ b/drivers/net/wireless/marvell/mwifiex/fw.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: Firmware specific macros & structures * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_FW_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/ie.c b/drivers/net/wireless/marvell/mwifiex/ie.c index 40e99eaf5a30..26694cee15d3 100644 --- a/drivers/net/wireless/marvell/mwifiex/ie.c +++ b/drivers/net/wireless/marvell/mwifiex/ie.c @@ -1,21 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: management IE handling- setting and * deleting IE. * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "main.h" diff --git a/drivers/net/wireless/marvell/mwifiex/init.c b/drivers/net/wireless/marvell/mwifiex/init.c index 88c72d1827a0..fc77489cc511 100644 --- a/drivers/net/wireless/marvell/mwifiex/init.c +++ b/drivers/net/wireless/marvell/mwifiex/init.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: HW/FW Initialization * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/ioctl.h b/drivers/net/wireless/marvell/mwifiex/ioctl.h index 3db449efa167..091e7ca79376 100644 --- a/drivers/net/wireless/marvell/mwifiex/ioctl.h +++ b/drivers/net/wireless/marvell/mwifiex/ioctl.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: ioctl data structures & APIs * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_IOCTL_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/join.c b/drivers/net/wireless/marvell/mwifiex/join.c index 173ccf79cbfc..a6e254a1185c 100644 --- a/drivers/net/wireless/marvell/mwifiex/join.c +++ b/drivers/net/wireless/marvell/mwifiex/join.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: association and ad-hoc start/join * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/main.c b/drivers/net/wireless/marvell/mwifiex/main.c index ace7371c4773..da2e6557e684 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.c +++ b/drivers/net/wireless/marvell/mwifiex/main.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: major functions * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include diff --git a/drivers/net/wireless/marvell/mwifiex/main.h b/drivers/net/wireless/marvell/mwifiex/main.h index 332dd1c8db35..87729d251fed 100644 --- a/drivers/net/wireless/marvell/mwifiex/main.h +++ b/drivers/net/wireless/marvell/mwifiex/main.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: major data structures and prototypes * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_MAIN_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.c b/drivers/net/wireless/marvell/mwifiex/pcie.c index 43bdcbc9ef39..f7f9277602a5 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: PCIE specific handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include diff --git a/drivers/net/wireless/marvell/mwifiex/pcie.h b/drivers/net/wireless/marvell/mwifiex/pcie.h index 981e330c77d7..de901b3b59ad 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie.h @@ -1,22 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* @file mwifiex_pcie.h * * @brief This file contains definitions for PCI-E interface. * driver. * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_PCIE_H diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c index 0234cf3c2974..dd6d21f1dbfd 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.c @@ -1,19 +1,5 @@ -/* - * NXP Wireless LAN device driver: PCIE and platform specific quirks - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ +// SPDX-License-Identifier: GPL-2.0-only +// NXP Wireless LAN device driver: PCIE and platform specific quirks #include diff --git a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h index 8ec4176d698f..d6ff964aec5b 100644 --- a/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h +++ b/drivers/net/wireless/marvell/mwifiex/pcie_quirks.h @@ -1,19 +1,5 @@ -/* - * NXP Wireless LAN device driver: PCIE and platform specific quirks - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* NXP Wireless LAN device driver: PCIE and platform specific quirks */ #include "pcie.h" diff --git a/drivers/net/wireless/marvell/mwifiex/scan.c b/drivers/net/wireless/marvell/mwifiex/scan.c index 0b877f3f6b97..ac8001c84293 100644 --- a/drivers/net/wireless/marvell/mwifiex/scan.c +++ b/drivers/net/wireless/marvell/mwifiex/scan.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: scan ioctl and command handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.c b/drivers/net/wireless/marvell/mwifiex/sdio.c index 9b91580c4f92..b8dc3b5c9ad9 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.c +++ b/drivers/net/wireless/marvell/mwifiex/sdio.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: SDIO specific handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include diff --git a/drivers/net/wireless/marvell/mwifiex/sdio.h b/drivers/net/wireless/marvell/mwifiex/sdio.h index 28e8f76bdd58..3a24bb48b299 100644 --- a/drivers/net/wireless/marvell/mwifiex/sdio.h +++ b/drivers/net/wireless/marvell/mwifiex/sdio.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: SDIO specific definitions * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_SDIO_H diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c index 851ea58fb38e..512b5bb9cf6f 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmd.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: station command handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c index 1a4ae8a42a31..7b69d27e0c0e 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_cmdresp.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: station command response handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/sta_event.c b/drivers/net/wireless/marvell/mwifiex/sta_event.c index 7d42c5d2dbf6..b95e90a7d124 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_event.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_event.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: station event handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c index 4062e515697a..a2ad2b53f016 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_ioctl.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: functions for station ioctl * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/sta_rx.c b/drivers/net/wireless/marvell/mwifiex/sta_rx.c index 0d2adf887900..13659b02ba88 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_rx.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_rx.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: station RX data handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include diff --git a/drivers/net/wireless/marvell/mwifiex/sta_tx.c b/drivers/net/wireless/marvell/mwifiex/sta_tx.c index a9b5eb992220..13c0e67ededf 100644 --- a/drivers/net/wireless/marvell/mwifiex/sta_tx.c +++ b/drivers/net/wireless/marvell/mwifiex/sta_tx.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: station TX data handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/txrx.c b/drivers/net/wireless/marvell/mwifiex/txrx.c index a8479b879382..54c204608dab 100644 --- a/drivers/net/wireless/marvell/mwifiex/txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/txrx.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: generic TX/RX data handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c index 630e1679c3f9..e78a201cd150 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_cmd.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_cmd.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: AP specific command handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "main.h" diff --git a/drivers/net/wireless/marvell/mwifiex/uap_event.c b/drivers/net/wireless/marvell/mwifiex/uap_event.c index 2e25d72dcac5..58ef5020a46a 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_event.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_event.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: AP event handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c index 4e49ed21c5ce..e495f7eaea03 100644 --- a/drivers/net/wireless/marvell/mwifiex/uap_txrx.c +++ b/drivers/net/wireless/marvell/mwifiex/uap_txrx.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: AP TX and RX data handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/usb.c b/drivers/net/wireless/marvell/mwifiex/usb.c index 8f01fcbe9396..c2f2ce2a3f95 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.c +++ b/drivers/net/wireless/marvell/mwifiex/usb.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: USB specific handling * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "main.h" diff --git a/drivers/net/wireless/marvell/mwifiex/usb.h b/drivers/net/wireless/marvell/mwifiex/usb.h index 61a96b7fbf21..7e920b51994c 100644 --- a/drivers/net/wireless/marvell/mwifiex/usb.h +++ b/drivers/net/wireless/marvell/mwifiex/usb.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This file contains definitions for mwifiex USB interface driver. * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_USB_H diff --git a/drivers/net/wireless/marvell/mwifiex/util.c b/drivers/net/wireless/marvell/mwifiex/util.c index d5edb1e89f5b..94c2d219835d 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.c +++ b/drivers/net/wireless/marvell/mwifiex/util.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: utility functions * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/util.h b/drivers/net/wireless/marvell/mwifiex/util.h index 44aa80eb7827..4699c505c0a0 100644 --- a/drivers/net/wireless/marvell/mwifiex/util.h +++ b/drivers/net/wireless/marvell/mwifiex/util.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: utility functions * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_UTIL_H_ diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.c b/drivers/net/wireless/marvell/mwifiex/wmm.c index 0b375608df7d..00a5679b5c51 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.c +++ b/drivers/net/wireless/marvell/mwifiex/wmm.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * NXP Wireless LAN device driver: WMM * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #include "decl.h" diff --git a/drivers/net/wireless/marvell/mwifiex/wmm.h b/drivers/net/wireless/marvell/mwifiex/wmm.h index 1cb3d1804758..4f53a271dae0 100644 --- a/drivers/net/wireless/marvell/mwifiex/wmm.h +++ b/drivers/net/wireless/marvell/mwifiex/wmm.h @@ -1,20 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * NXP Wireless LAN device driver: WMM * * Copyright 2011-2020 NXP - * - * This software file (the "File") is distributed by NXP - * under the terms of the GNU General Public License Version 2, June 1991 - * (the "License"). You may use, redistribute and/or modify this File in - * accordance with the terms and conditions of the License, a copy of which - * is available by writing to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA or on the - * worldwide web at http://www.gnu.org/licenses/old-licenses/gpl-2.0.txt. - * - * THE FILE IS DISTRIBUTED AS-IS, WITHOUT WARRANTY OF ANY KIND, AND THE - * IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE - * ARE EXPRESSLY DISCLAIMED. The License provides additional details about - * this warranty disclaimer. */ #ifndef _MWIFIEX_WMM_H_ diff --git a/drivers/nvme/Kconfig b/drivers/nvme/Kconfig index 87ae409a32b9..656e46d938da 100644 --- a/drivers/nvme/Kconfig +++ b/drivers/nvme/Kconfig @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only menu "NVME Support" +source "drivers/nvme/common/Kconfig" source "drivers/nvme/host/Kconfig" source "drivers/nvme/target/Kconfig" diff --git a/drivers/nvme/Makefile b/drivers/nvme/Makefile index fb42c44609a8..eedca8c72098 100644 --- a/drivers/nvme/Makefile +++ b/drivers/nvme/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_NVME_COMMON) += common/ obj-y += host/ obj-y += target/ diff --git a/drivers/nvme/common/Kconfig b/drivers/nvme/common/Kconfig new file mode 100644 index 000000000000..4514f44362dd --- /dev/null +++ b/drivers/nvme/common/Kconfig @@ -0,0 +1,4 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config NVME_COMMON + tristate diff --git a/drivers/nvme/common/Makefile b/drivers/nvme/common/Makefile new file mode 100644 index 000000000000..720c625b8a52 --- /dev/null +++ b/drivers/nvme/common/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 + +ccflags-y += -I$(src) + +obj-$(CONFIG_NVME_COMMON) += nvme-common.o + +nvme-common-y += auth.o diff --git a/drivers/nvme/common/auth.c b/drivers/nvme/common/auth.c new file mode 100644 index 000000000000..04bd28f17dcc --- /dev/null +++ b/drivers/nvme/common/auth.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static u32 nvme_dhchap_seqnum; +static DEFINE_MUTEX(nvme_dhchap_mutex); + +u32 nvme_auth_get_seqnum(void) +{ + u32 seqnum; + + mutex_lock(&nvme_dhchap_mutex); + if (!nvme_dhchap_seqnum) + nvme_dhchap_seqnum = prandom_u32(); + else { + nvme_dhchap_seqnum++; + if (!nvme_dhchap_seqnum) + nvme_dhchap_seqnum++; + } + seqnum = nvme_dhchap_seqnum; + mutex_unlock(&nvme_dhchap_mutex); + return seqnum; +} +EXPORT_SYMBOL_GPL(nvme_auth_get_seqnum); + +static struct nvme_auth_dhgroup_map { + const char name[16]; + const char kpp[16]; +} dhgroup_map[] = { + [NVME_AUTH_DHGROUP_NULL] = { + .name = "null", .kpp = "null" }, + [NVME_AUTH_DHGROUP_2048] = { + .name = "ffdhe2048", .kpp = "ffdhe2048(dh)" }, + [NVME_AUTH_DHGROUP_3072] = { + .name = "ffdhe3072", .kpp = "ffdhe3072(dh)" }, + [NVME_AUTH_DHGROUP_4096] = { + .name = "ffdhe4096", .kpp = "ffdhe4096(dh)" }, + [NVME_AUTH_DHGROUP_6144] = { + .name = "ffdhe6144", .kpp = "ffdhe6144(dh)" }, + [NVME_AUTH_DHGROUP_8192] = { + .name = "ffdhe8192", .kpp = "ffdhe8192(dh)" }, +}; + +const char *nvme_auth_dhgroup_name(u8 dhgroup_id) +{ + if (dhgroup_id >= ARRAY_SIZE(dhgroup_map)) + return NULL; + return dhgroup_map[dhgroup_id].name; +} +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_name); + +const char *nvme_auth_dhgroup_kpp(u8 dhgroup_id) +{ + if (dhgroup_id >= ARRAY_SIZE(dhgroup_map)) + return NULL; + return dhgroup_map[dhgroup_id].kpp; +} +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_kpp); + +u8 nvme_auth_dhgroup_id(const char *dhgroup_name) +{ + int i; + + if (!dhgroup_name || !strlen(dhgroup_name)) + return NVME_AUTH_DHGROUP_INVALID; + for (i = 0; i < ARRAY_SIZE(dhgroup_map); i++) { + if (!strlen(dhgroup_map[i].name)) + continue; + if (!strncmp(dhgroup_map[i].name, dhgroup_name, + strlen(dhgroup_map[i].name))) + return i; + } + return NVME_AUTH_DHGROUP_INVALID; +} +EXPORT_SYMBOL_GPL(nvme_auth_dhgroup_id); + +static struct nvme_dhchap_hash_map { + int len; + const char hmac[15]; + const char digest[8]; +} hash_map[] = { + [NVME_AUTH_HASH_SHA256] = { + .len = 32, + .hmac = "hmac(sha256)", + .digest = "sha256", + }, + [NVME_AUTH_HASH_SHA384] = { + .len = 48, + .hmac = "hmac(sha384)", + .digest = "sha384", + }, + [NVME_AUTH_HASH_SHA512] = { + .len = 64, + .hmac = "hmac(sha512)", + .digest = "sha512", + }, +}; + +const char *nvme_auth_hmac_name(u8 hmac_id) +{ + if (hmac_id >= ARRAY_SIZE(hash_map)) + return NULL; + return hash_map[hmac_id].hmac; +} +EXPORT_SYMBOL_GPL(nvme_auth_hmac_name); + +const char *nvme_auth_digest_name(u8 hmac_id) +{ + if (hmac_id >= ARRAY_SIZE(hash_map)) + return NULL; + return hash_map[hmac_id].digest; +} +EXPORT_SYMBOL_GPL(nvme_auth_digest_name); + +u8 nvme_auth_hmac_id(const char *hmac_name) +{ + int i; + + if (!hmac_name || !strlen(hmac_name)) + return NVME_AUTH_HASH_INVALID; + + for (i = 0; i < ARRAY_SIZE(hash_map); i++) { + if (!strlen(hash_map[i].hmac)) + continue; + if (!strncmp(hash_map[i].hmac, hmac_name, + strlen(hash_map[i].hmac))) + return i; + } + return NVME_AUTH_HASH_INVALID; +} +EXPORT_SYMBOL_GPL(nvme_auth_hmac_id); + +size_t nvme_auth_hmac_hash_len(u8 hmac_id) +{ + if (hmac_id >= ARRAY_SIZE(hash_map)) + return 0; + return hash_map[hmac_id].len; +} +EXPORT_SYMBOL_GPL(nvme_auth_hmac_hash_len); + +struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, + u8 key_hash) +{ + struct nvme_dhchap_key *key; + unsigned char *p; + u32 crc; + int ret, key_len; + size_t allocated_len = strlen(secret); + + /* Secret might be affixed with a ':' */ + p = strrchr(secret, ':'); + if (p) + allocated_len = p - secret; + key = kzalloc(sizeof(*key), GFP_KERNEL); + if (!key) + return ERR_PTR(-ENOMEM); + key->key = kzalloc(allocated_len, GFP_KERNEL); + if (!key->key) { + ret = -ENOMEM; + goto out_free_key; + } + + key_len = base64_decode(secret, allocated_len, key->key); + if (key_len < 0) { + pr_debug("base64 key decoding error %d\n", + key_len); + ret = key_len; + goto out_free_secret; + } + + if (key_len != 36 && key_len != 52 && + key_len != 68) { + pr_err("Invalid key len %d\n", key_len); + ret = -EINVAL; + goto out_free_secret; + } + + if (key_hash > 0 && + (key_len - 4) != nvme_auth_hmac_hash_len(key_hash)) { + pr_err("Mismatched key len %d for %s\n", key_len, + nvme_auth_hmac_name(key_hash)); + ret = -EINVAL; + goto out_free_secret; + } + + /* The last four bytes is the CRC in little-endian format */ + key_len -= 4; + /* + * The linux implementation doesn't do pre- and post-increments, + * so we have to do it manually. + */ + crc = ~crc32(~0, key->key, key_len); + + if (get_unaligned_le32(key->key + key_len) != crc) { + pr_err("key crc mismatch (key %08x, crc %08x)\n", + get_unaligned_le32(key->key + key_len), crc); + ret = -EKEYREJECTED; + goto out_free_secret; + } + key->len = key_len; + key->hash = key_hash; + return key; +out_free_secret: + kfree_sensitive(key->key); +out_free_key: + kfree(key); + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(nvme_auth_extract_key); + +void nvme_auth_free_key(struct nvme_dhchap_key *key) +{ + if (!key) + return; + kfree_sensitive(key->key); + kfree(key); +} +EXPORT_SYMBOL_GPL(nvme_auth_free_key); + +u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn) +{ + const char *hmac_name; + struct crypto_shash *key_tfm; + struct shash_desc *shash; + u8 *transformed_key; + int ret; + + if (!key || !key->key) { + pr_warn("No key specified\n"); + return ERR_PTR(-ENOKEY); + } + if (key->hash == 0) { + transformed_key = kmemdup(key->key, key->len, GFP_KERNEL); + return transformed_key ? transformed_key : ERR_PTR(-ENOMEM); + } + hmac_name = nvme_auth_hmac_name(key->hash); + if (!hmac_name) { + pr_warn("Invalid key hash id %d\n", key->hash); + return ERR_PTR(-EINVAL); + } + + key_tfm = crypto_alloc_shash(hmac_name, 0, 0); + if (IS_ERR(key_tfm)) + return (u8 *)key_tfm; + + shash = kmalloc(sizeof(struct shash_desc) + + crypto_shash_descsize(key_tfm), + GFP_KERNEL); + if (!shash) { + ret = -ENOMEM; + goto out_free_key; + } + + transformed_key = kzalloc(crypto_shash_digestsize(key_tfm), GFP_KERNEL); + if (!transformed_key) { + ret = -ENOMEM; + goto out_free_shash; + } + + shash->tfm = key_tfm; + ret = crypto_shash_setkey(key_tfm, key->key, key->len); + if (ret < 0) + goto out_free_transformed_key; + ret = crypto_shash_init(shash); + if (ret < 0) + goto out_free_transformed_key; + ret = crypto_shash_update(shash, nqn, strlen(nqn)); + if (ret < 0) + goto out_free_transformed_key; + ret = crypto_shash_update(shash, "NVMe-over-Fabrics", 17); + if (ret < 0) + goto out_free_transformed_key; + ret = crypto_shash_final(shash, transformed_key); + if (ret < 0) + goto out_free_transformed_key; + + kfree(shash); + crypto_free_shash(key_tfm); + + return transformed_key; + +out_free_transformed_key: + kfree_sensitive(transformed_key); +out_free_shash: + kfree(shash); +out_free_key: + crypto_free_shash(key_tfm); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(nvme_auth_transform_key); + +static int nvme_auth_hash_skey(int hmac_id, u8 *skey, size_t skey_len, u8 *hkey) +{ + const char *digest_name; + struct crypto_shash *tfm; + int ret; + + digest_name = nvme_auth_digest_name(hmac_id); + if (!digest_name) { + pr_debug("%s: failed to get digest for %d\n", __func__, + hmac_id); + return -EINVAL; + } + tfm = crypto_alloc_shash(digest_name, 0, 0); + if (IS_ERR(tfm)) + return -ENOMEM; + + ret = crypto_shash_tfm_digest(tfm, skey, skey_len, hkey); + if (ret < 0) + pr_debug("%s: Failed to hash digest len %zu\n", __func__, + skey_len); + + crypto_free_shash(tfm); + return ret; +} + +int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, + u8 *challenge, u8 *aug, size_t hlen) +{ + struct crypto_shash *tfm; + struct shash_desc *desc; + u8 *hashed_key; + const char *hmac_name; + int ret; + + hashed_key = kmalloc(hlen, GFP_KERNEL); + if (!hashed_key) + return -ENOMEM; + + ret = nvme_auth_hash_skey(hmac_id, skey, + skey_len, hashed_key); + if (ret < 0) + goto out_free_key; + + hmac_name = nvme_auth_hmac_name(hmac_id); + if (!hmac_name) { + pr_warn("%s: invalid hash algorithm %d\n", + __func__, hmac_id); + ret = -EINVAL; + goto out_free_key; + } + + tfm = crypto_alloc_shash(hmac_name, 0, 0); + if (IS_ERR(tfm)) { + ret = PTR_ERR(tfm); + goto out_free_key; + } + + desc = kmalloc(sizeof(struct shash_desc) + crypto_shash_descsize(tfm), + GFP_KERNEL); + if (!desc) { + ret = -ENOMEM; + goto out_free_hash; + } + desc->tfm = tfm; + + ret = crypto_shash_setkey(tfm, hashed_key, hlen); + if (ret) + goto out_free_desc; + + ret = crypto_shash_init(desc); + if (ret) + goto out_free_desc; + + ret = crypto_shash_update(desc, challenge, hlen); + if (ret) + goto out_free_desc; + + ret = crypto_shash_final(desc, aug); +out_free_desc: + kfree_sensitive(desc); +out_free_hash: + crypto_free_shash(tfm); +out_free_key: + kfree_sensitive(hashed_key); + return ret; +} +EXPORT_SYMBOL_GPL(nvme_auth_augmented_challenge); + +int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid) +{ + int ret; + + ret = crypto_kpp_set_secret(dh_tfm, NULL, 0); + if (ret) + pr_debug("failed to set private key, error %d\n", ret); + + return ret; +} +EXPORT_SYMBOL_GPL(nvme_auth_gen_privkey); + +int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, + u8 *host_key, size_t host_key_len) +{ + struct kpp_request *req; + struct crypto_wait wait; + struct scatterlist dst; + int ret; + + req = kpp_request_alloc(dh_tfm, GFP_KERNEL); + if (!req) + return -ENOMEM; + + crypto_init_wait(&wait); + kpp_request_set_input(req, NULL, 0); + sg_init_one(&dst, host_key, host_key_len); + kpp_request_set_output(req, &dst, host_key_len); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + + ret = crypto_wait_req(crypto_kpp_generate_public_key(req), &wait); + kpp_request_free(req); + return ret; +} +EXPORT_SYMBOL_GPL(nvme_auth_gen_pubkey); + +int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, + u8 *ctrl_key, size_t ctrl_key_len, + u8 *sess_key, size_t sess_key_len) +{ + struct kpp_request *req; + struct crypto_wait wait; + struct scatterlist src, dst; + int ret; + + req = kpp_request_alloc(dh_tfm, GFP_KERNEL); + if (!req) + return -ENOMEM; + + crypto_init_wait(&wait); + sg_init_one(&src, ctrl_key, ctrl_key_len); + kpp_request_set_input(req, &src, ctrl_key_len); + sg_init_one(&dst, sess_key, sess_key_len); + kpp_request_set_output(req, &dst, sess_key_len); + kpp_request_set_callback(req, CRYPTO_TFM_REQ_MAY_BACKLOG, + crypto_req_done, &wait); + + ret = crypto_wait_req(crypto_kpp_compute_shared_secret(req), &wait); + + kpp_request_free(req); + return ret; +} +EXPORT_SYMBOL_GPL(nvme_auth_gen_shared_secret); + +int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key) +{ + struct nvme_dhchap_key *key; + u8 key_hash; + + if (!secret) { + *ret_key = NULL; + return 0; + } + + if (sscanf(secret, "DHHC-1:%hhd:%*s:", &key_hash) != 1) + return -EINVAL; + + /* Pass in the secret without the 'DHHC-1:XX:' prefix */ + key = nvme_auth_extract_key(secret + 10, key_hash); + if (IS_ERR(key)) { + *ret_key = NULL; + return PTR_ERR(key); + } + + *ret_key = key; + return 0; +} +EXPORT_SYMBOL_GPL(nvme_auth_generate_key); + +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/host/Kconfig b/drivers/nvme/host/Kconfig index 877d2ec4ea9f..2f6a7f8c94e8 100644 --- a/drivers/nvme/host/Kconfig +++ b/drivers/nvme/host/Kconfig @@ -92,6 +92,21 @@ config NVME_TCP If unsure, say N. +config NVME_AUTH + bool "NVM Express over Fabrics In-Band Authentication" + depends on NVME_CORE + select NVME_COMMON + select CRYPTO + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_DH + select CRYPTO_DH_RFC7919_GROUPS + help + This provides support for NVMe over Fabrics In-Band Authentication. + + If unsure, say N. + config NVME_APPLE tristate "Apple ANS2 NVM Express host driver" depends on OF && BLOCK diff --git a/drivers/nvme/host/Makefile b/drivers/nvme/host/Makefile index a36ae1612059..e27202d22c7d 100644 --- a/drivers/nvme/host/Makefile +++ b/drivers/nvme/host/Makefile @@ -10,12 +10,14 @@ obj-$(CONFIG_NVME_FC) += nvme-fc.o obj-$(CONFIG_NVME_TCP) += nvme-tcp.o obj-$(CONFIG_NVME_APPLE) += nvme-apple.o -nvme-core-y := core.o ioctl.o constants.o +nvme-core-y += core.o ioctl.o +nvme-core-$(CONFIG_NVME_VERBOSE_ERRORS) += constants.o nvme-core-$(CONFIG_TRACING) += trace.o nvme-core-$(CONFIG_NVME_MULTIPATH) += multipath.o nvme-core-$(CONFIG_BLK_DEV_ZONED) += zns.o nvme-core-$(CONFIG_FAULT_INJECTION_DEBUG_FS) += fault_inject.o nvme-core-$(CONFIG_NVME_HWMON) += hwmon.o +nvme-core-$(CONFIG_NVME_AUTH) += auth.o nvme-y += pci.o diff --git a/drivers/nvme/host/apple.c b/drivers/nvme/host/apple.c index 5c352d5d8ee6..5fc5ea196b40 100644 --- a/drivers/nvme/host/apple.c +++ b/drivers/nvme/host/apple.c @@ -845,11 +845,8 @@ static void apple_nvme_disable(struct apple_nvme *anv, bool shutdown) apple_nvme_handle_cq(&anv->adminq, true); spin_unlock_irqrestore(&anv->lock, flags); - blk_mq_tagset_busy_iter(&anv->tagset, nvme_cancel_request, &anv->ctrl); - blk_mq_tagset_busy_iter(&anv->admin_tagset, nvme_cancel_request, - &anv->ctrl); - blk_mq_tagset_wait_completed_request(&anv->tagset); - blk_mq_tagset_wait_completed_request(&anv->admin_tagset); + nvme_cancel_tagset(&anv->ctrl); + nvme_cancel_admin_tagset(&anv->ctrl); /* * The driver will not be starting up queues again if shutting down so @@ -1222,6 +1219,11 @@ static void apple_nvme_async_probe(void *data, async_cookie_t cookie) nvme_put_ctrl(&anv->ctrl); } +static void devm_apple_nvme_put_tag_set(void *data) +{ + blk_mq_free_tag_set(data); +} + static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) { int ret; @@ -1238,8 +1240,7 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) ret = blk_mq_alloc_tag_set(&anv->admin_tagset); if (ret) return ret; - ret = devm_add_action_or_reset(anv->dev, - (void (*)(void *))blk_mq_free_tag_set, + ret = devm_add_action_or_reset(anv->dev, devm_apple_nvme_put_tag_set, &anv->admin_tagset); if (ret) return ret; @@ -1263,8 +1264,8 @@ static int apple_nvme_alloc_tagsets(struct apple_nvme *anv) ret = blk_mq_alloc_tag_set(&anv->tagset); if (ret) return ret; - ret = devm_add_action_or_reset( - anv->dev, (void (*)(void *))blk_mq_free_tag_set, &anv->tagset); + ret = devm_add_action_or_reset(anv->dev, devm_apple_nvme_put_tag_set, + &anv->tagset); if (ret) return ret; @@ -1365,6 +1366,11 @@ static int apple_nvme_attach_genpd(struct apple_nvme *anv) return 0; } +static void devm_apple_nvme_mempool_destroy(void *data) +{ + mempool_destroy(data); +} + static int apple_nvme_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1462,8 +1468,8 @@ static int apple_nvme_probe(struct platform_device *pdev) ret = -ENOMEM; goto put_dev; } - ret = devm_add_action_or_reset( - anv->dev, (void (*)(void *))mempool_destroy, anv->iod_mempool); + ret = devm_add_action_or_reset(anv->dev, + devm_apple_nvme_mempool_destroy, anv->iod_mempool); if (ret) goto put_dev; diff --git a/drivers/nvme/host/auth.c b/drivers/nvme/host/auth.c new file mode 100644 index 000000000000..c8a6db7c4498 --- /dev/null +++ b/drivers/nvme/host/auth.c @@ -0,0 +1,1017 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020 Hannes Reinecke, SUSE Linux + */ + +#include +#include +#include +#include +#include +#include +#include "nvme.h" +#include "fabrics.h" +#include + +struct nvme_dhchap_queue_context { + struct list_head entry; + struct work_struct auth_work; + struct nvme_ctrl *ctrl; + struct crypto_shash *shash_tfm; + struct crypto_kpp *dh_tfm; + void *buf; + size_t buf_size; + int qid; + int error; + u32 s1; + u32 s2; + u16 transaction; + u8 status; + u8 hash_id; + size_t hash_len; + u8 dhgroup_id; + u8 c1[64]; + u8 c2[64]; + u8 response[64]; + u8 *host_response; + u8 *ctrl_key; + int ctrl_key_len; + u8 *host_key; + int host_key_len; + u8 *sess_key; + int sess_key_len; +}; + +#define nvme_auth_flags_from_qid(qid) \ + (qid == 0) ? 0 : BLK_MQ_REQ_NOWAIT | BLK_MQ_REQ_RESERVED +#define nvme_auth_queue_from_qid(ctrl, qid) \ + (qid == 0) ? (ctrl)->fabrics_q : (ctrl)->connect_q + +static int nvme_auth_submit(struct nvme_ctrl *ctrl, int qid, + void *data, size_t data_len, bool auth_send) +{ + struct nvme_command cmd = {}; + blk_mq_req_flags_t flags = nvme_auth_flags_from_qid(qid); + struct request_queue *q = nvme_auth_queue_from_qid(ctrl, qid); + int ret; + + cmd.auth_common.opcode = nvme_fabrics_command; + cmd.auth_common.secp = NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER; + cmd.auth_common.spsp0 = 0x01; + cmd.auth_common.spsp1 = 0x01; + if (auth_send) { + cmd.auth_send.fctype = nvme_fabrics_type_auth_send; + cmd.auth_send.tl = cpu_to_le32(data_len); + } else { + cmd.auth_receive.fctype = nvme_fabrics_type_auth_receive; + cmd.auth_receive.al = cpu_to_le32(data_len); + } + + ret = __nvme_submit_sync_cmd(q, &cmd, NULL, data, data_len, + qid == 0 ? NVME_QID_ANY : qid, + 0, flags); + if (ret > 0) + dev_warn(ctrl->device, + "qid %d auth_send failed with status %d\n", qid, ret); + else if (ret < 0) + dev_err(ctrl->device, + "qid %d auth_send failed with error %d\n", qid, ret); + return ret; +} + +static int nvme_auth_receive_validate(struct nvme_ctrl *ctrl, int qid, + struct nvmf_auth_dhchap_failure_data *data, + u16 transaction, u8 expected_msg) +{ + dev_dbg(ctrl->device, "%s: qid %d auth_type %d auth_id %x\n", + __func__, qid, data->auth_type, data->auth_id); + + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES && + data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { + return data->rescode_exp; + } + if (data->auth_type != NVME_AUTH_DHCHAP_MESSAGES || + data->auth_id != expected_msg) { + dev_warn(ctrl->device, + "qid %d invalid message %02x/%02x\n", + qid, data->auth_type, data->auth_id); + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE; + } + if (le16_to_cpu(data->t_id) != transaction) { + dev_warn(ctrl->device, + "qid %d invalid transaction ID %d\n", + qid, le16_to_cpu(data->t_id)); + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE; + } + return 0; +} + +static int nvme_auth_set_dhchap_negotiate_data(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + struct nvmf_auth_dhchap_negotiate_data *data = chap->buf; + size_t size = sizeof(*data) + sizeof(union nvmf_auth_protocol); + + if (chap->buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return -EINVAL; + } + memset((u8 *)chap->buf, 0, size); + data->auth_type = NVME_AUTH_COMMON_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; + data->t_id = cpu_to_le16(chap->transaction); + data->sc_c = 0; /* No secure channel concatenation */ + data->napd = 1; + data->auth_protocol[0].dhchap.authid = NVME_AUTH_DHCHAP_AUTH_ID; + data->auth_protocol[0].dhchap.halen = 3; + data->auth_protocol[0].dhchap.dhlen = 6; + data->auth_protocol[0].dhchap.idlist[0] = NVME_AUTH_HASH_SHA256; + data->auth_protocol[0].dhchap.idlist[1] = NVME_AUTH_HASH_SHA384; + data->auth_protocol[0].dhchap.idlist[2] = NVME_AUTH_HASH_SHA512; + data->auth_protocol[0].dhchap.idlist[30] = NVME_AUTH_DHGROUP_NULL; + data->auth_protocol[0].dhchap.idlist[31] = NVME_AUTH_DHGROUP_2048; + data->auth_protocol[0].dhchap.idlist[32] = NVME_AUTH_DHGROUP_3072; + data->auth_protocol[0].dhchap.idlist[33] = NVME_AUTH_DHGROUP_4096; + data->auth_protocol[0].dhchap.idlist[34] = NVME_AUTH_DHGROUP_6144; + data->auth_protocol[0].dhchap.idlist[35] = NVME_AUTH_DHGROUP_8192; + + return size; +} + +static int nvme_auth_process_dhchap_challenge(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + struct nvmf_auth_dhchap_challenge_data *data = chap->buf; + u16 dhvlen = le16_to_cpu(data->dhvlen); + size_t size = sizeof(*data) + data->hl + dhvlen; + const char *gid_name = nvme_auth_dhgroup_name(data->dhgid); + const char *hmac_name, *kpp_name; + + if (chap->buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return NVME_SC_INVALID_FIELD; + } + + hmac_name = nvme_auth_hmac_name(data->hashid); + if (!hmac_name) { + dev_warn(ctrl->device, + "qid %d: invalid HASH ID %d\n", + chap->qid, data->hashid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return NVME_SC_INVALID_FIELD; + } + + if (chap->hash_id == data->hashid && chap->shash_tfm && + !strcmp(crypto_shash_alg_name(chap->shash_tfm), hmac_name) && + crypto_shash_digestsize(chap->shash_tfm) == data->hl) { + dev_dbg(ctrl->device, + "qid %d: reuse existing hash %s\n", + chap->qid, hmac_name); + goto select_kpp; + } + + /* Reset if hash cannot be reused */ + if (chap->shash_tfm) { + crypto_free_shash(chap->shash_tfm); + chap->hash_id = 0; + chap->hash_len = 0; + } + chap->shash_tfm = crypto_alloc_shash(hmac_name, 0, + CRYPTO_ALG_ALLOCATES_MEMORY); + if (IS_ERR(chap->shash_tfm)) { + dev_warn(ctrl->device, + "qid %d: failed to allocate hash %s, error %ld\n", + chap->qid, hmac_name, PTR_ERR(chap->shash_tfm)); + chap->shash_tfm = NULL; + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; + return NVME_SC_AUTH_REQUIRED; + } + + if (crypto_shash_digestsize(chap->shash_tfm) != data->hl) { + dev_warn(ctrl->device, + "qid %d: invalid hash length %d\n", + chap->qid, data->hl); + crypto_free_shash(chap->shash_tfm); + chap->shash_tfm = NULL; + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return NVME_SC_AUTH_REQUIRED; + } + + /* Reset host response if the hash had been changed */ + if (chap->hash_id != data->hashid) { + kfree(chap->host_response); + chap->host_response = NULL; + } + + chap->hash_id = data->hashid; + chap->hash_len = data->hl; + dev_dbg(ctrl->device, "qid %d: selected hash %s\n", + chap->qid, hmac_name); + +select_kpp: + kpp_name = nvme_auth_dhgroup_kpp(data->dhgid); + if (!kpp_name) { + dev_warn(ctrl->device, + "qid %d: invalid DH group id %d\n", + chap->qid, data->dhgid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + /* Leave previous dh_tfm intact */ + return NVME_SC_AUTH_REQUIRED; + } + + /* Clear host and controller key to avoid accidental reuse */ + kfree_sensitive(chap->host_key); + chap->host_key = NULL; + chap->host_key_len = 0; + kfree_sensitive(chap->ctrl_key); + chap->ctrl_key = NULL; + chap->ctrl_key_len = 0; + + if (chap->dhgroup_id == data->dhgid && + (data->dhgid == NVME_AUTH_DHGROUP_NULL || chap->dh_tfm)) { + dev_dbg(ctrl->device, + "qid %d: reuse existing DH group %s\n", + chap->qid, gid_name); + goto skip_kpp; + } + + /* Reset dh_tfm if it can't be reused */ + if (chap->dh_tfm) { + crypto_free_kpp(chap->dh_tfm); + chap->dh_tfm = NULL; + } + + if (data->dhgid != NVME_AUTH_DHGROUP_NULL) { + if (dhvlen == 0) { + dev_warn(ctrl->device, + "qid %d: empty DH value\n", + chap->qid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + return NVME_SC_INVALID_FIELD; + } + + chap->dh_tfm = crypto_alloc_kpp(kpp_name, 0, 0); + if (IS_ERR(chap->dh_tfm)) { + int ret = PTR_ERR(chap->dh_tfm); + + dev_warn(ctrl->device, + "qid %d: error %d initializing DH group %s\n", + chap->qid, ret, gid_name); + chap->status = NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + chap->dh_tfm = NULL; + return NVME_SC_AUTH_REQUIRED; + } + dev_dbg(ctrl->device, "qid %d: selected DH group %s\n", + chap->qid, gid_name); + } else if (dhvlen != 0) { + dev_warn(ctrl->device, + "qid %d: invalid DH value for NULL DH\n", + chap->qid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return NVME_SC_INVALID_FIELD; + } + chap->dhgroup_id = data->dhgid; + +skip_kpp: + chap->s1 = le32_to_cpu(data->seqnum); + memcpy(chap->c1, data->cval, chap->hash_len); + if (dhvlen) { + chap->ctrl_key = kmalloc(dhvlen, GFP_KERNEL); + if (!chap->ctrl_key) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; + return NVME_SC_AUTH_REQUIRED; + } + chap->ctrl_key_len = dhvlen; + memcpy(chap->ctrl_key, data->cval + chap->hash_len, + dhvlen); + dev_dbg(ctrl->device, "ctrl public key %*ph\n", + (int)chap->ctrl_key_len, chap->ctrl_key); + } + + return 0; +} + +static int nvme_auth_set_dhchap_reply_data(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + struct nvmf_auth_dhchap_reply_data *data = chap->buf; + size_t size = sizeof(*data); + + size += 2 * chap->hash_len; + + if (chap->host_key_len) + size += chap->host_key_len; + + if (chap->buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return -EINVAL; + } + + memset(chap->buf, 0, size); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_REPLY; + data->t_id = cpu_to_le16(chap->transaction); + data->hl = chap->hash_len; + data->dhvlen = cpu_to_le16(chap->host_key_len); + memcpy(data->rval, chap->response, chap->hash_len); + if (ctrl->ctrl_key) { + get_random_bytes(chap->c2, chap->hash_len); + data->cvalid = 1; + chap->s2 = nvme_auth_get_seqnum(); + memcpy(data->rval + chap->hash_len, chap->c2, + chap->hash_len); + dev_dbg(ctrl->device, "%s: qid %d ctrl challenge %*ph\n", + __func__, chap->qid, (int)chap->hash_len, chap->c2); + } else { + memset(chap->c2, 0, chap->hash_len); + chap->s2 = 0; + } + data->seqnum = cpu_to_le32(chap->s2); + if (chap->host_key_len) { + dev_dbg(ctrl->device, "%s: qid %d host public key %*ph\n", + __func__, chap->qid, + chap->host_key_len, chap->host_key); + memcpy(data->rval + 2 * chap->hash_len, chap->host_key, + chap->host_key_len); + } + + return size; +} + +static int nvme_auth_process_dhchap_success1(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + struct nvmf_auth_dhchap_success1_data *data = chap->buf; + size_t size = sizeof(*data); + + if (ctrl->ctrl_key) + size += chap->hash_len; + + if (chap->buf_size < size) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return NVME_SC_INVALID_FIELD; + } + + if (data->hl != chap->hash_len) { + dev_warn(ctrl->device, + "qid %d: invalid hash length %u\n", + chap->qid, data->hl); + chap->status = NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + return NVME_SC_INVALID_FIELD; + } + + /* Just print out information for the admin queue */ + if (chap->qid == 0) + dev_info(ctrl->device, + "qid 0: authenticated with hash %s dhgroup %s\n", + nvme_auth_hmac_name(chap->hash_id), + nvme_auth_dhgroup_name(chap->dhgroup_id)); + + if (!data->rvalid) + return 0; + + /* Validate controller response */ + if (memcmp(chap->response, data->rval, data->hl)) { + dev_dbg(ctrl->device, "%s: qid %d ctrl response %*ph\n", + __func__, chap->qid, (int)chap->hash_len, data->rval); + dev_dbg(ctrl->device, "%s: qid %d host response %*ph\n", + __func__, chap->qid, (int)chap->hash_len, + chap->response); + dev_warn(ctrl->device, + "qid %d: controller authentication failed\n", + chap->qid); + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; + return NVME_SC_AUTH_REQUIRED; + } + + /* Just print out information for the admin queue */ + if (chap->qid == 0) + dev_info(ctrl->device, + "qid 0: controller authenticated\n"); + return 0; +} + +static int nvme_auth_set_dhchap_success2_data(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + struct nvmf_auth_dhchap_success2_data *data = chap->buf; + size_t size = sizeof(*data); + + memset(chap->buf, 0, size); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2; + data->t_id = cpu_to_le16(chap->transaction); + + return size; +} + +static int nvme_auth_set_dhchap_failure2_data(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + struct nvmf_auth_dhchap_failure_data *data = chap->buf; + size_t size = sizeof(*data); + + memset(chap->buf, 0, size); + data->auth_type = NVME_AUTH_COMMON_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; + data->t_id = cpu_to_le16(chap->transaction); + data->rescode = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED; + data->rescode_exp = chap->status; + + return size; +} + +static int nvme_auth_dhchap_setup_host_response(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + u8 buf[4], *challenge = chap->c1; + int ret; + + dev_dbg(ctrl->device, "%s: qid %d host response seq %u transaction %d\n", + __func__, chap->qid, chap->s1, chap->transaction); + + if (!chap->host_response) { + chap->host_response = nvme_auth_transform_key(ctrl->host_key, + ctrl->opts->host->nqn); + if (IS_ERR(chap->host_response)) { + ret = PTR_ERR(chap->host_response); + chap->host_response = NULL; + return ret; + } + } else { + dev_dbg(ctrl->device, "%s: qid %d re-using host response\n", + __func__, chap->qid); + } + + ret = crypto_shash_setkey(chap->shash_tfm, + chap->host_response, ctrl->host_key->len); + if (ret) { + dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n", + chap->qid, ret); + goto out; + } + + if (chap->dh_tfm) { + challenge = kmalloc(chap->hash_len, GFP_KERNEL); + if (!challenge) { + ret = -ENOMEM; + goto out; + } + ret = nvme_auth_augmented_challenge(chap->hash_id, + chap->sess_key, + chap->sess_key_len, + chap->c1, challenge, + chap->hash_len); + if (ret) + goto out; + } + + shash->tfm = chap->shash_tfm; + ret = crypto_shash_init(shash); + if (ret) + goto out; + ret = crypto_shash_update(shash, challenge, chap->hash_len); + if (ret) + goto out; + put_unaligned_le32(chap->s1, buf); + ret = crypto_shash_update(shash, buf, 4); + if (ret) + goto out; + put_unaligned_le16(chap->transaction, buf); + ret = crypto_shash_update(shash, buf, 2); + if (ret) + goto out; + memset(buf, 0, sizeof(buf)); + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, "HostHost", 8); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn)); + if (ret) + goto out; + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn)); + if (ret) + goto out; + ret = crypto_shash_final(shash, chap->response); +out: + if (challenge != chap->c1) + kfree(challenge); + return ret; +} + +static int nvme_auth_dhchap_setup_ctrl_response(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + SHASH_DESC_ON_STACK(shash, chap->shash_tfm); + u8 *ctrl_response; + u8 buf[4], *challenge = chap->c2; + int ret; + + ctrl_response = nvme_auth_transform_key(ctrl->ctrl_key, + ctrl->opts->subsysnqn); + if (IS_ERR(ctrl_response)) { + ret = PTR_ERR(ctrl_response); + return ret; + } + ret = crypto_shash_setkey(chap->shash_tfm, + ctrl_response, ctrl->ctrl_key->len); + if (ret) { + dev_warn(ctrl->device, "qid %d: failed to set key, error %d\n", + chap->qid, ret); + goto out; + } + + if (chap->dh_tfm) { + challenge = kmalloc(chap->hash_len, GFP_KERNEL); + if (!challenge) { + ret = -ENOMEM; + goto out; + } + ret = nvme_auth_augmented_challenge(chap->hash_id, + chap->sess_key, + chap->sess_key_len, + chap->c2, challenge, + chap->hash_len); + if (ret) + goto out; + } + dev_dbg(ctrl->device, "%s: qid %d ctrl response seq %u transaction %d\n", + __func__, chap->qid, chap->s2, chap->transaction); + dev_dbg(ctrl->device, "%s: qid %d challenge %*ph\n", + __func__, chap->qid, (int)chap->hash_len, challenge); + dev_dbg(ctrl->device, "%s: qid %d subsysnqn %s\n", + __func__, chap->qid, ctrl->opts->subsysnqn); + dev_dbg(ctrl->device, "%s: qid %d hostnqn %s\n", + __func__, chap->qid, ctrl->opts->host->nqn); + shash->tfm = chap->shash_tfm; + ret = crypto_shash_init(shash); + if (ret) + goto out; + ret = crypto_shash_update(shash, challenge, chap->hash_len); + if (ret) + goto out; + put_unaligned_le32(chap->s2, buf); + ret = crypto_shash_update(shash, buf, 4); + if (ret) + goto out; + put_unaligned_le16(chap->transaction, buf); + ret = crypto_shash_update(shash, buf, 2); + if (ret) + goto out; + memset(buf, 0, 4); + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, "Controller", 10); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->opts->subsysnqn, + strlen(ctrl->opts->subsysnqn)); + if (ret) + goto out; + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->opts->host->nqn, + strlen(ctrl->opts->host->nqn)); + if (ret) + goto out; + ret = crypto_shash_final(shash, chap->response); +out: + if (challenge != chap->c2) + kfree(challenge); + kfree(ctrl_response); + return ret; +} + +static int nvme_auth_dhchap_exponential(struct nvme_ctrl *ctrl, + struct nvme_dhchap_queue_context *chap) +{ + int ret; + + if (chap->host_key && chap->host_key_len) { + dev_dbg(ctrl->device, + "qid %d: reusing host key\n", chap->qid); + goto gen_sesskey; + } + ret = nvme_auth_gen_privkey(chap->dh_tfm, chap->dhgroup_id); + if (ret < 0) { + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return ret; + } + + chap->host_key_len = crypto_kpp_maxsize(chap->dh_tfm); + + chap->host_key = kzalloc(chap->host_key_len, GFP_KERNEL); + if (!chap->host_key) { + chap->host_key_len = 0; + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; + return -ENOMEM; + } + ret = nvme_auth_gen_pubkey(chap->dh_tfm, + chap->host_key, chap->host_key_len); + if (ret) { + dev_dbg(ctrl->device, + "failed to generate public key, error %d\n", ret); + kfree(chap->host_key); + chap->host_key = NULL; + chap->host_key_len = 0; + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return ret; + } + +gen_sesskey: + chap->sess_key_len = chap->host_key_len; + chap->sess_key = kmalloc(chap->sess_key_len, GFP_KERNEL); + if (!chap->sess_key) { + chap->sess_key_len = 0; + chap->status = NVME_AUTH_DHCHAP_FAILURE_FAILED; + return -ENOMEM; + } + + ret = nvme_auth_gen_shared_secret(chap->dh_tfm, + chap->ctrl_key, chap->ctrl_key_len, + chap->sess_key, chap->sess_key_len); + if (ret) { + dev_dbg(ctrl->device, + "failed to generate shared secret, error %d\n", ret); + kfree_sensitive(chap->sess_key); + chap->sess_key = NULL; + chap->sess_key_len = 0; + chap->status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + return ret; + } + dev_dbg(ctrl->device, "shared secret %*ph\n", + (int)chap->sess_key_len, chap->sess_key); + return 0; +} + +static void __nvme_auth_reset(struct nvme_dhchap_queue_context *chap) +{ + kfree_sensitive(chap->host_response); + chap->host_response = NULL; + kfree_sensitive(chap->host_key); + chap->host_key = NULL; + chap->host_key_len = 0; + kfree_sensitive(chap->ctrl_key); + chap->ctrl_key = NULL; + chap->ctrl_key_len = 0; + kfree_sensitive(chap->sess_key); + chap->sess_key = NULL; + chap->sess_key_len = 0; + chap->status = 0; + chap->error = 0; + chap->s1 = 0; + chap->s2 = 0; + chap->transaction = 0; + memset(chap->c1, 0, sizeof(chap->c1)); + memset(chap->c2, 0, sizeof(chap->c2)); +} + +static void __nvme_auth_free(struct nvme_dhchap_queue_context *chap) +{ + __nvme_auth_reset(chap); + if (chap->shash_tfm) + crypto_free_shash(chap->shash_tfm); + if (chap->dh_tfm) + crypto_free_kpp(chap->dh_tfm); + kfree_sensitive(chap->ctrl_key); + kfree_sensitive(chap->host_key); + kfree_sensitive(chap->sess_key); + kfree_sensitive(chap->host_response); + kfree(chap->buf); + kfree(chap); +} + +static void __nvme_auth_work(struct work_struct *work) +{ + struct nvme_dhchap_queue_context *chap = + container_of(work, struct nvme_dhchap_queue_context, auth_work); + struct nvme_ctrl *ctrl = chap->ctrl; + size_t tl; + int ret = 0; + + chap->transaction = ctrl->transaction++; + + /* DH-HMAC-CHAP Step 1: send negotiate */ + dev_dbg(ctrl->device, "%s: qid %d send negotiate\n", + __func__, chap->qid); + ret = nvme_auth_set_dhchap_negotiate_data(ctrl, chap); + if (ret < 0) { + chap->error = ret; + return; + } + tl = ret; + ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true); + if (ret) { + chap->error = ret; + return; + } + + /* DH-HMAC-CHAP Step 2: receive challenge */ + dev_dbg(ctrl->device, "%s: qid %d receive challenge\n", + __func__, chap->qid); + + memset(chap->buf, 0, chap->buf_size); + ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, chap->buf_size, false); + if (ret) { + dev_warn(ctrl->device, + "qid %d failed to receive challenge, %s %d\n", + chap->qid, ret < 0 ? "error" : "nvme status", ret); + chap->error = ret; + return; + } + ret = nvme_auth_receive_validate(ctrl, chap->qid, chap->buf, chap->transaction, + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE); + if (ret) { + chap->status = ret; + chap->error = NVME_SC_AUTH_REQUIRED; + return; + } + + ret = nvme_auth_process_dhchap_challenge(ctrl, chap); + if (ret) { + /* Invalid challenge parameters */ + chap->error = ret; + goto fail2; + } + + if (chap->ctrl_key_len) { + dev_dbg(ctrl->device, + "%s: qid %d DH exponential\n", + __func__, chap->qid); + ret = nvme_auth_dhchap_exponential(ctrl, chap); + if (ret) { + chap->error = ret; + goto fail2; + } + } + + dev_dbg(ctrl->device, "%s: qid %d host response\n", + __func__, chap->qid); + ret = nvme_auth_dhchap_setup_host_response(ctrl, chap); + if (ret) { + chap->error = ret; + goto fail2; + } + + /* DH-HMAC-CHAP Step 3: send reply */ + dev_dbg(ctrl->device, "%s: qid %d send reply\n", + __func__, chap->qid); + ret = nvme_auth_set_dhchap_reply_data(ctrl, chap); + if (ret < 0) { + chap->error = ret; + goto fail2; + } + + tl = ret; + ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true); + if (ret) { + chap->error = ret; + goto fail2; + } + + /* DH-HMAC-CHAP Step 4: receive success1 */ + dev_dbg(ctrl->device, "%s: qid %d receive success1\n", + __func__, chap->qid); + + memset(chap->buf, 0, chap->buf_size); + ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, chap->buf_size, false); + if (ret) { + dev_warn(ctrl->device, + "qid %d failed to receive success1, %s %d\n", + chap->qid, ret < 0 ? "error" : "nvme status", ret); + chap->error = ret; + return; + } + ret = nvme_auth_receive_validate(ctrl, chap->qid, + chap->buf, chap->transaction, + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1); + if (ret) { + chap->status = ret; + chap->error = NVME_SC_AUTH_REQUIRED; + return; + } + + if (ctrl->ctrl_key) { + dev_dbg(ctrl->device, + "%s: qid %d controller response\n", + __func__, chap->qid); + ret = nvme_auth_dhchap_setup_ctrl_response(ctrl, chap); + if (ret) { + chap->error = ret; + goto fail2; + } + } + + ret = nvme_auth_process_dhchap_success1(ctrl, chap); + if (ret) { + /* Controller authentication failed */ + chap->error = NVME_SC_AUTH_REQUIRED; + goto fail2; + } + + if (ctrl->ctrl_key) { + /* DH-HMAC-CHAP Step 5: send success2 */ + dev_dbg(ctrl->device, "%s: qid %d send success2\n", + __func__, chap->qid); + tl = nvme_auth_set_dhchap_success2_data(ctrl, chap); + ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true); + if (ret) + chap->error = ret; + } + if (!ret) { + chap->error = 0; + return; + } + +fail2: + dev_dbg(ctrl->device, "%s: qid %d send failure2, status %x\n", + __func__, chap->qid, chap->status); + tl = nvme_auth_set_dhchap_failure2_data(ctrl, chap); + ret = nvme_auth_submit(ctrl, chap->qid, chap->buf, tl, true); + /* + * only update error if send failure2 failed and no other + * error had been set during authentication. + */ + if (ret && !chap->error) + chap->error = ret; +} + +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) +{ + struct nvme_dhchap_queue_context *chap; + + if (!ctrl->host_key) { + dev_warn(ctrl->device, "qid %d: no key\n", qid); + return -ENOKEY; + } + + if (ctrl->opts->dhchap_ctrl_secret && !ctrl->ctrl_key) { + dev_warn(ctrl->device, "qid %d: invalid ctrl key\n", qid); + return -ENOKEY; + } + + mutex_lock(&ctrl->dhchap_auth_mutex); + /* Check if the context is already queued */ + list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { + WARN_ON(!chap->buf); + if (chap->qid == qid) { + dev_dbg(ctrl->device, "qid %d: re-using context\n", qid); + mutex_unlock(&ctrl->dhchap_auth_mutex); + flush_work(&chap->auth_work); + __nvme_auth_reset(chap); + queue_work(nvme_wq, &chap->auth_work); + return 0; + } + } + chap = kzalloc(sizeof(*chap), GFP_KERNEL); + if (!chap) { + mutex_unlock(&ctrl->dhchap_auth_mutex); + return -ENOMEM; + } + chap->qid = (qid == NVME_QID_ANY) ? 0 : qid; + chap->ctrl = ctrl; + + /* + * Allocate a large enough buffer for the entire negotiation: + * 4k should be enough to ffdhe8192. + */ + chap->buf_size = 4096; + chap->buf = kzalloc(chap->buf_size, GFP_KERNEL); + if (!chap->buf) { + mutex_unlock(&ctrl->dhchap_auth_mutex); + kfree(chap); + return -ENOMEM; + } + + INIT_WORK(&chap->auth_work, __nvme_auth_work); + list_add(&chap->entry, &ctrl->dhchap_auth_list); + mutex_unlock(&ctrl->dhchap_auth_mutex); + queue_work(nvme_wq, &chap->auth_work); + return 0; +} +EXPORT_SYMBOL_GPL(nvme_auth_negotiate); + +int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid) +{ + struct nvme_dhchap_queue_context *chap; + int ret; + + mutex_lock(&ctrl->dhchap_auth_mutex); + list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { + if (chap->qid != qid) + continue; + mutex_unlock(&ctrl->dhchap_auth_mutex); + flush_work(&chap->auth_work); + ret = chap->error; + return ret; + } + mutex_unlock(&ctrl->dhchap_auth_mutex); + return -ENXIO; +} +EXPORT_SYMBOL_GPL(nvme_auth_wait); + +void nvme_auth_reset(struct nvme_ctrl *ctrl) +{ + struct nvme_dhchap_queue_context *chap; + + mutex_lock(&ctrl->dhchap_auth_mutex); + list_for_each_entry(chap, &ctrl->dhchap_auth_list, entry) { + mutex_unlock(&ctrl->dhchap_auth_mutex); + flush_work(&chap->auth_work); + __nvme_auth_reset(chap); + } + mutex_unlock(&ctrl->dhchap_auth_mutex); +} +EXPORT_SYMBOL_GPL(nvme_auth_reset); + +static void nvme_dhchap_auth_work(struct work_struct *work) +{ + struct nvme_ctrl *ctrl = + container_of(work, struct nvme_ctrl, dhchap_auth_work); + int ret, q; + + /* Authenticate admin queue first */ + ret = nvme_auth_negotiate(ctrl, 0); + if (ret) { + dev_warn(ctrl->device, + "qid 0: error %d setting up authentication\n", ret); + return; + } + ret = nvme_auth_wait(ctrl, 0); + if (ret) { + dev_warn(ctrl->device, + "qid 0: authentication failed\n"); + return; + } + + for (q = 1; q < ctrl->queue_count; q++) { + ret = nvme_auth_negotiate(ctrl, q); + if (ret) { + dev_warn(ctrl->device, + "qid %d: error %d setting up authentication\n", + q, ret); + break; + } + } + + /* + * Failure is a soft-state; credentials remain valid until + * the controller terminates the connection. + */ +} + +void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) +{ + INIT_LIST_HEAD(&ctrl->dhchap_auth_list); + INIT_WORK(&ctrl->dhchap_auth_work, nvme_dhchap_auth_work); + mutex_init(&ctrl->dhchap_auth_mutex); + if (!ctrl->opts) + return; + nvme_auth_generate_key(ctrl->opts->dhchap_secret, &ctrl->host_key); + nvme_auth_generate_key(ctrl->opts->dhchap_ctrl_secret, &ctrl->ctrl_key); +} +EXPORT_SYMBOL_GPL(nvme_auth_init_ctrl); + +void nvme_auth_stop(struct nvme_ctrl *ctrl) +{ + struct nvme_dhchap_queue_context *chap = NULL, *tmp; + + cancel_work_sync(&ctrl->dhchap_auth_work); + mutex_lock(&ctrl->dhchap_auth_mutex); + list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry) + cancel_work_sync(&chap->auth_work); + mutex_unlock(&ctrl->dhchap_auth_mutex); +} +EXPORT_SYMBOL_GPL(nvme_auth_stop); + +void nvme_auth_free(struct nvme_ctrl *ctrl) +{ + struct nvme_dhchap_queue_context *chap = NULL, *tmp; + + mutex_lock(&ctrl->dhchap_auth_mutex); + list_for_each_entry_safe(chap, tmp, &ctrl->dhchap_auth_list, entry) { + list_del_init(&chap->entry); + flush_work(&chap->auth_work); + __nvme_auth_free(chap); + } + mutex_unlock(&ctrl->dhchap_auth_mutex); + if (ctrl->host_key) { + nvme_auth_free_key(ctrl->host_key); + ctrl->host_key = NULL; + } + if (ctrl->ctrl_key) { + nvme_auth_free_key(ctrl->ctrl_key); + ctrl->ctrl_key = NULL; + } +} +EXPORT_SYMBOL_GPL(nvme_auth_free); diff --git a/drivers/nvme/host/constants.c b/drivers/nvme/host/constants.c index 4910543f00ff..e958d5015585 100644 --- a/drivers/nvme/host/constants.c +++ b/drivers/nvme/host/constants.c @@ -6,7 +6,6 @@ #include "nvme.h" -#ifdef CONFIG_NVME_VERBOSE_ERRORS static const char * const nvme_ops[] = { [nvme_cmd_flush] = "Flush", [nvme_cmd_write] = "Write", @@ -178,6 +177,7 @@ const unsigned char *nvme_get_opcode_str(u8 opcode) return nvme_ops[opcode]; return "Unknown"; } +EXPORT_SYMBOL_GPL(nvme_get_opcode_str); const unsigned char *nvme_get_admin_opcode_str(u8 opcode) { @@ -185,4 +185,3 @@ const unsigned char *nvme_get_admin_opcode_str(u8 opcode) return nvme_admin_ops[opcode]; return "Unknown"; } -#endif /* CONFIG_NVME_VERBOSE_ERRORS */ diff --git a/drivers/nvme/host/core.c b/drivers/nvme/host/core.c index 2533b88e66d5..2429b11eb9a8 100644 --- a/drivers/nvme/host/core.c +++ b/drivers/nvme/host/core.c @@ -24,12 +24,22 @@ #include "nvme.h" #include "fabrics.h" +#include #define CREATE_TRACE_POINTS #include "trace.h" #define NVME_MINORS (1U << MINORBITS) +struct nvme_ns_info { + struct nvme_ns_ids ids; + u32 nsid; + __le32 anagrpid; + bool is_shared; + bool is_readonly; + bool is_ready; +}; + unsigned int admin_timeout = 60; module_param(admin_timeout, uint, 0644); MODULE_PARM_DESC(admin_timeout, "timeout in seconds for admin commands"); @@ -330,6 +340,7 @@ enum nvme_disposition { COMPLETE, RETRY, FAILOVER, + AUTHENTICATE, }; static inline enum nvme_disposition nvme_decide_disposition(struct request *req) @@ -337,6 +348,9 @@ static inline enum nvme_disposition nvme_decide_disposition(struct request *req) if (likely(nvme_req(req)->status == 0)) return COMPLETE; + if ((nvme_req(req)->status & 0x7ff) == NVME_SC_AUTH_REQUIRED) + return AUTHENTICATE; + if (blk_noretry_request(req) || (nvme_req(req)->status & NVME_SC_DNR) || nvme_req(req)->retries >= nvme_max_retries) @@ -375,11 +389,13 @@ static inline void nvme_end_req(struct request *req) void nvme_complete_rq(struct request *req) { + struct nvme_ctrl *ctrl = nvme_req(req)->ctrl; + trace_nvme_complete_rq(req); nvme_cleanup_cmd(req); - if (nvme_req(req)->ctrl->kas) - nvme_req(req)->ctrl->comp_seen = true; + if (ctrl->kas) + ctrl->comp_seen = true; switch (nvme_decide_disposition(req)) { case COMPLETE: @@ -391,6 +407,14 @@ void nvme_complete_rq(struct request *req) case FAILOVER: nvme_failover_req(req); return; + case AUTHENTICATE: +#ifdef CONFIG_NVME_AUTH + queue_work(nvme_wq, &ctrl->dhchap_auth_work); + nvme_retry_req(req); +#else + nvme_end_req(req); +#endif + return; } } EXPORT_SYMBOL_GPL(nvme_complete_rq); @@ -702,7 +726,9 @@ bool __nvme_check_ready(struct nvme_ctrl *ctrl, struct request *rq, switch (ctrl->state) { case NVME_CTRL_CONNECTING: if (blk_rq_is_passthrough(rq) && nvme_is_fabrics(req->cmd) && - req->cmd->fabrics.fctype == nvme_fabrics_type_connect) + (req->cmd->fabrics.fctype == nvme_fabrics_type_connect || + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_send || + req->cmd->fabrics.fctype == nvme_fabrics_type_auth_receive)) return true; break; default: @@ -990,8 +1016,7 @@ static int nvme_execute_rq(struct request *rq, bool at_head) */ int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, union nvme_result *result, void *buffer, unsigned bufflen, - unsigned timeout, int qid, int at_head, - blk_mq_req_flags_t flags) + int qid, int at_head, blk_mq_req_flags_t flags) { struct request *req; int ret; @@ -1000,15 +1025,12 @@ int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, req = blk_mq_alloc_request(q, nvme_req_op(cmd), flags); else req = blk_mq_alloc_request_hctx(q, nvme_req_op(cmd), flags, - qid ? qid - 1 : 0); + qid - 1); if (IS_ERR(req)) return PTR_ERR(req); nvme_init_request(req, cmd); - if (timeout) - req->timeout = timeout; - if (buffer && bufflen) { ret = blk_rq_map_kern(q, req, buffer, bufflen, GFP_KERNEL); if (ret) @@ -1028,7 +1050,7 @@ EXPORT_SYMBOL_GPL(__nvme_submit_sync_cmd); int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, void *buffer, unsigned bufflen) { - return __nvme_submit_sync_cmd(q, cmd, NULL, buffer, bufflen, 0, + return __nvme_submit_sync_cmd(q, cmd, NULL, buffer, bufflen, NVME_QID_ANY, 0, 0); } EXPORT_SYMBOL_GPL(nvme_submit_sync_cmd); @@ -1329,8 +1351,8 @@ static int nvme_process_ns_desc(struct nvme_ctrl *ctrl, struct nvme_ns_ids *ids, } } -static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, unsigned nsid, - struct nvme_ns_ids *ids) +static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, + struct nvme_ns_info *info) { struct nvme_command c = { }; bool csi_seen = false; @@ -1343,7 +1365,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, unsigned nsid, return 0; c.identify.opcode = nvme_admin_identify; - c.identify.nsid = cpu_to_le32(nsid); + c.identify.nsid = cpu_to_le32(info->nsid); c.identify.cns = NVME_ID_CNS_NS_DESC_LIST; data = kzalloc(NVME_IDENTIFY_DATA_SIZE, GFP_KERNEL); @@ -1355,7 +1377,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, unsigned nsid, if (status) { dev_warn(ctrl->device, "Identify Descriptors failed (nsid=%u, status=0x%x)\n", - nsid, status); + info->nsid, status); goto free_data; } @@ -1365,7 +1387,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, unsigned nsid, if (cur->nidl == 0) break; - len = nvme_process_ns_desc(ctrl, ids, cur, &csi_seen); + len = nvme_process_ns_desc(ctrl, &info->ids, cur, &csi_seen); if (len < 0) break; @@ -1374,7 +1396,7 @@ static int nvme_identify_ns_descs(struct nvme_ctrl *ctrl, unsigned nsid, if (nvme_multi_css(ctrl) && !csi_seen) { dev_warn(ctrl->device, "Command set not reported for nsid:%d\n", - nsid); + info->nsid); status = -EINVAL; } @@ -1384,7 +1406,7 @@ free_data: } static int nvme_identify_ns(struct nvme_ctrl *ctrl, unsigned nsid, - struct nvme_ns_ids *ids, struct nvme_id_ns **id) + struct nvme_id_ns **id) { struct nvme_command c = { }; int error; @@ -1407,20 +1429,6 @@ static int nvme_identify_ns(struct nvme_ctrl *ctrl, unsigned nsid, error = NVME_SC_INVALID_NS | NVME_SC_DNR; if ((*id)->ncap == 0) /* namespace not allocated or attached */ goto out_free_id; - - - if (ctrl->quirks & NVME_QUIRK_BOGUS_NID) { - dev_info(ctrl->device, - "Ignoring bogus Namespace Identifiers\n"); - } else { - if (ctrl->vs >= NVME_VS(1, 1, 0) && - !memchr_inv(ids->eui64, 0, sizeof(ids->eui64))) - memcpy(ids->eui64, (*id)->eui64, sizeof(ids->eui64)); - if (ctrl->vs >= NVME_VS(1, 2, 0) && - !memchr_inv(ids->nguid, 0, sizeof(ids->nguid))) - memcpy(ids->nguid, (*id)->nguid, sizeof(ids->nguid)); - } - return 0; out_free_id: @@ -1428,30 +1436,59 @@ out_free_id: return error; } -static int nvme_identify_ns_cs_indep(struct nvme_ctrl *ctrl, unsigned nsid, - struct nvme_id_ns_cs_indep **id) +static int nvme_ns_info_from_identify(struct nvme_ctrl *ctrl, + struct nvme_ns_info *info) { + struct nvme_ns_ids *ids = &info->ids; + struct nvme_id_ns *id; + int ret; + + ret = nvme_identify_ns(ctrl, info->nsid, &id); + if (ret) + return ret; + info->anagrpid = id->anagrpid; + info->is_shared = id->nmic & NVME_NS_NMIC_SHARED; + info->is_readonly = id->nsattr & NVME_NS_ATTR_RO; + info->is_ready = true; + if (ctrl->quirks & NVME_QUIRK_BOGUS_NID) { + dev_info(ctrl->device, + "Ignoring bogus Namespace Identifiers\n"); + } else { + if (ctrl->vs >= NVME_VS(1, 1, 0) && + !memchr_inv(ids->eui64, 0, sizeof(ids->eui64))) + memcpy(ids->eui64, id->eui64, sizeof(ids->eui64)); + if (ctrl->vs >= NVME_VS(1, 2, 0) && + !memchr_inv(ids->nguid, 0, sizeof(ids->nguid))) + memcpy(ids->nguid, id->nguid, sizeof(ids->nguid)); + } + kfree(id); + return 0; +} + +static int nvme_ns_info_from_id_cs_indep(struct nvme_ctrl *ctrl, + struct nvme_ns_info *info) +{ + struct nvme_id_ns_cs_indep *id; struct nvme_command c = { .identify.opcode = nvme_admin_identify, - .identify.nsid = cpu_to_le32(nsid), + .identify.nsid = cpu_to_le32(info->nsid), .identify.cns = NVME_ID_CNS_NS_CS_INDEP, }; int ret; - *id = kmalloc(sizeof(**id), GFP_KERNEL); - if (!*id) + id = kmalloc(sizeof(*id), GFP_KERNEL); + if (!id) return -ENOMEM; - ret = nvme_submit_sync_cmd(ctrl->admin_q, &c, *id, sizeof(**id)); - if (ret) { - dev_warn(ctrl->device, - "Identify namespace (CS independent) failed (%d)\n", - ret); - kfree(*id); - return ret; + ret = nvme_submit_sync_cmd(ctrl->admin_q, &c, id, sizeof(*id)); + if (!ret) { + info->anagrpid = id->anagrpid; + info->is_shared = id->nmic & NVME_NS_NMIC_SHARED; + info->is_readonly = id->nsattr & NVME_NS_ATTR_RO; + info->is_ready = id->nstat & NVME_NSTAT_NRDY; } - - return 0; + kfree(id); + return ret; } static int nvme_features(struct nvme_ctrl *dev, u8 op, unsigned int fid, @@ -1466,7 +1503,7 @@ static int nvme_features(struct nvme_ctrl *dev, u8 op, unsigned int fid, c.features.dword11 = cpu_to_le32(dword11); ret = __nvme_submit_sync_cmd(dev->admin_q, &c, &res, - buffer, buflen, 0, NVME_QID_ANY, 0, 0); + buffer, buflen, NVME_QID_ANY, 0, 0); if (ret >= 0 && result) *result = le32_to_cpu(res.u32); return ret; @@ -1875,6 +1912,11 @@ static void nvme_update_disk_info(struct gendisk *disk, ns->ctrl->max_zeroes_sectors); } +static bool nvme_ns_is_readonly(struct nvme_ns *ns, struct nvme_ns_info *info) +{ + return info->is_readonly || test_bit(NVME_NS_FORCE_RO, &ns->flags); +} + static inline bool nvme_first_scan(struct gendisk *disk) { /* nvme_alloc_ns() scans the disk prior to adding it */ @@ -1912,12 +1954,44 @@ static void nvme_set_chunk_sectors(struct nvme_ns *ns, struct nvme_id_ns *id) blk_queue_chunk_sectors(ns->queue, iob); } -static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_id_ns *id) +static int nvme_update_ns_info_generic(struct nvme_ns *ns, + struct nvme_ns_info *info) { - unsigned lbaf = nvme_lbaf_index(id->flbas); + blk_mq_freeze_queue(ns->disk->queue); + nvme_set_queue_limits(ns->ctrl, ns->queue); + set_disk_ro(ns->disk, nvme_ns_is_readonly(ns, info)); + blk_mq_unfreeze_queue(ns->disk->queue); + + if (nvme_ns_head_multipath(ns->head)) { + blk_mq_freeze_queue(ns->head->disk->queue); + set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info)); + nvme_mpath_revalidate_paths(ns); + blk_stack_limits(&ns->head->disk->queue->limits, + &ns->queue->limits, 0); + ns->head->disk->flags |= GENHD_FL_HIDDEN; + blk_mq_unfreeze_queue(ns->head->disk->queue); + } + + /* Hide the block-interface for these devices */ + ns->disk->flags |= GENHD_FL_HIDDEN; + set_bit(NVME_NS_READY, &ns->flags); + + return 0; +} + +static int nvme_update_ns_info_block(struct nvme_ns *ns, + struct nvme_ns_info *info) +{ + struct nvme_id_ns *id; + unsigned lbaf; int ret; + ret = nvme_identify_ns(ns->ctrl, info->nsid, &id); + if (ret) + return ret; + blk_mq_freeze_queue(ns->disk->queue); + lbaf = nvme_lbaf_index(id->flbas); ns->lba_shift = id->lbaf[lbaf].ds; nvme_set_queue_limits(ns->ctrl, ns->queue); @@ -1927,36 +2001,35 @@ static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_id_ns *id) if (ns->head->ids.csi == NVME_CSI_ZNS) { ret = nvme_update_zone_info(ns, lbaf); - if (ret) - goto out_unfreeze; + if (ret) { + blk_mq_unfreeze_queue(ns->disk->queue); + goto out; + } } - set_disk_ro(ns->disk, (id->nsattr & NVME_NS_ATTR_RO) || - test_bit(NVME_NS_FORCE_RO, &ns->flags)); + set_disk_ro(ns->disk, nvme_ns_is_readonly(ns, info)); set_bit(NVME_NS_READY, &ns->flags); blk_mq_unfreeze_queue(ns->disk->queue); if (blk_queue_is_zoned(ns->queue)) { ret = nvme_revalidate_zones(ns); if (ret && !nvme_first_scan(ns->disk)) - return ret; + goto out; } if (nvme_ns_head_multipath(ns->head)) { blk_mq_freeze_queue(ns->head->disk->queue); nvme_update_disk_info(ns->head->disk, ns, id); - set_disk_ro(ns->head->disk, - (id->nsattr & NVME_NS_ATTR_RO) || - test_bit(NVME_NS_FORCE_RO, &ns->flags)); + set_disk_ro(ns->head->disk, nvme_ns_is_readonly(ns, info)); nvme_mpath_revalidate_paths(ns); blk_stack_limits(&ns->head->disk->queue->limits, &ns->queue->limits, 0); disk_update_readahead(ns->head->disk); blk_mq_unfreeze_queue(ns->head->disk->queue); } - return 0; -out_unfreeze: + ret = 0; +out: /* * If probing fails due an unsupported feature, hide the block device, * but still allow other access. @@ -1966,10 +2039,31 @@ out_unfreeze: set_bit(NVME_NS_READY, &ns->flags); ret = 0; } - blk_mq_unfreeze_queue(ns->disk->queue); + kfree(id); return ret; } +static int nvme_update_ns_info(struct nvme_ns *ns, struct nvme_ns_info *info) +{ + switch (info->ids.csi) { + case NVME_CSI_ZNS: + if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED)) { + dev_info(ns->ctrl->device, + "block device for nsid %u not supported without CONFIG_BLK_DEV_ZONED\n", + info->nsid); + return nvme_update_ns_info_generic(ns, info); + } + return nvme_update_ns_info_block(ns, info); + case NVME_CSI_NVM: + return nvme_update_ns_info_block(ns, info); + default: + dev_info(ns->ctrl->device, + "block device for nsid %u not supported (csi %u)\n", + info->nsid, info->ids.csi); + return nvme_update_ns_info_generic(ns, info); + } +} + static char nvme_pr_type(enum pr_type type) { switch (type) { @@ -2103,7 +2197,7 @@ int nvme_sec_submit(void *data, u16 spsp, u8 secp, void *buffer, size_t len, cmd.common.cdw10 = cpu_to_le32(((u32)secp) << 24 | ((u32)spsp) << 8); cmd.common.cdw11 = cpu_to_le32(len); - return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, 0, + return __nvme_submit_sync_cmd(ctrl->admin_q, &cmd, NULL, buffer, len, NVME_QID_ANY, 1, 0); } EXPORT_SYMBOL_GPL(nvme_sec_submit); @@ -2123,6 +2217,7 @@ static int nvme_report_zones(struct gendisk *disk, sector_t sector, static const struct block_device_operations nvme_bdev_ops = { .owner = THIS_MODULE, .ioctl = nvme_ioctl, + .compat_ioctl = blkdev_compat_ptr_ioctl, .open = nvme_open, .release = nvme_release, .getgeo = nvme_getgeo, @@ -3613,6 +3708,108 @@ static ssize_t dctype_show(struct device *dev, } static DEVICE_ATTR_RO(dctype); +#ifdef CONFIG_NVME_AUTH +static ssize_t nvme_ctrl_dhchap_secret_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + struct nvmf_ctrl_options *opts = ctrl->opts; + + if (!opts->dhchap_secret) + return sysfs_emit(buf, "none\n"); + return sysfs_emit(buf, "%s\n", opts->dhchap_secret); +} + +static ssize_t nvme_ctrl_dhchap_secret_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + struct nvmf_ctrl_options *opts = ctrl->opts; + char *dhchap_secret; + + if (!ctrl->opts->dhchap_secret) + return -EINVAL; + if (count < 7) + return -EINVAL; + if (memcmp(buf, "DHHC-1:", 7)) + return -EINVAL; + + dhchap_secret = kzalloc(count + 1, GFP_KERNEL); + if (!dhchap_secret) + return -ENOMEM; + memcpy(dhchap_secret, buf, count); + nvme_auth_stop(ctrl); + if (strcmp(dhchap_secret, opts->dhchap_secret)) { + int ret; + + ret = nvme_auth_generate_key(dhchap_secret, &ctrl->host_key); + if (ret) + return ret; + kfree(opts->dhchap_secret); + opts->dhchap_secret = dhchap_secret; + /* Key has changed; re-authentication with new key */ + nvme_auth_reset(ctrl); + } + /* Start re-authentication */ + dev_info(ctrl->device, "re-authenticating controller\n"); + queue_work(nvme_wq, &ctrl->dhchap_auth_work); + + return count; +} +static DEVICE_ATTR(dhchap_secret, S_IRUGO | S_IWUSR, + nvme_ctrl_dhchap_secret_show, nvme_ctrl_dhchap_secret_store); + +static ssize_t nvme_ctrl_dhchap_ctrl_secret_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + struct nvmf_ctrl_options *opts = ctrl->opts; + + if (!opts->dhchap_ctrl_secret) + return sysfs_emit(buf, "none\n"); + return sysfs_emit(buf, "%s\n", opts->dhchap_ctrl_secret); +} + +static ssize_t nvme_ctrl_dhchap_ctrl_secret_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct nvme_ctrl *ctrl = dev_get_drvdata(dev); + struct nvmf_ctrl_options *opts = ctrl->opts; + char *dhchap_secret; + + if (!ctrl->opts->dhchap_ctrl_secret) + return -EINVAL; + if (count < 7) + return -EINVAL; + if (memcmp(buf, "DHHC-1:", 7)) + return -EINVAL; + + dhchap_secret = kzalloc(count + 1, GFP_KERNEL); + if (!dhchap_secret) + return -ENOMEM; + memcpy(dhchap_secret, buf, count); + nvme_auth_stop(ctrl); + if (strcmp(dhchap_secret, opts->dhchap_ctrl_secret)) { + int ret; + + ret = nvme_auth_generate_key(dhchap_secret, &ctrl->ctrl_key); + if (ret) + return ret; + kfree(opts->dhchap_ctrl_secret); + opts->dhchap_ctrl_secret = dhchap_secret; + /* Key has changed; re-authentication with new key */ + nvme_auth_reset(ctrl); + } + /* Start re-authentication */ + dev_info(ctrl->device, "re-authenticating controller\n"); + queue_work(nvme_wq, &ctrl->dhchap_auth_work); + + return count; +} +static DEVICE_ATTR(dhchap_ctrl_secret, S_IRUGO | S_IWUSR, + nvme_ctrl_dhchap_ctrl_secret_show, nvme_ctrl_dhchap_ctrl_secret_store); +#endif + static struct attribute *nvme_dev_attrs[] = { &dev_attr_reset_controller.attr, &dev_attr_rescan_controller.attr, @@ -3636,6 +3833,10 @@ static struct attribute *nvme_dev_attrs[] = { &dev_attr_kato.attr, &dev_attr_cntrltype.attr, &dev_attr_dctype.attr, +#ifdef CONFIG_NVME_AUTH + &dev_attr_dhchap_secret.attr, + &dev_attr_dhchap_ctrl_secret.attr, +#endif NULL }; @@ -3659,6 +3860,12 @@ static umode_t nvme_dev_attrs_are_visible(struct kobject *kobj, return 0; if (a == &dev_attr_fast_io_fail_tmo.attr && !ctrl->opts) return 0; +#ifdef CONFIG_NVME_AUTH + if (a == &dev_attr_dhchap_secret.attr && !ctrl->opts) + return 0; + if (a == &dev_attr_dhchap_ctrl_secret.attr && !ctrl->opts) + return 0; +#endif return a->mode; } @@ -3786,7 +3993,7 @@ static int nvme_add_ns_cdev(struct nvme_ns *ns) } static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, - unsigned nsid, struct nvme_ns_ids *ids, bool is_shared) + struct nvme_ns_info *info) { struct nvme_ns_head *head; size_t size = sizeof(*head); @@ -3808,9 +4015,9 @@ static struct nvme_ns_head *nvme_alloc_ns_head(struct nvme_ctrl *ctrl, if (ret) goto out_ida_remove; head->subsys = ctrl->subsys; - head->ns_id = nsid; - head->ids = *ids; - head->shared = is_shared; + head->ns_id = info->nsid; + head->ids = info->ids; + head->shared = info->is_shared; kref_init(&head->ref); if (head->ids.csi) { @@ -3867,54 +4074,54 @@ static int nvme_global_check_duplicate_ids(struct nvme_subsystem *this, return ret; } -static int nvme_init_ns_head(struct nvme_ns *ns, unsigned nsid, - struct nvme_ns_ids *ids, bool is_shared) +static int nvme_init_ns_head(struct nvme_ns *ns, struct nvme_ns_info *info) { struct nvme_ctrl *ctrl = ns->ctrl; struct nvme_ns_head *head = NULL; int ret; - ret = nvme_global_check_duplicate_ids(ctrl->subsys, ids); + ret = nvme_global_check_duplicate_ids(ctrl->subsys, &info->ids); if (ret) { dev_err(ctrl->device, - "globally duplicate IDs for nsid %d\n", nsid); + "globally duplicate IDs for nsid %d\n", info->nsid); nvme_print_device_info(ctrl); return ret; } mutex_lock(&ctrl->subsys->lock); - head = nvme_find_ns_head(ctrl, nsid); + head = nvme_find_ns_head(ctrl, info->nsid); if (!head) { - ret = nvme_subsys_check_duplicate_ids(ctrl->subsys, ids); + ret = nvme_subsys_check_duplicate_ids(ctrl->subsys, &info->ids); if (ret) { dev_err(ctrl->device, "duplicate IDs in subsystem for nsid %d\n", - nsid); + info->nsid); goto out_unlock; } - head = nvme_alloc_ns_head(ctrl, nsid, ids, is_shared); + head = nvme_alloc_ns_head(ctrl, info); if (IS_ERR(head)) { ret = PTR_ERR(head); goto out_unlock; } } else { ret = -EINVAL; - if (!is_shared || !head->shared) { + if (!info->is_shared || !head->shared) { dev_err(ctrl->device, - "Duplicate unshared namespace %d\n", nsid); + "Duplicate unshared namespace %d\n", + info->nsid); goto out_put_ns_head; } - if (!nvme_ns_ids_equal(&head->ids, ids)) { + if (!nvme_ns_ids_equal(&head->ids, &info->ids)) { dev_err(ctrl->device, "IDs don't match for shared namespace %d\n", - nsid); + info->nsid); goto out_put_ns_head; } if (!multipath && !list_empty(&head->list)) { dev_warn(ctrl->device, "Found shared namespace %d, but multipathing not supported.\n", - nsid); + info->nsid); dev_warn_once(ctrl->device, "Support for shared namespaces without CONFIG_NVME_MULTIPATH is deprecated and will be removed in Linux 6.0\n."); } @@ -3968,20 +4175,15 @@ static void nvme_ns_add_to_ctrl_list(struct nvme_ns *ns) list_add(&ns->list, &ns->ctrl->namespaces); } -static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid, - struct nvme_ns_ids *ids) +static void nvme_alloc_ns(struct nvme_ctrl *ctrl, struct nvme_ns_info *info) { struct nvme_ns *ns; struct gendisk *disk; - struct nvme_id_ns *id; int node = ctrl->numa_node; - if (nvme_identify_ns(ctrl, nsid, ids, &id)) - return; - ns = kzalloc_node(sizeof(*ns), GFP_KERNEL, node); if (!ns) - goto out_free_id; + return; disk = blk_mq_alloc_disk(ctrl->tagset, ns); if (IS_ERR(disk)) @@ -4002,7 +4204,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid, ns->ctrl = ctrl; kref_init(&ns->kref); - if (nvme_init_ns_head(ns, nsid, ids, id->nmic & NVME_NS_NMIC_SHARED)) + if (nvme_init_ns_head(ns, info)) goto out_cleanup_disk; /* @@ -4028,7 +4230,7 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid, ns->head->instance); } - if (nvme_update_ns_info(ns, id)) + if (nvme_update_ns_info(ns, info)) goto out_unlink_ns; down_write(&ctrl->namespaces_rwsem); @@ -4042,9 +4244,8 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid, if (!nvme_ns_head_multipath(ns->head)) nvme_add_ns_cdev(ns); - nvme_mpath_add_disk(ns, id); + nvme_mpath_add_disk(ns, info->anagrpid); nvme_fault_inject_init(&ns->fault_inject, ns->disk->disk_name); - kfree(id); return; @@ -4064,8 +4265,6 @@ static void nvme_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid, put_disk(disk); out_free_ns: kfree(ns); - out_free_id: - kfree(id); } static void nvme_ns_remove(struct nvme_ns *ns) @@ -4123,29 +4322,21 @@ static void nvme_ns_remove_by_nsid(struct nvme_ctrl *ctrl, u32 nsid) } } -static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_ids *ids) +static void nvme_validate_ns(struct nvme_ns *ns, struct nvme_ns_info *info) { - struct nvme_id_ns *id; int ret = NVME_SC_INVALID_NS | NVME_SC_DNR; if (test_bit(NVME_NS_DEAD, &ns->flags)) goto out; - ret = nvme_identify_ns(ns->ctrl, ns->head->ns_id, ids, &id); - if (ret) - goto out; - ret = NVME_SC_INVALID_NS | NVME_SC_DNR; - if (!nvme_ns_ids_equal(&ns->head->ids, ids)) { + if (!nvme_ns_ids_equal(&ns->head->ids, &info->ids)) { dev_err(ns->ctrl->device, "identifiers changed for nsid %d\n", ns->head->ns_id); - goto out_free_id; + goto out; } - ret = nvme_update_ns_info(ns, id); - -out_free_id: - kfree(id); + ret = nvme_update_ns_info(ns, info); out: /* * Only remove the namespace if we got a fatal error back from the @@ -4157,59 +4348,47 @@ out: nvme_ns_remove(ns); } -static void nvme_validate_or_alloc_ns(struct nvme_ctrl *ctrl, unsigned nsid) +static void nvme_scan_ns(struct nvme_ctrl *ctrl, unsigned nsid) { - struct nvme_ns_ids ids = { }; - struct nvme_id_ns_cs_indep *id; + struct nvme_ns_info info = { .nsid = nsid }; struct nvme_ns *ns; - bool ready = true; - if (nvme_identify_ns_descs(ctrl, nsid, &ids)) + if (nvme_identify_ns_descs(ctrl, &info)) return; - /* - * Check if the namespace is ready. If not ignore it, we will get an - * AEN once it becomes ready and restart the scan. - */ - if ((ctrl->cap & NVME_CAP_CRMS_CRIMS) && - !nvme_identify_ns_cs_indep(ctrl, nsid, &id)) { - ready = id->nstat & NVME_NSTAT_NRDY; - kfree(id); + if (info.ids.csi != NVME_CSI_NVM && !nvme_multi_css(ctrl)) { + dev_warn(ctrl->device, + "command set not reported for nsid: %d\n", nsid); + return; } - if (!ready) + /* + * If available try to use the Command Set Idependent Identify Namespace + * data structure to find all the generic information that is needed to + * set up a namespace. If not fall back to the legacy version. + */ + if ((ctrl->cap & NVME_CAP_CRMS_CRIMS) || + (info.ids.csi != NVME_CSI_NVM && info.ids.csi != NVME_CSI_ZNS)) { + if (nvme_ns_info_from_id_cs_indep(ctrl, &info)) + return; + } else { + if (nvme_ns_info_from_identify(ctrl, &info)) + return; + } + + /* + * Ignore the namespace if it is not ready. We will get an AEN once it + * becomes ready and restart the scan. + */ + if (!info.is_ready) return; ns = nvme_find_get_ns(ctrl, nsid); if (ns) { - nvme_validate_ns(ns, &ids); + nvme_validate_ns(ns, &info); nvme_put_ns(ns); - return; - } - - switch (ids.csi) { - case NVME_CSI_NVM: - nvme_alloc_ns(ctrl, nsid, &ids); - break; - case NVME_CSI_ZNS: - if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED)) { - dev_warn(ctrl->device, - "nsid %u not supported without CONFIG_BLK_DEV_ZONED\n", - nsid); - break; - } - if (!nvme_multi_css(ctrl)) { - dev_warn(ctrl->device, - "command set not reported for nsid: %d\n", - nsid); - break; - } - nvme_alloc_ns(ctrl, nsid, &ids); - break; - default: - dev_warn(ctrl->device, "unknown csi %u for nsid %u\n", - ids.csi, nsid); - break; + } else { + nvme_alloc_ns(ctrl, &info); } } @@ -4265,7 +4444,7 @@ static int nvme_scan_ns_list(struct nvme_ctrl *ctrl) if (!nsid) /* end of the list? */ goto out; - nvme_validate_or_alloc_ns(ctrl, nsid); + nvme_scan_ns(ctrl, nsid); while (++prev < nsid) nvme_ns_remove_by_nsid(ctrl, prev); } @@ -4288,7 +4467,7 @@ static void nvme_scan_ns_sequential(struct nvme_ctrl *ctrl) kfree(id); for (i = 1; i <= nn; i++) - nvme_validate_or_alloc_ns(ctrl, i); + nvme_scan_ns(ctrl, i); nvme_remove_invalid_namespaces(ctrl, nn); } @@ -4525,9 +4704,19 @@ static void nvme_fw_act_work(struct work_struct *work) nvme_get_fw_slot_info(ctrl); } +static u32 nvme_aer_type(u32 result) +{ + return result & 0x7; +} + +static u32 nvme_aer_subtype(u32 result) +{ + return (result & 0xff00) >> 8; +} + static void nvme_handle_aen_notice(struct nvme_ctrl *ctrl, u32 result) { - u32 aer_notice_type = (result & 0xff00) >> 8; + u32 aer_notice_type = nvme_aer_subtype(result); trace_nvme_async_event(ctrl, aer_notice_type); @@ -4542,8 +4731,10 @@ static void nvme_handle_aen_notice(struct nvme_ctrl *ctrl, u32 result) * recovery actions from interfering with the controller's * firmware activation. */ - if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) + if (nvme_change_ctrl_state(ctrl, NVME_CTRL_RESETTING)) { + nvme_auth_stop(ctrl); queue_work(nvme_wq, &ctrl->fw_act_work); + } break; #ifdef CONFIG_NVME_MULTIPATH case NVME_AER_NOTICE_ANA: @@ -4560,11 +4751,19 @@ static void nvme_handle_aen_notice(struct nvme_ctrl *ctrl, u32 result) } } +static void nvme_handle_aer_persistent_error(struct nvme_ctrl *ctrl) +{ + trace_nvme_async_event(ctrl, NVME_AER_ERROR); + dev_warn(ctrl->device, "resetting controller due to AER\n"); + nvme_reset_ctrl(ctrl); +} + void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status, volatile union nvme_result *res) { u32 result = le32_to_cpu(res->u32); - u32 aer_type = result & 0x07; + u32 aer_type = nvme_aer_type(result); + u32 aer_subtype = nvme_aer_subtype(result); if (le16_to_cpu(status) >> 1 != NVME_SC_SUCCESS) return; @@ -4574,6 +4773,15 @@ void nvme_complete_async_event(struct nvme_ctrl *ctrl, __le16 status, nvme_handle_aen_notice(ctrl, result); break; case NVME_AER_ERROR: + /* + * For a persistent internal error, don't run async_event_work + * to submit a new AER. The controller reset will do it. + */ + if (aer_subtype == NVME_AER_ERROR_PERSIST_INT_ERR) { + nvme_handle_aer_persistent_error(ctrl); + return; + } + fallthrough; case NVME_AER_SMART: case NVME_AER_CSS: case NVME_AER_VS: @@ -4590,6 +4798,7 @@ EXPORT_SYMBOL_GPL(nvme_complete_async_event); void nvme_stop_ctrl(struct nvme_ctrl *ctrl) { nvme_mpath_stop(ctrl); + nvme_auth_stop(ctrl); nvme_stop_keep_alive(ctrl); nvme_stop_failfast_work(ctrl); flush_work(&ctrl->async_event_work); @@ -4649,6 +4858,8 @@ static void nvme_free_ctrl(struct device *dev) nvme_free_cels(ctrl); nvme_mpath_uninit(ctrl); + nvme_auth_stop(ctrl); + nvme_auth_free(ctrl); __free_page(ctrl->discard_page); if (subsys) { @@ -4739,6 +4950,7 @@ int nvme_init_ctrl(struct nvme_ctrl *ctrl, struct device *dev, nvme_fault_inject_init(&ctrl->fault_inject, dev_name(ctrl->device)); nvme_mpath_init_ctrl(ctrl); + nvme_auth_init_ctrl(ctrl); return 0; out_free_name: diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c index ee79a6d639b4..5207a2348257 100644 --- a/drivers/nvme/host/fabrics.c +++ b/drivers/nvme/host/fabrics.c @@ -152,7 +152,7 @@ int nvmf_reg_read32(struct nvme_ctrl *ctrl, u32 off, u32 *val) cmd.prop_get.fctype = nvme_fabrics_type_property_get; cmd.prop_get.offset = cpu_to_le32(off); - ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, 0, + ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, NVME_QID_ANY, 0, 0); if (ret >= 0) @@ -198,7 +198,7 @@ int nvmf_reg_read64(struct nvme_ctrl *ctrl, u32 off, u64 *val) cmd.prop_get.attrib = 1; cmd.prop_get.offset = cpu_to_le32(off); - ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, 0, + ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, NULL, 0, NVME_QID_ANY, 0, 0); if (ret >= 0) @@ -243,7 +243,7 @@ int nvmf_reg_write32(struct nvme_ctrl *ctrl, u32 off, u32 val) cmd.prop_set.offset = cpu_to_le32(off); cmd.prop_set.value = cpu_to_le64(val); - ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, NULL, NULL, 0, 0, + ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, NULL, NULL, 0, NVME_QID_ANY, 0, 0); if (unlikely(ret)) dev_err(ctrl->device, @@ -331,6 +331,10 @@ static void nvmf_log_connect_error(struct nvme_ctrl *ctrl, dev_err(ctrl->device, "Connect command failed: host path error\n"); break; + case NVME_SC_AUTH_REQUIRED: + dev_err(ctrl->device, + "Connect command failed: authentication required\n"); + break; default: dev_err(ctrl->device, "Connect command failed, error wo/DNR bit: %d\n", @@ -365,6 +369,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) union nvme_result res; struct nvmf_connect_data *data; int ret; + u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect; @@ -389,7 +394,7 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) strncpy(data->hostnqn, ctrl->opts->host->nqn, NVMF_NQN_SIZE); ret = __nvme_submit_sync_cmd(ctrl->fabrics_q, &cmd, &res, - data, sizeof(*data), 0, NVME_QID_ANY, 1, + data, sizeof(*data), NVME_QID_ANY, 1, BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT); if (ret) { nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), @@ -397,8 +402,25 @@ int nvmf_connect_admin_queue(struct nvme_ctrl *ctrl) goto out_free_data; } - ctrl->cntlid = le16_to_cpu(res.u16); - + result = le32_to_cpu(res.u32); + ctrl->cntlid = result & 0xFFFF; + if ((result >> 16) & 0x3) { + /* Authentication required */ + ret = nvme_auth_negotiate(ctrl, 0); + if (ret) { + dev_warn(ctrl->device, + "qid 0: authentication setup failed\n"); + ret = NVME_SC_AUTH_REQUIRED; + goto out_free_data; + } + ret = nvme_auth_wait(ctrl, 0); + if (ret) + dev_warn(ctrl->device, + "qid 0: authentication failed\n"); + else + dev_info(ctrl->device, + "qid 0: authenticated\n"); + } out_free_data: kfree(data); return ret; @@ -431,6 +453,7 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) struct nvmf_connect_data *data; union nvme_result res; int ret; + u32 result; cmd.connect.opcode = nvme_fabrics_command; cmd.connect.fctype = nvme_fabrics_type_connect; @@ -450,12 +473,27 @@ int nvmf_connect_io_queue(struct nvme_ctrl *ctrl, u16 qid) strncpy(data->hostnqn, ctrl->opts->host->nqn, NVMF_NQN_SIZE); ret = __nvme_submit_sync_cmd(ctrl->connect_q, &cmd, &res, - data, sizeof(*data), 0, qid, 1, + data, sizeof(*data), qid, 1, BLK_MQ_REQ_RESERVED | BLK_MQ_REQ_NOWAIT); if (ret) { nvmf_log_connect_error(ctrl, ret, le32_to_cpu(res.u32), &cmd, data); } + result = le32_to_cpu(res.u32); + if ((result >> 16) & 2) { + /* Authentication required */ + ret = nvme_auth_negotiate(ctrl, qid); + if (ret) { + dev_warn(ctrl->device, + "qid %d: authentication setup failed\n", qid); + ret = NVME_SC_AUTH_REQUIRED; + } else { + ret = nvme_auth_wait(ctrl, qid); + if (ret) + dev_warn(ctrl->device, + "qid %u: authentication failed\n", qid); + } + } kfree(data); return ret; } @@ -548,6 +586,8 @@ static const match_table_t opt_tokens = { { NVMF_OPT_TOS, "tos=%d" }, { NVMF_OPT_FAIL_FAST_TMO, "fast_io_fail_tmo=%d" }, { NVMF_OPT_DISCOVERY, "discovery" }, + { NVMF_OPT_DHCHAP_SECRET, "dhchap_secret=%s" }, + { NVMF_OPT_DHCHAP_CTRL_SECRET, "dhchap_ctrl_secret=%s" }, { NVMF_OPT_ERR, NULL } }; @@ -829,6 +869,34 @@ static int nvmf_parse_options(struct nvmf_ctrl_options *opts, case NVMF_OPT_DISCOVERY: opts->discovery_nqn = true; break; + case NVMF_OPT_DHCHAP_SECRET: + p = match_strdup(args); + if (!p) { + ret = -ENOMEM; + goto out; + } + if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) { + pr_err("Invalid DH-CHAP secret %s\n", p); + ret = -EINVAL; + goto out; + } + kfree(opts->dhchap_secret); + opts->dhchap_secret = p; + break; + case NVMF_OPT_DHCHAP_CTRL_SECRET: + p = match_strdup(args); + if (!p) { + ret = -ENOMEM; + goto out; + } + if (strlen(p) < 11 || strncmp(p, "DHHC-1:", 7)) { + pr_err("Invalid DH-CHAP secret %s\n", p); + ret = -EINVAL; + goto out; + } + kfree(opts->dhchap_ctrl_secret); + opts->dhchap_ctrl_secret = p; + break; default: pr_warn("unknown parameter or missing value '%s' in ctrl creation request\n", p); @@ -947,6 +1015,8 @@ void nvmf_free_options(struct nvmf_ctrl_options *opts) kfree(opts->subsysnqn); kfree(opts->host_traddr); kfree(opts->host_iface); + kfree(opts->dhchap_secret); + kfree(opts->dhchap_ctrl_secret); kfree(opts); } EXPORT_SYMBOL_GPL(nvmf_free_options); @@ -956,7 +1026,8 @@ EXPORT_SYMBOL_GPL(nvmf_free_options); NVMF_OPT_KATO | NVMF_OPT_HOSTNQN | \ NVMF_OPT_HOST_ID | NVMF_OPT_DUP_CONNECT |\ NVMF_OPT_DISABLE_SQFLOW | NVMF_OPT_DISCOVERY |\ - NVMF_OPT_FAIL_FAST_TMO) + NVMF_OPT_FAIL_FAST_TMO | NVMF_OPT_DHCHAP_SECRET |\ + NVMF_OPT_DHCHAP_CTRL_SECRET) static struct nvme_ctrl * nvmf_create_ctrl(struct device *dev, const char *buf) @@ -1192,7 +1263,14 @@ static void __exit nvmf_exit(void) BUILD_BUG_ON(sizeof(struct nvmf_connect_command) != 64); BUILD_BUG_ON(sizeof(struct nvmf_property_get_command) != 64); BUILD_BUG_ON(sizeof(struct nvmf_property_set_command) != 64); + BUILD_BUG_ON(sizeof(struct nvmf_auth_send_command) != 64); + BUILD_BUG_ON(sizeof(struct nvmf_auth_receive_command) != 64); BUILD_BUG_ON(sizeof(struct nvmf_connect_data) != 1024); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_negotiate_data) != 8); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_challenge_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_reply_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success1_data) != 16); + BUILD_BUG_ON(sizeof(struct nvmf_auth_dhchap_success2_data) != 16); } MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvme/host/fabrics.h b/drivers/nvme/host/fabrics.h index 46d6e194ac2b..a6e22116e139 100644 --- a/drivers/nvme/host/fabrics.h +++ b/drivers/nvme/host/fabrics.h @@ -68,6 +68,8 @@ enum { NVMF_OPT_FAIL_FAST_TMO = 1 << 20, NVMF_OPT_HOST_IFACE = 1 << 21, NVMF_OPT_DISCOVERY = 1 << 22, + NVMF_OPT_DHCHAP_SECRET = 1 << 23, + NVMF_OPT_DHCHAP_CTRL_SECRET = 1 << 24, }; /** @@ -97,6 +99,9 @@ enum { * @max_reconnects: maximum number of allowed reconnect attempts before removing * the controller, (-1) means reconnect forever, zero means remove * immediately; + * @dhchap_secret: DH-HMAC-CHAP secret + * @dhchap_ctrl_secret: DH-HMAC-CHAP controller secret for bi-directional + * authentication * @disable_sqflow: disable controller sq flow control * @hdr_digest: generate/verify header digest (TCP) * @data_digest: generate/verify data digest (TCP) @@ -121,6 +126,8 @@ struct nvmf_ctrl_options { unsigned int kato; struct nvmf_host *host; int max_reconnects; + char *dhchap_secret; + char *dhchap_ctrl_secret; bool disable_sqflow; bool hdr_digest; bool data_digest; diff --git a/drivers/nvme/host/multipath.c b/drivers/nvme/host/multipath.c index f26640ccb955..6ef497c75a16 100644 --- a/drivers/nvme/host/multipath.c +++ b/drivers/nvme/host/multipath.c @@ -346,7 +346,7 @@ static void nvme_ns_head_submit_bio(struct bio *bio) * different queue via blk_steal_bios(), so we need to use the bio_split * pool from the original queue to allocate the bvecs from. */ - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); srcu_idx = srcu_read_lock(&head->srcu); ns = nvme_find_path(head); @@ -408,6 +408,7 @@ const struct block_device_operations nvme_ns_head_ops = { .open = nvme_ns_head_open, .release = nvme_ns_head_release, .ioctl = nvme_ns_head_ioctl, + .compat_ioctl = blkdev_compat_ptr_ioctl, .getgeo = nvme_getgeo, .report_zones = nvme_ns_head_report_zones, .pr_ops = &nvme_pr_ops, @@ -800,16 +801,16 @@ static int nvme_lookup_ana_group_desc(struct nvme_ctrl *ctrl, return -ENXIO; /* just break out of the loop */ } -void nvme_mpath_add_disk(struct nvme_ns *ns, struct nvme_id_ns *id) +void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid) { if (nvme_ctrl_use_ana(ns->ctrl)) { struct nvme_ana_group_desc desc = { - .grpid = id->anagrpid, + .grpid = anagrpid, .state = 0, }; mutex_lock(&ns->ctrl->ana_lock); - ns->ana_grpid = le32_to_cpu(id->anagrpid); + ns->ana_grpid = le32_to_cpu(anagrpid); nvme_parse_ana_log(ns->ctrl, &desc, nvme_lookup_ana_group_desc); mutex_unlock(&ns->ctrl->ana_lock); if (desc.state) { diff --git a/drivers/nvme/host/nvme.h b/drivers/nvme/host/nvme.h index 7e0a925bf3be..bdc0ff7ed9ab 100644 --- a/drivers/nvme/host/nvme.h +++ b/drivers/nvme/host/nvme.h @@ -140,7 +140,7 @@ enum nvme_quirks { NVME_QUIRK_DMA_ADDRESS_BITS_48 = (1 << 16), /* - * The controller requires the command_id value be be limited, so skip + * The controller requires the command_id value be limited, so skip * encoding the generation sequence number. */ NVME_QUIRK_SKIP_CID_GEN = (1 << 17), @@ -328,6 +328,15 @@ struct nvme_ctrl { struct work_struct ana_work; #endif +#ifdef CONFIG_NVME_AUTH + struct work_struct dhchap_auth_work; + struct list_head dhchap_auth_list; + struct mutex dhchap_auth_mutex; + struct nvme_dhchap_key *host_key; + struct nvme_dhchap_key *ctrl_key; + u16 transaction; +#endif + /* Power saving configuration */ u64 ps_max_latency_us; bool apst_enabled; @@ -781,7 +790,7 @@ int nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, void *buf, unsigned bufflen); int __nvme_submit_sync_cmd(struct request_queue *q, struct nvme_command *cmd, union nvme_result *result, void *buffer, unsigned bufflen, - unsigned timeout, int qid, int at_head, + int qid, int at_head, blk_mq_req_flags_t flags); int nvme_set_features(struct nvme_ctrl *dev, unsigned int fid, unsigned int dword11, void *buffer, size_t buflen, @@ -837,7 +846,7 @@ void nvme_mpath_default_iopolicy(struct nvme_subsystem *subsys); void nvme_failover_req(struct request *req); void nvme_kick_requeue_lists(struct nvme_ctrl *ctrl); int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl,struct nvme_ns_head *head); -void nvme_mpath_add_disk(struct nvme_ns *ns, struct nvme_id_ns *id); +void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid); void nvme_mpath_remove_disk(struct nvme_ns_head *head); int nvme_mpath_init_identify(struct nvme_ctrl *ctrl, struct nvme_id_ctrl *id); void nvme_mpath_init_ctrl(struct nvme_ctrl *ctrl); @@ -879,8 +888,7 @@ static inline int nvme_mpath_alloc_disk(struct nvme_ctrl *ctrl, { return 0; } -static inline void nvme_mpath_add_disk(struct nvme_ns *ns, - struct nvme_id_ns *id) +static inline void nvme_mpath_add_disk(struct nvme_ns *ns, __le32 anagrpid) { } static inline void nvme_mpath_remove_disk(struct nvme_ns_head *head) @@ -992,6 +1000,27 @@ static inline bool nvme_ctrl_sgl_supported(struct nvme_ctrl *ctrl) return ctrl->sgls & ((1 << 0) | (1 << 1)); } +#ifdef CONFIG_NVME_AUTH +void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl); +void nvme_auth_stop(struct nvme_ctrl *ctrl); +int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid); +int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid); +void nvme_auth_reset(struct nvme_ctrl *ctrl); +void nvme_auth_free(struct nvme_ctrl *ctrl); +#else +static inline void nvme_auth_init_ctrl(struct nvme_ctrl *ctrl) {}; +static inline void nvme_auth_stop(struct nvme_ctrl *ctrl) {}; +static inline int nvme_auth_negotiate(struct nvme_ctrl *ctrl, int qid) +{ + return -EPROTONOSUPPORT; +} +static inline int nvme_auth_wait(struct nvme_ctrl *ctrl, int qid) +{ + return NVME_SC_AUTH_REQUIRED; +} +static inline void nvme_auth_free(struct nvme_ctrl *ctrl) {}; +#endif + u32 nvme_command_effects(struct nvme_ctrl *ctrl, struct nvme_ns *ns, u8 opcode); int nvme_execute_passthru_rq(struct request *rq); diff --git a/drivers/nvme/host/pci.c b/drivers/nvme/host/pci.c index 7e7d4802ac6b..71a4f26ba476 100644 --- a/drivers/nvme/host/pci.c +++ b/drivers/nvme/host/pci.c @@ -670,7 +670,6 @@ static blk_status_t nvme_pci_setup_prps(struct nvme_dev *dev, prp_list = dma_pool_alloc(pool, GFP_ATOMIC, &prp_dma); if (!prp_list) { - iod->first_dma = dma_addr; iod->npages = -1; return BLK_STS_RESOURCE; } @@ -1435,8 +1434,10 @@ static enum blk_eh_timer_return nvme_timeout(struct request *req) cmd.abort.sqid = cpu_to_le16(nvmeq->qid); dev_warn(nvmeq->dev->ctrl.device, - "I/O %d QID %d timeout, aborting\n", - req->tag, nvmeq->qid); + "I/O %d (%s) QID %d timeout, aborting\n", + req->tag, + nvme_get_opcode_str(nvme_req(req)->cmd->common.opcode), + nvmeq->qid); abort_req = blk_mq_alloc_request(dev->ctrl.admin_q, nvme_req_op(&cmd), BLK_MQ_REQ_NOWAIT); @@ -1765,37 +1766,35 @@ static void nvme_dev_remove_admin(struct nvme_dev *dev) } } -static int nvme_alloc_admin_tags(struct nvme_dev *dev) +static int nvme_pci_alloc_admin_tag_set(struct nvme_dev *dev) { - if (!dev->ctrl.admin_q) { - dev->admin_tagset.ops = &nvme_mq_admin_ops; - dev->admin_tagset.nr_hw_queues = 1; + struct blk_mq_tag_set *set = &dev->admin_tagset; - dev->admin_tagset.queue_depth = NVME_AQ_MQ_TAG_DEPTH; - dev->admin_tagset.timeout = NVME_ADMIN_TIMEOUT; - dev->admin_tagset.numa_node = dev->ctrl.numa_node; - dev->admin_tagset.cmd_size = sizeof(struct nvme_iod); - dev->admin_tagset.flags = BLK_MQ_F_NO_SCHED; - dev->admin_tagset.driver_data = dev; + set->ops = &nvme_mq_admin_ops; + set->nr_hw_queues = 1; - if (blk_mq_alloc_tag_set(&dev->admin_tagset)) - return -ENOMEM; - dev->ctrl.admin_tagset = &dev->admin_tagset; + set->queue_depth = NVME_AQ_MQ_TAG_DEPTH; + set->timeout = NVME_ADMIN_TIMEOUT; + set->numa_node = dev->ctrl.numa_node; + set->cmd_size = sizeof(struct nvme_iod); + set->flags = BLK_MQ_F_NO_SCHED; + set->driver_data = dev; - dev->ctrl.admin_q = blk_mq_init_queue(&dev->admin_tagset); - if (IS_ERR(dev->ctrl.admin_q)) { - blk_mq_free_tag_set(&dev->admin_tagset); - dev->ctrl.admin_q = NULL; - return -ENOMEM; - } - if (!blk_get_queue(dev->ctrl.admin_q)) { - nvme_dev_remove_admin(dev); - dev->ctrl.admin_q = NULL; - return -ENODEV; - } - } else - nvme_start_admin_queue(&dev->ctrl); + if (blk_mq_alloc_tag_set(set)) + return -ENOMEM; + dev->ctrl.admin_tagset = set; + dev->ctrl.admin_q = blk_mq_init_queue(set); + if (IS_ERR(dev->ctrl.admin_q)) { + blk_mq_free_tag_set(set); + dev->ctrl.admin_q = NULL; + return -ENOMEM; + } + if (!blk_get_queue(dev->ctrl.admin_q)) { + nvme_dev_remove_admin(dev); + dev->ctrl.admin_q = NULL; + return -ENODEV; + } return 0; } @@ -2534,47 +2533,45 @@ static bool __nvme_disable_io_queues(struct nvme_dev *dev, u8 opcode) return true; } -static void nvme_dev_add(struct nvme_dev *dev) +static void nvme_pci_alloc_tag_set(struct nvme_dev *dev) { + struct blk_mq_tag_set * set = &dev->tagset; int ret; - if (!dev->ctrl.tagset) { - dev->tagset.ops = &nvme_mq_ops; - dev->tagset.nr_hw_queues = dev->online_queues - 1; - dev->tagset.nr_maps = 2; /* default + read */ - if (dev->io_queues[HCTX_TYPE_POLL]) - dev->tagset.nr_maps++; - dev->tagset.timeout = NVME_IO_TIMEOUT; - dev->tagset.numa_node = dev->ctrl.numa_node; - dev->tagset.queue_depth = min_t(unsigned int, dev->q_depth, - BLK_MQ_MAX_DEPTH) - 1; - dev->tagset.cmd_size = sizeof(struct nvme_iod); - dev->tagset.flags = BLK_MQ_F_SHOULD_MERGE; - dev->tagset.driver_data = dev; + set->ops = &nvme_mq_ops; + set->nr_hw_queues = dev->online_queues - 1; + set->nr_maps = 2; /* default + read */ + if (dev->io_queues[HCTX_TYPE_POLL]) + set->nr_maps++; + set->timeout = NVME_IO_TIMEOUT; + set->numa_node = dev->ctrl.numa_node; + set->queue_depth = min_t(unsigned, dev->q_depth, BLK_MQ_MAX_DEPTH) - 1; + set->cmd_size = sizeof(struct nvme_iod); + set->flags = BLK_MQ_F_SHOULD_MERGE; + set->driver_data = dev; - /* - * Some Apple controllers requires tags to be unique - * across admin and IO queue, so reserve the first 32 - * tags of the IO queue. - */ - if (dev->ctrl.quirks & NVME_QUIRK_SHARED_TAGS) - dev->tagset.reserved_tags = NVME_AQ_DEPTH; + /* + * Some Apple controllers requires tags to be unique + * across admin and IO queue, so reserve the first 32 + * tags of the IO queue. + */ + if (dev->ctrl.quirks & NVME_QUIRK_SHARED_TAGS) + set->reserved_tags = NVME_AQ_DEPTH; - ret = blk_mq_alloc_tag_set(&dev->tagset); - if (ret) { - dev_warn(dev->ctrl.device, - "IO queues tagset allocation failed %d\n", ret); - return; - } - dev->ctrl.tagset = &dev->tagset; - } else { - blk_mq_update_nr_hw_queues(&dev->tagset, dev->online_queues - 1); - - /* Free previously allocated queues that are no longer usable */ - nvme_free_queues(dev, dev->online_queues); + ret = blk_mq_alloc_tag_set(set); + if (ret) { + dev_warn(dev->ctrl.device, + "IO queues tagset allocation failed %d\n", ret); + return; } + dev->ctrl.tagset = set; +} - nvme_dbbuf_set(dev); +static void nvme_pci_update_nr_queues(struct nvme_dev *dev) +{ + blk_mq_update_nr_hw_queues(&dev->tagset, dev->online_queues - 1); + /* free previously allocated queues that are no longer usable */ + nvme_free_queues(dev, dev->online_queues); } static int nvme_pci_enable(struct nvme_dev *dev) @@ -2725,10 +2722,8 @@ static void nvme_dev_disable(struct nvme_dev *dev, bool shutdown) nvme_pci_disable(dev); nvme_reap_pending_cqes(dev); - blk_mq_tagset_busy_iter(&dev->tagset, nvme_cancel_request, &dev->ctrl); - blk_mq_tagset_busy_iter(&dev->admin_tagset, nvme_cancel_request, &dev->ctrl); - blk_mq_tagset_wait_completed_request(&dev->tagset); - blk_mq_tagset_wait_completed_request(&dev->admin_tagset); + nvme_cancel_tagset(&dev->ctrl); + nvme_cancel_admin_tagset(&dev->ctrl); /* * The driver will not be starting up queues again if shutting down so @@ -2842,9 +2837,13 @@ static void nvme_reset_work(struct work_struct *work) if (result) goto out_unlock; - result = nvme_alloc_admin_tags(dev); - if (result) - goto out_unlock; + if (!dev->ctrl.admin_q) { + result = nvme_pci_alloc_admin_tag_set(dev); + if (result) + goto out_unlock; + } else { + nvme_start_admin_queue(&dev->ctrl); + } /* * Limit the max command size to prevent iod->sg allocations going @@ -2923,7 +2922,11 @@ static void nvme_reset_work(struct work_struct *work) } else { nvme_start_queues(&dev->ctrl); nvme_wait_freeze(&dev->ctrl); - nvme_dev_add(dev); + if (!dev->ctrl.tagset) + nvme_pci_alloc_tag_set(dev); + else + nvme_pci_update_nr_queues(dev); + nvme_dbbuf_set(dev); nvme_unfreeze(&dev->ctrl); } diff --git a/drivers/nvme/host/rdma.c b/drivers/nvme/host/rdma.c index 4665aebd944d..3100643be299 100644 --- a/drivers/nvme/host/rdma.c +++ b/drivers/nvme/host/rdma.c @@ -29,7 +29,7 @@ #include "fabrics.h" -#define NVME_RDMA_CONNECT_TIMEOUT_MS 3000 /* 3 second */ +#define NVME_RDMA_CM_TIMEOUT_MS 3000 /* 3 second */ #define NVME_RDMA_MAX_SEGMENTS 256 @@ -248,12 +248,9 @@ static int nvme_rdma_wait_for_cm(struct nvme_rdma_queue *queue) { int ret; - ret = wait_for_completion_interruptible_timeout(&queue->cm_done, - msecs_to_jiffies(NVME_RDMA_CONNECT_TIMEOUT_MS) + 1); - if (ret < 0) + ret = wait_for_completion_interruptible(&queue->cm_done); + if (ret) return ret; - if (ret == 0) - return -ETIMEDOUT; WARN_ON_ONCE(queue->cm_error > 0); return queue->cm_error; } @@ -612,7 +609,7 @@ static int nvme_rdma_alloc_queue(struct nvme_rdma_ctrl *ctrl, queue->cm_error = -ETIMEDOUT; ret = rdma_resolve_addr(queue->cm_id, src_addr, (struct sockaddr *)&ctrl->addr, - NVME_RDMA_CONNECT_TIMEOUT_MS); + NVME_RDMA_CM_TIMEOUT_MS); if (ret) { dev_info(ctrl->ctrl.device, "rdma_resolve_addr failed (%d).\n", ret); @@ -790,50 +787,54 @@ out_free_queues: return ret; } -static struct blk_mq_tag_set *nvme_rdma_alloc_tagset(struct nvme_ctrl *nctrl, - bool admin) +static int nvme_rdma_alloc_admin_tag_set(struct nvme_ctrl *nctrl) { struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl); - struct blk_mq_tag_set *set; + struct blk_mq_tag_set *set = &ctrl->admin_tag_set; int ret; - if (admin) { - set = &ctrl->admin_tag_set; - memset(set, 0, sizeof(*set)); - set->ops = &nvme_rdma_admin_mq_ops; - set->queue_depth = NVME_AQ_MQ_TAG_DEPTH; - set->reserved_tags = NVMF_RESERVED_TAGS; - set->numa_node = nctrl->numa_node; - set->cmd_size = sizeof(struct nvme_rdma_request) + - NVME_RDMA_DATA_SGL_SIZE; - set->driver_data = ctrl; - set->nr_hw_queues = 1; - set->timeout = NVME_ADMIN_TIMEOUT; - set->flags = BLK_MQ_F_NO_SCHED; - } else { - set = &ctrl->tag_set; - memset(set, 0, sizeof(*set)); - set->ops = &nvme_rdma_mq_ops; - set->queue_depth = nctrl->sqsize + 1; - set->reserved_tags = NVMF_RESERVED_TAGS; - set->numa_node = nctrl->numa_node; - set->flags = BLK_MQ_F_SHOULD_MERGE; - set->cmd_size = sizeof(struct nvme_rdma_request) + - NVME_RDMA_DATA_SGL_SIZE; - if (nctrl->max_integrity_segments) - set->cmd_size += sizeof(struct nvme_rdma_sgl) + - NVME_RDMA_METADATA_SGL_SIZE; - set->driver_data = ctrl; - set->nr_hw_queues = nctrl->queue_count - 1; - set->timeout = NVME_IO_TIMEOUT; - set->nr_maps = nctrl->opts->nr_poll_queues ? HCTX_MAX_TYPES : 2; - } - + memset(set, 0, sizeof(*set)); + set->ops = &nvme_rdma_admin_mq_ops; + set->queue_depth = NVME_AQ_MQ_TAG_DEPTH; + set->reserved_tags = NVMF_RESERVED_TAGS; + set->numa_node = nctrl->numa_node; + set->cmd_size = sizeof(struct nvme_rdma_request) + + NVME_RDMA_DATA_SGL_SIZE; + set->driver_data = ctrl; + set->nr_hw_queues = 1; + set->timeout = NVME_ADMIN_TIMEOUT; + set->flags = BLK_MQ_F_NO_SCHED; ret = blk_mq_alloc_tag_set(set); - if (ret) - return ERR_PTR(ret); + if (!ret) + ctrl->ctrl.admin_tagset = set; + return ret; +} - return set; +static int nvme_rdma_alloc_tag_set(struct nvme_ctrl *nctrl) +{ + struct nvme_rdma_ctrl *ctrl = to_rdma_ctrl(nctrl); + struct blk_mq_tag_set *set = &ctrl->tag_set; + int ret; + + memset(set, 0, sizeof(*set)); + set->ops = &nvme_rdma_mq_ops; + set->queue_depth = nctrl->sqsize + 1; + set->reserved_tags = NVMF_RESERVED_TAGS; + set->numa_node = nctrl->numa_node; + set->flags = BLK_MQ_F_SHOULD_MERGE; + set->cmd_size = sizeof(struct nvme_rdma_request) + + NVME_RDMA_DATA_SGL_SIZE; + if (nctrl->max_integrity_segments) + set->cmd_size += sizeof(struct nvme_rdma_sgl) + + NVME_RDMA_METADATA_SGL_SIZE; + set->driver_data = ctrl; + set->nr_hw_queues = nctrl->queue_count - 1; + set->timeout = NVME_IO_TIMEOUT; + set->nr_maps = nctrl->opts->nr_poll_queues ? HCTX_MAX_TYPES : 2; + ret = blk_mq_alloc_tag_set(set); + if (!ret) + ctrl->ctrl.tagset = set; + return ret; } static void nvme_rdma_destroy_admin_queue(struct nvme_rdma_ctrl *ctrl, @@ -885,11 +886,9 @@ static int nvme_rdma_configure_admin_queue(struct nvme_rdma_ctrl *ctrl, goto out_free_queue; if (new) { - ctrl->ctrl.admin_tagset = nvme_rdma_alloc_tagset(&ctrl->ctrl, true); - if (IS_ERR(ctrl->ctrl.admin_tagset)) { - error = PTR_ERR(ctrl->ctrl.admin_tagset); + error = nvme_rdma_alloc_admin_tag_set(&ctrl->ctrl); + if (error) goto out_free_async_qe; - } ctrl->ctrl.fabrics_q = blk_mq_init_queue(&ctrl->admin_tag_set); if (IS_ERR(ctrl->ctrl.fabrics_q)) { @@ -972,11 +971,9 @@ static int nvme_rdma_configure_io_queues(struct nvme_rdma_ctrl *ctrl, bool new) return ret; if (new) { - ctrl->ctrl.tagset = nvme_rdma_alloc_tagset(&ctrl->ctrl, false); - if (IS_ERR(ctrl->ctrl.tagset)) { - ret = PTR_ERR(ctrl->ctrl.tagset); + ret = nvme_rdma_alloc_tag_set(&ctrl->ctrl); + if (ret) goto out_free_io_queues; - } ret = nvme_ctrl_init_connect_q(&(ctrl->ctrl)); if (ret) @@ -1205,6 +1202,7 @@ static void nvme_rdma_error_recovery_work(struct work_struct *work) struct nvme_rdma_ctrl *ctrl = container_of(work, struct nvme_rdma_ctrl, err_work); + nvme_auth_stop(&ctrl->ctrl); nvme_stop_keep_alive(&ctrl->ctrl); flush_work(&ctrl->ctrl.async_event_work); nvme_rdma_teardown_io_queues(ctrl, false); @@ -1894,7 +1892,7 @@ static int nvme_rdma_addr_resolved(struct nvme_rdma_queue *queue) if (ctrl->opts->tos >= 0) rdma_set_service_type(queue->cm_id, ctrl->opts->tos); - ret = rdma_resolve_route(queue->cm_id, NVME_RDMA_CONNECT_TIMEOUT_MS); + ret = rdma_resolve_route(queue->cm_id, NVME_RDMA_CM_TIMEOUT_MS); if (ret) { dev_err(ctrl->device, "rdma_resolve_route failed (%d).\n", queue->cm_error); diff --git a/drivers/nvme/host/tcp.c b/drivers/nvme/host/tcp.c index b95ee85053e3..e82dcfcda29b 100644 --- a/drivers/nvme/host/tcp.c +++ b/drivers/nvme/host/tcp.c @@ -209,9 +209,11 @@ static inline u8 nvme_tcp_ddgst_len(struct nvme_tcp_queue *queue) return queue->data_digest ? NVME_TCP_DIGEST_LENGTH : 0; } -static inline size_t nvme_tcp_inline_data_size(struct nvme_tcp_queue *queue) +static inline size_t nvme_tcp_inline_data_size(struct nvme_tcp_request *req) { - return queue->cmnd_capsule_len - sizeof(struct nvme_command); + if (nvme_is_fabrics(req->req.cmd)) + return NVME_TCP_ADMIN_CCSZ; + return req->queue->cmnd_capsule_len - sizeof(struct nvme_command); } static inline bool nvme_tcp_async_req(struct nvme_tcp_request *req) @@ -229,7 +231,7 @@ static inline bool nvme_tcp_has_inline_data(struct nvme_tcp_request *req) rq = blk_mq_rq_from_pdu(req); return rq_data_dir(rq) == WRITE && req->data_len && - req->data_len <= nvme_tcp_inline_data_size(req->queue); + req->data_len <= nvme_tcp_inline_data_size(req); } static inline struct page *nvme_tcp_req_cur_page(struct nvme_tcp_request *req) @@ -1685,45 +1687,49 @@ static int nvme_tcp_start_queue(struct nvme_ctrl *nctrl, int idx) return ret; } -static struct blk_mq_tag_set *nvme_tcp_alloc_tagset(struct nvme_ctrl *nctrl, - bool admin) +static int nvme_tcp_alloc_admin_tag_set(struct nvme_ctrl *nctrl) { struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl); - struct blk_mq_tag_set *set; + struct blk_mq_tag_set *set = &ctrl->admin_tag_set; int ret; - if (admin) { - set = &ctrl->admin_tag_set; - memset(set, 0, sizeof(*set)); - set->ops = &nvme_tcp_admin_mq_ops; - set->queue_depth = NVME_AQ_MQ_TAG_DEPTH; - set->reserved_tags = NVMF_RESERVED_TAGS; - set->numa_node = nctrl->numa_node; - set->flags = BLK_MQ_F_BLOCKING; - set->cmd_size = sizeof(struct nvme_tcp_request); - set->driver_data = ctrl; - set->nr_hw_queues = 1; - set->timeout = NVME_ADMIN_TIMEOUT; - } else { - set = &ctrl->tag_set; - memset(set, 0, sizeof(*set)); - set->ops = &nvme_tcp_mq_ops; - set->queue_depth = nctrl->sqsize + 1; - set->reserved_tags = NVMF_RESERVED_TAGS; - set->numa_node = nctrl->numa_node; - set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING; - set->cmd_size = sizeof(struct nvme_tcp_request); - set->driver_data = ctrl; - set->nr_hw_queues = nctrl->queue_count - 1; - set->timeout = NVME_IO_TIMEOUT; - set->nr_maps = nctrl->opts->nr_poll_queues ? HCTX_MAX_TYPES : 2; - } - + memset(set, 0, sizeof(*set)); + set->ops = &nvme_tcp_admin_mq_ops; + set->queue_depth = NVME_AQ_MQ_TAG_DEPTH; + set->reserved_tags = NVMF_RESERVED_TAGS; + set->numa_node = nctrl->numa_node; + set->flags = BLK_MQ_F_BLOCKING; + set->cmd_size = sizeof(struct nvme_tcp_request); + set->driver_data = ctrl; + set->nr_hw_queues = 1; + set->timeout = NVME_ADMIN_TIMEOUT; ret = blk_mq_alloc_tag_set(set); - if (ret) - return ERR_PTR(ret); + if (!ret) + nctrl->admin_tagset = set; + return ret; +} - return set; +static int nvme_tcp_alloc_tag_set(struct nvme_ctrl *nctrl) +{ + struct nvme_tcp_ctrl *ctrl = to_tcp_ctrl(nctrl); + struct blk_mq_tag_set *set = &ctrl->tag_set; + int ret; + + memset(set, 0, sizeof(*set)); + set->ops = &nvme_tcp_mq_ops; + set->queue_depth = nctrl->sqsize + 1; + set->reserved_tags = NVMF_RESERVED_TAGS; + set->numa_node = nctrl->numa_node; + set->flags = BLK_MQ_F_SHOULD_MERGE | BLK_MQ_F_BLOCKING; + set->cmd_size = sizeof(struct nvme_tcp_request); + set->driver_data = ctrl; + set->nr_hw_queues = nctrl->queue_count - 1; + set->timeout = NVME_IO_TIMEOUT; + set->nr_maps = nctrl->opts->nr_poll_queues ? HCTX_MAX_TYPES : 2; + ret = blk_mq_alloc_tag_set(set); + if (!ret) + nctrl->tagset = set; + return ret; } static void nvme_tcp_free_admin_queue(struct nvme_ctrl *ctrl) @@ -1899,11 +1905,9 @@ static int nvme_tcp_configure_io_queues(struct nvme_ctrl *ctrl, bool new) return ret; if (new) { - ctrl->tagset = nvme_tcp_alloc_tagset(ctrl, false); - if (IS_ERR(ctrl->tagset)) { - ret = PTR_ERR(ctrl->tagset); + ret = nvme_tcp_alloc_tag_set(ctrl); + if (ret) goto out_free_io_queues; - } ret = nvme_ctrl_init_connect_q(ctrl); if (ret) @@ -1968,11 +1972,9 @@ static int nvme_tcp_configure_admin_queue(struct nvme_ctrl *ctrl, bool new) return error; if (new) { - ctrl->admin_tagset = nvme_tcp_alloc_tagset(ctrl, true); - if (IS_ERR(ctrl->admin_tagset)) { - error = PTR_ERR(ctrl->admin_tagset); + error = nvme_tcp_alloc_admin_tag_set(ctrl); + if (error) goto out_free_queue; - } ctrl->fabrics_q = blk_mq_init_queue(ctrl->admin_tagset); if (IS_ERR(ctrl->fabrics_q)) { @@ -2173,6 +2175,7 @@ static void nvme_tcp_error_recovery_work(struct work_struct *work) struct nvme_tcp_ctrl, err_work); struct nvme_ctrl *ctrl = &tcp_ctrl->ctrl; + nvme_auth_stop(ctrl); nvme_stop_keep_alive(ctrl); flush_work(&ctrl->async_event_work); nvme_tcp_teardown_io_queues(ctrl, false); @@ -2371,7 +2374,7 @@ static blk_status_t nvme_tcp_map_data(struct nvme_tcp_queue *queue, if (!blk_rq_nr_phys_segments(rq)) nvme_tcp_set_sg_null(c); else if (rq_data_dir(rq) == WRITE && - req->data_len <= nvme_tcp_inline_data_size(queue)) + req->data_len <= nvme_tcp_inline_data_size(req)) nvme_tcp_set_sg_inline(queue, c, req->data_len); else nvme_tcp_set_sg_host_data(c, req->data_len); @@ -2406,7 +2409,7 @@ static blk_status_t nvme_tcp_setup_cmd_pdu(struct nvme_ns *ns, nvme_tcp_init_iter(req, rq_data_dir(rq)); if (rq_data_dir(rq) == WRITE && - req->data_len <= nvme_tcp_inline_data_size(queue)) + req->data_len <= nvme_tcp_inline_data_size(req)) req->pdu_len = req->data_len; pdu->hdr.type = nvme_tcp_cmd; diff --git a/drivers/nvme/host/trace.c b/drivers/nvme/host/trace.c index 2a89c5aa0790..1c36fcedea20 100644 --- a/drivers/nvme/host/trace.c +++ b/drivers/nvme/host/trace.c @@ -287,6 +287,34 @@ static const char *nvme_trace_fabrics_property_get(struct trace_seq *p, u8 *spc) return ret; } +static const char *nvme_trace_fabrics_auth_send(struct trace_seq *p, u8 *spc) +{ + const char *ret = trace_seq_buffer_ptr(p); + u8 spsp0 = spc[1]; + u8 spsp1 = spc[2]; + u8 secp = spc[3]; + u32 tl = get_unaligned_le32(spc + 4); + + trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, tl=%u", + spsp0, spsp1, secp, tl); + trace_seq_putc(p, 0); + return ret; +} + +static const char *nvme_trace_fabrics_auth_receive(struct trace_seq *p, u8 *spc) +{ + const char *ret = trace_seq_buffer_ptr(p); + u8 spsp0 = spc[1]; + u8 spsp1 = spc[2]; + u8 secp = spc[3]; + u32 al = get_unaligned_le32(spc + 4); + + trace_seq_printf(p, "spsp0=%02x, spsp1=%02x, secp=%02x, al=%u", + spsp0, spsp1, secp, al); + trace_seq_putc(p, 0); + return ret; +} + static const char *nvme_trace_fabrics_common(struct trace_seq *p, u8 *spc) { const char *ret = trace_seq_buffer_ptr(p); @@ -306,6 +334,10 @@ const char *nvme_trace_parse_fabrics_cmd(struct trace_seq *p, return nvme_trace_fabrics_connect(p, spc); case nvme_fabrics_type_property_get: return nvme_trace_fabrics_property_get(p, spc); + case nvme_fabrics_type_auth_send: + return nvme_trace_fabrics_auth_send(p, spc); + case nvme_fabrics_type_auth_receive: + return nvme_trace_fabrics_auth_receive(p, spc); default: return nvme_trace_fabrics_common(p, spc); } diff --git a/drivers/nvme/host/trace.h b/drivers/nvme/host/trace.h index 37c7f4c89f92..6f0eaf6a1528 100644 --- a/drivers/nvme/host/trace.h +++ b/drivers/nvme/host/trace.h @@ -98,7 +98,7 @@ TRACE_EVENT(nvme_complete_rq, TP_fast_assign( __entry->ctrl_id = nvme_req(req)->ctrl->instance; __entry->qid = nvme_req_qid(req); - __entry->cid = req->tag; + __entry->cid = nvme_req(req)->cmd->common.command_id; __entry->result = le64_to_cpu(nvme_req(req)->result.u64); __entry->retries = nvme_req(req)->retries; __entry->flags = nvme_req(req)->flags; diff --git a/drivers/nvme/target/Kconfig b/drivers/nvme/target/Kconfig index 973561c93888..79fc64035ee3 100644 --- a/drivers/nvme/target/Kconfig +++ b/drivers/nvme/target/Kconfig @@ -83,3 +83,18 @@ config NVME_TARGET_TCP devices over TCP. If unsure, say N. + +config NVME_TARGET_AUTH + bool "NVMe over Fabrics In-band Authentication support" + depends on NVME_TARGET + select NVME_COMMON + select CRYPTO + select CRYPTO_HMAC + select CRYPTO_SHA256 + select CRYPTO_SHA512 + select CRYPTO_DH + select CRYPTO_DH_RFC7919_GROUPS + help + This enables support for NVMe over Fabrics In-band Authentication + + If unsure, say N. diff --git a/drivers/nvme/target/Makefile b/drivers/nvme/target/Makefile index 9837e580fa7e..c66820102493 100644 --- a/drivers/nvme/target/Makefile +++ b/drivers/nvme/target/Makefile @@ -13,6 +13,7 @@ nvmet-y += core.o configfs.o admin-cmd.o fabrics-cmd.o \ discovery.o io-cmd-file.o io-cmd-bdev.o nvmet-$(CONFIG_NVME_TARGET_PASSTHRU) += passthru.o nvmet-$(CONFIG_BLK_DEV_ZONED) += zns.o +nvmet-$(CONFIG_NVME_TARGET_AUTH) += fabrics-cmd-auth.o auth.o nvme-loop-y += loop.o nvmet-rdma-y += rdma.o nvmet-fc-y += fc.o diff --git a/drivers/nvme/target/admin-cmd.c b/drivers/nvme/target/admin-cmd.c index 397daaf51f1b..fc8a957fad0a 100644 --- a/drivers/nvme/target/admin-cmd.c +++ b/drivers/nvme/target/admin-cmd.c @@ -1017,7 +1017,9 @@ u16 nvmet_parse_admin_cmd(struct nvmet_req *req) u16 ret; if (nvme_is_fabrics(cmd)) - return nvmet_parse_fabrics_cmd(req); + return nvmet_parse_fabrics_admin_cmd(req); + if (unlikely(!nvmet_check_auth_status(req))) + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR; if (nvmet_is_disc_subsys(nvmet_req_subsys(req))) return nvmet_parse_discovery_cmd(req); diff --git a/drivers/nvme/target/auth.c b/drivers/nvme/target/auth.c new file mode 100644 index 000000000000..cf690df34775 --- /dev/null +++ b/drivers/nvme/target/auth.c @@ -0,0 +1,525 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NVMe over Fabrics DH-HMAC-CHAP authentication. + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. + * All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "nvmet.h" + +int nvmet_auth_set_key(struct nvmet_host *host, const char *secret, + bool set_ctrl) +{ + unsigned char key_hash; + char *dhchap_secret; + + if (sscanf(secret, "DHHC-1:%hhd:%*s", &key_hash) != 1) + return -EINVAL; + if (key_hash > 3) { + pr_warn("Invalid DH-HMAC-CHAP hash id %d\n", + key_hash); + return -EINVAL; + } + if (key_hash > 0) { + /* Validate selected hash algorithm */ + const char *hmac = nvme_auth_hmac_name(key_hash); + + if (!crypto_has_shash(hmac, 0, 0)) { + pr_err("DH-HMAC-CHAP hash %s unsupported\n", hmac); + return -ENOTSUPP; + } + } + dhchap_secret = kstrdup(secret, GFP_KERNEL); + if (!dhchap_secret) + return -ENOMEM; + if (set_ctrl) { + host->dhchap_ctrl_secret = strim(dhchap_secret); + host->dhchap_ctrl_key_hash = key_hash; + } else { + host->dhchap_secret = strim(dhchap_secret); + host->dhchap_key_hash = key_hash; + } + return 0; +} + +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id) +{ + const char *dhgroup_kpp; + int ret = 0; + + pr_debug("%s: ctrl %d selecting dhgroup %d\n", + __func__, ctrl->cntlid, dhgroup_id); + + if (ctrl->dh_tfm) { + if (ctrl->dh_gid == dhgroup_id) { + pr_debug("%s: ctrl %d reuse existing DH group %d\n", + __func__, ctrl->cntlid, dhgroup_id); + return 0; + } + crypto_free_kpp(ctrl->dh_tfm); + ctrl->dh_tfm = NULL; + ctrl->dh_gid = 0; + } + + if (dhgroup_id == NVME_AUTH_DHGROUP_NULL) + return 0; + + dhgroup_kpp = nvme_auth_dhgroup_kpp(dhgroup_id); + if (!dhgroup_kpp) { + pr_debug("%s: ctrl %d invalid DH group %d\n", + __func__, ctrl->cntlid, dhgroup_id); + return -EINVAL; + } + ctrl->dh_tfm = crypto_alloc_kpp(dhgroup_kpp, 0, 0); + if (IS_ERR(ctrl->dh_tfm)) { + pr_debug("%s: ctrl %d failed to setup DH group %d, err %ld\n", + __func__, ctrl->cntlid, dhgroup_id, + PTR_ERR(ctrl->dh_tfm)); + ret = PTR_ERR(ctrl->dh_tfm); + ctrl->dh_tfm = NULL; + ctrl->dh_gid = 0; + } else { + ctrl->dh_gid = dhgroup_id; + pr_debug("%s: ctrl %d setup DH group %d\n", + __func__, ctrl->cntlid, ctrl->dh_gid); + ret = nvme_auth_gen_privkey(ctrl->dh_tfm, ctrl->dh_gid); + if (ret < 0) { + pr_debug("%s: ctrl %d failed to generate private key, err %d\n", + __func__, ctrl->cntlid, ret); + kfree_sensitive(ctrl->dh_key); + return ret; + } + ctrl->dh_keysize = crypto_kpp_maxsize(ctrl->dh_tfm); + kfree_sensitive(ctrl->dh_key); + ctrl->dh_key = kzalloc(ctrl->dh_keysize, GFP_KERNEL); + if (!ctrl->dh_key) { + pr_warn("ctrl %d failed to allocate public key\n", + ctrl->cntlid); + return -ENOMEM; + } + ret = nvme_auth_gen_pubkey(ctrl->dh_tfm, ctrl->dh_key, + ctrl->dh_keysize); + if (ret < 0) { + pr_warn("ctrl %d failed to generate public key\n", + ctrl->cntlid); + kfree(ctrl->dh_key); + ctrl->dh_key = NULL; + } + } + + return ret; +} + +int nvmet_setup_auth(struct nvmet_ctrl *ctrl) +{ + int ret = 0; + struct nvmet_host_link *p; + struct nvmet_host *host = NULL; + const char *hash_name; + + down_read(&nvmet_config_sem); + if (nvmet_is_disc_subsys(ctrl->subsys)) + goto out_unlock; + + if (ctrl->subsys->allow_any_host) + goto out_unlock; + + list_for_each_entry(p, &ctrl->subsys->hosts, entry) { + pr_debug("check %s\n", nvmet_host_name(p->host)); + if (strcmp(nvmet_host_name(p->host), ctrl->hostnqn)) + continue; + host = p->host; + break; + } + if (!host) { + pr_debug("host %s not found\n", ctrl->hostnqn); + ret = -EPERM; + goto out_unlock; + } + + ret = nvmet_setup_dhgroup(ctrl, host->dhchap_dhgroup_id); + if (ret < 0) + pr_warn("Failed to setup DH group"); + + if (!host->dhchap_secret) { + pr_debug("No authentication provided\n"); + goto out_unlock; + } + + if (host->dhchap_hash_id == ctrl->shash_id) { + pr_debug("Re-use existing hash ID %d\n", + ctrl->shash_id); + } else { + hash_name = nvme_auth_hmac_name(host->dhchap_hash_id); + if (!hash_name) { + pr_warn("Hash ID %d invalid\n", host->dhchap_hash_id); + ret = -EINVAL; + goto out_unlock; + } + ctrl->shash_id = host->dhchap_hash_id; + } + + /* Skip the 'DHHC-1:XX:' prefix */ + nvme_auth_free_key(ctrl->host_key); + ctrl->host_key = nvme_auth_extract_key(host->dhchap_secret + 10, + host->dhchap_key_hash); + if (IS_ERR(ctrl->host_key)) { + ret = PTR_ERR(ctrl->host_key); + ctrl->host_key = NULL; + goto out_free_hash; + } + pr_debug("%s: using hash %s key %*ph\n", __func__, + ctrl->host_key->hash > 0 ? + nvme_auth_hmac_name(ctrl->host_key->hash) : "none", + (int)ctrl->host_key->len, ctrl->host_key->key); + + nvme_auth_free_key(ctrl->ctrl_key); + if (!host->dhchap_ctrl_secret) { + ctrl->ctrl_key = NULL; + goto out_unlock; + } + + ctrl->ctrl_key = nvme_auth_extract_key(host->dhchap_ctrl_secret + 10, + host->dhchap_ctrl_key_hash); + if (IS_ERR(ctrl->ctrl_key)) { + ret = PTR_ERR(ctrl->ctrl_key); + ctrl->ctrl_key = NULL; + } + pr_debug("%s: using ctrl hash %s key %*ph\n", __func__, + ctrl->ctrl_key->hash > 0 ? + nvme_auth_hmac_name(ctrl->ctrl_key->hash) : "none", + (int)ctrl->ctrl_key->len, ctrl->ctrl_key->key); + +out_free_hash: + if (ret) { + if (ctrl->host_key) { + nvme_auth_free_key(ctrl->host_key); + ctrl->host_key = NULL; + } + ctrl->shash_id = 0; + } +out_unlock: + up_read(&nvmet_config_sem); + + return ret; +} + +void nvmet_auth_sq_free(struct nvmet_sq *sq) +{ + cancel_delayed_work(&sq->auth_expired_work); + kfree(sq->dhchap_c1); + sq->dhchap_c1 = NULL; + kfree(sq->dhchap_c2); + sq->dhchap_c2 = NULL; + kfree(sq->dhchap_skey); + sq->dhchap_skey = NULL; +} + +void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) +{ + ctrl->shash_id = 0; + + if (ctrl->dh_tfm) { + crypto_free_kpp(ctrl->dh_tfm); + ctrl->dh_tfm = NULL; + ctrl->dh_gid = 0; + } + kfree_sensitive(ctrl->dh_key); + ctrl->dh_key = NULL; + + if (ctrl->host_key) { + nvme_auth_free_key(ctrl->host_key); + ctrl->host_key = NULL; + } + if (ctrl->ctrl_key) { + nvme_auth_free_key(ctrl->ctrl_key); + ctrl->ctrl_key = NULL; + } +} + +bool nvmet_check_auth_status(struct nvmet_req *req) +{ + if (req->sq->ctrl->host_key && + !req->sq->authenticated) + return false; + return true; +} + +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, + unsigned int shash_len) +{ + struct crypto_shash *shash_tfm; + struct shash_desc *shash; + struct nvmet_ctrl *ctrl = req->sq->ctrl; + const char *hash_name; + u8 *challenge = req->sq->dhchap_c1, *host_response; + u8 buf[4]; + int ret; + + hash_name = nvme_auth_hmac_name(ctrl->shash_id); + if (!hash_name) { + pr_warn("Hash ID %d invalid\n", ctrl->shash_id); + return -EINVAL; + } + + shash_tfm = crypto_alloc_shash(hash_name, 0, 0); + if (IS_ERR(shash_tfm)) { + pr_err("failed to allocate shash %s\n", hash_name); + return PTR_ERR(shash_tfm); + } + + if (shash_len != crypto_shash_digestsize(shash_tfm)) { + pr_debug("%s: hash len mismatch (len %d digest %d)\n", + __func__, shash_len, + crypto_shash_digestsize(shash_tfm)); + ret = -EINVAL; + goto out_free_tfm; + } + + host_response = nvme_auth_transform_key(ctrl->host_key, ctrl->hostnqn); + if (IS_ERR(host_response)) { + ret = PTR_ERR(host_response); + goto out_free_tfm; + } + + ret = crypto_shash_setkey(shash_tfm, host_response, + ctrl->host_key->len); + if (ret) + goto out_free_response; + + if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { + challenge = kmalloc(shash_len, GFP_KERNEL); + if (!challenge) { + ret = -ENOMEM; + goto out_free_response; + } + ret = nvme_auth_augmented_challenge(ctrl->shash_id, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len, + req->sq->dhchap_c1, + challenge, shash_len); + if (ret) + goto out_free_response; + } + + pr_debug("ctrl %d qid %d host response seq %u transaction %d\n", + ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, + req->sq->dhchap_tid); + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), + GFP_KERNEL); + if (!shash) { + ret = -ENOMEM; + goto out_free_response; + } + shash->tfm = shash_tfm; + ret = crypto_shash_init(shash); + if (ret) + goto out; + ret = crypto_shash_update(shash, challenge, shash_len); + if (ret) + goto out; + put_unaligned_le32(req->sq->dhchap_s1, buf); + ret = crypto_shash_update(shash, buf, 4); + if (ret) + goto out; + put_unaligned_le16(req->sq->dhchap_tid, buf); + ret = crypto_shash_update(shash, buf, 2); + if (ret) + goto out; + memset(buf, 0, 4); + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, "HostHost", 8); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); + if (ret) + goto out; + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->subsysnqn, + strlen(ctrl->subsysnqn)); + if (ret) + goto out; + ret = crypto_shash_final(shash, response); +out: + if (challenge != req->sq->dhchap_c1) + kfree(challenge); + kfree(shash); +out_free_response: + kfree_sensitive(host_response); +out_free_tfm: + crypto_free_shash(shash_tfm); + return 0; +} + +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, + unsigned int shash_len) +{ + struct crypto_shash *shash_tfm; + struct shash_desc *shash; + struct nvmet_ctrl *ctrl = req->sq->ctrl; + const char *hash_name; + u8 *challenge = req->sq->dhchap_c2, *ctrl_response; + u8 buf[4]; + int ret; + + hash_name = nvme_auth_hmac_name(ctrl->shash_id); + if (!hash_name) { + pr_warn("Hash ID %d invalid\n", ctrl->shash_id); + return -EINVAL; + } + + shash_tfm = crypto_alloc_shash(hash_name, 0, 0); + if (IS_ERR(shash_tfm)) { + pr_err("failed to allocate shash %s\n", hash_name); + return PTR_ERR(shash_tfm); + } + + if (shash_len != crypto_shash_digestsize(shash_tfm)) { + pr_debug("%s: hash len mismatch (len %d digest %d)\n", + __func__, shash_len, + crypto_shash_digestsize(shash_tfm)); + ret = -EINVAL; + goto out_free_tfm; + } + + ctrl_response = nvme_auth_transform_key(ctrl->ctrl_key, + ctrl->subsysnqn); + if (IS_ERR(ctrl_response)) { + ret = PTR_ERR(ctrl_response); + goto out_free_tfm; + } + + ret = crypto_shash_setkey(shash_tfm, ctrl_response, + ctrl->ctrl_key->len); + if (ret) + goto out_free_response; + + if (ctrl->dh_gid != NVME_AUTH_DHGROUP_NULL) { + challenge = kmalloc(shash_len, GFP_KERNEL); + if (!challenge) { + ret = -ENOMEM; + goto out_free_response; + } + ret = nvme_auth_augmented_challenge(ctrl->shash_id, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len, + req->sq->dhchap_c2, + challenge, shash_len); + if (ret) + goto out_free_response; + } + + shash = kzalloc(sizeof(*shash) + crypto_shash_descsize(shash_tfm), + GFP_KERNEL); + if (!shash) { + ret = -ENOMEM; + goto out_free_response; + } + shash->tfm = shash_tfm; + + ret = crypto_shash_init(shash); + if (ret) + goto out; + ret = crypto_shash_update(shash, challenge, shash_len); + if (ret) + goto out; + put_unaligned_le32(req->sq->dhchap_s2, buf); + ret = crypto_shash_update(shash, buf, 4); + if (ret) + goto out; + put_unaligned_le16(req->sq->dhchap_tid, buf); + ret = crypto_shash_update(shash, buf, 2); + if (ret) + goto out; + memset(buf, 0, 4); + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, "Controller", 10); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->subsysnqn, + strlen(ctrl->subsysnqn)); + if (ret) + goto out; + ret = crypto_shash_update(shash, buf, 1); + if (ret) + goto out; + ret = crypto_shash_update(shash, ctrl->hostnqn, strlen(ctrl->hostnqn)); + if (ret) + goto out; + ret = crypto_shash_final(shash, response); +out: + if (challenge != req->sq->dhchap_c2) + kfree(challenge); + kfree(shash); +out_free_response: + kfree_sensitive(ctrl_response); +out_free_tfm: + crypto_free_shash(shash_tfm); + return 0; +} + +int nvmet_auth_ctrl_exponential(struct nvmet_req *req, + u8 *buf, int buf_size) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + int ret = 0; + + if (!ctrl->dh_key) { + pr_warn("ctrl %d no DH public key!\n", ctrl->cntlid); + return -ENOKEY; + } + if (buf_size != ctrl->dh_keysize) { + pr_warn("ctrl %d DH public key size mismatch, need %zu is %d\n", + ctrl->cntlid, ctrl->dh_keysize, buf_size); + ret = -EINVAL; + } else { + memcpy(buf, ctrl->dh_key, buf_size); + pr_debug("%s: ctrl %d public key %*ph\n", __func__, + ctrl->cntlid, (int)buf_size, buf); + } + + return ret; +} + +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, + u8 *pkey, int pkey_size) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + int ret; + + req->sq->dhchap_skey_len = ctrl->dh_keysize; + req->sq->dhchap_skey = kzalloc(req->sq->dhchap_skey_len, GFP_KERNEL); + if (!req->sq->dhchap_skey) + return -ENOMEM; + ret = nvme_auth_gen_shared_secret(ctrl->dh_tfm, + pkey, pkey_size, + req->sq->dhchap_skey, + req->sq->dhchap_skey_len); + if (ret) + pr_debug("failed to compute shared secret, err %d\n", ret); + else + pr_debug("%s: shared secret %*ph\n", __func__, + (int)req->sq->dhchap_skey_len, + req->sq->dhchap_skey); + + return ret; +} diff --git a/drivers/nvme/target/configfs.c b/drivers/nvme/target/configfs.c index ff77c3d2354f..2bcd60758919 100644 --- a/drivers/nvme/target/configfs.c +++ b/drivers/nvme/target/configfs.c @@ -11,6 +11,11 @@ #include #include #include +#ifdef CONFIG_NVME_TARGET_AUTH +#include +#endif +#include +#include #include "nvmet.h" @@ -1680,10 +1685,133 @@ static const struct config_item_type nvmet_ports_type = { static struct config_group nvmet_subsystems_group; static struct config_group nvmet_ports_group; +#ifdef CONFIG_NVME_TARGET_AUTH +static ssize_t nvmet_host_dhchap_key_show(struct config_item *item, + char *page) +{ + u8 *dhchap_secret = to_host(item)->dhchap_secret; + + if (!dhchap_secret) + return sprintf(page, "\n"); + return sprintf(page, "%s\n", dhchap_secret); +} + +static ssize_t nvmet_host_dhchap_key_store(struct config_item *item, + const char *page, size_t count) +{ + struct nvmet_host *host = to_host(item); + int ret; + + ret = nvmet_auth_set_key(host, page, false); + /* + * Re-authentication is a soft state, so keep the + * current authentication valid until the host + * requests re-authentication. + */ + return ret < 0 ? ret : count; +} + +CONFIGFS_ATTR(nvmet_host_, dhchap_key); + +static ssize_t nvmet_host_dhchap_ctrl_key_show(struct config_item *item, + char *page) +{ + u8 *dhchap_secret = to_host(item)->dhchap_ctrl_secret; + + if (!dhchap_secret) + return sprintf(page, "\n"); + return sprintf(page, "%s\n", dhchap_secret); +} + +static ssize_t nvmet_host_dhchap_ctrl_key_store(struct config_item *item, + const char *page, size_t count) +{ + struct nvmet_host *host = to_host(item); + int ret; + + ret = nvmet_auth_set_key(host, page, true); + /* + * Re-authentication is a soft state, so keep the + * current authentication valid until the host + * requests re-authentication. + */ + return ret < 0 ? ret : count; +} + +CONFIGFS_ATTR(nvmet_host_, dhchap_ctrl_key); + +static ssize_t nvmet_host_dhchap_hash_show(struct config_item *item, + char *page) +{ + struct nvmet_host *host = to_host(item); + const char *hash_name = nvme_auth_hmac_name(host->dhchap_hash_id); + + return sprintf(page, "%s\n", hash_name ? hash_name : "none"); +} + +static ssize_t nvmet_host_dhchap_hash_store(struct config_item *item, + const char *page, size_t count) +{ + struct nvmet_host *host = to_host(item); + u8 hmac_id; + + hmac_id = nvme_auth_hmac_id(page); + if (hmac_id == NVME_AUTH_HASH_INVALID) + return -EINVAL; + if (!crypto_has_shash(nvme_auth_hmac_name(hmac_id), 0, 0)) + return -ENOTSUPP; + host->dhchap_hash_id = hmac_id; + return count; +} + +CONFIGFS_ATTR(nvmet_host_, dhchap_hash); + +static ssize_t nvmet_host_dhchap_dhgroup_show(struct config_item *item, + char *page) +{ + struct nvmet_host *host = to_host(item); + const char *dhgroup = nvme_auth_dhgroup_name(host->dhchap_dhgroup_id); + + return sprintf(page, "%s\n", dhgroup ? dhgroup : "none"); +} + +static ssize_t nvmet_host_dhchap_dhgroup_store(struct config_item *item, + const char *page, size_t count) +{ + struct nvmet_host *host = to_host(item); + int dhgroup_id; + + dhgroup_id = nvme_auth_dhgroup_id(page); + if (dhgroup_id == NVME_AUTH_DHGROUP_INVALID) + return -EINVAL; + if (dhgroup_id != NVME_AUTH_DHGROUP_NULL) { + const char *kpp = nvme_auth_dhgroup_kpp(dhgroup_id); + + if (!crypto_has_kpp(kpp, 0, 0)) + return -EINVAL; + } + host->dhchap_dhgroup_id = dhgroup_id; + return count; +} + +CONFIGFS_ATTR(nvmet_host_, dhchap_dhgroup); + +static struct configfs_attribute *nvmet_host_attrs[] = { + &nvmet_host_attr_dhchap_key, + &nvmet_host_attr_dhchap_ctrl_key, + &nvmet_host_attr_dhchap_hash, + &nvmet_host_attr_dhchap_dhgroup, + NULL, +}; +#endif /* CONFIG_NVME_TARGET_AUTH */ + static void nvmet_host_release(struct config_item *item) { struct nvmet_host *host = to_host(item); +#ifdef CONFIG_NVME_TARGET_AUTH + kfree(host->dhchap_secret); +#endif kfree(host); } @@ -1693,6 +1821,9 @@ static struct configfs_item_operations nvmet_host_item_ops = { static const struct config_item_type nvmet_host_type = { .ct_item_ops = &nvmet_host_item_ops, +#ifdef CONFIG_NVME_TARGET_AUTH + .ct_attrs = nvmet_host_attrs, +#endif .ct_owner = THIS_MODULE, }; @@ -1705,6 +1836,11 @@ static struct config_group *nvmet_hosts_make_group(struct config_group *group, if (!host) return ERR_PTR(-ENOMEM); +#ifdef CONFIG_NVME_TARGET_AUTH + /* Default to SHA256 */ + host->dhchap_hash_id = NVME_AUTH_HASH_SHA256; +#endif + config_group_init_type_name(&host->group, name, &nvmet_host_type); return &host->group; diff --git a/drivers/nvme/target/core.c b/drivers/nvme/target/core.c index c27660a660d9..a1345790005f 100644 --- a/drivers/nvme/target/core.c +++ b/drivers/nvme/target/core.c @@ -795,6 +795,7 @@ void nvmet_sq_destroy(struct nvmet_sq *sq) wait_for_completion(&sq->confirm_done); wait_for_completion(&sq->free_done); percpu_ref_exit(&sq->ref); + nvmet_auth_sq_free(sq); if (ctrl) { /* @@ -865,8 +866,15 @@ static inline u16 nvmet_io_cmd_check_access(struct nvmet_req *req) static u16 nvmet_parse_io_cmd(struct nvmet_req *req) { + struct nvme_command *cmd = req->cmd; u16 ret; + if (nvme_is_fabrics(cmd)) + return nvmet_parse_fabrics_io_cmd(req); + + if (unlikely(!nvmet_check_auth_status(req))) + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR; + ret = nvmet_check_ctrl_status(req); if (unlikely(ret)) return ret; @@ -1271,6 +1279,11 @@ u16 nvmet_check_ctrl_status(struct nvmet_req *req) req->cmd->common.opcode, req->sq->qid); return NVME_SC_CMD_SEQ_ERROR | NVME_SC_DNR; } + + if (unlikely(!nvmet_check_auth_status(req))) { + pr_warn("qid %d not authenticated\n", req->sq->qid); + return NVME_SC_AUTH_REQUIRED | NVME_SC_DNR; + } return 0; } @@ -1467,6 +1480,8 @@ static void nvmet_ctrl_free(struct kref *ref) flush_work(&ctrl->async_event_work); cancel_work_sync(&ctrl->fatal_err_work); + nvmet_destroy_auth(ctrl); + ida_free(&cntlid_ida, ctrl->cntlid); nvmet_async_events_free(ctrl); diff --git a/drivers/nvme/target/fabrics-cmd-auth.c b/drivers/nvme/target/fabrics-cmd-auth.c new file mode 100644 index 000000000000..c851814d6cb0 --- /dev/null +++ b/drivers/nvme/target/fabrics-cmd-auth.c @@ -0,0 +1,544 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NVMe over Fabrics DH-HMAC-CHAP authentication command handling. + * Copyright (c) 2020 Hannes Reinecke, SUSE Software Solutions. + * All rights reserved. + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +#include +#include +#include +#include +#include +#include "nvmet.h" + +static void nvmet_auth_expired_work(struct work_struct *work) +{ + struct nvmet_sq *sq = container_of(to_delayed_work(work), + struct nvmet_sq, auth_expired_work); + + pr_debug("%s: ctrl %d qid %d transaction %u expired, resetting\n", + __func__, sq->ctrl->cntlid, sq->qid, sq->dhchap_tid); + sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; + sq->dhchap_tid = -1; +} + +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req) +{ + u32 result = le32_to_cpu(req->cqe->result.u32); + + /* Initialize in-band authentication */ + INIT_DELAYED_WORK(&req->sq->auth_expired_work, + nvmet_auth_expired_work); + req->sq->authenticated = false; + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; + result |= (u32)NVME_CONNECT_AUTHREQ_ATR << 16; + req->cqe->result.u32 = cpu_to_le32(result); +} + +static u16 nvmet_auth_negotiate(struct nvmet_req *req, void *d) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + struct nvmf_auth_dhchap_negotiate_data *data = d; + int i, hash_id = 0, fallback_hash_id = 0, dhgid, fallback_dhgid; + + pr_debug("%s: ctrl %d qid %d: data sc_d %d napd %d authid %d halen %d dhlen %d\n", + __func__, ctrl->cntlid, req->sq->qid, + data->sc_c, data->napd, data->auth_protocol[0].dhchap.authid, + data->auth_protocol[0].dhchap.halen, + data->auth_protocol[0].dhchap.dhlen); + req->sq->dhchap_tid = le16_to_cpu(data->t_id); + if (data->sc_c) + return NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH; + + if (data->napd != 1) + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + + if (data->auth_protocol[0].dhchap.authid != + NVME_AUTH_DHCHAP_AUTH_ID) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + + for (i = 0; i < data->auth_protocol[0].dhchap.halen; i++) { + u8 host_hmac_id = data->auth_protocol[0].dhchap.idlist[i]; + + if (!fallback_hash_id && + crypto_has_shash(nvme_auth_hmac_name(host_hmac_id), 0, 0)) + fallback_hash_id = host_hmac_id; + if (ctrl->shash_id != host_hmac_id) + continue; + hash_id = ctrl->shash_id; + break; + } + if (hash_id == 0) { + if (fallback_hash_id == 0) { + pr_debug("%s: ctrl %d qid %d: no usable hash found\n", + __func__, ctrl->cntlid, req->sq->qid); + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + } + pr_debug("%s: ctrl %d qid %d: no usable hash found, falling back to %s\n", + __func__, ctrl->cntlid, req->sq->qid, + nvme_auth_hmac_name(fallback_hash_id)); + ctrl->shash_id = fallback_hash_id; + } + + dhgid = -1; + fallback_dhgid = -1; + for (i = 0; i < data->auth_protocol[0].dhchap.dhlen; i++) { + int tmp_dhgid = data->auth_protocol[0].dhchap.idlist[i + 30]; + + if (tmp_dhgid != ctrl->dh_gid) { + dhgid = tmp_dhgid; + break; + } + if (fallback_dhgid < 0) { + const char *kpp = nvme_auth_dhgroup_kpp(tmp_dhgid); + + if (crypto_has_kpp(kpp, 0, 0)) + fallback_dhgid = tmp_dhgid; + } + } + if (dhgid < 0) { + if (fallback_dhgid < 0) { + pr_debug("%s: ctrl %d qid %d: no usable DH group found\n", + __func__, ctrl->cntlid, req->sq->qid); + return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + } + pr_debug("%s: ctrl %d qid %d: configured DH group %s not found\n", + __func__, ctrl->cntlid, req->sq->qid, + nvme_auth_dhgroup_name(fallback_dhgid)); + ctrl->dh_gid = fallback_dhgid; + } + pr_debug("%s: ctrl %d qid %d: selected DH group %s (%d)\n", + __func__, ctrl->cntlid, req->sq->qid, + nvme_auth_dhgroup_name(ctrl->dh_gid), ctrl->dh_gid); + return 0; +} + +static u16 nvmet_auth_reply(struct nvmet_req *req, void *d) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + struct nvmf_auth_dhchap_reply_data *data = d; + u16 dhvlen = le16_to_cpu(data->dhvlen); + u8 *response; + + pr_debug("%s: ctrl %d qid %d: data hl %d cvalid %d dhvlen %u\n", + __func__, ctrl->cntlid, req->sq->qid, + data->hl, data->cvalid, dhvlen); + + if (dhvlen) { + if (!ctrl->dh_tfm) + return NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + if (nvmet_auth_ctrl_sesskey(req, data->rval + 2 * data->hl, + dhvlen) < 0) + return NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE; + } + + response = kmalloc(data->hl, GFP_KERNEL); + if (!response) + return NVME_AUTH_DHCHAP_FAILURE_FAILED; + + if (!ctrl->host_key) { + pr_warn("ctrl %d qid %d no host key\n", + ctrl->cntlid, req->sq->qid); + kfree(response); + return NVME_AUTH_DHCHAP_FAILURE_FAILED; + } + if (nvmet_auth_host_hash(req, response, data->hl) < 0) { + pr_debug("ctrl %d qid %d host hash failed\n", + ctrl->cntlid, req->sq->qid); + kfree(response); + return NVME_AUTH_DHCHAP_FAILURE_FAILED; + } + + if (memcmp(data->rval, response, data->hl)) { + pr_info("ctrl %d qid %d host response mismatch\n", + ctrl->cntlid, req->sq->qid); + kfree(response); + return NVME_AUTH_DHCHAP_FAILURE_FAILED; + } + kfree(response); + 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); + 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, + req->sq->dhchap_c2); + req->sq->dhchap_s2 = le32_to_cpu(data->seqnum); + } else { + req->sq->authenticated = true; + req->sq->dhchap_c2 = NULL; + } + + return 0; +} + +static u16 nvmet_auth_failure2(struct nvmet_req *req, void *d) +{ + struct nvmf_auth_dhchap_failure_data *data = d; + + return data->rescode_exp; +} + +void nvmet_execute_auth_send(struct nvmet_req *req) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + struct nvmf_auth_dhchap_success2_data *data; + void *d; + u32 tl; + u16 status = 0; + + if (req->cmd->auth_send.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_send_command, secp); + goto done; + } + if (req->cmd->auth_send.spsp0 != 0x01) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_send_command, spsp0); + goto done; + } + if (req->cmd->auth_send.spsp1 != 0x01) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_send_command, spsp1); + goto done; + } + tl = le32_to_cpu(req->cmd->auth_send.tl); + if (!tl) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_send_command, tl); + goto done; + } + if (!nvmet_check_transfer_len(req, tl)) { + pr_debug("%s: transfer length mismatch (%u)\n", __func__, tl); + return; + } + + d = kmalloc(tl, GFP_KERNEL); + if (!d) { + status = NVME_SC_INTERNAL; + goto done; + } + + status = nvmet_copy_from_sgl(req, 0, d, tl); + if (status) { + kfree(d); + goto done; + } + + data = d; + pr_debug("%s: ctrl %d qid %d type %d id %d step %x\n", __func__, + ctrl->cntlid, req->sq->qid, data->auth_type, data->auth_id, + req->sq->dhchap_step); + if (data->auth_type != NVME_AUTH_COMMON_MESSAGES && + data->auth_type != NVME_AUTH_DHCHAP_MESSAGES) + goto done_failure1; + if (data->auth_type == NVME_AUTH_COMMON_MESSAGES) { + if (data->auth_id == NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE) { + /* Restart negotiation */ + pr_debug("%s: ctrl %d qid %d reset negotiation\n", __func__, + ctrl->cntlid, req->sq->qid); + if (!req->sq->qid) { + if (nvmet_setup_auth(ctrl) < 0) { + status = NVME_SC_INTERNAL; + pr_err("ctrl %d qid 0 failed to setup" + "re-authentication", + ctrl->cntlid); + goto done_failure1; + } + } + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE; + } else if (data->auth_id != req->sq->dhchap_step) + goto done_failure1; + /* Validate negotiation parameters */ + status = nvmet_auth_negotiate(req, d); + if (status == 0) + req->sq->dhchap_step = + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE; + else { + req->sq->dhchap_step = + NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; + req->sq->dhchap_status = status; + status = 0; + } + goto done_kfree; + } + if (data->auth_id != req->sq->dhchap_step) { + pr_debug("%s: ctrl %d qid %d step mismatch (%d != %d)\n", + __func__, ctrl->cntlid, req->sq->qid, + data->auth_id, req->sq->dhchap_step); + goto done_failure1; + } + if (le16_to_cpu(data->t_id) != req->sq->dhchap_tid) { + pr_debug("%s: ctrl %d qid %d invalid transaction %d (expected %d)\n", + __func__, ctrl->cntlid, req->sq->qid, + le16_to_cpu(data->t_id), + req->sq->dhchap_tid); + req->sq->dhchap_step = + NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; + req->sq->dhchap_status = + NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD; + goto done_kfree; + } + + switch (data->auth_id) { + case NVME_AUTH_DHCHAP_MESSAGE_REPLY: + status = nvmet_auth_reply(req, d); + if (status == 0) + req->sq->dhchap_step = + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; + else { + req->sq->dhchap_step = + NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; + req->sq->dhchap_status = status; + status = 0; + } + goto done_kfree; + break; + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2: + req->sq->authenticated = true; + pr_debug("%s: ctrl %d qid %d ctrl authenticated\n", + __func__, ctrl->cntlid, req->sq->qid); + goto done_kfree; + break; + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE2: + status = nvmet_auth_failure2(req, d); + if (status) { + pr_warn("ctrl %d qid %d: authentication failed (%d)\n", + ctrl->cntlid, req->sq->qid, status); + req->sq->dhchap_status = status; + req->sq->authenticated = false; + status = 0; + } + goto done_kfree; + break; + default: + req->sq->dhchap_status = + NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE; + req->sq->dhchap_step = + NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; + req->sq->authenticated = false; + goto done_kfree; + break; + } +done_failure1: + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE; + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE2; + +done_kfree: + kfree(d); +done: + pr_debug("%s: ctrl %d qid %d dhchap status %x step %x\n", __func__, + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status, req->sq->dhchap_step); + if (status) + pr_debug("%s: ctrl %d qid %d nvme status %x error loc %d\n", + __func__, ctrl->cntlid, req->sq->qid, + status, req->error_loc); + req->cqe->result.u64 = 0; + nvmet_req_complete(req, status); + if (req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 && + req->sq->dhchap_step != NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) { + unsigned long auth_expire_secs = ctrl->kato ? ctrl->kato : 120; + + mod_delayed_work(system_wq, &req->sq->auth_expired_work, + auth_expire_secs * HZ); + return; + } + /* Final states, clear up variables */ + nvmet_auth_sq_free(req->sq); + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE2) + nvmet_ctrl_fatal_error(ctrl); +} + +static int nvmet_auth_challenge(struct nvmet_req *req, void *d, int al) +{ + struct nvmf_auth_dhchap_challenge_data *data = d; + struct nvmet_ctrl *ctrl = req->sq->ctrl; + int ret = 0; + int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id); + int data_size = sizeof(*d) + hash_len; + + if (ctrl->dh_tfm) + data_size += ctrl->dh_keysize; + if (al < data_size) { + pr_debug("%s: buffer too small (al %d need %d)\n", __func__, + al, data_size); + return -EINVAL; + } + memset(data, 0, data_size); + req->sq->dhchap_s1 = nvme_auth_get_seqnum(); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE; + data->t_id = cpu_to_le16(req->sq->dhchap_tid); + data->hashid = ctrl->shash_id; + data->hl = hash_len; + data->seqnum = cpu_to_le32(req->sq->dhchap_s1); + req->sq->dhchap_c1 = kmalloc(data->hl, GFP_KERNEL); + if (!req->sq->dhchap_c1) + return -ENOMEM; + get_random_bytes(req->sq->dhchap_c1, data->hl); + memcpy(data->cval, req->sq->dhchap_c1, data->hl); + if (ctrl->dh_tfm) { + data->dhgid = ctrl->dh_gid; + data->dhvlen = cpu_to_le16(ctrl->dh_keysize); + ret = nvmet_auth_ctrl_exponential(req, data->cval + data->hl, + ctrl->dh_keysize); + } + pr_debug("%s: ctrl %d qid %d seq %d transaction %d hl %d dhvlen %zu\n", + __func__, ctrl->cntlid, req->sq->qid, req->sq->dhchap_s1, + req->sq->dhchap_tid, data->hl, ctrl->dh_keysize); + return ret; +} + +static int nvmet_auth_success1(struct nvmet_req *req, void *d, int al) +{ + struct nvmf_auth_dhchap_success1_data *data = d; + struct nvmet_ctrl *ctrl = req->sq->ctrl; + int hash_len = nvme_auth_hmac_hash_len(ctrl->shash_id); + + WARN_ON(al < sizeof(*data)); + memset(data, 0, sizeof(*data)); + data->auth_type = NVME_AUTH_DHCHAP_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1; + data->t_id = cpu_to_le16(req->sq->dhchap_tid); + data->hl = hash_len; + if (req->sq->dhchap_c2) { + if (!ctrl->ctrl_key) { + pr_warn("ctrl %d qid %d no ctrl key\n", + ctrl->cntlid, req->sq->qid); + return NVME_AUTH_DHCHAP_FAILURE_FAILED; + } + if (nvmet_auth_ctrl_hash(req, data->rval, data->hl)) + return NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE; + data->rvalid = 1; + pr_debug("ctrl %d qid %d response %*ph\n", + ctrl->cntlid, req->sq->qid, data->hl, data->rval); + } + return 0; +} + +static void nvmet_auth_failure1(struct nvmet_req *req, void *d, int al) +{ + struct nvmf_auth_dhchap_failure_data *data = d; + + WARN_ON(al < sizeof(*data)); + data->auth_type = NVME_AUTH_COMMON_MESSAGES; + data->auth_id = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; + data->t_id = cpu_to_le16(req->sq->dhchap_tid); + data->rescode = NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED; + data->rescode_exp = req->sq->dhchap_status; +} + +void nvmet_execute_auth_receive(struct nvmet_req *req) +{ + struct nvmet_ctrl *ctrl = req->sq->ctrl; + void *d; + u32 al; + u16 status = 0; + + if (req->cmd->auth_receive.secp != NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_receive_command, secp); + goto done; + } + if (req->cmd->auth_receive.spsp0 != 0x01) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_receive_command, spsp0); + goto done; + } + if (req->cmd->auth_receive.spsp1 != 0x01) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_receive_command, spsp1); + goto done; + } + al = le32_to_cpu(req->cmd->auth_receive.al); + if (!al) { + status = NVME_SC_INVALID_FIELD | NVME_SC_DNR; + req->error_loc = + offsetof(struct nvmf_auth_receive_command, al); + goto done; + } + if (!nvmet_check_transfer_len(req, al)) { + pr_debug("%s: transfer length mismatch (%u)\n", __func__, al); + return; + } + + d = kmalloc(al, GFP_KERNEL); + if (!d) { + status = NVME_SC_INTERNAL; + goto done; + } + pr_debug("%s: ctrl %d qid %d step %x\n", __func__, + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); + switch (req->sq->dhchap_step) { + case NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE: + if (nvmet_auth_challenge(req, d, al) < 0) { + pr_warn("ctrl %d qid %d: challenge error (%d)\n", + ctrl->cntlid, req->sq->qid, status); + status = NVME_SC_INTERNAL; + break; + } + if (status) { + req->sq->dhchap_status = status; + nvmet_auth_failure1(req, d, al); + pr_warn("ctrl %d qid %d: challenge status (%x)\n", + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status); + status = 0; + break; + } + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_REPLY; + break; + case NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1: + status = nvmet_auth_success1(req, d, al); + if (status) { + req->sq->dhchap_status = status; + req->sq->authenticated = false; + nvmet_auth_failure1(req, d, al); + pr_warn("ctrl %d qid %d: success1 status (%x)\n", + ctrl->cntlid, req->sq->qid, + req->sq->dhchap_status); + break; + } + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2; + break; + case NVME_AUTH_DHCHAP_MESSAGE_FAILURE1: + req->sq->authenticated = false; + nvmet_auth_failure1(req, d, al); + pr_warn("ctrl %d qid %d failure1 (%x)\n", + ctrl->cntlid, req->sq->qid, req->sq->dhchap_status); + break; + default: + pr_warn("ctrl %d qid %d unhandled step (%d)\n", + ctrl->cntlid, req->sq->qid, req->sq->dhchap_step); + req->sq->dhchap_step = NVME_AUTH_DHCHAP_MESSAGE_FAILURE1; + req->sq->dhchap_status = NVME_AUTH_DHCHAP_FAILURE_FAILED; + nvmet_auth_failure1(req, d, al); + status = 0; + break; + } + + status = nvmet_copy_to_sgl(req, 0, d, al); + kfree(d); +done: + req->cqe->result.u64 = 0; + nvmet_req_complete(req, status); + if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2) + nvmet_auth_sq_free(req->sq); + else if (req->sq->dhchap_step == NVME_AUTH_DHCHAP_MESSAGE_FAILURE1) { + nvmet_auth_sq_free(req->sq); + nvmet_ctrl_fatal_error(ctrl); + } +} diff --git a/drivers/nvme/target/fabrics-cmd.c b/drivers/nvme/target/fabrics-cmd.c index 70fb587e9413..f91a56180d3d 100644 --- a/drivers/nvme/target/fabrics-cmd.c +++ b/drivers/nvme/target/fabrics-cmd.c @@ -82,7 +82,7 @@ static void nvmet_execute_prop_get(struct nvmet_req *req) nvmet_req_complete(req, status); } -u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req) +u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req) { struct nvme_command *cmd = req->cmd; @@ -93,6 +93,37 @@ u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req) case nvme_fabrics_type_property_get: req->execute = nvmet_execute_prop_get; break; +#ifdef CONFIG_NVME_TARGET_AUTH + case nvme_fabrics_type_auth_send: + req->execute = nvmet_execute_auth_send; + break; + case nvme_fabrics_type_auth_receive: + req->execute = nvmet_execute_auth_receive; + break; +#endif + default: + pr_debug("received unknown capsule type 0x%x\n", + cmd->fabrics.fctype); + req->error_loc = offsetof(struct nvmf_common_command, fctype); + return NVME_SC_INVALID_OPCODE | NVME_SC_DNR; + } + + return 0; +} + +u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req) +{ + struct nvme_command *cmd = req->cmd; + + switch (cmd->fabrics.fctype) { +#ifdef CONFIG_NVME_TARGET_AUTH + case nvme_fabrics_type_auth_send: + req->execute = nvmet_execute_auth_send; + break; + case nvme_fabrics_type_auth_receive: + req->execute = nvmet_execute_auth_receive; + break; +#endif default: pr_debug("received unknown capsule type 0x%x\n", cmd->fabrics.fctype); @@ -173,6 +204,7 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) struct nvmf_connect_data *d; struct nvmet_ctrl *ctrl = NULL; u16 status = 0; + int ret; if (!nvmet_check_transfer_len(req, sizeof(struct nvmf_connect_data))) return; @@ -215,18 +247,32 @@ static void nvmet_execute_admin_connect(struct nvmet_req *req) uuid_copy(&ctrl->hostid, &d->hostid); + ret = nvmet_setup_auth(ctrl); + if (ret < 0) { + pr_err("Failed to setup authentication, error %d\n", ret); + nvmet_ctrl_put(ctrl); + if (ret == -EPERM) + status = (NVME_SC_CONNECT_INVALID_HOST | NVME_SC_DNR); + else + status = NVME_SC_INTERNAL; + goto out; + } + status = nvmet_install_queue(ctrl, req); if (status) { nvmet_ctrl_put(ctrl); goto out; } - pr_info("creating %s controller %d for subsystem %s for NQN %s%s.\n", + pr_info("creating %s controller %d for subsystem %s for NQN %s%s%s.\n", nvmet_is_disc_subsys(ctrl->subsys) ? "discovery" : "nvm", ctrl->cntlid, ctrl->subsys->subsysnqn, ctrl->hostnqn, - ctrl->pi_support ? " T10-PI is enabled" : ""); + ctrl->pi_support ? " T10-PI is enabled" : "", + nvmet_has_auth(ctrl) ? " with DH-HMAC-CHAP" : ""); req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid); + if (nvmet_has_auth(ctrl)) + nvmet_init_auth(ctrl, req); out: kfree(d); complete: @@ -286,6 +332,9 @@ static void nvmet_execute_io_connect(struct nvmet_req *req) req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid); pr_debug("adding queue %d to ctrl %d.\n", qid, ctrl->cntlid); + req->cqe->result.u16 = cpu_to_le16(ctrl->cntlid); + if (nvmet_has_auth(ctrl)) + nvmet_init_auth(ctrl, req); out: kfree(d); diff --git a/drivers/nvme/target/loop.c b/drivers/nvme/target/loop.c index 0f5c77e22a0a..9750a7fca268 100644 --- a/drivers/nvme/target/loop.c +++ b/drivers/nvme/target/loop.c @@ -424,9 +424,7 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl) { if (ctrl->ctrl.queue_count > 1) { nvme_stop_queues(&ctrl->ctrl); - blk_mq_tagset_busy_iter(&ctrl->tag_set, - nvme_cancel_request, &ctrl->ctrl); - blk_mq_tagset_wait_completed_request(&ctrl->tag_set); + nvme_cancel_tagset(&ctrl->ctrl); nvme_loop_destroy_io_queues(ctrl); } @@ -434,9 +432,7 @@ static void nvme_loop_shutdown_ctrl(struct nvme_loop_ctrl *ctrl) if (ctrl->ctrl.state == NVME_CTRL_LIVE) nvme_shutdown_ctrl(&ctrl->ctrl); - blk_mq_tagset_busy_iter(&ctrl->admin_tag_set, - nvme_cancel_request, &ctrl->ctrl); - blk_mq_tagset_wait_completed_request(&ctrl->admin_tag_set); + nvme_cancel_admin_tagset(&ctrl->ctrl); nvme_loop_destroy_admin_queue(ctrl); } diff --git a/drivers/nvme/target/nvmet.h b/drivers/nvme/target/nvmet.h index 2b3e5719f24e..6ffeeb0a1c49 100644 --- a/drivers/nvme/target/nvmet.h +++ b/drivers/nvme/target/nvmet.h @@ -108,6 +108,19 @@ struct nvmet_sq { u16 size; u32 sqhd; bool sqhd_disabled; +#ifdef CONFIG_NVME_TARGET_AUTH + struct delayed_work auth_expired_work; + bool authenticated; + u16 dhchap_tid; + u16 dhchap_status; + int dhchap_step; + u8 *dhchap_c1; + u8 *dhchap_c2; + u32 dhchap_s1; + u32 dhchap_s2; + u8 *dhchap_skey; + int dhchap_skey_len; +#endif struct completion free_done; struct completion confirm_done; }; @@ -209,6 +222,15 @@ struct nvmet_ctrl { u64 err_counter; struct nvme_error_slot slots[NVMET_ERROR_LOG_SLOTS]; bool pi_support; +#ifdef CONFIG_NVME_TARGET_AUTH + struct nvme_dhchap_key *host_key; + struct nvme_dhchap_key *ctrl_key; + u8 shash_id; + struct crypto_kpp *dh_tfm; + u8 dh_gid; + u8 *dh_key; + size_t dh_keysize; +#endif }; struct nvmet_subsys { @@ -271,6 +293,12 @@ static inline struct nvmet_subsys *namespaces_to_subsys( struct nvmet_host { struct config_group group; + u8 *dhchap_secret; + u8 *dhchap_ctrl_secret; + u8 dhchap_key_hash; + u8 dhchap_ctrl_key_hash; + u8 dhchap_hash_id; + u8 dhchap_dhgroup_id; }; static inline struct nvmet_host *to_host(struct config_item *item) @@ -420,7 +448,8 @@ u16 nvmet_file_parse_io_cmd(struct nvmet_req *req); u16 nvmet_bdev_zns_parse_io_cmd(struct nvmet_req *req); u16 nvmet_parse_admin_cmd(struct nvmet_req *req); u16 nvmet_parse_discovery_cmd(struct nvmet_req *req); -u16 nvmet_parse_fabrics_cmd(struct nvmet_req *req); +u16 nvmet_parse_fabrics_admin_cmd(struct nvmet_req *req); +u16 nvmet_parse_fabrics_io_cmd(struct nvmet_req *req); bool nvmet_req_init(struct nvmet_req *req, struct nvmet_cq *cq, struct nvmet_sq *sq, const struct nvmet_fabrics_ops *ops); @@ -668,4 +697,48 @@ static inline void nvmet_req_bio_put(struct nvmet_req *req, struct bio *bio) bio_put(bio); } +#ifdef CONFIG_NVME_TARGET_AUTH +void nvmet_execute_auth_send(struct nvmet_req *req); +void nvmet_execute_auth_receive(struct nvmet_req *req); +int nvmet_auth_set_key(struct nvmet_host *host, const char *secret, + bool set_ctrl); +int nvmet_auth_set_host_hash(struct nvmet_host *host, const char *hash); +int nvmet_setup_auth(struct nvmet_ctrl *ctrl); +void nvmet_init_auth(struct nvmet_ctrl *ctrl, struct nvmet_req *req); +void nvmet_destroy_auth(struct nvmet_ctrl *ctrl); +void nvmet_auth_sq_free(struct nvmet_sq *sq); +int nvmet_setup_dhgroup(struct nvmet_ctrl *ctrl, u8 dhgroup_id); +bool nvmet_check_auth_status(struct nvmet_req *req); +int nvmet_auth_host_hash(struct nvmet_req *req, u8 *response, + unsigned int hash_len); +int nvmet_auth_ctrl_hash(struct nvmet_req *req, u8 *response, + unsigned int hash_len); +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl) +{ + return ctrl->host_key != NULL; +} +int nvmet_auth_ctrl_exponential(struct nvmet_req *req, + u8 *buf, int buf_size); +int nvmet_auth_ctrl_sesskey(struct nvmet_req *req, + u8 *buf, int buf_size); +#else +static inline int nvmet_setup_auth(struct nvmet_ctrl *ctrl) +{ + return 0; +} +static inline void nvmet_init_auth(struct nvmet_ctrl *ctrl, + struct nvmet_req *req) {}; +static inline void nvmet_destroy_auth(struct nvmet_ctrl *ctrl) {}; +static inline void nvmet_auth_sq_free(struct nvmet_sq *sq) {}; +static inline bool nvmet_check_auth_status(struct nvmet_req *req) +{ + return true; +} +static inline bool nvmet_has_auth(struct nvmet_ctrl *ctrl) +{ + return false; +} +static inline const char *nvmet_dhchap_dhgroup_name(u8 dhgid) { return NULL; } +#endif + #endif /* _NVMET_H */ diff --git a/drivers/nvme/target/tcp.c b/drivers/nvme/target/tcp.c index 0a9542599ad1..dc3b4dc8fe08 100644 --- a/drivers/nvme/target/tcp.c +++ b/drivers/nvme/target/tcp.c @@ -1839,7 +1839,8 @@ static int __init nvmet_tcp_init(void) { int ret; - nvmet_tcp_wq = alloc_workqueue("nvmet_tcp_wq", WQ_HIGHPRI, 0); + nvmet_tcp_wq = alloc_workqueue("nvmet_tcp_wq", + WQ_MEM_RECLAIM | WQ_HIGHPRI, 0); if (!nvmet_tcp_wq) return -ENOMEM; diff --git a/drivers/nvmem/bcm-ocotp.c b/drivers/nvmem/bcm-ocotp.c index dfea96c52463..a128c7f5e351 100644 --- a/drivers/nvmem/bcm-ocotp.c +++ b/drivers/nvmem/bcm-ocotp.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include #include diff --git a/drivers/of/base.c b/drivers/of/base.c index d4f98c8469ed..7fa960bd3df1 100644 --- a/drivers/of/base.c +++ b/drivers/of/base.c @@ -1919,6 +1919,8 @@ void of_alias_scan(void * (*dt_alloc)(u64 size, u64 align)) of_property_read_string(of_aliases, "stdout", &name); if (name) of_stdout = of_find_node_opts_by_path(name, &of_stdout_options); + if (of_stdout) + of_stdout->fwnode.flags |= FWNODE_FLAG_BEST_EFFORT; } if (!of_aliases) @@ -2077,7 +2079,7 @@ struct device_node *of_find_next_cache_node(const struct device_node *np) * * @cpu: cpu number(logical index) for which the last cache level is needed * - * Return: The the level at which the last cache is present. It is exactly + * Return: The level at which the last cache is present. It is exactly * same as the total number of cache levels for the given logical cpu. */ int of_find_last_cache_level(unsigned int cpu) diff --git a/drivers/of/device.c b/drivers/of/device.c index 874f031442dc..75b6cbffa755 100644 --- a/drivers/of/device.c +++ b/drivers/of/device.c @@ -81,8 +81,11 @@ of_dma_set_restricted_buffer(struct device *dev, struct device_node *np) * restricted-dma-pool region is allowed. */ if (of_device_is_compatible(node, "restricted-dma-pool") && - of_device_is_available(node)) + of_device_is_available(node)) { + of_node_put(node); break; + } + of_node_put(node); } /* diff --git a/drivers/of/fdt.c b/drivers/of/fdt.c index a8f5b6532165..e02a30c92719 100644 --- a/drivers/of/fdt.c +++ b/drivers/of/fdt.c @@ -246,7 +246,7 @@ static int populate_node(const void *blob, } *pnp = np; - return true; + return 0; } static void reverse_nodes(struct device_node *parent) @@ -477,8 +477,8 @@ void *initial_boot_params __ro_after_init; static u32 of_fdt_crc32; -static int __init early_init_dt_reserve_memory_arch(phys_addr_t base, - phys_addr_t size, bool nomap) +static int __init early_init_dt_reserve_memory(phys_addr_t base, + phys_addr_t size, bool nomap) { if (nomap) { /* @@ -525,15 +525,15 @@ static int __init __reserved_mem_reserve_reg(unsigned long node, size = dt_mem_next_cell(dt_root_size_cells, &prop); if (size && - early_init_dt_reserve_memory_arch(base, size, nomap) == 0) { + early_init_dt_reserve_memory(base, size, nomap) == 0) { pr_debug("Reserved memory: reserved region for node '%s': base %pa, size %lu MiB\n", uname, &base, (unsigned long)(size / SZ_1M)); if (!nomap) kmemleak_alloc_phys(base, size, 0, 0); } else - pr_info("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", - uname, &base, (unsigned long)(size / SZ_1M)); + pr_err("Reserved memory: failed to reserve memory for node '%s': base %pa, size %lu MiB\n", + uname, &base, (unsigned long)(size / SZ_1M)); len -= t_len; if (first) { @@ -644,7 +644,7 @@ void __init early_init_fdt_scan_reserved_mem(void) fdt_get_mem_rsv(initial_boot_params, n, &base, &size); if (!size) break; - early_init_dt_reserve_memory_arch(base, size, false); + memblock_reserve(base, size); } fdt_scan_reserved_mem(); @@ -661,9 +661,8 @@ void __init early_init_fdt_reserve_self(void) return; /* Reserve the dtb region */ - early_init_dt_reserve_memory_arch(__pa(initial_boot_params), - fdt_totalsize(initial_boot_params), - false); + memblock_reserve(__pa(initial_boot_params), + fdt_totalsize(initial_boot_params)); } /** diff --git a/drivers/of/kexec.c b/drivers/of/kexec.c index f2e58ddfaed2..e6c01db393f9 100644 --- a/drivers/of/kexec.c +++ b/drivers/of/kexec.c @@ -128,6 +128,7 @@ int __init ima_get_kexec_buffer(void **addr, size_t *size) { int ret, len; unsigned long tmp_addr; + unsigned long start_pfn, end_pfn; size_t tmp_size; const void *prop; @@ -139,6 +140,22 @@ int __init ima_get_kexec_buffer(void **addr, size_t *size) if (ret) return ret; + /* Do some sanity on the returned size for the ima-kexec buffer */ + if (!tmp_size) + return -ENOENT; + + /* + * Calculate the PFNs for the buffer and ensure + * they are with in addressable memory. + */ + start_pfn = PHYS_PFN(tmp_addr); + end_pfn = PHYS_PFN(tmp_addr + tmp_size - 1); + if (!page_is_ram(start_pfn) || !page_is_ram(end_pfn)) { + pr_warn("IMA buffer at 0x%lx, size = 0x%zx beyond memory\n", + tmp_addr, tmp_size); + return -EINVAL; + } + *addr = __va(tmp_addr); *size = tmp_size; diff --git a/drivers/of/of_reserved_mem.c b/drivers/of/of_reserved_mem.c index 75caa6f5d36f..65f3b02a0e4e 100644 --- a/drivers/of/of_reserved_mem.c +++ b/drivers/of/of_reserved_mem.c @@ -156,7 +156,8 @@ static int __init __reserved_mem_alloc_size(unsigned long node, } if (base == 0) { - pr_info("failed to allocate memory for node '%s'\n", uname); + pr_err("failed to allocate memory for node '%s': size %lu MiB\n", + uname, (unsigned long)(size / SZ_1M)); return -ENOMEM; } diff --git a/drivers/of/overlay.c b/drivers/of/overlay.c index 4044ddcb02c6..bd8ff4df723d 100644 --- a/drivers/of/overlay.c +++ b/drivers/of/overlay.c @@ -903,12 +903,6 @@ static int of_overlay_apply(struct overlay_changeset *ovcs) { int ret = 0, ret_revert, ret_tmp; - if (devicetree_corrupt()) { - pr_err("devicetree state suspect, refuse to apply overlay\n"); - ret = -EBUSY; - goto out; - } - ret = of_resolve_phandles(ovcs->overlay_root); if (ret) goto out; @@ -983,6 +977,11 @@ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, *ret_ovcs_id = 0; + if (devicetree_corrupt()) { + pr_err("devicetree state suspect, refuse to apply overlay\n"); + return -EBUSY; + } + if (overlay_fdt_size < sizeof(struct fdt_header) || fdt_check_header(overlay_fdt)) { pr_err("Invalid overlay_fdt header\n"); @@ -1044,20 +1043,15 @@ int of_overlay_fdt_apply(const void *overlay_fdt, u32 overlay_fdt_size, * goto err_free_ovcs. Instead, the caller of of_overlay_fdt_apply() * can call of_overlay_remove(); */ - - mutex_unlock(&of_mutex); - of_overlay_mutex_unlock(); - *ret_ovcs_id = ovcs->id; - - return ret; + goto out_unlock; err_free_ovcs: free_overlay_changeset(ovcs); +out_unlock: mutex_unlock(&of_mutex); of_overlay_mutex_unlock(); - return ret; } EXPORT_SYMBOL_GPL(of_overlay_fdt_apply); diff --git a/drivers/of/unittest.c b/drivers/of/unittest.c index 7f6bba18c515..eafa8ffefbd0 100644 --- a/drivers/of/unittest.c +++ b/drivers/of/unittest.c @@ -1602,7 +1602,7 @@ static int unittest_gpio_probe(struct platform_device *pdev) platform_set_drvdata(pdev, devptr); - devptr->chip.of_node = pdev->dev.of_node; + devptr->chip.fwnode = dev_fwnode(&pdev->dev); devptr->chip.label = "of-unittest-gpio"; devptr->chip.base = -1; /* dynamic allocation */ devptr->chip.ngpio = 5; @@ -1611,7 +1611,7 @@ static int unittest_gpio_probe(struct platform_device *pdev) ret = gpiochip_add_data(&devptr->chip, NULL); unittest(!ret, - "gpiochip_add_data() for node @%pOF failed, ret = %d\n", devptr->chip.of_node, ret); + "gpiochip_add_data() for node @%pfw failed, ret = %d\n", devptr->chip.fwnode, ret); if (!ret) unittest_gpio_probe_pass_count++; @@ -1620,20 +1620,19 @@ static int unittest_gpio_probe(struct platform_device *pdev) static int unittest_gpio_remove(struct platform_device *pdev) { - struct unittest_gpio_dev *gdev = platform_get_drvdata(pdev); + struct unittest_gpio_dev *devptr = platform_get_drvdata(pdev); struct device *dev = &pdev->dev; - struct device_node *np = pdev->dev.of_node; - dev_dbg(dev, "%s for node @%pOF\n", __func__, np); + dev_dbg(dev, "%s for node @%pfw\n", __func__, devptr->chip.fwnode); - if (!gdev) + if (!devptr) return -EINVAL; - if (gdev->chip.base != -1) - gpiochip_remove(&gdev->chip); + if (devptr->chip.base != -1) + gpiochip_remove(&devptr->chip); platform_set_drvdata(pdev, NULL); - kfree(gdev); + kfree(devptr); return 0; } diff --git a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c index 548e46776100..cc29b08e49eb 100644 --- a/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c +++ b/drivers/phy/broadcom/phy-bcm-cygnus-pcie.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2015 Broadcom Corporation #include #include diff --git a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c index 9e7434a0d3e0..2eaa41f8fc70 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-pcie.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-pcie.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include #include diff --git a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c index 65a399acc845..36ad02c33ac5 100644 --- a/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c +++ b/drivers/phy/broadcom/phy-bcm-ns2-usbdrd.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2017 Broadcom #include #include diff --git a/drivers/phy/motorola/phy-cpcap-usb.c b/drivers/phy/motorola/phy-cpcap-usb.c index 6ee478bc5211..2f8210167b77 100644 --- a/drivers/phy/motorola/phy-cpcap-usb.c +++ b/drivers/phy/motorola/phy-cpcap-usb.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Motorola CPCAP PMIC USB PHY driver * Copyright (C) 2017 Tony Lindgren @@ -5,15 +6,6 @@ * Some parts based on earlier Motorola Linux kernel tree code in * board-mapphone-usb.c and cpcap-usb-det.c: * Copyright (C) 2007 - 2011 Motorola, Inc. - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index 9fe6ea6fdae5..fb619908f912 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -1,13 +1,4 @@ -/* - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only #include #include diff --git a/drivers/pinctrl/bcm/pinctrl-bcm281xx.c b/drivers/pinctrl/bcm/pinctrl-bcm281xx.c index 9ab1f427286a..fd52a83387ef 100644 --- a/drivers/pinctrl/bcm/pinctrl-bcm281xx.c +++ b/drivers/pinctrl/bcm/pinctrl-bcm281xx.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013-2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013-2017 Broadcom #include #include diff --git a/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c b/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c index 4344c5732400..5251460f6327 100644 --- a/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c +++ b/drivers/pinctrl/bcm/pinctrl-cygnus-mux.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014-2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014-2017 Broadcom /* * Broadcom Cygnus IOMUX driver diff --git a/drivers/pinctrl/bcm/pinctrl-ns2-mux.c b/drivers/pinctrl/bcm/pinctrl-ns2-mux.c index 0fe4a1fcdf00..960e253f0be4 100644 --- a/drivers/pinctrl/bcm/pinctrl-ns2-mux.c +++ b/drivers/pinctrl/bcm/pinctrl-ns2-mux.c @@ -1,13 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2016 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * * This file contains the Northstar2 IOMUX driver that supports group * based PINMUX configuration. The PWM is functional only when the diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c index 643dbd315033..3c792bf03bda 100644 --- a/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c +++ b/drivers/pinctrl/bcm/pinctrl-nsp-gpio.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014-2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014-2017 Broadcom /* * This file contains the Broadcom Northstar Plus (NSP) GPIO driver that diff --git a/drivers/pinctrl/bcm/pinctrl-nsp-mux.c b/drivers/pinctrl/bcm/pinctrl-nsp-mux.c index f1d60a708815..db8f79920ff0 100644 --- a/drivers/pinctrl/bcm/pinctrl-nsp-mux.c +++ b/drivers/pinctrl/bcm/pinctrl-nsp-mux.c @@ -1,13 +1,5 @@ +// SPDX-License-Identifier: GPL-2.0-only /* Copyright (C) 2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. * * This file contains the Northstar plus (NSP) IOMUX driver that supports * group based PINMUX configuration. The Northstar plus IOMUX controller diff --git a/drivers/pinctrl/devicetree.c b/drivers/pinctrl/devicetree.c index 3fb238714718..ef898ee8ca6b 100644 --- a/drivers/pinctrl/devicetree.c +++ b/drivers/pinctrl/devicetree.c @@ -129,7 +129,7 @@ static int dt_to_map_one_config(struct pinctrl *p, np_pctldev = of_get_next_parent(np_pctldev); if (!np_pctldev || of_node_is_root(np_pctldev)) { of_node_put(np_pctldev); - ret = driver_deferred_probe_check_state(p->dev); + ret = -ENODEV; /* keep deferring if modules are enabled */ if (IS_ENABLED(CONFIG_MODULES) && !allow_default && ret < 0) ret = -EPROBE_DEFER; diff --git a/drivers/pinctrl/intel/pinctrl-intel.c b/drivers/pinctrl/intel/pinctrl-intel.c index ffc045f7bf00..fd093e36c3a8 100644 --- a/drivers/pinctrl/intel/pinctrl-intel.c +++ b/drivers/pinctrl/intel/pinctrl-intel.c @@ -1641,16 +1641,14 @@ EXPORT_SYMBOL_GPL(intel_pinctrl_probe_by_uid); const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_device *pdev) { + const struct intel_pinctrl_soc_data * const *table; const struct intel_pinctrl_soc_data *data = NULL; - const struct intel_pinctrl_soc_data **table; - struct acpi_device *adev; - unsigned int i; - adev = ACPI_COMPANION(&pdev->dev); - if (adev) { - const void *match = device_get_match_data(&pdev->dev); + table = device_get_match_data(&pdev->dev); + if (table) { + struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); + unsigned int i; - table = (const struct intel_pinctrl_soc_data **)match; for (i = 0; table[i]; i++) { if (!strcmp(adev->pnp.unique_id, table[i]->uid)) { data = table[i]; @@ -1664,7 +1662,7 @@ const struct intel_pinctrl_soc_data *intel_pinctrl_get_soc_data(struct platform_ if (!id) return ERR_PTR(-ENODEV); - table = (const struct intel_pinctrl_soc_data **)id->driver_data; + table = (const struct intel_pinctrl_soc_data * const *)id->driver_data; data = table[pdev->id]; } diff --git a/drivers/pinctrl/pinctrl-as3722.c b/drivers/pinctrl/pinctrl-as3722.c index 4313756b52e6..f0e5d87ac50b 100644 --- a/drivers/pinctrl/pinctrl-as3722.c +++ b/drivers/pinctrl/pinctrl-as3722.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * ams AS3722 pin control and GPIO driver. * * Copyright (c) 2013, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #include diff --git a/drivers/pinctrl/pinctrl-palmas.c b/drivers/pinctrl/pinctrl-palmas.c index d42f18cb1bc7..fecc25d35d02 100644 --- a/drivers/pinctrl/pinctrl-palmas.c +++ b/drivers/pinctrl/pinctrl-palmas.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * pinctrl-palmas.c -- TI PALMAS series pin control driver. * * Copyright (c) 2013, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #include diff --git a/drivers/pinctrl/pinctrl-utils.c b/drivers/pinctrl/pinctrl-utils.c index 93df0d4c0a24..3580e0fd94ed 100644 --- a/drivers/pinctrl/pinctrl-utils.c +++ b/drivers/pinctrl/pinctrl-utils.c @@ -1,23 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Utils functions to implement the pincontrol driver. * * Copyright (c) 2013, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #include #include diff --git a/drivers/pinctrl/pinctrl-utils.h b/drivers/pinctrl/pinctrl-utils.h index 8f9f2d28c5b8..cec407a8cc4e 100644 --- a/drivers/pinctrl/pinctrl-utils.h +++ b/drivers/pinctrl/pinctrl-utils.h @@ -1,23 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Utils functions to implement the pincontrol driver. * * Copyright (c) 2013, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #ifndef __PINCTRL_UTILS_H__ #define __PINCTRL_UTILS_H__ diff --git a/drivers/platform/Kconfig b/drivers/platform/Kconfig index 18fc6a08569e..b437847b6237 100644 --- a/drivers/platform/Kconfig +++ b/drivers/platform/Kconfig @@ -1,7 +1,4 @@ # SPDX-License-Identifier: GPL-2.0-only -if X86 -source "drivers/platform/x86/Kconfig" -endif if MIPS source "drivers/platform/mips/Kconfig" endif @@ -15,3 +12,5 @@ source "drivers/platform/mellanox/Kconfig" source "drivers/platform/olpc/Kconfig" source "drivers/platform/surface/Kconfig" + +source "drivers/platform/x86/Kconfig" diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index 717299cbccac..c45fb376d653 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -139,7 +139,7 @@ config CROS_EC_PROTO config CROS_KBD_LED_BACKLIGHT tristate "Backlight LED support for Chrome OS keyboards" - depends on LEDS_CLASS && ACPI + depends on LEDS_CLASS && (ACPI || CROS_EC) help This option enables support for the keyboard backlight LEDs on select Chrome OS systems. @@ -267,4 +267,13 @@ config CHROMEOS_PRIVACY_SCREEN source "drivers/platform/chrome/wilco_ec/Kconfig" +# Kunit test cases +config CROS_KUNIT + tristate "Kunit tests for ChromeOS" if !KUNIT_ALL_TESTS + depends on KUNIT && CROS_EC + default KUNIT_ALL_TESTS + select CROS_EC_PROTO + help + ChromeOS Kunit tests. + endif # CHROMEOS_PLATFORMS diff --git a/drivers/platform/chrome/Makefile b/drivers/platform/chrome/Makefile index 52f5a2dde8b8..f7e74a845afc 100644 --- a/drivers/platform/chrome/Makefile +++ b/drivers/platform/chrome/Makefile @@ -30,3 +30,8 @@ obj-$(CONFIG_CROS_USBPD_LOGGER) += cros_usbpd_logger.o obj-$(CONFIG_CROS_USBPD_NOTIFY) += cros_usbpd_notify.o obj-$(CONFIG_WILCO_EC) += wilco_ec/ + +# Kunit test cases +obj-$(CONFIG_CROS_KUNIT) += cros_kunit.o +cros_kunit-objs := cros_kunit_util.o +cros_kunit-objs += cros_ec_proto_test.o diff --git a/drivers/platform/chrome/cros_ec.c b/drivers/platform/chrome/cros_ec.c index b3e94cdf7d1a..8aace50d446d 100644 --- a/drivers/platform/chrome/cros_ec.c +++ b/drivers/platform/chrome/cros_ec.c @@ -19,9 +19,6 @@ #include "cros_ec.h" -#define CROS_EC_DEV_EC_INDEX 0 -#define CROS_EC_DEV_PD_INDEX 1 - static struct cros_ec_platform ec_p = { .ec_name = CROS_EC_DEV_NAME, .cmd_offset = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_EC_INDEX), @@ -135,16 +132,16 @@ static int cros_ec_sleep_event(struct cros_ec_device *ec_dev, u8 sleep_event) buf.msg.command = EC_CMD_HOST_SLEEP_EVENT; ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg); - - /* For now, report failure to transition to S0ix with a warning. */ + /* Report failure to transition to system wide suspend with a warning. */ if (ret >= 0 && ec_dev->host_sleep_v1 && - (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME)) { + (sleep_event == HOST_SLEEP_EVENT_S0IX_RESUME || + sleep_event == HOST_SLEEP_EVENT_S3_RESUME)) { ec_dev->last_resume_result = buf.u.resp1.resume_response.sleep_transitions; WARN_ONCE(buf.u.resp1.resume_response.sleep_transitions & EC_HOST_RESUME_SLEEP_TIMEOUT, - "EC detected sleep transition timeout. Total slp_s0 transitions: %d", + "EC detected sleep transition timeout. Total sleep transitions: %d", buf.u.resp1.resume_response.sleep_transitions & EC_HOST_RESUME_SLEEP_TRANSITIONS_MASK); } diff --git a/drivers/platform/chrome/cros_ec_proto.c b/drivers/platform/chrome/cros_ec_proto.c index ff767dccdf0f..05d2e8765a66 100644 --- a/drivers/platform/chrome/cros_ec_proto.c +++ b/drivers/platform/chrome/cros_ec_proto.c @@ -52,8 +52,8 @@ static int cros_ec_map_error(uint32_t result) return ret; } -static int prepare_packet(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) +static int prepare_tx(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) { struct ec_host_request *request; u8 *out; @@ -85,98 +85,13 @@ static int prepare_packet(struct cros_ec_device *ec_dev, return sizeof(*request) + msg->outsize; } -static int send_command(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) -{ - int ret; - int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg); - - if (ec_dev->proto_version > 2) - xfer_fxn = ec_dev->pkt_xfer; - else - xfer_fxn = ec_dev->cmd_xfer; - - if (!xfer_fxn) { - /* - * This error can happen if a communication error happened and - * the EC is trying to use protocol v2, on an underlying - * communication mechanism that does not support v2. - */ - dev_err_once(ec_dev->dev, - "missing EC transfer API, cannot send command\n"); - return -EIO; - } - - trace_cros_ec_request_start(msg); - ret = (*xfer_fxn)(ec_dev, msg); - trace_cros_ec_request_done(msg, ret); - if (msg->result == EC_RES_IN_PROGRESS) { - int i; - struct cros_ec_command *status_msg; - struct ec_response_get_comms_status *status; - - status_msg = kmalloc(sizeof(*status_msg) + sizeof(*status), - GFP_KERNEL); - if (!status_msg) - return -ENOMEM; - - status_msg->version = 0; - status_msg->command = EC_CMD_GET_COMMS_STATUS; - status_msg->insize = sizeof(*status); - status_msg->outsize = 0; - - /* - * Query the EC's status until it's no longer busy or - * we encounter an error. - */ - for (i = 0; i < EC_COMMAND_RETRIES; i++) { - usleep_range(10000, 11000); - - trace_cros_ec_request_start(status_msg); - ret = (*xfer_fxn)(ec_dev, status_msg); - trace_cros_ec_request_done(status_msg, ret); - if (ret == -EAGAIN) - continue; - if (ret < 0) - break; - - msg->result = status_msg->result; - if (status_msg->result != EC_RES_SUCCESS) - break; - - status = (struct ec_response_get_comms_status *) - status_msg->data; - if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) - break; - } - - kfree(status_msg); - } - - return ret; -} - -/** - * cros_ec_prepare_tx() - Prepare an outgoing message in the output buffer. - * @ec_dev: Device to register. - * @msg: Message to write. - * - * This is intended to be used by all ChromeOS EC drivers, but at present - * only SPI uses it. Once LPC uses the same protocol it can start using it. - * I2C could use it now, with a refactor of the existing code. - * - * Return: number of prepared bytes on success or negative error code. - */ -int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg) +static int prepare_tx_legacy(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) { u8 *out; u8 csum; int i; - if (ec_dev->proto_version > 2) - return prepare_packet(ec_dev, msg); - if (msg->outsize > EC_PROTO2_MAX_PARAM_SIZE) return -EINVAL; @@ -191,6 +106,106 @@ int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, return EC_MSG_TX_PROTO_BYTES + msg->outsize; } + +static int cros_ec_xfer_command(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) +{ + int ret; + int (*xfer_fxn)(struct cros_ec_device *ec, struct cros_ec_command *msg); + + if (ec_dev->proto_version > 2) + xfer_fxn = ec_dev->pkt_xfer; + else + xfer_fxn = ec_dev->cmd_xfer; + + if (!xfer_fxn) { + /* + * This error can happen if a communication error happened and + * the EC is trying to use protocol v2, on an underlying + * communication mechanism that does not support v2. + */ + dev_err_once(ec_dev->dev, "missing EC transfer API, cannot send command\n"); + return -EIO; + } + + trace_cros_ec_request_start(msg); + ret = (*xfer_fxn)(ec_dev, msg); + trace_cros_ec_request_done(msg, ret); + + return ret; +} + +static int cros_ec_wait_until_complete(struct cros_ec_device *ec_dev, uint32_t *result) +{ + struct { + struct cros_ec_command msg; + struct ec_response_get_comms_status status; + } __packed buf; + struct cros_ec_command *msg = &buf.msg; + struct ec_response_get_comms_status *status = &buf.status; + int ret = 0, i; + + msg->version = 0; + msg->command = EC_CMD_GET_COMMS_STATUS; + msg->insize = sizeof(*status); + msg->outsize = 0; + + /* Query the EC's status until it's no longer busy or we encounter an error. */ + for (i = 0; i < EC_COMMAND_RETRIES; ++i) { + usleep_range(10000, 11000); + + ret = cros_ec_xfer_command(ec_dev, msg); + if (ret == -EAGAIN) + continue; + if (ret < 0) + return ret; + + *result = msg->result; + if (msg->result != EC_RES_SUCCESS) + return ret; + + if (ret == 0) { + ret = -EPROTO; + break; + } + + if (!(status->flags & EC_COMMS_STATUS_PROCESSING)) + return ret; + } + + if (i >= EC_COMMAND_RETRIES) + ret = -EAGAIN; + + return ret; +} + +static int cros_ec_send_command(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) +{ + int ret = cros_ec_xfer_command(ec_dev, msg); + + if (msg->result == EC_RES_IN_PROGRESS) + ret = cros_ec_wait_until_complete(ec_dev, &msg->result); + + return ret; +} + +/** + * cros_ec_prepare_tx() - Prepare an outgoing message in the output buffer. + * @ec_dev: Device to register. + * @msg: Message to write. + * + * This is used by all ChromeOS EC drivers to prepare the outgoing message + * according to different protocol versions. + * + * Return: number of prepared bytes on success or negative error code. + */ +int cros_ec_prepare_tx(struct cros_ec_device *ec_dev, + struct cros_ec_command *msg) +{ + if (ec_dev->proto_version > 2) + return prepare_tx(ec_dev, msg); + + return prepare_tx_legacy(ec_dev, msg); +} EXPORT_SYMBOL(cros_ec_prepare_tx); /** @@ -199,9 +214,12 @@ EXPORT_SYMBOL(cros_ec_prepare_tx); * @msg: Message to check. * * This is used by ChromeOS EC drivers to check the ec_msg->result for - * errors and to warn about them. + * EC_RES_IN_PROGRESS and to warn about them. * - * Return: 0 on success or negative error code. + * The function should not check for furthermore error codes. Otherwise, + * it would break the ABI. + * + * Return: -EAGAIN if ec_msg->result == EC_RES_IN_PROGRESS. Otherwise, 0. */ int cros_ec_check_result(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) @@ -228,59 +246,66 @@ EXPORT_SYMBOL(cros_ec_check_result); * * @ec_dev: EC device to call * @msg: message structure to use - * @mask: result when function returns >=0. + * @mask: result when function returns 0. * * LOCKING: * the caller has ec_dev->lock mutex, or the caller knows there is * no other command in progress. */ -static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, - struct cros_ec_command *msg, - uint32_t *mask) +static int cros_ec_get_host_event_wake_mask(struct cros_ec_device *ec_dev, uint32_t *mask) { + struct cros_ec_command *msg; struct ec_response_host_event_mask *r; - int ret; + int ret, mapped; + + msg = kzalloc(sizeof(*msg) + sizeof(*r), GFP_KERNEL); + if (!msg) + return -ENOMEM; msg->command = EC_CMD_HOST_EVENT_GET_WAKE_MASK; - msg->version = 0; - msg->outsize = 0; msg->insize = sizeof(*r); - ret = send_command(ec_dev, msg); - if (ret >= 0) { - if (msg->result == EC_RES_INVALID_COMMAND) - return -EOPNOTSUPP; - if (msg->result != EC_RES_SUCCESS) - return -EPROTO; - } - if (ret > 0) { - r = (struct ec_response_host_event_mask *)msg->data; - *mask = r->mask; + ret = cros_ec_send_command(ec_dev, msg); + if (ret < 0) + goto exit; + + mapped = cros_ec_map_error(msg->result); + if (mapped) { + ret = mapped; + goto exit; } + if (ret == 0) { + ret = -EPROTO; + goto exit; + } + + r = (struct ec_response_host_event_mask *)msg->data; + *mask = r->mask; + ret = 0; +exit: + kfree(msg); return ret; } -static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, - int devidx, - struct cros_ec_command *msg) +static int cros_ec_get_proto_info(struct cros_ec_device *ec_dev, int devidx) { - /* - * Try using v3+ to query for supported protocols. If this - * command fails, fall back to v2. Returns the highest protocol - * supported by the EC. - * Also sets the max request/response/passthru size. - */ - int ret; + struct cros_ec_command *msg; + struct ec_response_get_protocol_info *info; + int ret, mapped; - if (!ec_dev->pkt_xfer) - return -EPROTONOSUPPORT; + ec_dev->proto_version = 3; + if (devidx > 0) + ec_dev->max_passthru = 0; + + msg = kzalloc(sizeof(*msg) + sizeof(*info), GFP_KERNEL); + if (!msg) + return -ENOMEM; - memset(msg, 0, sizeof(*msg)); msg->command = EC_CMD_PASSTHRU_OFFSET(devidx) | EC_CMD_GET_PROTOCOL_INFO; - msg->insize = sizeof(struct ec_response_get_protocol_info); + msg->insize = sizeof(*info); - ret = send_command(ec_dev, msg); + ret = cros_ec_send_command(ec_dev, msg); /* * Send command once again when timeout occurred. * Fingerprint MCU (FPMCU) is restarted during system boot which @@ -289,68 +314,115 @@ static int cros_ec_host_command_proto_query(struct cros_ec_device *ec_dev, * attempt because we waited at least EC_MSG_DEADLINE_MS. */ if (ret == -ETIMEDOUT) - ret = send_command(ec_dev, msg); + ret = cros_ec_send_command(ec_dev, msg); if (ret < 0) { dev_dbg(ec_dev->dev, "failed to check for EC[%d] protocol version: %d\n", devidx, ret); - return ret; + goto exit; } - if (devidx > 0 && msg->result == EC_RES_INVALID_COMMAND) - return -ENODEV; - else if (msg->result != EC_RES_SUCCESS) - return msg->result; + mapped = cros_ec_map_error(msg->result); + if (mapped) { + ret = mapped; + goto exit; + } - return 0; + if (ret == 0) { + ret = -EPROTO; + goto exit; + } + + info = (struct ec_response_get_protocol_info *)msg->data; + + switch (devidx) { + case CROS_EC_DEV_EC_INDEX: + ec_dev->max_request = info->max_request_packet_size - + sizeof(struct ec_host_request); + ec_dev->max_response = info->max_response_packet_size - + sizeof(struct ec_host_response); + ec_dev->proto_version = min(EC_HOST_REQUEST_VERSION, + fls(info->protocol_versions) - 1); + ec_dev->din_size = info->max_response_packet_size + EC_MAX_RESPONSE_OVERHEAD; + ec_dev->dout_size = info->max_request_packet_size + EC_MAX_REQUEST_OVERHEAD; + + dev_dbg(ec_dev->dev, "using proto v%u\n", ec_dev->proto_version); + break; + case CROS_EC_DEV_PD_INDEX: + ec_dev->max_passthru = info->max_request_packet_size - + sizeof(struct ec_host_request); + + dev_dbg(ec_dev->dev, "found PD chip\n"); + break; + default: + dev_dbg(ec_dev->dev, "unknown passthru index: %d\n", devidx); + break; + } + + ret = 0; +exit: + kfree(msg); + return ret; } -static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) +static int cros_ec_get_proto_info_legacy(struct cros_ec_device *ec_dev) { struct cros_ec_command *msg; - struct ec_params_hello *hello_params; - struct ec_response_hello *hello_response; - int ret; - int len = max(sizeof(*hello_params), sizeof(*hello_response)); + struct ec_params_hello *params; + struct ec_response_hello *response; + int ret, mapped; - msg = kmalloc(sizeof(*msg) + len, GFP_KERNEL); + ec_dev->proto_version = 2; + + msg = kzalloc(sizeof(*msg) + max(sizeof(*params), sizeof(*response)), GFP_KERNEL); if (!msg) return -ENOMEM; - msg->version = 0; msg->command = EC_CMD_HELLO; - hello_params = (struct ec_params_hello *)msg->data; - msg->outsize = sizeof(*hello_params); - hello_response = (struct ec_response_hello *)msg->data; - msg->insize = sizeof(*hello_response); + msg->insize = sizeof(*response); + msg->outsize = sizeof(*params); - hello_params->in_data = 0xa0b0c0d0; - - ret = send_command(ec_dev, msg); + params = (struct ec_params_hello *)msg->data; + params->in_data = 0xa0b0c0d0; + ret = cros_ec_send_command(ec_dev, msg); if (ret < 0) { - dev_dbg(ec_dev->dev, - "EC failed to respond to v2 hello: %d\n", - ret); + dev_dbg(ec_dev->dev, "EC failed to respond to v2 hello: %d\n", ret); goto exit; - } else if (msg->result != EC_RES_SUCCESS) { - dev_err(ec_dev->dev, - "EC responded to v2 hello with error: %d\n", - msg->result); - ret = msg->result; + } + + mapped = cros_ec_map_error(msg->result); + if (mapped) { + ret = mapped; + dev_err(ec_dev->dev, "EC responded to v2 hello with error: %d\n", msg->result); goto exit; - } else if (hello_response->out_data != 0xa1b2c3d4) { + } + + if (ret == 0) { + ret = -EPROTO; + goto exit; + } + + response = (struct ec_response_hello *)msg->data; + if (response->out_data != 0xa1b2c3d4) { dev_err(ec_dev->dev, "EC responded to v2 hello with bad result: %u\n", - hello_response->out_data); + response->out_data); ret = -EBADMSG; goto exit; } - ret = 0; + ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE; + ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; + ec_dev->max_passthru = 0; + ec_dev->pkt_xfer = NULL; + ec_dev->din_size = EC_PROTO2_MSG_BYTES; + ec_dev->dout_size = EC_PROTO2_MSG_BYTES; - exit: + dev_dbg(ec_dev->dev, "falling back to proto v2\n"); + ret = 0; +exit: kfree(msg); return ret; } @@ -371,13 +443,12 @@ static int cros_ec_host_command_proto_query_v2(struct cros_ec_device *ec_dev) * the caller has ec_dev->lock mutex or the caller knows there is * no other command in progress. */ -static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, - u16 cmd, u32 *mask) +static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, u16 cmd, u32 *mask) { struct ec_params_get_cmd_versions *pver; struct ec_response_get_cmd_versions *rver; struct cros_ec_command *msg; - int ret; + int ret, mapped; msg = kmalloc(sizeof(*msg) + max(sizeof(*rver), sizeof(*pver)), GFP_KERNEL); @@ -392,14 +463,26 @@ static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, pver = (struct ec_params_get_cmd_versions *)msg->data; pver->cmd = cmd; - ret = send_command(ec_dev, msg); - if (ret > 0) { - rver = (struct ec_response_get_cmd_versions *)msg->data; - *mask = rver->version_mask; + ret = cros_ec_send_command(ec_dev, msg); + if (ret < 0) + goto exit; + + mapped = cros_ec_map_error(msg->result); + if (mapped) { + ret = mapped; + goto exit; } - kfree(msg); + if (ret == 0) { + ret = -EPROTO; + goto exit; + } + rver = (struct ec_response_get_cmd_versions *)msg->data; + *mask = rver->version_mask; + ret = 0; +exit: + kfree(msg); return ret; } @@ -413,71 +496,17 @@ static int cros_ec_get_host_command_version_mask(struct cros_ec_device *ec_dev, int cros_ec_query_all(struct cros_ec_device *ec_dev) { struct device *dev = ec_dev->dev; - struct cros_ec_command *proto_msg; - struct ec_response_get_protocol_info *proto_info; - u32 ver_mask = 0; + u32 ver_mask; int ret; - proto_msg = kzalloc(sizeof(*proto_msg) + sizeof(*proto_info), - GFP_KERNEL); - if (!proto_msg) - return -ENOMEM; - /* First try sending with proto v3. */ - ec_dev->proto_version = 3; - ret = cros_ec_host_command_proto_query(ec_dev, 0, proto_msg); - - if (ret == 0) { - proto_info = (struct ec_response_get_protocol_info *) - proto_msg->data; - ec_dev->max_request = proto_info->max_request_packet_size - - sizeof(struct ec_host_request); - ec_dev->max_response = proto_info->max_response_packet_size - - sizeof(struct ec_host_response); - ec_dev->proto_version = - min(EC_HOST_REQUEST_VERSION, - fls(proto_info->protocol_versions) - 1); - dev_dbg(ec_dev->dev, - "using proto v%u\n", - ec_dev->proto_version); - - ec_dev->din_size = ec_dev->max_response + - sizeof(struct ec_host_response) + - EC_MAX_RESPONSE_OVERHEAD; - ec_dev->dout_size = ec_dev->max_request + - sizeof(struct ec_host_request) + - EC_MAX_REQUEST_OVERHEAD; - - /* - * Check for PD - */ - ret = cros_ec_host_command_proto_query(ec_dev, 1, proto_msg); - - if (ret) { - dev_dbg(ec_dev->dev, "no PD chip found: %d\n", ret); - ec_dev->max_passthru = 0; - } else { - dev_dbg(ec_dev->dev, "found PD chip\n"); - ec_dev->max_passthru = - proto_info->max_request_packet_size - - sizeof(struct ec_host_request); - } + if (!cros_ec_get_proto_info(ec_dev, CROS_EC_DEV_EC_INDEX)) { + /* Check for PD. */ + cros_ec_get_proto_info(ec_dev, CROS_EC_DEV_PD_INDEX); } else { /* Try querying with a v2 hello message. */ - ec_dev->proto_version = 2; - ret = cros_ec_host_command_proto_query_v2(ec_dev); - - if (ret == 0) { - /* V2 hello succeeded. */ - dev_dbg(ec_dev->dev, "falling back to proto v2\n"); - - ec_dev->max_request = EC_PROTO2_MAX_PARAM_SIZE; - ec_dev->max_response = EC_PROTO2_MAX_PARAM_SIZE; - ec_dev->max_passthru = 0; - ec_dev->pkt_xfer = NULL; - ec_dev->din_size = EC_PROTO2_MSG_BYTES; - ec_dev->dout_size = EC_PROTO2_MSG_BYTES; - } else { + ret = cros_ec_get_proto_info_legacy(ec_dev); + if (ret) { /* * It's possible for a test to occur too early when * the EC isn't listening. If this happens, we'll @@ -485,7 +514,7 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) */ ec_dev->proto_version = EC_PROTO_VERSION_UNKNOWN; dev_dbg(ec_dev->dev, "EC query failed: %d\n", ret); - goto exit; + return ret; } } @@ -506,26 +535,21 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) } /* Probe if MKBP event is supported */ - ret = cros_ec_get_host_command_version_mask(ec_dev, - EC_CMD_GET_NEXT_EVENT, - &ver_mask); - if (ret < 0 || ver_mask == 0) + ret = cros_ec_get_host_command_version_mask(ec_dev, EC_CMD_GET_NEXT_EVENT, &ver_mask); + if (ret < 0 || ver_mask == 0) { ec_dev->mkbp_event_supported = 0; - else + } else { ec_dev->mkbp_event_supported = fls(ver_mask); - dev_dbg(ec_dev->dev, "MKBP support version %u\n", - ec_dev->mkbp_event_supported - 1); + dev_dbg(ec_dev->dev, "MKBP support version %u\n", ec_dev->mkbp_event_supported - 1); + } /* Probe if host sleep v1 is supported for S0ix failure detection. */ - ret = cros_ec_get_host_command_version_mask(ec_dev, - EC_CMD_HOST_SLEEP_EVENT, - &ver_mask); - ec_dev->host_sleep_v1 = (ret >= 0 && (ver_mask & EC_VER_MASK(1))); + ret = cros_ec_get_host_command_version_mask(ec_dev, EC_CMD_HOST_SLEEP_EVENT, &ver_mask); + ec_dev->host_sleep_v1 = (ret == 0 && (ver_mask & EC_VER_MASK(1))); /* Get host event wake mask. */ - ret = cros_ec_get_host_event_wake_mask(ec_dev, proto_msg, - &ec_dev->host_event_wake_mask); + ret = cros_ec_get_host_event_wake_mask(ec_dev, &ec_dev->host_event_wake_mask); if (ret < 0) { /* * If the EC doesn't support EC_CMD_HOST_EVENT_GET_WAKE_MASK, @@ -556,7 +580,6 @@ int cros_ec_query_all(struct cros_ec_device *ec_dev) ret = 0; exit: - kfree(proto_msg); return ret; } EXPORT_SYMBOL(cros_ec_query_all); @@ -601,7 +624,7 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) msg->insize = ec_dev->max_response; } - if (msg->command < EC_CMD_PASSTHRU_OFFSET(1)) { + if (msg->command < EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX)) { if (msg->outsize > ec_dev->max_request) { dev_err(ec_dev->dev, "request of size %u is too big (max: %u)\n", @@ -621,7 +644,7 @@ int cros_ec_cmd_xfer(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) } } - ret = send_command(ec_dev, msg); + ret = cros_ec_send_command(ec_dev, msg); mutex_unlock(&ec_dev->lock); return ret; @@ -852,8 +875,8 @@ bool cros_ec_check_features(struct cros_ec_dev *ec, int feature) if (features->flags[0] == -1U && features->flags[1] == -1U) { /* features bitmap not read yet */ - ret = cros_ec_command(ec->ec_dev, 0, EC_CMD_GET_FEATURES + ec->cmd_offset, - NULL, 0, features, sizeof(*features)); + ret = cros_ec_cmd(ec->ec_dev, 0, EC_CMD_GET_FEATURES + ec->cmd_offset, + NULL, 0, features, sizeof(*features)); if (ret < 0) { dev_warn(ec->dev, "cannot get EC features: %d\n", ret); memset(features, 0, sizeof(*features)); @@ -934,7 +957,7 @@ int cros_ec_get_sensor_count(struct cros_ec_dev *ec) EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count); /** - * cros_ec_command - Send a command to the EC. + * cros_ec_cmd - Send a command to the EC. * * @ec_dev: EC device * @version: EC command version @@ -946,13 +969,13 @@ EXPORT_SYMBOL_GPL(cros_ec_get_sensor_count); * * Return: >= 0 on success, negative error number on failure. */ -int cros_ec_command(struct cros_ec_device *ec_dev, - unsigned int version, - int command, - void *outdata, - int outsize, - void *indata, - int insize) +int cros_ec_cmd(struct cros_ec_device *ec_dev, + unsigned int version, + int command, + void *outdata, + size_t outsize, + void *indata, + size_t insize) { struct cros_ec_command *msg; int ret; @@ -979,4 +1002,4 @@ error: kfree(msg); return ret; } -EXPORT_SYMBOL_GPL(cros_ec_command); +EXPORT_SYMBOL_GPL(cros_ec_cmd); diff --git a/drivers/platform/chrome/cros_ec_proto_test.c b/drivers/platform/chrome/cros_ec_proto_test.c new file mode 100644 index 000000000000..c6a83df91ae1 --- /dev/null +++ b/drivers/platform/chrome/cros_ec_proto_test.c @@ -0,0 +1,2753 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Kunit tests for ChromeOS Embedded Controller protocol. + */ + +#include + +#include +#include +#include + +#include "cros_ec.h" +#include "cros_kunit_util.h" + +#define BUFSIZE 512 + +struct cros_ec_proto_test_priv { + struct cros_ec_device ec_dev; + u8 dout[BUFSIZE]; + u8 din[BUFSIZE]; + struct cros_ec_command *msg; + u8 _msg[BUFSIZE]; +}; + +static void cros_ec_proto_test_prepare_tx_legacy_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct cros_ec_command *msg = priv->msg; + int ret, i; + u8 csum; + + ec_dev->proto_version = 2; + + msg->command = EC_CMD_HELLO; + msg->outsize = EC_PROTO2_MAX_PARAM_SIZE; + msg->data[0] = 0xde; + msg->data[1] = 0xad; + msg->data[2] = 0xbe; + msg->data[3] = 0xef; + + ret = cros_ec_prepare_tx(ec_dev, msg); + + KUNIT_EXPECT_EQ(test, ret, EC_MSG_TX_PROTO_BYTES + EC_PROTO2_MAX_PARAM_SIZE); + KUNIT_EXPECT_EQ(test, ec_dev->dout[0], EC_CMD_VERSION0); + KUNIT_EXPECT_EQ(test, ec_dev->dout[1], EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, ec_dev->dout[2], EC_PROTO2_MAX_PARAM_SIZE); + KUNIT_EXPECT_EQ(test, EC_MSG_TX_HEADER_BYTES, 3); + KUNIT_EXPECT_EQ(test, ec_dev->dout[EC_MSG_TX_HEADER_BYTES + 0], 0xde); + KUNIT_EXPECT_EQ(test, ec_dev->dout[EC_MSG_TX_HEADER_BYTES + 1], 0xad); + KUNIT_EXPECT_EQ(test, ec_dev->dout[EC_MSG_TX_HEADER_BYTES + 2], 0xbe); + KUNIT_EXPECT_EQ(test, ec_dev->dout[EC_MSG_TX_HEADER_BYTES + 3], 0xef); + for (i = 4; i < EC_PROTO2_MAX_PARAM_SIZE; ++i) + KUNIT_EXPECT_EQ(test, ec_dev->dout[EC_MSG_TX_HEADER_BYTES + i], 0); + + csum = EC_CMD_VERSION0; + csum += EC_CMD_HELLO; + csum += EC_PROTO2_MAX_PARAM_SIZE; + csum += 0xde; + csum += 0xad; + csum += 0xbe; + csum += 0xef; + KUNIT_EXPECT_EQ(test, + ec_dev->dout[EC_MSG_TX_HEADER_BYTES + EC_PROTO2_MAX_PARAM_SIZE], + csum); +} + +static void cros_ec_proto_test_prepare_tx_legacy_bad_msg_outsize(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct cros_ec_command *msg = priv->msg; + int ret; + + ec_dev->proto_version = 2; + + msg->outsize = EC_PROTO2_MAX_PARAM_SIZE + 1; + + ret = cros_ec_prepare_tx(ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void cros_ec_proto_test_prepare_tx_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct cros_ec_command *msg = priv->msg; + struct ec_host_request *request = (struct ec_host_request *)ec_dev->dout; + int ret, i; + u8 csum; + + msg->command = EC_CMD_HELLO; + msg->outsize = 0x88; + msg->data[0] = 0xde; + msg->data[1] = 0xad; + msg->data[2] = 0xbe; + msg->data[3] = 0xef; + + ret = cros_ec_prepare_tx(ec_dev, msg); + + KUNIT_EXPECT_EQ(test, ret, sizeof(*request) + 0x88); + + KUNIT_EXPECT_EQ(test, request->struct_version, EC_HOST_REQUEST_VERSION); + KUNIT_EXPECT_EQ(test, request->command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, request->command_version, 0); + KUNIT_EXPECT_EQ(test, request->data_len, 0x88); + KUNIT_EXPECT_EQ(test, ec_dev->dout[sizeof(*request) + 0], 0xde); + KUNIT_EXPECT_EQ(test, ec_dev->dout[sizeof(*request) + 1], 0xad); + KUNIT_EXPECT_EQ(test, ec_dev->dout[sizeof(*request) + 2], 0xbe); + KUNIT_EXPECT_EQ(test, ec_dev->dout[sizeof(*request) + 3], 0xef); + for (i = 4; i < 0x88; ++i) + KUNIT_EXPECT_EQ(test, ec_dev->dout[sizeof(*request) + i], 0); + + csum = EC_HOST_REQUEST_VERSION; + csum += EC_CMD_HELLO; + csum += 0x88; + csum += 0xde; + csum += 0xad; + csum += 0xbe; + csum += 0xef; + KUNIT_EXPECT_EQ(test, request->checksum, (u8)-csum); +} + +static void cros_ec_proto_test_prepare_tx_bad_msg_outsize(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct cros_ec_command *msg = priv->msg; + int ret; + + msg->outsize = ec_dev->dout_size - sizeof(struct ec_host_request) + 1; + + ret = cros_ec_prepare_tx(ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, -EINVAL); +} + +static void cros_ec_proto_test_check_result(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct cros_ec_command *msg = priv->msg; + int ret, i; + static enum ec_status status[] = { + EC_RES_SUCCESS, + EC_RES_INVALID_COMMAND, + EC_RES_ERROR, + EC_RES_INVALID_PARAM, + EC_RES_ACCESS_DENIED, + EC_RES_INVALID_RESPONSE, + EC_RES_INVALID_VERSION, + EC_RES_INVALID_CHECKSUM, + EC_RES_UNAVAILABLE, + EC_RES_TIMEOUT, + EC_RES_OVERFLOW, + EC_RES_INVALID_HEADER, + EC_RES_REQUEST_TRUNCATED, + EC_RES_RESPONSE_TOO_BIG, + EC_RES_BUS_ERROR, + EC_RES_BUSY, + EC_RES_INVALID_HEADER_VERSION, + EC_RES_INVALID_HEADER_CRC, + EC_RES_INVALID_DATA_CRC, + EC_RES_DUP_UNAVAILABLE, + }; + + for (i = 0; i < ARRAY_SIZE(status); ++i) { + msg->result = status[i]; + ret = cros_ec_check_result(ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, 0); + } + + msg->result = EC_RES_IN_PROGRESS; + ret = cros_ec_check_result(ec_dev, msg); + KUNIT_EXPECT_EQ(test, ret, -EAGAIN); +} + +static void cros_ec_proto_test_query_all_pretest(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + + /* + * cros_ec_query_all() will free din and dout and allocate them again to fit the usage by + * calling devm_kfree() and devm_kzalloc(). Set them to NULL as they aren't managed by + * ec_dev->dev but allocated statically in struct cros_ec_proto_test_priv + * (see cros_ec_proto_test_init()). + */ + ec_dev->din = NULL; + ec_dev->dout = NULL; +} + +static void cros_ec_proto_test_query_all_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->protocol_versions = BIT(3) | BIT(2); + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbf; + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_response_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_cmd_versions *)mock->o_data; + data->version_mask = BIT(6) | BIT(5); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + struct ec_response_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_cmd_versions *)mock->o_data; + data->version_mask = BIT(1); + } + + /* For cros_ec_get_host_event_wake_mask(). */ + { + struct ec_response_host_event_mask *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_host_event_mask *)mock->o_data; + data->mask = 0xbeef; + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + KUNIT_EXPECT_EQ(test, ec_dev->max_request, 0xbe - sizeof(struct ec_host_request)); + KUNIT_EXPECT_EQ(test, ec_dev->max_response, 0xef - sizeof(struct ec_host_response)); + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, 3); + KUNIT_EXPECT_EQ(test, ec_dev->din_size, 0xef + EC_MAX_RESPONSE_OVERHEAD); + KUNIT_EXPECT_EQ(test, ec_dev->dout_size, 0xbe + EC_MAX_REQUEST_OVERHEAD); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + KUNIT_EXPECT_EQ(test, ec_dev->max_passthru, 0xbf - sizeof(struct ec_host_request)); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_params_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_get_cmd_versions *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, EC_CMD_GET_NEXT_EVENT); + + KUNIT_EXPECT_EQ(test, ec_dev->mkbp_event_supported, 7); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + struct ec_params_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_get_cmd_versions *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, EC_CMD_HOST_SLEEP_EVENT); + + KUNIT_EXPECT_TRUE(test, ec_dev->host_sleep_v1); + } + + /* For cros_ec_get_host_event_wake_mask(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HOST_EVENT_GET_WAKE_MASK); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_host_event_mask)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + KUNIT_EXPECT_EQ(test, ec_dev->host_event_wake_mask, 0xbeef); + } +} + +static void cros_ec_proto_test_query_all_no_pd_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->max_passthru = 0xbf; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + KUNIT_EXPECT_EQ(test, ec_dev->max_passthru, 0); + } +} + +static void cros_ec_proto_test_query_all_no_pd_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->max_passthru = 0xbf; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + KUNIT_EXPECT_EQ(test, ec_dev->max_passthru, 0); + } +} + +static void cros_ec_proto_test_query_all_legacy_normal_v3_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + struct ec_response_hello *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_hello *)mock->o_data; + data->out_data = 0xa1b2c3d4; + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + struct ec_params_hello *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_hello)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_hello *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->in_data, 0xa0b0c0d0); + + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, 2); + KUNIT_EXPECT_EQ(test, ec_dev->max_request, EC_PROTO2_MAX_PARAM_SIZE); + KUNIT_EXPECT_EQ(test, ec_dev->max_response, EC_PROTO2_MAX_PARAM_SIZE); + KUNIT_EXPECT_EQ(test, ec_dev->max_passthru, 0); + KUNIT_EXPECT_PTR_EQ(test, ec_dev->pkt_xfer, NULL); + KUNIT_EXPECT_EQ(test, ec_dev->din_size, EC_PROTO2_MSG_BYTES); + KUNIT_EXPECT_EQ(test, ec_dev->dout_size, EC_PROTO2_MSG_BYTES); + } +} + +static void cros_ec_proto_test_query_all_legacy_normal_v3_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + struct ec_response_hello *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_hello *)mock->o_data; + data->out_data = 0xa1b2c3d4; + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + struct ec_params_hello *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_hello)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_hello *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->in_data, 0xa0b0c0d0); + + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, 2); + KUNIT_EXPECT_EQ(test, ec_dev->max_request, EC_PROTO2_MAX_PARAM_SIZE); + KUNIT_EXPECT_EQ(test, ec_dev->max_response, EC_PROTO2_MAX_PARAM_SIZE); + KUNIT_EXPECT_EQ(test, ec_dev->max_passthru, 0); + KUNIT_EXPECT_PTR_EQ(test, ec_dev->pkt_xfer, NULL); + KUNIT_EXPECT_EQ(test, ec_dev->din_size, EC_PROTO2_MSG_BYTES); + KUNIT_EXPECT_EQ(test, ec_dev->dout_size, EC_PROTO2_MSG_BYTES); + } +} + +static void cros_ec_proto_test_query_all_legacy_xfer_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, -EIO, EC_RES_SUCCESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, -EIO); + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, EC_PROTO_VERSION_UNKNOWN); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_hello)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_hello)); + } +} + +static void cros_ec_proto_test_query_all_legacy_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, -EOPNOTSUPP); + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, EC_PROTO_VERSION_UNKNOWN); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_hello)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_hello)); + } +} + +static void cros_ec_proto_test_query_all_legacy_data_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + struct ec_response_hello *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_hello *)mock->o_data; + data->out_data = 0xbeefbfbf; + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, -EBADMSG); + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, EC_PROTO_VERSION_UNKNOWN); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_hello)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_hello)); + } +} + +static void cros_ec_proto_test_query_all_legacy_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, -EPROTO); + KUNIT_EXPECT_EQ(test, ec_dev->proto_version, EC_PROTO_VERSION_UNKNOWN); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info_legacy(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_hello)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_hello)); + } +} + +static void cros_ec_proto_test_query_all_no_mkbp(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->mkbp_event_supported = 0xbf; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_response_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_cmd_versions *)mock->o_data; + data->version_mask = 0; + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_params_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_get_cmd_versions *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, EC_CMD_GET_NEXT_EVENT); + + KUNIT_EXPECT_EQ(test, ec_dev->mkbp_event_supported, 0); + } +} + +static void cros_ec_proto_test_query_all_no_mkbp_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->mkbp_event_supported = 0xbf; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_params_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_get_cmd_versions *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, EC_CMD_GET_NEXT_EVENT); + + KUNIT_EXPECT_EQ(test, ec_dev->mkbp_event_supported, 0); + } +} + +static void cros_ec_proto_test_query_all_no_mkbp_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->mkbp_event_supported = 0xbf; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_params_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_get_cmd_versions *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, EC_CMD_GET_NEXT_EVENT); + + KUNIT_EXPECT_EQ(test, ec_dev->mkbp_event_supported, 0); + } +} + +static void cros_ec_proto_test_query_all_no_host_sleep(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->host_sleep_v1 = true; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + struct ec_response_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_cmd_versions *)mock->o_data; + data->version_mask = 0; + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + + KUNIT_EXPECT_FALSE(test, ec_dev->host_sleep_v1); + } +} + +static void cros_ec_proto_test_query_all_no_host_sleep_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->host_sleep_v1 = true; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + struct ec_response_get_cmd_versions *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* In order to pollute next cros_ec_get_host_command_version_mask(). */ + data = (struct ec_response_get_cmd_versions *)mock->o_data; + data->version_mask = 0xbeef; + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + + KUNIT_EXPECT_FALSE(test, ec_dev->host_sleep_v1); + } +} + +static void cros_ec_proto_test_query_all_default_wake_mask_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->host_event_wake_mask = U32_MAX; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_event_wake_mask(). */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + } + + /* For cros_ec_get_host_event_wake_mask(). */ + { + u32 mask; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HOST_EVENT_GET_WAKE_MASK); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_host_event_mask)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + mask = ec_dev->host_event_wake_mask; + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS), 0); + } +} + +static void cros_ec_proto_test_query_all_default_wake_mask_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + + /* Set some garbage bytes. */ + ec_dev->host_event_wake_mask = U32_MAX; + + /* For cros_ec_get_proto_info() without passthru. */ + { + struct ec_response_get_protocol_info *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + /* + * Although it doesn't check the value, provides valid sizes so that + * cros_ec_query_all() allocates din and dout correctly. + */ + data = (struct ec_response_get_protocol_info *)mock->o_data; + data->max_request_packet_size = 0xbe; + data->max_response_packet_size = 0xef; + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For get_host_event_wake_mask(). */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + cros_ec_proto_test_query_all_pretest(test); + ret = cros_ec_query_all(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); + + /* For cros_ec_get_proto_info() without passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_proto_info() with passthru. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, + EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) | + EC_CMD_GET_PROTOCOL_INFO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_protocol_info)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + /* For cros_ec_get_host_command_version_mask() for MKBP. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + } + + /* For cros_ec_get_host_command_version_mask() for host sleep v1. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_CMD_VERSIONS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_cmd_versions)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(struct ec_params_get_cmd_versions)); + } + + /* For get_host_event_wake_mask(). */ + { + u32 mask; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HOST_EVENT_GET_WAKE_MASK); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_host_event_mask)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + + mask = ec_dev->host_event_wake_mask; + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_LID_CLOSED), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_LOW), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_CRITICAL), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_PD_MCU), 0); + KUNIT_EXPECT_EQ(test, mask & EC_HOST_EVENT_MASK(EC_HOST_EVENT_BATTERY_STATUS), 0); + } +} + +static void cros_ec_proto_test_cmd_xfer_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct { + struct cros_ec_command msg; + u8 data[0x100]; + } __packed buf; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->max_passthru = 0xdd; + + buf.msg.version = 0; + buf.msg.command = EC_CMD_HELLO; + buf.msg.insize = 4; + buf.msg.outsize = 2; + buf.data[0] = 0x55; + buf.data[1] = 0xaa; + + { + u8 *data; + + mock = cros_kunit_ec_xfer_mock_add(test, 4); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (u8 *)mock->o_data; + data[0] = 0xaa; + data[1] = 0x55; + data[2] = 0xcc; + data[3] = 0x33; + } + + ret = cros_ec_cmd_xfer(ec_dev, &buf.msg); + KUNIT_EXPECT_EQ(test, ret, 4); + + { + u8 *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, 4); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 2); + + data = (u8 *)mock->i_data; + KUNIT_EXPECT_EQ(test, data[0], 0x55); + KUNIT_EXPECT_EQ(test, data[1], 0xaa); + + KUNIT_EXPECT_EQ(test, buf.data[0], 0xaa); + KUNIT_EXPECT_EQ(test, buf.data[1], 0x55); + KUNIT_EXPECT_EQ(test, buf.data[2], 0xcc); + KUNIT_EXPECT_EQ(test, buf.data[3], 0x33); + } +} + +static void cros_ec_proto_test_cmd_xfer_excess_msg_insize(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct { + struct cros_ec_command msg; + u8 data[0x100]; + } __packed buf; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->max_passthru = 0xdd; + + buf.msg.version = 0; + buf.msg.command = EC_CMD_HELLO; + buf.msg.insize = 0xee + 1; + buf.msg.outsize = 2; + + { + mock = cros_kunit_ec_xfer_mock_add(test, 0xcc); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_cmd_xfer(ec_dev, &buf.msg); + KUNIT_EXPECT_EQ(test, ret, 0xcc); + + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_HELLO); + KUNIT_EXPECT_EQ(test, mock->msg.insize, 0xee); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 2); + } +} + +static void cros_ec_proto_test_cmd_xfer_excess_msg_outsize_without_passthru(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + struct { + struct cros_ec_command msg; + u8 data[0x100]; + } __packed buf; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->max_passthru = 0xdd; + + buf.msg.version = 0; + buf.msg.command = EC_CMD_HELLO; + buf.msg.insize = 4; + buf.msg.outsize = 0xff + 1; + + ret = cros_ec_cmd_xfer(ec_dev, &buf.msg); + KUNIT_EXPECT_EQ(test, ret, -EMSGSIZE); +} + +static void cros_ec_proto_test_cmd_xfer_excess_msg_outsize_with_passthru(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + struct { + struct cros_ec_command msg; + u8 data[0x100]; + } __packed buf; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->max_passthru = 0xdd; + + buf.msg.version = 0; + buf.msg.command = EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX) + EC_CMD_HELLO; + buf.msg.insize = 4; + buf.msg.outsize = 0xdd + 1; + + ret = cros_ec_cmd_xfer(ec_dev, &buf.msg); + KUNIT_EXPECT_EQ(test, ret, -EMSGSIZE); +} + +static void cros_ec_proto_test_cmd_xfer_protocol_v3_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->proto_version = 3; + ec_dev->cmd_xfer = cros_kunit_ec_cmd_xfer_mock; + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, cros_kunit_ec_cmd_xfer_mock_called, 0); + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 1); +} + +static void cros_ec_proto_test_cmd_xfer_protocol_v3_no_op(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->proto_version = 3; + ec_dev->cmd_xfer = cros_kunit_ec_cmd_xfer_mock; + ec_dev->pkt_xfer = NULL; + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EIO); +} + +static void cros_ec_proto_test_cmd_xfer_protocol_v2_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->proto_version = 2; + ec_dev->cmd_xfer = cros_kunit_ec_cmd_xfer_mock; + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, cros_kunit_ec_cmd_xfer_mock_called, 1); + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 0); +} + +static void cros_ec_proto_test_cmd_xfer_protocol_v2_no_op(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->proto_version = 2; + ec_dev->cmd_xfer = NULL; + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EIO); +} + +static void cros_ec_proto_test_cmd_xfer_in_progress_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS. */ + { + struct ec_response_get_comms_status *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_comms_status *)mock->o_data; + data->flags = 0; + } + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, sizeof(struct ec_response_get_comms_status)); + + KUNIT_EXPECT_EQ(test, msg.result, EC_RES_SUCCESS); + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_COMMS_STATUS); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_comms_status)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } + + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 2); +} + +static void cros_ec_proto_test_cmd_xfer_in_progress_retries_eagain(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS EC_COMMAND_RETRIES times. */ + cros_kunit_ec_xfer_mock_default_ret = -EAGAIN; + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EAGAIN); + + /* For EC_CMD_GET_COMMS_STATUS EC_COMMAND_RETRIES times. */ + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 51); +} + +static void cros_ec_proto_test_cmd_xfer_in_progress_retries_status_processing(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS EC_COMMAND_RETRIES times. */ + { + struct ec_response_get_comms_status *data; + int i; + + for (i = 0; i < 50; ++i) { + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_comms_status *)mock->o_data; + data->flags |= EC_COMMS_STATUS_PROCESSING; + } + } + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EAGAIN); + + /* For EC_CMD_GET_COMMS_STATUS EC_COMMAND_RETRIES times. */ + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 51); +} + +static void cros_ec_proto_test_cmd_xfer_in_progress_xfer_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, -EIO, EC_RES_SUCCESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EIO); +} + +static void cros_ec_proto_test_cmd_xfer_in_progress_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_INVALID_COMMAND, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, 0); + + KUNIT_EXPECT_EQ(test, msg.result, EC_RES_INVALID_COMMAND); + + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 2); +} + +static void cros_ec_proto_test_cmd_xfer_in_progress_return0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + ec_dev->pkt_xfer = cros_kunit_ec_pkt_xfer_mock; + + /* For the first host command to return EC_RES_IN_PROGRESS. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, EC_RES_IN_PROGRESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For EC_CMD_GET_COMMS_STATUS. */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_cmd_xfer(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EPROTO); + + KUNIT_EXPECT_EQ(test, cros_kunit_ec_pkt_xfer_mock_called, 2); +} + +static void cros_ec_proto_test_cmd_xfer_status_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + /* For cros_ec_cmd_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_add(test, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_cmd_xfer_status(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void cros_ec_proto_test_cmd_xfer_status_xfer_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_command msg; + + memset(&msg, 0, sizeof(msg)); + + /* For cros_ec_cmd_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, -EPROTO, EC_RES_SUCCESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_cmd_xfer_status(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, -EPROTO); +} + +static void cros_ec_proto_test_cmd_xfer_status_return_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret, i; + struct cros_ec_command msg; + static const int map[] = { + [EC_RES_SUCCESS] = 0, + [EC_RES_INVALID_COMMAND] = -EOPNOTSUPP, + [EC_RES_ERROR] = -EIO, + [EC_RES_INVALID_PARAM] = -EINVAL, + [EC_RES_ACCESS_DENIED] = -EACCES, + [EC_RES_INVALID_RESPONSE] = -EPROTO, + [EC_RES_INVALID_VERSION] = -ENOPROTOOPT, + [EC_RES_INVALID_CHECKSUM] = -EBADMSG, + /* + * EC_RES_IN_PROGRESS is special because cros_ec_send_command() has extra logic to + * handle it. Note that default cros_kunit_ec_xfer_mock_default_ret == 0 thus + * cros_ec_xfer_command() in cros_ec_wait_until_complete() returns 0. As a result, + * it returns -EPROTO without calling cros_ec_map_error(). + */ + [EC_RES_IN_PROGRESS] = -EPROTO, + [EC_RES_UNAVAILABLE] = -ENODATA, + [EC_RES_TIMEOUT] = -ETIMEDOUT, + [EC_RES_OVERFLOW] = -EOVERFLOW, + [EC_RES_INVALID_HEADER] = -EBADR, + [EC_RES_REQUEST_TRUNCATED] = -EBADR, + [EC_RES_RESPONSE_TOO_BIG] = -EFBIG, + [EC_RES_BUS_ERROR] = -EFAULT, + [EC_RES_BUSY] = -EBUSY, + [EC_RES_INVALID_HEADER_VERSION] = -EBADMSG, + [EC_RES_INVALID_HEADER_CRC] = -EBADMSG, + [EC_RES_INVALID_DATA_CRC] = -EBADMSG, + [EC_RES_DUP_UNAVAILABLE] = -ENODATA, + }; + + memset(&msg, 0, sizeof(msg)); + + for (i = 0; i < ARRAY_SIZE(map); ++i) { + mock = cros_kunit_ec_xfer_mock_addx(test, 0, i, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + ret = cros_ec_cmd_xfer_status(ec_dev, &msg); + KUNIT_EXPECT_EQ(test, ret, map[i]); + } +} + +static void cros_ec_proto_test_get_next_event_no_mkbp_event(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + bool wake_event, more_events; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->mkbp_event_supported = 0; + + /* Set some garbage bytes. */ + wake_event = false; + more_events = true; + + /* For get_keyboard_state_event(). */ + { + union ec_response_get_next_data_v1 *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (union ec_response_get_next_data_v1 *)mock->o_data; + data->host_event = 0xbeef; + } + + ret = cros_ec_get_next_event(ec_dev, &wake_event, &more_events); + KUNIT_EXPECT_EQ(test, ret, sizeof(union ec_response_get_next_data_v1)); + + KUNIT_EXPECT_EQ(test, ec_dev->event_data.event_type, EC_MKBP_EVENT_KEY_MATRIX); + KUNIT_EXPECT_EQ(test, ec_dev->event_data.data.host_event, 0xbeef); + + KUNIT_EXPECT_TRUE(test, wake_event); + KUNIT_EXPECT_FALSE(test, more_events); + + /* For get_keyboard_state_event(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_MKBP_STATE); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(union ec_response_get_next_data_v1)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } +} + +static void cros_ec_proto_test_get_next_event_mkbp_event_ec_suspended(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + + ec_dev->mkbp_event_supported = 1; + ec_dev->suspended = true; + + ret = cros_ec_get_next_event(ec_dev, NULL, NULL); + KUNIT_EXPECT_EQ(test, ret, -EHOSTDOWN); +} + +static void cros_ec_proto_test_get_next_event_mkbp_event_version0(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + bool wake_event, more_events; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->mkbp_event_supported = 1; + + /* Set some garbage bytes. */ + wake_event = true; + more_events = false; + + /* For get_next_event_xfer(). */ + { + struct ec_response_get_next_event *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_next_event *)mock->o_data; + data->event_type = EC_MKBP_EVENT_SENSOR_FIFO | EC_MKBP_HAS_MORE_EVENTS; + data->data.sysrq = 0xbeef; + } + + ret = cros_ec_get_next_event(ec_dev, &wake_event, &more_events); + KUNIT_EXPECT_EQ(test, ret, sizeof(struct ec_response_get_next_event)); + + KUNIT_EXPECT_EQ(test, ec_dev->event_data.event_type, EC_MKBP_EVENT_SENSOR_FIFO); + KUNIT_EXPECT_EQ(test, ec_dev->event_data.data.sysrq, 0xbeef); + + KUNIT_EXPECT_FALSE(test, wake_event); + KUNIT_EXPECT_TRUE(test, more_events); + + /* For get_next_event_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_NEXT_EVENT); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_get_next_event)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } +} + +static void cros_ec_proto_test_get_next_event_mkbp_event_version2(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + bool wake_event, more_events; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->mkbp_event_supported = 3; + + /* Set some garbage bytes. */ + wake_event = false; + more_events = true; + + /* For get_next_event_xfer(). */ + { + struct ec_response_get_next_event_v1 *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_next_event_v1 *)mock->o_data; + data->event_type = EC_MKBP_EVENT_FINGERPRINT; + data->data.sysrq = 0xbeef; + } + + ret = cros_ec_get_next_event(ec_dev, &wake_event, &more_events); + KUNIT_EXPECT_EQ(test, ret, sizeof(struct ec_response_get_next_event_v1)); + + KUNIT_EXPECT_EQ(test, ec_dev->event_data.event_type, EC_MKBP_EVENT_FINGERPRINT); + KUNIT_EXPECT_EQ(test, ec_dev->event_data.data.sysrq, 0xbeef); + + KUNIT_EXPECT_TRUE(test, wake_event); + KUNIT_EXPECT_FALSE(test, more_events); + + /* For get_next_event_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 2); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_NEXT_EVENT); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_next_event_v1)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } +} + +static void cros_ec_proto_test_get_next_event_mkbp_event_host_event_rtc(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + bool wake_event; + struct ec_response_get_next_event_v1 *data; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->mkbp_event_supported = 3; + ec_dev->host_event_wake_mask = U32_MAX; + + /* Set some garbage bytes. */ + wake_event = true; + + /* For get_next_event_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_add(test, + sizeof(data->event_type) + + sizeof(data->data.host_event)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_next_event_v1 *)mock->o_data; + data->event_type = EC_MKBP_EVENT_HOST_EVENT; + put_unaligned_le32(EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC), &data->data.host_event); + } + + ret = cros_ec_get_next_event(ec_dev, &wake_event, NULL); + KUNIT_EXPECT_EQ(test, ret, sizeof(data->event_type) + sizeof(data->data.host_event)); + + KUNIT_EXPECT_EQ(test, ec_dev->event_data.event_type, EC_MKBP_EVENT_HOST_EVENT); + + KUNIT_EXPECT_FALSE(test, wake_event); + + /* For get_next_event_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 2); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_NEXT_EVENT); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_next_event_v1)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } +} + +static void cros_ec_proto_test_get_next_event_mkbp_event_host_event_masked(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + bool wake_event; + struct ec_response_get_next_event_v1 *data; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->mkbp_event_supported = 3; + ec_dev->host_event_wake_mask = U32_MAX & ~EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED); + + /* Set some garbage bytes. */ + wake_event = true; + + /* For get_next_event_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_add(test, + sizeof(data->event_type) + + sizeof(data->data.host_event)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_next_event_v1 *)mock->o_data; + data->event_type = EC_MKBP_EVENT_HOST_EVENT; + put_unaligned_le32(EC_HOST_EVENT_MASK(EC_HOST_EVENT_AC_DISCONNECTED), + &data->data.host_event); + } + + ret = cros_ec_get_next_event(ec_dev, &wake_event, NULL); + KUNIT_EXPECT_EQ(test, ret, sizeof(data->event_type) + sizeof(data->data.host_event)); + + KUNIT_EXPECT_EQ(test, ec_dev->event_data.event_type, EC_MKBP_EVENT_HOST_EVENT); + + KUNIT_EXPECT_FALSE(test, wake_event); + + /* For get_next_event_xfer(). */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 2); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_NEXT_EVENT); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_get_next_event_v1)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } +} + +static void cros_ec_proto_test_get_host_event_no_mkbp_event(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + + ec_dev->mkbp_event_supported = 0; + + ret = cros_ec_get_host_event(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void cros_ec_proto_test_get_host_event_not_host_event(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + + ec_dev->mkbp_event_supported = 1; + ec_dev->event_data.event_type = EC_MKBP_EVENT_FINGERPRINT; + + ret = cros_ec_get_host_event(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void cros_ec_proto_test_get_host_event_wrong_event_size(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + + ec_dev->mkbp_event_supported = 1; + ec_dev->event_data.event_type = EC_MKBP_EVENT_HOST_EVENT; + ec_dev->event_size = 0xff; + + ret = cros_ec_get_host_event(ec_dev); + KUNIT_EXPECT_EQ(test, ret, 0); +} + +static void cros_ec_proto_test_get_host_event_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + int ret; + + ec_dev->mkbp_event_supported = 1; + ec_dev->event_data.event_type = EC_MKBP_EVENT_HOST_EVENT; + ec_dev->event_size = sizeof(ec_dev->event_data.data.host_event); + put_unaligned_le32(EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC), + &ec_dev->event_data.data.host_event); + + ret = cros_ec_get_host_event(ec_dev); + KUNIT_EXPECT_EQ(test, ret, EC_HOST_EVENT_MASK(EC_HOST_EVENT_RTC)); +} + +static void cros_ec_proto_test_check_features_cached(struct kunit *test) +{ + int ret, i; + struct cros_ec_dev ec; + + ec.features.flags[0] = EC_FEATURE_MASK_0(EC_FEATURE_FINGERPRINT); + ec.features.flags[1] = EC_FEATURE_MASK_0(EC_FEATURE_SCP); + + for (i = 0; i < EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK; ++i) { + ret = cros_ec_check_features(&ec, i); + switch (i) { + case EC_FEATURE_FINGERPRINT: + case EC_FEATURE_SCP: + KUNIT_EXPECT_TRUE(test, ret); + break; + default: + KUNIT_EXPECT_FALSE(test, ret); + break; + } + } +} + +static void cros_ec_proto_test_check_features_not_cached(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret, i; + struct cros_ec_dev ec; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec.ec_dev = ec_dev; + ec.dev = ec_dev->dev; + ec.cmd_offset = 0; + ec.features.flags[0] = -1; + ec.features.flags[1] = -1; + + /* For EC_CMD_GET_FEATURES. */ + { + struct ec_response_get_features *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_get_features *)mock->o_data; + data->flags[0] = EC_FEATURE_MASK_0(EC_FEATURE_FINGERPRINT); + data->flags[1] = EC_FEATURE_MASK_0(EC_FEATURE_SCP); + } + + for (i = 0; i < EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK; ++i) { + ret = cros_ec_check_features(&ec, i); + switch (i) { + case EC_FEATURE_FINGERPRINT: + case EC_FEATURE_SCP: + KUNIT_EXPECT_TRUE(test, ret); + break; + default: + KUNIT_EXPECT_FALSE(test, ret); + break; + } + } + + /* For EC_CMD_GET_FEATURES. */ + { + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_GET_FEATURES); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_get_features)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, 0); + } +} + +static void cros_ec_proto_test_get_sensor_count_normal(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_dev ec; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec.ec_dev = ec_dev; + ec.dev = ec_dev->dev; + ec.cmd_offset = 0; + + /* For EC_CMD_MOTION_SENSE_CMD. */ + { + struct ec_response_motion_sense *data; + + mock = cros_kunit_ec_xfer_mock_add(test, sizeof(*data)); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (struct ec_response_motion_sense *)mock->o_data; + data->dump.sensor_count = 0xbf; + } + + ret = cros_ec_get_sensor_count(&ec); + KUNIT_EXPECT_EQ(test, ret, 0xbf); + + /* For EC_CMD_MOTION_SENSE_CMD. */ + { + struct ec_params_motion_sense *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 1); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_MOTION_SENSE_CMD); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_motion_sense)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_motion_sense *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, MOTIONSENSE_CMD_DUMP); + } +} + +static void cros_ec_proto_test_get_sensor_count_xfer_error(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + struct cros_ec_dev ec; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec.ec_dev = ec_dev; + ec.dev = ec_dev->dev; + ec.cmd_offset = 0; + + /* For EC_CMD_MOTION_SENSE_CMD. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, -EPROTO, EC_RES_SUCCESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + ret = cros_ec_get_sensor_count(&ec); + KUNIT_EXPECT_EQ(test, ret, -EPROTO); + + /* For EC_CMD_MOTION_SENSE_CMD. */ + { + struct ec_params_motion_sense *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 1); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_MOTION_SENSE_CMD); + KUNIT_EXPECT_EQ(test, mock->msg.insize, sizeof(struct ec_response_motion_sense)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_motion_sense *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, MOTIONSENSE_CMD_DUMP); + } +} + +static void cros_ec_proto_test_get_sensor_count_legacy(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret, i; + struct cros_ec_dev ec; + struct { + u8 readmem_data; + int expected_result; + } test_data[] = { + { 0, 0 }, + { EC_MEMMAP_ACC_STATUS_PRESENCE_BIT, 2 }, + }; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + ec_dev->cmd_readmem = cros_kunit_readmem_mock; + ec.ec_dev = ec_dev; + ec.dev = ec_dev->dev; + ec.cmd_offset = 0; + + for (i = 0; i < ARRAY_SIZE(test_data); ++i) { + /* For EC_CMD_MOTION_SENSE_CMD. */ + { + mock = cros_kunit_ec_xfer_mock_addx(test, -EPROTO, EC_RES_SUCCESS, 0); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + } + + /* For readmem. */ + { + cros_kunit_readmem_mock_data = kunit_kzalloc(test, 1, GFP_KERNEL); + KUNIT_ASSERT_PTR_NE(test, cros_kunit_readmem_mock_data, NULL); + cros_kunit_readmem_mock_data[0] = test_data[i].readmem_data; + + cros_kunit_ec_xfer_mock_default_ret = 1; + } + + ret = cros_ec_get_sensor_count(&ec); + KUNIT_EXPECT_EQ(test, ret, test_data[i].expected_result); + + /* For EC_CMD_MOTION_SENSE_CMD. */ + { + struct ec_params_motion_sense *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 1); + KUNIT_EXPECT_EQ(test, mock->msg.command, EC_CMD_MOTION_SENSE_CMD); + KUNIT_EXPECT_EQ(test, mock->msg.insize, + sizeof(struct ec_response_motion_sense)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, sizeof(*data)); + + data = (struct ec_params_motion_sense *)mock->i_data; + KUNIT_EXPECT_EQ(test, data->cmd, MOTIONSENSE_CMD_DUMP); + } + + /* For readmem. */ + { + KUNIT_EXPECT_EQ(test, cros_kunit_readmem_mock_offset, EC_MEMMAP_ACC_STATUS); + } + } +} + +static void cros_ec_proto_test_ec_cmd(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + struct ec_xfer_mock *mock; + int ret; + u8 out[3], in[2]; + + ec_dev->max_request = 0xff; + ec_dev->max_response = 0xee; + + out[0] = 0xdd; + out[1] = 0xcc; + out[2] = 0xbb; + + { + u8 *data; + + mock = cros_kunit_ec_xfer_mock_add(test, 2); + KUNIT_ASSERT_PTR_NE(test, mock, NULL); + + data = (u8 *)mock->o_data; + data[0] = 0xaa; + data[1] = 0x99; + } + + ret = cros_ec_cmd(ec_dev, 0x88, 0x77, out, ARRAY_SIZE(out), in, ARRAY_SIZE(in)); + KUNIT_EXPECT_EQ(test, ret, 2); + + { + u8 *data; + + mock = cros_kunit_ec_xfer_mock_next(); + KUNIT_EXPECT_PTR_NE(test, mock, NULL); + + KUNIT_EXPECT_EQ(test, mock->msg.version, 0x88); + KUNIT_EXPECT_EQ(test, mock->msg.command, 0x77); + KUNIT_EXPECT_EQ(test, mock->msg.insize, ARRAY_SIZE(in)); + KUNIT_EXPECT_EQ(test, mock->msg.outsize, ARRAY_SIZE(out)); + + data = (u8 *)mock->i_data; + KUNIT_EXPECT_EQ(test, data[0], 0xdd); + KUNIT_EXPECT_EQ(test, data[1], 0xcc); + KUNIT_EXPECT_EQ(test, data[2], 0xbb); + } +} + +static void cros_ec_proto_test_release(struct device *dev) +{ +} + +static int cros_ec_proto_test_init(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv; + struct cros_ec_device *ec_dev; + + priv = kunit_kzalloc(test, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + test->priv = priv; + + ec_dev = &priv->ec_dev; + ec_dev->dout = (u8 *)priv->dout; + ec_dev->dout_size = ARRAY_SIZE(priv->dout); + ec_dev->din = (u8 *)priv->din; + ec_dev->din_size = ARRAY_SIZE(priv->din); + ec_dev->proto_version = EC_HOST_REQUEST_VERSION; + ec_dev->dev = kunit_kzalloc(test, sizeof(*ec_dev->dev), GFP_KERNEL); + if (!ec_dev->dev) + return -ENOMEM; + device_initialize(ec_dev->dev); + dev_set_name(ec_dev->dev, "cros_ec_proto_test"); + ec_dev->dev->release = cros_ec_proto_test_release; + ec_dev->cmd_xfer = cros_kunit_ec_xfer_mock; + ec_dev->pkt_xfer = cros_kunit_ec_xfer_mock; + + priv->msg = (struct cros_ec_command *)priv->_msg; + + cros_kunit_mock_reset(); + + return 0; +} + +static void cros_ec_proto_test_exit(struct kunit *test) +{ + struct cros_ec_proto_test_priv *priv = test->priv; + struct cros_ec_device *ec_dev = &priv->ec_dev; + + put_device(ec_dev->dev); +} + +static struct kunit_case cros_ec_proto_test_cases[] = { + KUNIT_CASE(cros_ec_proto_test_prepare_tx_legacy_normal), + KUNIT_CASE(cros_ec_proto_test_prepare_tx_legacy_bad_msg_outsize), + KUNIT_CASE(cros_ec_proto_test_prepare_tx_normal), + KUNIT_CASE(cros_ec_proto_test_prepare_tx_bad_msg_outsize), + KUNIT_CASE(cros_ec_proto_test_check_result), + KUNIT_CASE(cros_ec_proto_test_query_all_normal), + KUNIT_CASE(cros_ec_proto_test_query_all_no_pd_return_error), + KUNIT_CASE(cros_ec_proto_test_query_all_no_pd_return0), + KUNIT_CASE(cros_ec_proto_test_query_all_legacy_normal_v3_return_error), + KUNIT_CASE(cros_ec_proto_test_query_all_legacy_normal_v3_return0), + KUNIT_CASE(cros_ec_proto_test_query_all_legacy_xfer_error), + KUNIT_CASE(cros_ec_proto_test_query_all_legacy_return_error), + KUNIT_CASE(cros_ec_proto_test_query_all_legacy_data_error), + KUNIT_CASE(cros_ec_proto_test_query_all_legacy_return0), + KUNIT_CASE(cros_ec_proto_test_query_all_no_mkbp), + KUNIT_CASE(cros_ec_proto_test_query_all_no_mkbp_return_error), + KUNIT_CASE(cros_ec_proto_test_query_all_no_mkbp_return0), + KUNIT_CASE(cros_ec_proto_test_query_all_no_host_sleep), + KUNIT_CASE(cros_ec_proto_test_query_all_no_host_sleep_return0), + KUNIT_CASE(cros_ec_proto_test_query_all_default_wake_mask_return_error), + KUNIT_CASE(cros_ec_proto_test_query_all_default_wake_mask_return0), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_normal), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_excess_msg_insize), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_excess_msg_outsize_without_passthru), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_excess_msg_outsize_with_passthru), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v3_normal), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v3_no_op), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v2_normal), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_protocol_v2_no_op), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_normal), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_retries_eagain), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_retries_status_processing), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_xfer_error), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_return_error), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_in_progress_return0), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_status_normal), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_status_xfer_error), + KUNIT_CASE(cros_ec_proto_test_cmd_xfer_status_return_error), + KUNIT_CASE(cros_ec_proto_test_get_next_event_no_mkbp_event), + KUNIT_CASE(cros_ec_proto_test_get_next_event_mkbp_event_ec_suspended), + KUNIT_CASE(cros_ec_proto_test_get_next_event_mkbp_event_version0), + KUNIT_CASE(cros_ec_proto_test_get_next_event_mkbp_event_version2), + KUNIT_CASE(cros_ec_proto_test_get_next_event_mkbp_event_host_event_rtc), + KUNIT_CASE(cros_ec_proto_test_get_next_event_mkbp_event_host_event_masked), + KUNIT_CASE(cros_ec_proto_test_get_host_event_no_mkbp_event), + KUNIT_CASE(cros_ec_proto_test_get_host_event_not_host_event), + KUNIT_CASE(cros_ec_proto_test_get_host_event_wrong_event_size), + KUNIT_CASE(cros_ec_proto_test_get_host_event_normal), + KUNIT_CASE(cros_ec_proto_test_check_features_cached), + KUNIT_CASE(cros_ec_proto_test_check_features_not_cached), + KUNIT_CASE(cros_ec_proto_test_get_sensor_count_normal), + KUNIT_CASE(cros_ec_proto_test_get_sensor_count_xfer_error), + KUNIT_CASE(cros_ec_proto_test_get_sensor_count_legacy), + KUNIT_CASE(cros_ec_proto_test_ec_cmd), + {} +}; + +static struct kunit_suite cros_ec_proto_test_suite = { + .name = "cros_ec_proto_test", + .init = cros_ec_proto_test_init, + .exit = cros_ec_proto_test_exit, + .test_cases = cros_ec_proto_test_cases, +}; + +kunit_test_suite(cros_ec_proto_test_suite); + +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/chrome/cros_ec_trace.h b/drivers/platform/chrome/cros_ec_trace.h index 9bb5cd2c98b8..d7e407de88df 100644 --- a/drivers/platform/chrome/cros_ec_trace.h +++ b/drivers/platform/chrome/cros_ec_trace.h @@ -30,8 +30,8 @@ TRACE_EVENT(cros_ec_request_start, ), TP_fast_assign( __entry->version = cmd->version; - __entry->offset = cmd->command / EC_CMD_PASSTHRU_OFFSET(1); - __entry->command = cmd->command % EC_CMD_PASSTHRU_OFFSET(1); + __entry->offset = cmd->command / EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX); + __entry->command = cmd->command % EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX); __entry->outsize = cmd->outsize; __entry->insize = cmd->insize; ), @@ -55,8 +55,8 @@ TRACE_EVENT(cros_ec_request_done, ), TP_fast_assign( __entry->version = cmd->version; - __entry->offset = cmd->command / EC_CMD_PASSTHRU_OFFSET(1); - __entry->command = cmd->command % EC_CMD_PASSTHRU_OFFSET(1); + __entry->offset = cmd->command / EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX); + __entry->command = cmd->command % EC_CMD_PASSTHRU_OFFSET(CROS_EC_DEV_PD_INDEX); __entry->outsize = cmd->outsize; __entry->insize = cmd->insize; __entry->result = cmd->result; diff --git a/drivers/platform/chrome/cros_ec_typec.c b/drivers/platform/chrome/cros_ec_typec.c index 7cb2e35c4ded..de6ee0f926a6 100644 --- a/drivers/platform/chrome/cros_ec_typec.c +++ b/drivers/platform/chrome/cros_ec_typec.c @@ -25,6 +25,8 @@ #define DRV_NAME "cros-ec-typec" +#define DP_PORT_VDO (BIT(DP_PIN_ASSIGN_C) | BIT(DP_PIN_ASSIGN_D) | DP_CAP_DFP_D) + /* Supported alt modes. */ enum { CROS_EC_ALTMODE_DP = 0, @@ -60,8 +62,7 @@ struct cros_typec_port { uint8_t mux_flags; uint8_t role; - /* Port alt modes. */ - struct typec_altmode p_altmode[CROS_EC_ALTMODE_MAX]; + struct typec_altmode *port_altmode[CROS_EC_ALTMODE_MAX]; /* Flag indicating that PD partner discovery data parsing is completed. */ bool sop_disc_done; @@ -254,6 +255,14 @@ static void cros_typec_remove_cable(struct cros_typec_data *typec, port->sop_prime_disc_done = false; } +static void cros_typec_unregister_port_altmodes(struct cros_typec_port *port) +{ + int i; + + for (i = 0; i < CROS_EC_ALTMODE_MAX; i++) + typec_unregister_altmode(port->port_altmode[i]); +} + static void cros_unregister_ports(struct cros_typec_data *typec) { int i; @@ -268,34 +277,49 @@ static void cros_unregister_ports(struct cros_typec_data *typec) usb_role_switch_put(typec->ports[i]->role_sw); typec_switch_put(typec->ports[i]->ori_sw); typec_mux_put(typec->ports[i]->mux); + cros_typec_unregister_port_altmodes(typec->ports[i]); typec_unregister_port(typec->ports[i]->port); } } /* - * Fake the alt mode structs until we actually start registering Type C port - * and partner alt modes. + * Register port alt modes with known values till we start retrieving + * port capabilities from the EC. */ -static void cros_typec_register_port_altmodes(struct cros_typec_data *typec, +static int cros_typec_register_port_altmodes(struct cros_typec_data *typec, int port_num) { struct cros_typec_port *port = typec->ports[port_num]; + struct typec_altmode_desc desc; + struct typec_altmode *amode; /* All PD capable CrOS devices are assumed to support DP altmode. */ - port->p_altmode[CROS_EC_ALTMODE_DP].svid = USB_TYPEC_DP_SID; - port->p_altmode[CROS_EC_ALTMODE_DP].mode = USB_TYPEC_DP_MODE; + desc.svid = USB_TYPEC_DP_SID, + desc.mode = USB_TYPEC_DP_MODE, + desc.vdo = DP_PORT_VDO, + amode = typec_port_register_altmode(port->port, &desc); + if (IS_ERR(amode)) + return PTR_ERR(amode); + port->port_altmode[CROS_EC_ALTMODE_DP] = amode; /* * Register TBT compatibility alt mode. The EC will not enter the mode * if it doesn't support it, so it's safe to register it unconditionally * here for now. */ - port->p_altmode[CROS_EC_ALTMODE_TBT].svid = USB_TYPEC_TBT_SID; - port->p_altmode[CROS_EC_ALTMODE_TBT].mode = TYPEC_ANY_MODE; + memset(&desc, 0, sizeof(desc)); + desc.svid = USB_TYPEC_TBT_SID, + desc.mode = TYPEC_ANY_MODE, + amode = typec_port_register_altmode(port->port, &desc); + if (IS_ERR(amode)) + return PTR_ERR(amode); + port->port_altmode[CROS_EC_ALTMODE_TBT] = amode; port->state.alt = NULL; port->state.mode = TYPEC_STATE_USB; port->state.data = NULL; + + return 0; } static int cros_typec_init_ports(struct cros_typec_data *typec) @@ -352,8 +376,8 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) cros_port->port = typec_register_port(dev, cap); if (IS_ERR(cros_port->port)) { - dev_err(dev, "Failed to register port %d\n", port_num); ret = PTR_ERR(cros_port->port); + dev_err_probe(dev, ret, "Failed to register port %d\n", port_num); goto unregister_ports; } @@ -362,7 +386,11 @@ static int cros_typec_init_ports(struct cros_typec_data *typec) dev_dbg(dev, "No switch control for port %d\n", port_num); - cros_typec_register_port_altmodes(typec, port_num); + ret = cros_typec_register_port_altmodes(typec, port_num); + if (ret) { + dev_err(dev, "Failed to register port altmodes\n"); + goto unregister_ports; + } cros_port->disc_data = devm_kzalloc(dev, EC_PROTO2_MAX_RESPONSE_SIZE, GFP_KERNEL); if (!cros_port->disc_data) { @@ -431,7 +459,7 @@ static int cros_typec_enable_tbt(struct cros_typec_data *typec, data.enter_vdo |= TBT_ENTER_MODE_ACTIVE_CABLE; if (!port->state.alt) { - port->state.alt = &port->p_altmode[CROS_EC_ALTMODE_TBT]; + port->state.alt = port->port_altmode[CROS_EC_ALTMODE_TBT]; ret = cros_typec_usb_safe_state(port); if (ret) return ret; @@ -473,7 +501,7 @@ static int cros_typec_enable_dp(struct cros_typec_data *typec, /* Configuration VDO. */ dp_data.conf = DP_CONF_SET_PIN_ASSIGN(pd_ctrl->dp_mode); if (!port->state.alt) { - port->state.alt = &port->p_altmode[CROS_EC_ALTMODE_DP]; + port->state.alt = port->port_altmode[CROS_EC_ALTMODE_DP]; ret = cros_typec_usb_safe_state(port); if (ret) return ret; @@ -525,8 +553,8 @@ static int cros_typec_configure_mux(struct cros_typec_data *typec, int port_num, enum typec_orientation orientation; int ret; - ret = cros_ec_command(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO, - &req, sizeof(req), &resp, sizeof(resp)); + ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_MUX_INFO, + &req, sizeof(req), &resp, sizeof(resp)); if (ret < 0) { dev_warn(typec->dev, "Failed to get mux info for port: %d, err = %d\n", port_num, ret); @@ -585,8 +613,8 @@ mux_ack: /* Sending Acknowledgment to EC */ mux_ack.port = port_num; - if (cros_ec_command(typec->ec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack, - sizeof(mux_ack), NULL, 0) < 0) + if (cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_MUX_ACK, &mux_ack, + sizeof(mux_ack), NULL, 0) < 0) dev_warn(typec->dev, "Failed to send Mux ACK to EC for port: %d\n", port_num); @@ -754,8 +782,8 @@ static int cros_typec_handle_sop_prime_disc(struct cros_typec_data *typec, int p int ret = 0; memset(disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); - ret = cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), - disc, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_ec_cmd(typec->ec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + disc, EC_PROTO2_MAX_RESPONSE_SIZE); if (ret < 0) { dev_err(typec->dev, "Failed to get SOP' discovery data for port: %d\n", port_num); goto sop_prime_disc_exit; @@ -837,8 +865,8 @@ static int cros_typec_handle_sop_disc(struct cros_typec_data *typec, int port_nu typec_partner_set_pd_revision(port->partner, pd_revision); memset(sop_disc, 0, EC_PROTO2_MAX_RESPONSE_SIZE); - ret = cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), - sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE); + ret = cros_ec_cmd(typec->ec, 0, EC_CMD_TYPEC_DISCOVERY, &req, sizeof(req), + sop_disc, EC_PROTO2_MAX_RESPONSE_SIZE); if (ret < 0) { dev_err(typec->dev, "Failed to get SOP discovery data for port: %d\n", port_num); goto disc_exit; @@ -870,8 +898,8 @@ static int cros_typec_send_clear_event(struct cros_typec_data *typec, int port_n .clear_events_mask = events_mask, }; - return cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_CONTROL, &req, - sizeof(req), NULL, 0); + return cros_ec_cmd(typec->ec, 0, EC_CMD_TYPEC_CONTROL, &req, + sizeof(req), NULL, 0); } static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num) @@ -882,8 +910,8 @@ static void cros_typec_handle_status(struct cros_typec_data *typec, int port_num }; int ret; - ret = cros_ec_command(typec->ec, 0, EC_CMD_TYPEC_STATUS, &req, sizeof(req), - &resp, sizeof(resp)); + ret = cros_ec_cmd(typec->ec, 0, EC_CMD_TYPEC_STATUS, &req, sizeof(req), + &resp, sizeof(resp)); if (ret < 0) { dev_warn(typec->dev, "EC_CMD_TYPEC_STATUS failed for port: %d\n", port_num); return; @@ -960,9 +988,9 @@ static int cros_typec_port_update(struct cros_typec_data *typec, int port_num) req.mux = USB_PD_CTRL_MUX_NO_CHANGE; req.swap = USB_PD_CTRL_SWAP_NONE; - ret = cros_ec_command(typec->ec, typec->pd_ctrl_ver, - EC_CMD_USB_PD_CONTROL, &req, sizeof(req), - &resp, sizeof(resp)); + ret = cros_ec_cmd(typec->ec, typec->pd_ctrl_ver, + EC_CMD_USB_PD_CONTROL, &req, sizeof(req), + &resp, sizeof(resp)); if (ret < 0) return ret; @@ -997,9 +1025,8 @@ static int cros_typec_get_cmd_version(struct cros_typec_data *typec) /* We're interested in the PD control command version. */ req_v1.cmd = EC_CMD_USB_PD_CONTROL; - ret = cros_ec_command(typec->ec, 1, EC_CMD_GET_CMD_VERSIONS, - &req_v1, sizeof(req_v1), &resp, - sizeof(resp)); + ret = cros_ec_cmd(typec->ec, 1, EC_CMD_GET_CMD_VERSIONS, + &req_v1, sizeof(req_v1), &resp, sizeof(resp)); if (ret < 0) return ret; @@ -1090,8 +1117,8 @@ static int cros_typec_probe(struct platform_device *pdev) typec->typec_cmd_supported = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_CMD); typec->needs_mux_ack = cros_ec_check_features(ec_dev, EC_FEATURE_TYPEC_MUX_REQUIRE_AP_ACK); - ret = cros_ec_command(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, - &resp, sizeof(resp)); + ret = cros_ec_cmd(typec->ec, 0, EC_CMD_USB_PD_PORTS, NULL, 0, + &resp, sizeof(resp)); if (ret < 0) return ret; diff --git a/drivers/platform/chrome/cros_kbd_led_backlight.c b/drivers/platform/chrome/cros_kbd_led_backlight.c index aa409f0201fb..793fd3f1015d 100644 --- a/drivers/platform/chrome/cros_kbd_led_backlight.c +++ b/drivers/platform/chrome/cros_kbd_led_backlight.c @@ -4,24 +4,60 @@ // Copyright (C) 2012 Google, Inc. #include -#include #include #include -#include #include #include +#include +#include +#include +#include +#include #include +#include #include +struct keyboard_led { + struct led_classdev cdev; + struct cros_ec_device *ec; +}; + +/** + * struct keyboard_led_drvdata - keyboard LED driver data. + * @init: Init function. + * @brightness_get: Get LED brightness level. + * @brightness_set: Set LED brightness level. Must not sleep. + * @brightness_set_blocking: Set LED brightness level. It can block the + * caller for the time required for accessing a + * LED device register + * @max_brightness: Maximum brightness. + * + * See struct led_classdev in include/linux/leds.h for more details. + */ +struct keyboard_led_drvdata { + int (*init)(struct platform_device *pdev); + + enum led_brightness (*brightness_get)(struct led_classdev *led_cdev); + + void (*brightness_set)(struct led_classdev *led_cdev, + enum led_brightness brightness); + int (*brightness_set_blocking)(struct led_classdev *led_cdev, + enum led_brightness brightness); + + enum led_brightness max_brightness; +}; + +#define KEYBOARD_BACKLIGHT_MAX 100 + +#ifdef CONFIG_ACPI + /* Keyboard LED ACPI Device must be defined in firmware */ #define ACPI_KEYBOARD_BACKLIGHT_DEVICE "\\_SB.KBLT" #define ACPI_KEYBOARD_BACKLIGHT_READ ACPI_KEYBOARD_BACKLIGHT_DEVICE ".KBQC" #define ACPI_KEYBOARD_BACKLIGHT_WRITE ACPI_KEYBOARD_BACKLIGHT_DEVICE ".KBCM" -#define ACPI_KEYBOARD_BACKLIGHT_MAX 100 - -static void keyboard_led_set_brightness(struct led_classdev *cdev, - enum led_brightness brightness) +static void keyboard_led_set_brightness_acpi(struct led_classdev *cdev, + enum led_brightness brightness) { union acpi_object param; struct acpi_object_list input; @@ -40,7 +76,7 @@ static void keyboard_led_set_brightness(struct led_classdev *cdev, } static enum led_brightness -keyboard_led_get_brightness(struct led_classdev *cdev) +keyboard_led_get_brightness_acpi(struct led_classdev *cdev) { unsigned long long brightness; acpi_status status; @@ -56,12 +92,10 @@ keyboard_led_get_brightness(struct led_classdev *cdev) return brightness; } -static int keyboard_led_probe(struct platform_device *pdev) +static int keyboard_led_init_acpi(struct platform_device *pdev) { - struct led_classdev *cdev; acpi_handle handle; acpi_status status; - int error; /* Look for the keyboard LED ACPI Device */ status = acpi_get_handle(ACPI_ROOT_OBJECT, @@ -73,33 +107,151 @@ static int keyboard_led_probe(struct platform_device *pdev) return -ENXIO; } - cdev = devm_kzalloc(&pdev->dev, sizeof(*cdev), GFP_KERNEL); - if (!cdev) + return 0; +} + +static const struct keyboard_led_drvdata keyboard_led_drvdata_acpi = { + .init = keyboard_led_init_acpi, + .brightness_set = keyboard_led_set_brightness_acpi, + .brightness_get = keyboard_led_get_brightness_acpi, + .max_brightness = KEYBOARD_BACKLIGHT_MAX, +}; + +#endif /* CONFIG_ACPI */ + +#if IS_ENABLED(CONFIG_CROS_EC) + +static int +keyboard_led_set_brightness_ec_pwm(struct led_classdev *cdev, + enum led_brightness brightness) +{ + struct { + struct cros_ec_command msg; + struct ec_params_pwm_set_keyboard_backlight params; + } __packed buf; + struct ec_params_pwm_set_keyboard_backlight *params = &buf.params; + struct cros_ec_command *msg = &buf.msg; + struct keyboard_led *keyboard_led = container_of(cdev, struct keyboard_led, cdev); + + memset(&buf, 0, sizeof(buf)); + + msg->command = EC_CMD_PWM_SET_KEYBOARD_BACKLIGHT; + msg->outsize = sizeof(*params); + + params->percent = brightness; + + return cros_ec_cmd_xfer_status(keyboard_led->ec, msg); +} + +static enum led_brightness +keyboard_led_get_brightness_ec_pwm(struct led_classdev *cdev) +{ + struct { + struct cros_ec_command msg; + struct ec_response_pwm_get_keyboard_backlight resp; + } __packed buf; + struct ec_response_pwm_get_keyboard_backlight *resp = &buf.resp; + struct cros_ec_command *msg = &buf.msg; + struct keyboard_led *keyboard_led = container_of(cdev, struct keyboard_led, cdev); + int ret; + + memset(&buf, 0, sizeof(buf)); + + msg->command = EC_CMD_PWM_GET_KEYBOARD_BACKLIGHT; + msg->insize = sizeof(*resp); + + ret = cros_ec_cmd_xfer_status(keyboard_led->ec, msg); + if (ret < 0) + return ret; + + return resp->percent; +} + +static int keyboard_led_init_ec_pwm(struct platform_device *pdev) +{ + struct keyboard_led *keyboard_led = platform_get_drvdata(pdev); + + keyboard_led->ec = dev_get_drvdata(pdev->dev.parent); + if (!keyboard_led->ec) { + dev_err(&pdev->dev, "no parent EC device\n"); + return -EINVAL; + } + + return 0; +} + +static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm = { + .init = keyboard_led_init_ec_pwm, + .brightness_set_blocking = keyboard_led_set_brightness_ec_pwm, + .brightness_get = keyboard_led_get_brightness_ec_pwm, + .max_brightness = KEYBOARD_BACKLIGHT_MAX, +}; + +#else /* IS_ENABLED(CONFIG_CROS_EC) */ + +static const __maybe_unused struct keyboard_led_drvdata keyboard_led_drvdata_ec_pwm = {}; + +#endif /* IS_ENABLED(CONFIG_CROS_EC) */ + +static int keyboard_led_probe(struct platform_device *pdev) +{ + const struct keyboard_led_drvdata *drvdata; + struct keyboard_led *keyboard_led; + int error; + + drvdata = device_get_match_data(&pdev->dev); + if (!drvdata) + return -EINVAL; + + keyboard_led = devm_kzalloc(&pdev->dev, sizeof(*keyboard_led), GFP_KERNEL); + if (!keyboard_led) return -ENOMEM; + platform_set_drvdata(pdev, keyboard_led); - cdev->name = "chromeos::kbd_backlight"; - cdev->max_brightness = ACPI_KEYBOARD_BACKLIGHT_MAX; - cdev->flags |= LED_CORE_SUSPENDRESUME; - cdev->brightness_set = keyboard_led_set_brightness; - cdev->brightness_get = keyboard_led_get_brightness; + if (drvdata->init) { + error = drvdata->init(pdev); + if (error) + return error; + } - error = devm_led_classdev_register(&pdev->dev, cdev); + keyboard_led->cdev.name = "chromeos::kbd_backlight"; + keyboard_led->cdev.flags |= LED_CORE_SUSPENDRESUME; + keyboard_led->cdev.max_brightness = drvdata->max_brightness; + keyboard_led->cdev.brightness_set = drvdata->brightness_set; + keyboard_led->cdev.brightness_set_blocking = drvdata->brightness_set_blocking; + keyboard_led->cdev.brightness_get = drvdata->brightness_get; + + error = devm_led_classdev_register(&pdev->dev, &keyboard_led->cdev); if (error) return error; return 0; } -static const struct acpi_device_id keyboard_led_id[] = { - { "GOOG0002", 0 }, +#ifdef CONFIG_ACPI +static const struct acpi_device_id keyboard_led_acpi_match[] = { + { "GOOG0002", (kernel_ulong_t)&keyboard_led_drvdata_acpi }, { } }; -MODULE_DEVICE_TABLE(acpi, keyboard_led_id); +MODULE_DEVICE_TABLE(acpi, keyboard_led_acpi_match); +#endif + +#ifdef CONFIG_OF +static const struct of_device_id keyboard_led_of_match[] = { + { + .compatible = "google,cros-kbd-led-backlight", + .data = &keyboard_led_drvdata_ec_pwm, + }, + {} +}; +MODULE_DEVICE_TABLE(of, keyboard_led_of_match); +#endif static struct platform_driver keyboard_led_driver = { .driver = { .name = "chromeos-keyboard-leds", - .acpi_match_table = ACPI_PTR(keyboard_led_id), + .acpi_match_table = ACPI_PTR(keyboard_led_acpi_match), + .of_match_table = of_match_ptr(keyboard_led_of_match), }, .probe = keyboard_led_probe, }; diff --git a/drivers/platform/chrome/cros_kunit_util.c b/drivers/platform/chrome/cros_kunit_util.c new file mode 100644 index 000000000000..f0fda96b11bd --- /dev/null +++ b/drivers/platform/chrome/cros_kunit_util.c @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * CrOS Kunit tests utilities. + */ + +#include + +#include +#include +#include +#include + +#include "cros_ec.h" +#include "cros_kunit_util.h" + +int cros_kunit_ec_xfer_mock_default_result; +int cros_kunit_ec_xfer_mock_default_ret; +int cros_kunit_ec_cmd_xfer_mock_called; +int cros_kunit_ec_pkt_xfer_mock_called; + +static struct list_head cros_kunit_ec_xfer_mock_in; +static struct list_head cros_kunit_ec_xfer_mock_out; + +int cros_kunit_ec_xfer_mock(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) +{ + struct ec_xfer_mock *mock; + + mock = list_first_entry_or_null(&cros_kunit_ec_xfer_mock_in, struct ec_xfer_mock, list); + if (!mock) { + msg->result = cros_kunit_ec_xfer_mock_default_result; + return cros_kunit_ec_xfer_mock_default_ret; + } + + list_del(&mock->list); + + memcpy(&mock->msg, msg, sizeof(*msg)); + if (msg->outsize) { + mock->i_data = kunit_kzalloc(mock->test, msg->outsize, GFP_KERNEL); + if (mock->i_data) + memcpy(mock->i_data, msg->data, msg->outsize); + } + + msg->result = mock->result; + if (msg->insize) + memcpy(msg->data, mock->o_data, min(msg->insize, mock->o_data_len)); + + list_add_tail(&mock->list, &cros_kunit_ec_xfer_mock_out); + + return mock->ret; +} + +int cros_kunit_ec_cmd_xfer_mock(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) +{ + ++cros_kunit_ec_cmd_xfer_mock_called; + return cros_kunit_ec_xfer_mock(ec_dev, msg); +} + +int cros_kunit_ec_pkt_xfer_mock(struct cros_ec_device *ec_dev, struct cros_ec_command *msg) +{ + ++cros_kunit_ec_pkt_xfer_mock_called; + return cros_kunit_ec_xfer_mock(ec_dev, msg); +} + +struct ec_xfer_mock *cros_kunit_ec_xfer_mock_add(struct kunit *test, size_t size) +{ + return cros_kunit_ec_xfer_mock_addx(test, size, EC_RES_SUCCESS, size); +} + +struct ec_xfer_mock *cros_kunit_ec_xfer_mock_addx(struct kunit *test, + int ret, int result, size_t size) +{ + struct ec_xfer_mock *mock; + + mock = kunit_kzalloc(test, sizeof(*mock), GFP_KERNEL); + if (!mock) + return NULL; + + list_add_tail(&mock->list, &cros_kunit_ec_xfer_mock_in); + mock->test = test; + + mock->ret = ret; + mock->result = result; + mock->o_data = kunit_kzalloc(test, size, GFP_KERNEL); + if (!mock->o_data) + return NULL; + mock->o_data_len = size; + + return mock; +} + +struct ec_xfer_mock *cros_kunit_ec_xfer_mock_next(void) +{ + struct ec_xfer_mock *mock; + + mock = list_first_entry_or_null(&cros_kunit_ec_xfer_mock_out, struct ec_xfer_mock, list); + if (mock) + list_del(&mock->list); + + return mock; +} + +int cros_kunit_readmem_mock_offset; +u8 *cros_kunit_readmem_mock_data; +int cros_kunit_readmem_mock_ret; + +int cros_kunit_readmem_mock(struct cros_ec_device *ec_dev, unsigned int offset, + unsigned int bytes, void *dest) +{ + cros_kunit_readmem_mock_offset = offset; + + memcpy(dest, cros_kunit_readmem_mock_data, bytes); + + return cros_kunit_readmem_mock_ret; +} + +void cros_kunit_mock_reset(void) +{ + cros_kunit_ec_xfer_mock_default_result = 0; + cros_kunit_ec_xfer_mock_default_ret = 0; + cros_kunit_ec_cmd_xfer_mock_called = 0; + cros_kunit_ec_pkt_xfer_mock_called = 0; + INIT_LIST_HEAD(&cros_kunit_ec_xfer_mock_in); + INIT_LIST_HEAD(&cros_kunit_ec_xfer_mock_out); + + cros_kunit_readmem_mock_offset = 0; + cros_kunit_readmem_mock_data = NULL; + cros_kunit_readmem_mock_ret = 0; +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/chrome/cros_kunit_util.h b/drivers/platform/chrome/cros_kunit_util.h new file mode 100644 index 000000000000..414002271c9c --- /dev/null +++ b/drivers/platform/chrome/cros_kunit_util.h @@ -0,0 +1,48 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * CrOS Kunit tests utilities. + */ + +#ifndef _CROS_KUNIT_UTIL_H_ +#define _CROS_KUNIT_UTIL_H_ + +#include + +struct ec_xfer_mock { + struct list_head list; + struct kunit *test; + + /* input */ + struct cros_ec_command msg; + void *i_data; + + /* output */ + int ret; + int result; + void *o_data; + u32 o_data_len; +}; + +extern int cros_kunit_ec_xfer_mock_default_result; +extern int cros_kunit_ec_xfer_mock_default_ret; +extern int cros_kunit_ec_cmd_xfer_mock_called; +extern int cros_kunit_ec_pkt_xfer_mock_called; + +int cros_kunit_ec_xfer_mock(struct cros_ec_device *ec_dev, struct cros_ec_command *msg); +int cros_kunit_ec_cmd_xfer_mock(struct cros_ec_device *ec_dev, struct cros_ec_command *msg); +int cros_kunit_ec_pkt_xfer_mock(struct cros_ec_device *ec_dev, struct cros_ec_command *msg); +struct ec_xfer_mock *cros_kunit_ec_xfer_mock_add(struct kunit *test, size_t size); +struct ec_xfer_mock *cros_kunit_ec_xfer_mock_addx(struct kunit *test, + int ret, int result, size_t size); +struct ec_xfer_mock *cros_kunit_ec_xfer_mock_next(void); + +extern int cros_kunit_readmem_mock_offset; +extern u8 *cros_kunit_readmem_mock_data; +extern int cros_kunit_readmem_mock_ret; + +int cros_kunit_readmem_mock(struct cros_ec_device *ec_dev, unsigned int offset, + unsigned int bytes, void *dest); + +void cros_kunit_mock_reset(void); + +#endif diff --git a/drivers/platform/chrome/cros_usbpd_notify.c b/drivers/platform/chrome/cros_usbpd_notify.c index 91ce6be91aac..4b5a81c9dc6d 100644 --- a/drivers/platform/chrome/cros_usbpd_notify.c +++ b/drivers/platform/chrome/cros_usbpd_notify.c @@ -71,8 +71,8 @@ static void cros_usbpd_get_event_and_notify(struct device *dev, } /* Check for PD host events on EC. */ - ret = cros_ec_command(ec_dev, 0, EC_CMD_PD_HOST_EVENT_STATUS, - NULL, 0, &host_event_status, sizeof(host_event_status)); + ret = cros_ec_cmd(ec_dev, 0, EC_CMD_PD_HOST_EVENT_STATUS, + NULL, 0, &host_event_status, sizeof(host_event_status)); if (ret < 0) { dev_warn(dev, "Can't get host event status (err: %d)\n", ret); goto send_notify; diff --git a/drivers/platform/chrome/wilco_ec/event.c b/drivers/platform/chrome/wilco_ec/event.c index 814518509739..32e400590be5 100644 --- a/drivers/platform/chrome/wilco_ec/event.c +++ b/drivers/platform/chrome/wilco_ec/event.c @@ -343,7 +343,7 @@ static __poll_t event_poll(struct file *filp, poll_table *wait) * * Removes the first event from the queue, places it in the passed buffer. * - * If there are no events in the the queue, then one of two things happens, + * If there are no events in the queue, then one of two things happens, * depending on if the file was opened in nonblocking mode: If in nonblocking * mode, then return -EAGAIN to say there's no data. If in blocking mode, then * block until an event is available. diff --git a/drivers/platform/mellanox/mlxreg-io.c b/drivers/platform/mellanox/mlxreg-io.c index 2c2686d5c2fc..ddc08abf398c 100644 --- a/drivers/platform/mellanox/mlxreg-io.c +++ b/drivers/platform/mellanox/mlxreg-io.c @@ -31,6 +31,7 @@ * @group: sysfs attribute group; * @groups: list of sysfs attribute group for hwmon registration; * @regsize: size of a register value; + * @io_lock: user access locking; */ struct mlxreg_io_priv_data { struct platform_device *pdev; @@ -41,6 +42,7 @@ struct mlxreg_io_priv_data { struct attribute_group group; const struct attribute_group *groups[2]; int regsize; + struct mutex io_lock; /* Protects user access. */ }; static int @@ -116,14 +118,19 @@ mlxreg_io_attr_show(struct device *dev, struct device_attribute *attr, u32 regval = 0; int ret; + mutex_lock(&priv->io_lock); + ret = mlxreg_io_get_reg(priv->pdata->regmap, data, 0, true, priv->regsize, ®val); if (ret) goto access_error; + mutex_unlock(&priv->io_lock); + return sprintf(buf, "%u\n", regval); access_error: + mutex_unlock(&priv->io_lock); return ret; } @@ -145,6 +152,8 @@ mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr, if (ret) return ret; + mutex_lock(&priv->io_lock); + ret = mlxreg_io_get_reg(priv->pdata->regmap, data, input_val, false, priv->regsize, ®val); if (ret) @@ -154,9 +163,12 @@ mlxreg_io_attr_store(struct device *dev, struct device_attribute *attr, if (ret) goto access_error; + mutex_unlock(&priv->io_lock); + return len; access_error: + mutex_unlock(&priv->io_lock); dev_err(&priv->pdev->dev, "Bus access error\n"); return ret; } @@ -246,16 +258,27 @@ static int mlxreg_io_probe(struct platform_device *pdev) return PTR_ERR(priv->hwmon); } + mutex_init(&priv->io_lock); dev_set_drvdata(&pdev->dev, priv); return 0; } +static int mlxreg_io_remove(struct platform_device *pdev) +{ + struct mlxreg_io_priv_data *priv = dev_get_drvdata(&pdev->dev); + + mutex_destroy(&priv->io_lock); + + return 0; +} + static struct platform_driver mlxreg_io_driver = { .driver = { .name = "mlxreg-io", }, .probe = mlxreg_io_probe, + .remove = mlxreg_io_remove, }; module_platform_driver(mlxreg_io_driver); diff --git a/drivers/platform/mellanox/mlxreg-lc.c b/drivers/platform/mellanox/mlxreg-lc.c index c897a2f15840..55834ccb4ac7 100644 --- a/drivers/platform/mellanox/mlxreg-lc.c +++ b/drivers/platform/mellanox/mlxreg-lc.c @@ -716,8 +716,12 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, switch (regval) { case MLXREG_LC_SN4800_C16: err = mlxreg_lc_sn4800_c16_config_init(mlxreg_lc, regmap, data); - if (err) + if (err) { + dev_err(dev, "Failed to config client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, + data->hpdev.brdinfo->addr); return err; + } break; default: return -ENODEV; @@ -730,8 +734,11 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, mlxreg_lc->mux = platform_device_register_resndata(dev, "i2c-mux-mlxcpld", data->hpdev.nr, NULL, 0, mlxreg_lc->mux_data, sizeof(*mlxreg_lc->mux_data)); - if (IS_ERR(mlxreg_lc->mux)) + if (IS_ERR(mlxreg_lc->mux)) { + dev_err(dev, "Failed to create mux infra for client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); return PTR_ERR(mlxreg_lc->mux); + } /* Register IO access driver. */ if (mlxreg_lc->io_data) { @@ -740,6 +747,9 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, platform_device_register_resndata(dev, "mlxreg-io", data->hpdev.nr, NULL, 0, mlxreg_lc->io_data, sizeof(*mlxreg_lc->io_data)); if (IS_ERR(mlxreg_lc->io_regs)) { + dev_err(dev, "Failed to create regio for client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, + data->hpdev.brdinfo->addr); err = PTR_ERR(mlxreg_lc->io_regs); goto fail_register_io; } @@ -753,6 +763,9 @@ mlxreg_lc_config_init(struct mlxreg_lc *mlxreg_lc, void *regmap, mlxreg_lc->led_data, sizeof(*mlxreg_lc->led_data)); if (IS_ERR(mlxreg_lc->led)) { + dev_err(dev, "Failed to create LED objects for client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, + data->hpdev.brdinfo->addr); err = PTR_ERR(mlxreg_lc->led); goto fail_register_led; } @@ -809,7 +822,8 @@ static int mlxreg_lc_probe(struct platform_device *pdev) if (!data->hpdev.adapter) { dev_err(&pdev->dev, "Failed to get adapter for bus %d\n", data->hpdev.nr); - return -EFAULT; + err = -EFAULT; + goto i2c_get_adapter_fail; } /* Create device at the top of line card I2C tree.*/ @@ -818,32 +832,40 @@ static int mlxreg_lc_probe(struct platform_device *pdev) if (IS_ERR(data->hpdev.client)) { dev_err(&pdev->dev, "Failed to create client %s at bus %d at addr 0x%02x\n", data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); - - i2c_put_adapter(data->hpdev.adapter); - data->hpdev.adapter = NULL; - return PTR_ERR(data->hpdev.client); + err = PTR_ERR(data->hpdev.client); + goto i2c_new_device_fail; } regmap = devm_regmap_init_i2c(data->hpdev.client, &mlxreg_lc_regmap_conf); if (IS_ERR(regmap)) { + dev_err(&pdev->dev, "Failed to create regmap for client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); err = PTR_ERR(regmap); - goto mlxreg_lc_probe_fail; + goto devm_regmap_init_i2c_fail; } /* Set default registers. */ for (i = 0; i < mlxreg_lc_regmap_conf.num_reg_defaults; i++) { err = regmap_write(regmap, mlxreg_lc_regmap_default[i].reg, mlxreg_lc_regmap_default[i].def); - if (err) - goto mlxreg_lc_probe_fail; + if (err) { + dev_err(&pdev->dev, "Failed to set default regmap %d for client %s at bus %d at addr 0x%02x\n", + i, data->hpdev.brdinfo->type, data->hpdev.nr, + data->hpdev.brdinfo->addr); + goto regmap_write_fail; + } } /* Sync registers with hardware. */ regcache_mark_dirty(regmap); err = regcache_sync(regmap); - if (err) - goto mlxreg_lc_probe_fail; + if (err) { + dev_err(&pdev->dev, "Failed to sync regmap for client %s at bus %d at addr 0x%02x\n", + data->hpdev.brdinfo->type, data->hpdev.nr, data->hpdev.brdinfo->addr); + err = PTR_ERR(regmap); + goto regcache_sync_fail; + } par_pdata = data->hpdev.brdinfo->platform_data; mlxreg_lc->par_regmap = par_pdata->regmap; @@ -854,12 +876,27 @@ static int mlxreg_lc_probe(struct platform_device *pdev) /* Configure line card. */ err = mlxreg_lc_config_init(mlxreg_lc, regmap, data); if (err) - goto mlxreg_lc_probe_fail; + goto mlxreg_lc_config_init_fail; return err; -mlxreg_lc_probe_fail: +mlxreg_lc_config_init_fail: +regcache_sync_fail: +regmap_write_fail: +devm_regmap_init_i2c_fail: + if (data->hpdev.client) { + i2c_unregister_device(data->hpdev.client); + data->hpdev.client = NULL; + } +i2c_new_device_fail: i2c_put_adapter(data->hpdev.adapter); + data->hpdev.adapter = NULL; +i2c_get_adapter_fail: + /* Clear event notification callback and handle. */ + if (data->notifier) { + data->notifier->user_handler = NULL; + data->notifier->handle = NULL; + } return err; } @@ -868,11 +905,18 @@ static int mlxreg_lc_remove(struct platform_device *pdev) struct mlxreg_core_data *data = dev_get_platdata(&pdev->dev); struct mlxreg_lc *mlxreg_lc = platform_get_drvdata(pdev); - /* Clear event notification callback. */ - if (data->notifier) { - data->notifier->user_handler = NULL; - data->notifier->handle = NULL; - } + /* + * Probing and removing are invoked by hotplug events raised upon line card insertion and + * removing. If probing procedure fails all data is cleared. However, hotplug event still + * will be raised on line card removing and activate removing procedure. In this case there + * is nothing to remove. + */ + if (!data->notifier || !data->notifier->handle) + return 0; + + /* Clear event notification callback and handle. */ + data->notifier->user_handler = NULL; + data->notifier->handle = NULL; /* Destroy static I2C device feeding by main power. */ mlxreg_lc_destroy_static_devices(mlxreg_lc, mlxreg_lc->main_devs, diff --git a/drivers/platform/olpc/olpc-ec.c b/drivers/platform/olpc/olpc-ec.c index 4ff5c3a12991..921520475ff6 100644 --- a/drivers/platform/olpc/olpc-ec.c +++ b/drivers/platform/olpc/olpc-ec.c @@ -264,7 +264,7 @@ static ssize_t ec_dbgfs_cmd_write(struct file *file, const char __user *buf, int i, m; unsigned char ec_cmd[EC_MAX_CMD_ARGS]; unsigned int ec_cmd_int[EC_MAX_CMD_ARGS]; - char cmdbuf[64]; + char cmdbuf[64] = ""; int ec_cmd_bytes; mutex_lock(&ec_dbgfs_lock); diff --git a/drivers/platform/surface/Kconfig b/drivers/platform/surface/Kconfig index eb79fbed8059..b629e82af97c 100644 --- a/drivers/platform/surface/Kconfig +++ b/drivers/platform/surface/Kconfig @@ -72,18 +72,45 @@ config SURFACE_AGGREGATOR_CDEV The provided interface is intended for debugging and development only, and should not be used otherwise. +config SURFACE_AGGREGATOR_HUB + tristate "Surface System Aggregator Module Subsystem Device Hubs" + depends on SURFACE_AGGREGATOR + depends on SURFACE_AGGREGATOR_BUS + help + Device-hub drivers for Surface System Aggregator Module (SSAM) subsystem + devices. + + Provides subsystem hub drivers which manage client devices on various + SSAM subsystems. In some subsystems, notably the BAS subsystem managing + devices contained in the base of the Surface Book 3 and the KIP subsystem + managing type-cover devices in the Surface Pro 8 and Surface Pro X, + devices can be (hot-)removed. Hub devices and drivers are required to + manage these subdevices. + + Devices managed via these hubs are: + - Battery/AC devices (Surface Book 3). + - HID input devices (7th-generation and later models with detachable + input devices). + + Select M (recommended) or Y here if you want support for the above + mentioned devices on the corresponding Surface models. Without this + module, the respective devices mentioned above will not be instantiated + and thus any functionality provided by them will be missing, even when + drivers for these devices are present. This module only provides the + respective subsystem hubs. Both drivers and device specification (e.g. + via the Surface Aggregator Registry) for these devices still need to be + selected via other options. + config SURFACE_AGGREGATOR_REGISTRY tristate "Surface System Aggregator Module Device Registry" depends on SURFACE_AGGREGATOR depends on SURFACE_AGGREGATOR_BUS help - Device-registry and device-hubs for Surface System Aggregator Module - (SSAM) devices. + Device-registry for Surface System Aggregator Module (SSAM) devices. Provides a module and driver which act as a device-registry for SSAM client devices that cannot be detected automatically, e.g. via ACPI. - Such devices are instead provided via this registry and attached via - device hubs, also provided in this module. + Such devices are instead provided and managed via this registry. Devices provided via this registry are: - Platform profile (performance-/cooling-mode) device (5th- and later @@ -99,6 +126,29 @@ config SURFACE_AGGREGATOR_REGISTRY the respective client devices. Drivers for these devices still need to be selected via the other options. +config SURFACE_AGGREGATOR_TABLET_SWITCH + tristate "Surface Aggregator Generic Tablet-Mode Switch Driver" + depends on SURFACE_AGGREGATOR + depends on SURFACE_AGGREGATOR_BUS + depends on INPUT + help + Provides a tablet-mode switch input device on Microsoft Surface models + using the KIP subsystem for detachable keyboards (e.g. keyboard covers) + or the POS subsystem for device/screen posture changes. + + The KIP subsystem is used on newer Surface generations to handle + detachable input peripherals, specifically the keyboard cover (containing + keyboard and touchpad) on the Surface Pro 8 and Surface Pro X. The POS + subsystem is used for device posture change notifications on the Surface + Laptop Studio. This module provides a driver to let user-space know when + the device should be considered in tablet-mode due to the keyboard cover + being detached or folded back (essentially signaling when the keyboard is + not available for input). It does so by creating a tablet-mode switch + input device, sending the standard SW_TABLET_MODE event on mode change. + + Select M or Y here, if you want to provide tablet-mode switch input + events on the Surface Pro 8, Surface Pro X, and Surface Laptop Studio. + config SURFACE_DTX tristate "Surface DTX (Detachment System) Driver" depends on SURFACE_AGGREGATOR diff --git a/drivers/platform/surface/Makefile b/drivers/platform/surface/Makefile index 0fc9cd3e4dd9..53344330939b 100644 --- a/drivers/platform/surface/Makefile +++ b/drivers/platform/surface/Makefile @@ -9,7 +9,9 @@ obj-$(CONFIG_SURFACE_3_POWER_OPREGION) += surface3_power.o obj-$(CONFIG_SURFACE_ACPI_NOTIFY) += surface_acpi_notify.o obj-$(CONFIG_SURFACE_AGGREGATOR) += aggregator/ obj-$(CONFIG_SURFACE_AGGREGATOR_CDEV) += surface_aggregator_cdev.o +obj-$(CONFIG_SURFACE_AGGREGATOR_HUB) += surface_aggregator_hub.o obj-$(CONFIG_SURFACE_AGGREGATOR_REGISTRY) += surface_aggregator_registry.o +obj-$(CONFIG_SURFACE_AGGREGATOR_TABLET_SWITCH) += surface_aggregator_tabletsw.o obj-$(CONFIG_SURFACE_DTX) += surface_dtx.o obj-$(CONFIG_SURFACE_GPE) += surface_gpe.o obj-$(CONFIG_SURFACE_HOTPLUG) += surface_hotplug.o diff --git a/drivers/platform/surface/aggregator/Kconfig b/drivers/platform/surface/aggregator/Kconfig index cab020324256..c114f9dd5fe1 100644 --- a/drivers/platform/surface/aggregator/Kconfig +++ b/drivers/platform/surface/aggregator/Kconfig @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ -# Copyright (C) 2019-2021 Maximilian Luz +# Copyright (C) 2019-2022 Maximilian Luz menuconfig SURFACE_AGGREGATOR tristate "Microsoft Surface System Aggregator Module Subsystem and Drivers" diff --git a/drivers/platform/surface/aggregator/Makefile b/drivers/platform/surface/aggregator/Makefile index c0d550eda5cd..fdf664a217f9 100644 --- a/drivers/platform/surface/aggregator/Makefile +++ b/drivers/platform/surface/aggregator/Makefile @@ -1,5 +1,5 @@ # SPDX-License-Identifier: GPL-2.0+ -# Copyright (C) 2019-2021 Maximilian Luz +# Copyright (C) 2019-2022 Maximilian Luz # For include/trace/define_trace.h to include trace.h CFLAGS_core.o = -I$(src) diff --git a/drivers/platform/surface/aggregator/bus.c b/drivers/platform/surface/aggregator/bus.c index abbbb5b08b07..de539938896e 100644 --- a/drivers/platform/surface/aggregator/bus.c +++ b/drivers/platform/surface/aggregator/bus.c @@ -2,10 +2,11 @@ /* * Surface System Aggregator Module bus and device integration. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include +#include #include #include @@ -14,6 +15,9 @@ #include "bus.h" #include "controller.h" + +/* -- Device and bus functions. --------------------------------------------- */ + static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -46,6 +50,7 @@ static void ssam_device_release(struct device *dev) struct ssam_device *sdev = to_ssam_device(dev); ssam_controller_put(sdev->ctrl); + fwnode_handle_put(sdev->dev.fwnode); kfree(sdev); } @@ -363,6 +368,134 @@ void ssam_device_driver_unregister(struct ssam_device_driver *sdrv) } EXPORT_SYMBOL_GPL(ssam_device_driver_unregister); + +/* -- Bus registration. ----------------------------------------------------- */ + +/** + * ssam_bus_register() - Register and set-up the SSAM client device bus. + */ +int ssam_bus_register(void) +{ + return bus_register(&ssam_bus_type); +} + +/** + * ssam_bus_unregister() - Unregister the SSAM client device bus. + */ +void ssam_bus_unregister(void) +{ + return bus_unregister(&ssam_bus_type); +} + + +/* -- Helpers for controller and hub devices. ------------------------------- */ + +static int ssam_device_uid_from_string(const char *str, struct ssam_device_uid *uid) +{ + u8 d, tc, tid, iid, fn; + int n; + + n = sscanf(str, "%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn); + if (n != 5) + return -EINVAL; + + uid->domain = d; + uid->category = tc; + uid->target = tid; + uid->instance = iid; + uid->function = fn; + + return 0; +} + +static int ssam_get_uid_for_node(struct fwnode_handle *node, struct ssam_device_uid *uid) +{ + const char *str = fwnode_get_name(node); + + /* + * To simplify definitions of firmware nodes, we set the device name + * based on the UID of the device, prefixed with "ssam:". + */ + if (strncmp(str, "ssam:", strlen("ssam:")) != 0) + return -ENODEV; + + str += strlen("ssam:"); + return ssam_device_uid_from_string(str, uid); +} + +static int ssam_add_client_device(struct device *parent, struct ssam_controller *ctrl, + struct fwnode_handle *node) +{ + struct ssam_device_uid uid; + struct ssam_device *sdev; + int status; + + status = ssam_get_uid_for_node(node, &uid); + if (status) + return status; + + sdev = ssam_device_alloc(ctrl, uid); + if (!sdev) + return -ENOMEM; + + sdev->dev.parent = parent; + sdev->dev.fwnode = fwnode_handle_get(node); + + status = ssam_device_add(sdev); + if (status) + ssam_device_put(sdev); + + return status; +} + +/** + * __ssam_register_clients() - Register client devices defined under the + * given firmware node as children of the given device. + * @parent: The parent device under which clients should be registered. + * @ctrl: The controller with which client should be registered. + * @node: The firmware node holding definitions of the devices to be added. + * + * Register all clients that have been defined as children of the given root + * firmware node as children of the given parent device. The respective child + * firmware nodes will be associated with the correspondingly created child + * devices. + * + * The given controller will be used to instantiate the new devices. See + * ssam_device_add() for details. + * + * Note that, generally, the use of either ssam_device_register_clients() or + * ssam_register_clients() should be preferred as they directly use the + * firmware node and/or controller associated with the given device. This + * function is only intended for use when different device specifications (e.g. + * ACPI and firmware nodes) need to be combined (as is done in the platform hub + * of the device registry). + * + * Return: Returns zero on success, nonzero on failure. + */ +int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl, + struct fwnode_handle *node) +{ + struct fwnode_handle *child; + int status; + + fwnode_for_each_child_node(node, child) { + /* + * Try to add the device specified in the firmware node. If + * this fails with -ENODEV, the node does not specify any SSAM + * device, so ignore it and continue with the next one. + */ + status = ssam_add_client_device(parent, ctrl, child); + if (status && status != -ENODEV) + goto err; + } + + return 0; +err: + ssam_remove_clients(parent); + return status; +} +EXPORT_SYMBOL_GPL(__ssam_register_clients); + static int ssam_remove_device(struct device *dev, void *_data) { struct ssam_device *sdev = to_ssam_device(dev); @@ -387,19 +520,3 @@ void ssam_remove_clients(struct device *dev) device_for_each_child_reverse(dev, NULL, ssam_remove_device); } EXPORT_SYMBOL_GPL(ssam_remove_clients); - -/** - * ssam_bus_register() - Register and set-up the SSAM client device bus. - */ -int ssam_bus_register(void) -{ - return bus_register(&ssam_bus_type); -} - -/** - * ssam_bus_unregister() - Unregister the SSAM client device bus. - */ -void ssam_bus_unregister(void) -{ - return bus_unregister(&ssam_bus_type); -} diff --git a/drivers/platform/surface/aggregator/bus.h b/drivers/platform/surface/aggregator/bus.h index 6964ee84e79c..5b4dbf21906c 100644 --- a/drivers/platform/surface/aggregator/bus.h +++ b/drivers/platform/surface/aggregator/bus.h @@ -2,7 +2,7 @@ /* * Surface System Aggregator Module bus and device integration. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #ifndef _SURFACE_AGGREGATOR_BUS_H diff --git a/drivers/platform/surface/aggregator/controller.c b/drivers/platform/surface/aggregator/controller.c index b8c377b3f932..43e765199137 100644 --- a/drivers/platform/surface/aggregator/controller.c +++ b/drivers/platform/surface/aggregator/controller.c @@ -2,7 +2,7 @@ /* * Main SSAM/SSH controller structure and functionality. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include @@ -2199,16 +2199,26 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, } /** - * ssam_nf_refcount_disable_free() - Disable event for reference count entry if it is - * no longer in use and free the corresponding entry. + * ssam_nf_refcount_disable_free() - Disable event for reference count entry if + * it is no longer in use and free the corresponding entry. * @ctrl: The controller to disable the event on. * @entry: The reference count entry for the event to be disabled. * @flags: The flags used for enabling the event on the EC. + * @ec: Flag specifying if the event should actually be disabled on the EC. * - * If the reference count equals zero, i.e. the event is no longer requested by - * any client, the event will be disabled and the corresponding reference count - * entry freed. The reference count entry must not be used any more after a - * call to this function. + * If ``ec`` equals ``true`` and the reference count equals zero (i.e. the + * event is no longer requested by any client), the specified event will be + * disabled on the EC via the corresponding request. + * + * If ``ec`` equals ``false``, no request will be sent to the EC and the event + * can be considered in a detached state (i.e. no longer used but still + * enabled). Disabling an event via this method may be required for + * hot-removable devices, where event disable requests may time out after the + * device has been physically removed. + * + * In both cases, if the reference count equals zero, the corresponding + * reference count entry will be freed. The reference count entry must not be + * used any more after a call to this function. * * Also checks if the flags used for disabling the event match the flags used * for enabling the event and warns if they do not (regardless of reference @@ -2223,7 +2233,7 @@ static int ssam_nf_refcount_enable(struct ssam_controller *ctrl, * returns the status of the event-enable EC command. */ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, - struct ssam_nf_refcount_entry *entry, u8 flags) + struct ssam_nf_refcount_entry *entry, u8 flags, bool ec) { const struct ssam_event_registry reg = entry->key.reg; const struct ssam_event_id id = entry->key.id; @@ -2232,8 +2242,9 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, lockdep_assert_held(&nf->lock); - ssam_dbg(ctrl, "disabling event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", - reg.target_category, id.target_category, id.instance, entry->refcount); + ssam_dbg(ctrl, "%s event (reg: %#04x, tc: %#04x, iid: %#04x, rc: %d)\n", + ec ? "disabling" : "detaching", reg.target_category, id.target_category, + id.instance, entry->refcount); if (entry->flags != flags) { ssam_warn(ctrl, @@ -2242,7 +2253,7 @@ static int ssam_nf_refcount_disable_free(struct ssam_controller *ctrl, id.instance); } - if (entry->refcount == 0) { + if (ec && entry->refcount == 0) { status = ssam_ssh_event_disable(ctrl, reg, id, flags); kfree(entry); } @@ -2322,20 +2333,26 @@ int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notif EXPORT_SYMBOL_GPL(ssam_notifier_register); /** - * ssam_notifier_unregister() - Unregister an event notifier. - * @ctrl: The controller the notifier has been registered on. - * @n: The event notifier to unregister. + * __ssam_notifier_unregister() - Unregister an event notifier. + * @ctrl: The controller the notifier has been registered on. + * @n: The event notifier to unregister. + * @disable: Whether to disable the corresponding event on the EC. * * Unregister an event notifier. Decrement the usage counter of the associated * SAM event if the notifier is not marked as an observer. If the usage counter - * reaches zero, the event will be disabled. + * reaches zero and ``disable`` equals ``true``, the event will be disabled. + * + * Useful for hot-removable devices, where communication may fail once the + * device has been physically removed. In that case, specifying ``disable`` as + * ``false`` avoids communication with the EC. * * Return: Returns zero on success, %-ENOENT if the given notifier block has * not been registered on the controller. If the given notifier block was the * last one associated with its specific event, returns the status of the * event-disable EC-command. */ -int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n) +int __ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_notifier *n, + bool disable) { u16 rqid = ssh_tc_to_rqid(n->event.id.target_category); struct ssam_nf_refcount_entry *entry; @@ -2373,7 +2390,7 @@ int ssam_notifier_unregister(struct ssam_controller *ctrl, struct ssam_event_not goto remove; } - status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags); + status = ssam_nf_refcount_disable_free(ctrl, entry, n->event.flags, disable); } remove: @@ -2383,7 +2400,7 @@ remove: return status; } -EXPORT_SYMBOL_GPL(ssam_notifier_unregister); +EXPORT_SYMBOL_GPL(__ssam_notifier_unregister); /** * ssam_controller_event_enable() - Enable the specified event. @@ -2477,7 +2494,7 @@ int ssam_controller_event_disable(struct ssam_controller *ctrl, return -ENOENT; } - status = ssam_nf_refcount_disable_free(ctrl, entry, flags); + status = ssam_nf_refcount_disable_free(ctrl, entry, flags, true); mutex_unlock(&nf->lock); return status; diff --git a/drivers/platform/surface/aggregator/controller.h b/drivers/platform/surface/aggregator/controller.h index a0963c3562ff..f0d987abc51e 100644 --- a/drivers/platform/surface/aggregator/controller.h +++ b/drivers/platform/surface/aggregator/controller.h @@ -2,7 +2,7 @@ /* * Main SSAM/SSH controller structure and functionality. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #ifndef _SURFACE_AGGREGATOR_CONTROLLER_H diff --git a/drivers/platform/surface/aggregator/core.c b/drivers/platform/surface/aggregator/core.c index a62c5dfe42d6..1a6373dea109 100644 --- a/drivers/platform/surface/aggregator/core.c +++ b/drivers/platform/surface/aggregator/core.c @@ -7,7 +7,7 @@ * Handles communication via requests as well as enabling, disabling, and * relaying of events. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/aggregator/ssh_msgb.h b/drivers/platform/surface/aggregator/ssh_msgb.h index e562958ffdf0..f3ecad92eefd 100644 --- a/drivers/platform/surface/aggregator/ssh_msgb.h +++ b/drivers/platform/surface/aggregator/ssh_msgb.h @@ -2,7 +2,7 @@ /* * SSH message builder functions. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #ifndef _SURFACE_AGGREGATOR_SSH_MSGB_H diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.c b/drivers/platform/surface/aggregator/ssh_packet_layer.c index 8a4451c1ffe5..6748fe4ac5d5 100644 --- a/drivers/platform/surface/aggregator/ssh_packet_layer.c +++ b/drivers/platform/surface/aggregator/ssh_packet_layer.c @@ -2,7 +2,7 @@ /* * SSH packet transport layer. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/aggregator/ssh_packet_layer.h b/drivers/platform/surface/aggregator/ssh_packet_layer.h index 2eb329f0b91a..64633522f971 100644 --- a/drivers/platform/surface/aggregator/ssh_packet_layer.h +++ b/drivers/platform/surface/aggregator/ssh_packet_layer.h @@ -2,7 +2,7 @@ /* * SSH packet transport layer. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #ifndef _SURFACE_AGGREGATOR_SSH_PACKET_LAYER_H diff --git a/drivers/platform/surface/aggregator/ssh_parser.c b/drivers/platform/surface/aggregator/ssh_parser.c index b77912f8f13b..a6f668694365 100644 --- a/drivers/platform/surface/aggregator/ssh_parser.c +++ b/drivers/platform/surface/aggregator/ssh_parser.c @@ -2,7 +2,7 @@ /* * SSH message parser. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/aggregator/ssh_parser.h b/drivers/platform/surface/aggregator/ssh_parser.h index 3bd6e180fd16..801d8fa69fb5 100644 --- a/drivers/platform/surface/aggregator/ssh_parser.h +++ b/drivers/platform/surface/aggregator/ssh_parser.h @@ -2,7 +2,7 @@ /* * SSH message parser. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #ifndef _SURFACE_AGGREGATOR_SSH_PARSER_H diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.c b/drivers/platform/surface/aggregator/ssh_request_layer.c index 790f7f0eee98..f5565570f16c 100644 --- a/drivers/platform/surface/aggregator/ssh_request_layer.c +++ b/drivers/platform/surface/aggregator/ssh_request_layer.c @@ -2,7 +2,7 @@ /* * SSH request transport layer. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/aggregator/ssh_request_layer.h b/drivers/platform/surface/aggregator/ssh_request_layer.h index 9c3cbae2d4bd..4e387a031351 100644 --- a/drivers/platform/surface/aggregator/ssh_request_layer.h +++ b/drivers/platform/surface/aggregator/ssh_request_layer.h @@ -2,7 +2,7 @@ /* * SSH request transport layer. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #ifndef _SURFACE_AGGREGATOR_SSH_REQUEST_LAYER_H diff --git a/drivers/platform/surface/aggregator/trace.h b/drivers/platform/surface/aggregator/trace.h index de64cf169060..2a2c17771d01 100644 --- a/drivers/platform/surface/aggregator/trace.h +++ b/drivers/platform/surface/aggregator/trace.h @@ -2,7 +2,7 @@ /* * Trace points for SSAM/SSH. * - * Copyright (C) 2020-2021 Maximilian Luz + * Copyright (C) 2020-2022 Maximilian Luz */ #undef TRACE_SYSTEM @@ -76,7 +76,7 @@ TRACE_DEFINE_ENUM(SSAM_SSH_TC_HID); TRACE_DEFINE_ENUM(SSAM_SSH_TC_TCH); TRACE_DEFINE_ENUM(SSAM_SSH_TC_BKL); TRACE_DEFINE_ENUM(SSAM_SSH_TC_TAM); -TRACE_DEFINE_ENUM(SSAM_SSH_TC_ACC); +TRACE_DEFINE_ENUM(SSAM_SSH_TC_ACC0); TRACE_DEFINE_ENUM(SSAM_SSH_TC_UFI); TRACE_DEFINE_ENUM(SSAM_SSH_TC_USC); TRACE_DEFINE_ENUM(SSAM_SSH_TC_PEN); @@ -85,6 +85,11 @@ TRACE_DEFINE_ENUM(SSAM_SSH_TC_AUD); TRACE_DEFINE_ENUM(SSAM_SSH_TC_SMC); TRACE_DEFINE_ENUM(SSAM_SSH_TC_KPD); TRACE_DEFINE_ENUM(SSAM_SSH_TC_REG); +TRACE_DEFINE_ENUM(SSAM_SSH_TC_SPT); +TRACE_DEFINE_ENUM(SSAM_SSH_TC_SYS); +TRACE_DEFINE_ENUM(SSAM_SSH_TC_ACC1); +TRACE_DEFINE_ENUM(SSAM_SSH_TC_SHB); +TRACE_DEFINE_ENUM(SSAM_SSH_TC_POS); #define SSAM_PTR_UID_LEN 9 #define SSAM_U8_FIELD_NOT_APPLICABLE ((u16)-1) @@ -229,40 +234,45 @@ static inline u32 ssam_trace_get_request_tc(const struct ssh_packet *p) #define ssam_show_ssh_tc(rqid) \ __print_symbolic(rqid, \ - { SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \ - { SSAM_SSH_TC_SAM, "SAM" }, \ - { SSAM_SSH_TC_BAT, "BAT" }, \ - { SSAM_SSH_TC_TMP, "TMP" }, \ - { SSAM_SSH_TC_PMC, "PMC" }, \ - { SSAM_SSH_TC_FAN, "FAN" }, \ - { SSAM_SSH_TC_PoM, "PoM" }, \ - { SSAM_SSH_TC_DBG, "DBG" }, \ - { SSAM_SSH_TC_KBD, "KBD" }, \ - { SSAM_SSH_TC_FWU, "FWU" }, \ - { SSAM_SSH_TC_UNI, "UNI" }, \ - { SSAM_SSH_TC_LPC, "LPC" }, \ - { SSAM_SSH_TC_TCL, "TCL" }, \ - { SSAM_SSH_TC_SFL, "SFL" }, \ - { SSAM_SSH_TC_KIP, "KIP" }, \ - { SSAM_SSH_TC_EXT, "EXT" }, \ - { SSAM_SSH_TC_BLD, "BLD" }, \ - { SSAM_SSH_TC_BAS, "BAS" }, \ - { SSAM_SSH_TC_SEN, "SEN" }, \ - { SSAM_SSH_TC_SRQ, "SRQ" }, \ - { SSAM_SSH_TC_MCU, "MCU" }, \ - { SSAM_SSH_TC_HID, "HID" }, \ - { SSAM_SSH_TC_TCH, "TCH" }, \ - { SSAM_SSH_TC_BKL, "BKL" }, \ - { SSAM_SSH_TC_TAM, "TAM" }, \ - { SSAM_SSH_TC_ACC, "ACC" }, \ - { SSAM_SSH_TC_UFI, "UFI" }, \ - { SSAM_SSH_TC_USC, "USC" }, \ - { SSAM_SSH_TC_PEN, "PEN" }, \ - { SSAM_SSH_TC_VID, "VID" }, \ - { SSAM_SSH_TC_AUD, "AUD" }, \ - { SSAM_SSH_TC_SMC, "SMC" }, \ - { SSAM_SSH_TC_KPD, "KPD" }, \ - { SSAM_SSH_TC_REG, "REG" } \ + { SSAM_SSH_TC_NOT_APPLICABLE, "N/A" }, \ + { SSAM_SSH_TC_SAM, "SAM" }, \ + { SSAM_SSH_TC_BAT, "BAT" }, \ + { SSAM_SSH_TC_TMP, "TMP" }, \ + { SSAM_SSH_TC_PMC, "PMC" }, \ + { SSAM_SSH_TC_FAN, "FAN" }, \ + { SSAM_SSH_TC_PoM, "PoM" }, \ + { SSAM_SSH_TC_DBG, "DBG" }, \ + { SSAM_SSH_TC_KBD, "KBD" }, \ + { SSAM_SSH_TC_FWU, "FWU" }, \ + { SSAM_SSH_TC_UNI, "UNI" }, \ + { SSAM_SSH_TC_LPC, "LPC" }, \ + { SSAM_SSH_TC_TCL, "TCL" }, \ + { SSAM_SSH_TC_SFL, "SFL" }, \ + { SSAM_SSH_TC_KIP, "KIP" }, \ + { SSAM_SSH_TC_EXT, "EXT" }, \ + { SSAM_SSH_TC_BLD, "BLD" }, \ + { SSAM_SSH_TC_BAS, "BAS" }, \ + { SSAM_SSH_TC_SEN, "SEN" }, \ + { SSAM_SSH_TC_SRQ, "SRQ" }, \ + { SSAM_SSH_TC_MCU, "MCU" }, \ + { SSAM_SSH_TC_HID, "HID" }, \ + { SSAM_SSH_TC_TCH, "TCH" }, \ + { SSAM_SSH_TC_BKL, "BKL" }, \ + { SSAM_SSH_TC_TAM, "TAM" }, \ + { SSAM_SSH_TC_ACC0, "ACC0" }, \ + { SSAM_SSH_TC_UFI, "UFI" }, \ + { SSAM_SSH_TC_USC, "USC" }, \ + { SSAM_SSH_TC_PEN, "PEN" }, \ + { SSAM_SSH_TC_VID, "VID" }, \ + { SSAM_SSH_TC_AUD, "AUD" }, \ + { SSAM_SSH_TC_SMC, "SMC" }, \ + { SSAM_SSH_TC_KPD, "KPD" }, \ + { SSAM_SSH_TC_REG, "REG" }, \ + { SSAM_SSH_TC_SPT, "SPT" }, \ + { SSAM_SSH_TC_SYS, "SYS" }, \ + { SSAM_SSH_TC_ACC1, "ACC1" }, \ + { SSAM_SSH_TC_SHB, "SMB" }, \ + { SSAM_SSH_TC_POS, "POS" } \ ) DECLARE_EVENT_CLASS(ssam_frame_class, diff --git a/drivers/platform/surface/surface_acpi_notify.c b/drivers/platform/surface/surface_acpi_notify.c index 7b758f8cc137..44e317970557 100644 --- a/drivers/platform/surface/surface_acpi_notify.c +++ b/drivers/platform/surface/surface_acpi_notify.c @@ -8,7 +8,7 @@ * notifications sent from ACPI via the SAN interface by providing them to any * registered external driver. * - * Copyright (C) 2019-2020 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include @@ -37,6 +37,7 @@ struct san_data { #define to_san_data(ptr, member) \ container_of(ptr, struct san_data, member) +static struct workqueue_struct *san_wq; /* -- dGPU notifier interface. ---------------------------------------------- */ @@ -356,7 +357,7 @@ static u32 san_evt_bat_nf(struct ssam_event_notifier *nf, memcpy(&work->event, event, sizeof(struct ssam_event) + event->length); - schedule_delayed_work(&work->work, delay); + queue_delayed_work(san_wq, &work->work, delay); return SSAM_NOTIF_HANDLED; } @@ -861,7 +862,7 @@ static int san_remove(struct platform_device *pdev) * We have unregistered our event sources. Now we need to ensure that * all delayed works they may have spawned are run to completion. */ - flush_scheduled_work(); + flush_workqueue(san_wq); return 0; } @@ -881,7 +882,27 @@ static struct platform_driver surface_acpi_notify = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; -module_platform_driver(surface_acpi_notify); + +static int __init san_init(void) +{ + int ret; + + san_wq = alloc_workqueue("san_wq", 0, 0); + if (!san_wq) + return -ENOMEM; + ret = platform_driver_register(&surface_acpi_notify); + if (ret) + destroy_workqueue(san_wq); + return ret; +} +module_init(san_init); + +static void __exit san_exit(void) +{ + platform_driver_unregister(&surface_acpi_notify); + destroy_workqueue(san_wq); +} +module_exit(san_exit); MODULE_AUTHOR("Maximilian Luz "); MODULE_DESCRIPTION("Surface ACPI Notify driver for Surface System Aggregator Module"); diff --git a/drivers/platform/surface/surface_aggregator_cdev.c b/drivers/platform/surface/surface_aggregator_cdev.c index 30fb50fde450..492c82e69182 100644 --- a/drivers/platform/surface/surface_aggregator_cdev.c +++ b/drivers/platform/surface/surface_aggregator_cdev.c @@ -3,7 +3,7 @@ * Provides user-space access to the SSAM EC via the /dev/surface/aggregator * misc device. Intended for debugging and development. * - * Copyright (C) 2020-2021 Maximilian Luz + * Copyright (C) 2020-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/surface_aggregator_hub.c b/drivers/platform/surface/surface_aggregator_hub.c new file mode 100644 index 000000000000..43061514be38 --- /dev/null +++ b/drivers/platform/surface/surface_aggregator_hub.c @@ -0,0 +1,371 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Driver for Surface System Aggregator Module (SSAM) subsystem device hubs. + * + * Provides a driver for SSAM subsystems device hubs. This driver performs + * instantiation of the devices managed by said hubs and takes care of + * (hot-)removal. + * + * Copyright (C) 2020-2022 Maximilian Luz + */ + +#include +#include +#include +#include +#include + +#include + + +/* -- SSAM generic subsystem hub driver framework. -------------------------- */ + +enum ssam_hub_state { + SSAM_HUB_UNINITIALIZED, /* Only set during initialization. */ + SSAM_HUB_CONNECTED, + SSAM_HUB_DISCONNECTED, +}; + +enum ssam_hub_flags { + SSAM_HUB_HOT_REMOVED, +}; + +struct ssam_hub; + +struct ssam_hub_ops { + int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state); +}; + +struct ssam_hub { + struct ssam_device *sdev; + + enum ssam_hub_state state; + unsigned long flags; + + struct delayed_work update_work; + unsigned long connect_delay; + + struct ssam_event_notifier notif; + struct ssam_hub_ops ops; +}; + +struct ssam_hub_desc { + struct { + struct ssam_event_registry reg; + struct ssam_event_id id; + enum ssam_event_mask mask; + } event; + + struct { + u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event); + int (*get_state)(struct ssam_hub *hub, enum ssam_hub_state *state); + } ops; + + unsigned long connect_delay_ms; +}; + +static void ssam_hub_update_workfn(struct work_struct *work) +{ + struct ssam_hub *hub = container_of(work, struct ssam_hub, update_work.work); + enum ssam_hub_state state; + int status = 0; + + status = hub->ops.get_state(hub, &state); + if (status) + return; + + /* + * There is a small possibility that hub devices were hot-removed and + * re-added before we were able to remove them here. In that case, both + * the state returned by get_state() and the state of the hub will + * equal SSAM_HUB_CONNECTED and we would bail early below, which would + * leave child devices without proper (re-)initialization and the + * hot-remove flag set. + * + * Therefore, we check whether devices have been hot-removed via an + * additional flag on the hub and, in this case, override the returned + * hub state. In case of a missed disconnect (i.e. get_state returned + * "connected"), we further need to re-schedule this work (with the + * appropriate delay) as the actual connect work submission might have + * been merged with this one. + * + * This then leads to one of two cases: Either we submit an unnecessary + * work item (which will get ignored via either the queue or the state + * checks) or, in the unlikely case that the work is actually required, + * double the normal connect delay. + */ + if (test_and_clear_bit(SSAM_HUB_HOT_REMOVED, &hub->flags)) { + if (state == SSAM_HUB_CONNECTED) + schedule_delayed_work(&hub->update_work, hub->connect_delay); + + state = SSAM_HUB_DISCONNECTED; + } + + if (hub->state == state) + return; + hub->state = state; + + if (hub->state == SSAM_HUB_CONNECTED) + status = ssam_device_register_clients(hub->sdev); + else + ssam_remove_clients(&hub->sdev->dev); + + if (status) + dev_err(&hub->sdev->dev, "failed to update hub child devices: %d\n", status); +} + +static int ssam_hub_mark_hot_removed(struct device *dev, void *_data) +{ + struct ssam_device *sdev = to_ssam_device(dev); + + if (is_ssam_device(dev)) + ssam_device_mark_hot_removed(sdev); + + return 0; +} + +static void ssam_hub_update(struct ssam_hub *hub, bool connected) +{ + unsigned long delay; + + /* Mark devices as hot-removed before we remove any. */ + if (!connected) { + set_bit(SSAM_HUB_HOT_REMOVED, &hub->flags); + device_for_each_child_reverse(&hub->sdev->dev, NULL, ssam_hub_mark_hot_removed); + } + + /* + * Delay update when the base/keyboard cover is being connected to give + * devices/EC some time to set up. + */ + delay = connected ? hub->connect_delay : 0; + + schedule_delayed_work(&hub->update_work, delay); +} + +static int __maybe_unused ssam_hub_resume(struct device *dev) +{ + struct ssam_hub *hub = dev_get_drvdata(dev); + + schedule_delayed_work(&hub->update_work, 0); + return 0; +} +static SIMPLE_DEV_PM_OPS(ssam_hub_pm_ops, NULL, ssam_hub_resume); + +static int ssam_hub_probe(struct ssam_device *sdev) +{ + const struct ssam_hub_desc *desc; + struct ssam_hub *hub; + int status; + + desc = ssam_device_get_match_data(sdev); + if (!desc) { + WARN(1, "no driver match data specified"); + return -EINVAL; + } + + hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + hub->sdev = sdev; + hub->state = SSAM_HUB_UNINITIALIZED; + + hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ + hub->notif.base.fn = desc->ops.notify; + hub->notif.event.reg = desc->event.reg; + hub->notif.event.id = desc->event.id; + hub->notif.event.mask = desc->event.mask; + hub->notif.event.flags = SSAM_EVENT_SEQUENCED; + + hub->connect_delay = msecs_to_jiffies(desc->connect_delay_ms); + hub->ops.get_state = desc->ops.get_state; + + INIT_DELAYED_WORK(&hub->update_work, ssam_hub_update_workfn); + + ssam_device_set_drvdata(sdev, hub); + + status = ssam_device_notifier_register(sdev, &hub->notif); + if (status) + return status; + + schedule_delayed_work(&hub->update_work, 0); + return 0; +} + +static void ssam_hub_remove(struct ssam_device *sdev) +{ + struct ssam_hub *hub = ssam_device_get_drvdata(sdev); + + ssam_device_notifier_unregister(sdev, &hub->notif); + cancel_delayed_work_sync(&hub->update_work); + ssam_remove_clients(&sdev->dev); +} + + +/* -- SSAM base-subsystem hub driver. --------------------------------------- */ + +/* + * Some devices (especially battery) may need a bit of time to be fully usable + * after being (re-)connected. This delay has been determined via + * experimentation. + */ +#define SSAM_BASE_UPDATE_CONNECT_DELAY 2500 + +SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { + .target_category = SSAM_SSH_TC_BAS, + .target_id = 0x01, + .command_id = 0x0d, + .instance_id = 0x00, +}); + +#define SSAM_BAS_OPMODE_TABLET 0x00 +#define SSAM_EVENT_BAS_CID_CONNECTION 0x0c + +static int ssam_base_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state) +{ + u8 opmode; + int status; + + status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode); + if (status < 0) { + dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status); + return status; + } + + if (opmode != SSAM_BAS_OPMODE_TABLET) + *state = SSAM_HUB_CONNECTED; + else + *state = SSAM_HUB_DISCONNECTED; + + return 0; +} + +static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ + struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); + + if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) + return 0; + + if (event->length < 1) { + dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); + return 0; + } + + ssam_hub_update(hub, event->data[0]); + + /* + * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and + * consumed by the detachment system driver. We're just a (more or less) + * silent observer. + */ + return 0; +} + +static const struct ssam_hub_desc base_hub = { + .event = { + .reg = SSAM_EVENT_REGISTRY_SAM, + .id = { + .target_category = SSAM_SSH_TC_BAS, + .instance = 0, + }, + .mask = SSAM_EVENT_MASK_NONE, + }, + .ops = { + .notify = ssam_base_hub_notif, + .get_state = ssam_base_hub_query_state, + }, + .connect_delay_ms = SSAM_BASE_UPDATE_CONNECT_DELAY, +}; + + +/* -- SSAM KIP-subsystem hub driver. ---------------------------------------- */ + +/* + * Some devices may need a bit of time to be fully usable after being + * (re-)connected. This delay has been determined via experimentation. + */ +#define SSAM_KIP_UPDATE_CONNECT_DELAY 250 + +#define SSAM_EVENT_KIP_CID_CONNECTION 0x2c + +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_query_state, u8, { + .target_category = SSAM_SSH_TC_KIP, + .target_id = 0x01, + .command_id = 0x2c, + .instance_id = 0x00, +}); + +static int ssam_kip_hub_query_state(struct ssam_hub *hub, enum ssam_hub_state *state) +{ + int status; + u8 connected; + + status = ssam_retry(__ssam_kip_query_state, hub->sdev->ctrl, &connected); + if (status < 0) { + dev_err(&hub->sdev->dev, "failed to query KIP connection state: %d\n", status); + return status; + } + + *state = connected ? SSAM_HUB_CONNECTED : SSAM_HUB_DISCONNECTED; + return 0; +} + +static u32 ssam_kip_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ + struct ssam_hub *hub = container_of(nf, struct ssam_hub, notif); + + if (event->command_id != SSAM_EVENT_KIP_CID_CONNECTION) + return 0; /* Return "unhandled". */ + + if (event->length < 1) { + dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); + return 0; + } + + ssam_hub_update(hub, event->data[0]); + return SSAM_NOTIF_HANDLED; +} + +static const struct ssam_hub_desc kip_hub = { + .event = { + .reg = SSAM_EVENT_REGISTRY_SAM, + .id = { + .target_category = SSAM_SSH_TC_KIP, + .instance = 0, + }, + .mask = SSAM_EVENT_MASK_TARGET, + }, + .ops = { + .notify = ssam_kip_hub_notif, + .get_state = ssam_kip_hub_query_state, + }, + .connect_delay_ms = SSAM_KIP_UPDATE_CONNECT_DELAY, +}; + + +/* -- Driver registration. -------------------------------------------------- */ + +static const struct ssam_device_id ssam_hub_match[] = { + { SSAM_VDEV(HUB, 0x01, SSAM_SSH_TC_KIP, 0x00), (unsigned long)&kip_hub }, + { SSAM_VDEV(HUB, 0x02, SSAM_SSH_TC_BAS, 0x00), (unsigned long)&base_hub }, + { } +}; +MODULE_DEVICE_TABLE(ssam, ssam_hub_match); + +static struct ssam_device_driver ssam_subsystem_hub_driver = { + .probe = ssam_hub_probe, + .remove = ssam_hub_remove, + .match_table = ssam_hub_match, + .driver = { + .name = "surface_aggregator_subsystem_hub", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = &ssam_hub_pm_ops, + }, +}; +module_ssam_device_driver(ssam_subsystem_hub_driver); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Subsystem device hub driver for Surface System Aggregator Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/surface/surface_aggregator_registry.c b/drivers/platform/surface/surface_aggregator_registry.c index ce2bd88feeaa..d5655f6a4a41 100644 --- a/drivers/platform/surface/surface_aggregator_registry.c +++ b/drivers/platform/surface/surface_aggregator_registry.c @@ -6,19 +6,16 @@ * cannot be auto-detected. Provides device-hubs and performs instantiation * for these devices. * - * Copyright (C) 2020-2021 Maximilian Luz + * Copyright (C) 2020-2022 Maximilian Luz */ #include #include -#include #include #include #include #include -#include -#include #include @@ -41,9 +38,15 @@ static const struct software_node ssam_node_root = { .name = "ssam_platform_hub", }; +/* KIP device hub (connects keyboard cover devices on Surface Pro 8). */ +static const struct software_node ssam_node_hub_kip = { + .name = "ssam:00:00:01:0e:00", + .parent = &ssam_node_root, +}; + /* Base device hub (devices attached to Surface Book 3 base). */ static const struct software_node ssam_node_hub_base = { - .name = "ssam:00:00:02:00:00", + .name = "ssam:00:00:02:11:00", .parent = &ssam_node_root, }; @@ -71,6 +74,12 @@ static const struct software_node ssam_node_tmp_pprof = { .parent = &ssam_node_root, }; +/* Tablet-mode switch via KIP subsystem. */ +static const struct software_node ssam_node_kip_tablet_switch = { + .name = "ssam:01:0e:01:00:01", + .parent = &ssam_node_root, +}; + /* DTX / detachment-system device (Surface Book 3). */ static const struct software_node ssam_node_bas_dtx = { .name = "ssam:01:11:01:00:00", @@ -155,6 +164,36 @@ static const struct software_node ssam_node_hid_base_iid6 = { .parent = &ssam_node_hub_base, }; +/* HID keyboard (KIP hub). */ +static const struct software_node ssam_node_hid_kip_keyboard = { + .name = "ssam:01:15:02:01:00", + .parent = &ssam_node_hub_kip, +}; + +/* HID pen stash (KIP hub; pen taken / stashed away evens). */ +static const struct software_node ssam_node_hid_kip_penstash = { + .name = "ssam:01:15:02:02:00", + .parent = &ssam_node_hub_kip, +}; + +/* HID touchpad (KIP hub). */ +static const struct software_node ssam_node_hid_kip_touchpad = { + .name = "ssam:01:15:02:03:00", + .parent = &ssam_node_hub_kip, +}; + +/* HID device instance 5 (KIP hub, unknown HID device). */ +static const struct software_node ssam_node_hid_kip_iid5 = { + .name = "ssam:01:15:02:05:00", + .parent = &ssam_node_hub_kip, +}; + +/* Tablet-mode switch via POS subsystem. */ +static const struct software_node ssam_node_pos_tablet_switch = { + .name = "ssam:01:26:01:00:01", + .parent = &ssam_node_root, +}; + /* * Devices for 5th- and 6th-generations models: * - Surface Book 2, @@ -201,6 +240,7 @@ static const struct software_node *ssam_node_group_sls[] = { &ssam_node_bat_ac, &ssam_node_bat_main, &ssam_node_tmp_pprof, + &ssam_node_pos_tablet_switch, &ssam_node_hid_tid1_keyboard, &ssam_node_hid_tid1_penstash, &ssam_node_hid_tid1_touchpad, @@ -230,290 +270,19 @@ static const struct software_node *ssam_node_group_sp7[] = { static const struct software_node *ssam_node_group_sp8[] = { &ssam_node_root, + &ssam_node_hub_kip, &ssam_node_bat_ac, &ssam_node_bat_main, &ssam_node_tmp_pprof, - /* TODO: Add support for keyboard cover. */ + &ssam_node_kip_tablet_switch, + &ssam_node_hid_kip_keyboard, + &ssam_node_hid_kip_penstash, + &ssam_node_hid_kip_touchpad, + &ssam_node_hid_kip_iid5, NULL, }; -/* -- Device registry helper functions. ------------------------------------- */ - -static int ssam_uid_from_string(const char *str, struct ssam_device_uid *uid) -{ - u8 d, tc, tid, iid, fn; - int n; - - n = sscanf(str, "ssam:%hhx:%hhx:%hhx:%hhx:%hhx", &d, &tc, &tid, &iid, &fn); - if (n != 5) - return -EINVAL; - - uid->domain = d; - uid->category = tc; - uid->target = tid; - uid->instance = iid; - uid->function = fn; - - return 0; -} - -static int ssam_hub_add_device(struct device *parent, struct ssam_controller *ctrl, - struct fwnode_handle *node) -{ - struct ssam_device_uid uid; - struct ssam_device *sdev; - int status; - - status = ssam_uid_from_string(fwnode_get_name(node), &uid); - if (status) - return status; - - sdev = ssam_device_alloc(ctrl, uid); - if (!sdev) - return -ENOMEM; - - sdev->dev.parent = parent; - sdev->dev.fwnode = node; - - status = ssam_device_add(sdev); - if (status) - ssam_device_put(sdev); - - return status; -} - -static int ssam_hub_register_clients(struct device *parent, struct ssam_controller *ctrl, - struct fwnode_handle *node) -{ - struct fwnode_handle *child; - int status; - - fwnode_for_each_child_node(node, child) { - /* - * Try to add the device specified in the firmware node. If - * this fails with -EINVAL, the node does not specify any SSAM - * device, so ignore it and continue with the next one. - */ - - status = ssam_hub_add_device(parent, ctrl, child); - if (status && status != -EINVAL) - goto err; - } - - return 0; -err: - ssam_remove_clients(parent); - return status; -} - - -/* -- SSAM base-hub driver. ------------------------------------------------- */ - -/* - * Some devices (especially battery) may need a bit of time to be fully usable - * after being (re-)connected. This delay has been determined via - * experimentation. - */ -#define SSAM_BASE_UPDATE_CONNECT_DELAY msecs_to_jiffies(2500) - -enum ssam_base_hub_state { - SSAM_BASE_HUB_UNINITIALIZED, - SSAM_BASE_HUB_CONNECTED, - SSAM_BASE_HUB_DISCONNECTED, -}; - -struct ssam_base_hub { - struct ssam_device *sdev; - - enum ssam_base_hub_state state; - struct delayed_work update_work; - - struct ssam_event_notifier notif; -}; - -SSAM_DEFINE_SYNC_REQUEST_R(ssam_bas_query_opmode, u8, { - .target_category = SSAM_SSH_TC_BAS, - .target_id = 0x01, - .command_id = 0x0d, - .instance_id = 0x00, -}); - -#define SSAM_BAS_OPMODE_TABLET 0x00 -#define SSAM_EVENT_BAS_CID_CONNECTION 0x0c - -static int ssam_base_hub_query_state(struct ssam_base_hub *hub, enum ssam_base_hub_state *state) -{ - u8 opmode; - int status; - - status = ssam_retry(ssam_bas_query_opmode, hub->sdev->ctrl, &opmode); - if (status < 0) { - dev_err(&hub->sdev->dev, "failed to query base state: %d\n", status); - return status; - } - - if (opmode != SSAM_BAS_OPMODE_TABLET) - *state = SSAM_BASE_HUB_CONNECTED; - else - *state = SSAM_BASE_HUB_DISCONNECTED; - - return 0; -} - -static ssize_t ssam_base_hub_state_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct ssam_base_hub *hub = dev_get_drvdata(dev); - bool connected = hub->state == SSAM_BASE_HUB_CONNECTED; - - return sysfs_emit(buf, "%d\n", connected); -} - -static struct device_attribute ssam_base_hub_attr_state = - __ATTR(state, 0444, ssam_base_hub_state_show, NULL); - -static struct attribute *ssam_base_hub_attrs[] = { - &ssam_base_hub_attr_state.attr, - NULL, -}; - -static const struct attribute_group ssam_base_hub_group = { - .attrs = ssam_base_hub_attrs, -}; - -static void ssam_base_hub_update_workfn(struct work_struct *work) -{ - struct ssam_base_hub *hub = container_of(work, struct ssam_base_hub, update_work.work); - struct fwnode_handle *node = dev_fwnode(&hub->sdev->dev); - enum ssam_base_hub_state state; - int status = 0; - - status = ssam_base_hub_query_state(hub, &state); - if (status) - return; - - if (hub->state == state) - return; - hub->state = state; - - if (hub->state == SSAM_BASE_HUB_CONNECTED) - status = ssam_hub_register_clients(&hub->sdev->dev, hub->sdev->ctrl, node); - else - ssam_remove_clients(&hub->sdev->dev); - - if (status) - dev_err(&hub->sdev->dev, "failed to update base-hub devices: %d\n", status); -} - -static u32 ssam_base_hub_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) -{ - struct ssam_base_hub *hub = container_of(nf, struct ssam_base_hub, notif); - unsigned long delay; - - if (event->command_id != SSAM_EVENT_BAS_CID_CONNECTION) - return 0; - - if (event->length < 1) { - dev_err(&hub->sdev->dev, "unexpected payload size: %u\n", event->length); - return 0; - } - - /* - * Delay update when the base is being connected to give devices/EC - * some time to set up. - */ - delay = event->data[0] ? SSAM_BASE_UPDATE_CONNECT_DELAY : 0; - - schedule_delayed_work(&hub->update_work, delay); - - /* - * Do not return SSAM_NOTIF_HANDLED: The event should be picked up and - * consumed by the detachment system driver. We're just a (more or less) - * silent observer. - */ - return 0; -} - -static int __maybe_unused ssam_base_hub_resume(struct device *dev) -{ - struct ssam_base_hub *hub = dev_get_drvdata(dev); - - schedule_delayed_work(&hub->update_work, 0); - return 0; -} -static SIMPLE_DEV_PM_OPS(ssam_base_hub_pm_ops, NULL, ssam_base_hub_resume); - -static int ssam_base_hub_probe(struct ssam_device *sdev) -{ - struct ssam_base_hub *hub; - int status; - - hub = devm_kzalloc(&sdev->dev, sizeof(*hub), GFP_KERNEL); - if (!hub) - return -ENOMEM; - - hub->sdev = sdev; - hub->state = SSAM_BASE_HUB_UNINITIALIZED; - - hub->notif.base.priority = INT_MAX; /* This notifier should run first. */ - hub->notif.base.fn = ssam_base_hub_notif; - hub->notif.event.reg = SSAM_EVENT_REGISTRY_SAM; - hub->notif.event.id.target_category = SSAM_SSH_TC_BAS, - hub->notif.event.id.instance = 0, - hub->notif.event.mask = SSAM_EVENT_MASK_NONE; - hub->notif.event.flags = SSAM_EVENT_SEQUENCED; - - INIT_DELAYED_WORK(&hub->update_work, ssam_base_hub_update_workfn); - - ssam_device_set_drvdata(sdev, hub); - - status = ssam_notifier_register(sdev->ctrl, &hub->notif); - if (status) - return status; - - status = sysfs_create_group(&sdev->dev.kobj, &ssam_base_hub_group); - if (status) - goto err; - - schedule_delayed_work(&hub->update_work, 0); - return 0; - -err: - ssam_notifier_unregister(sdev->ctrl, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); - ssam_remove_clients(&sdev->dev); - return status; -} - -static void ssam_base_hub_remove(struct ssam_device *sdev) -{ - struct ssam_base_hub *hub = ssam_device_get_drvdata(sdev); - - sysfs_remove_group(&sdev->dev.kobj, &ssam_base_hub_group); - - ssam_notifier_unregister(sdev->ctrl, &hub->notif); - cancel_delayed_work_sync(&hub->update_work); - ssam_remove_clients(&sdev->dev); -} - -static const struct ssam_device_id ssam_base_hub_match[] = { - { SSAM_VDEV(HUB, 0x02, SSAM_ANY_IID, 0x00) }, - { }, -}; - -static struct ssam_device_driver ssam_base_hub_driver = { - .probe = ssam_base_hub_probe, - .remove = ssam_base_hub_remove, - .match_table = ssam_base_hub_match, - .driver = { - .name = "surface_aggregator_base_hub", - .probe_type = PROBE_PREFER_ASYNCHRONOUS, - .pm = &ssam_base_hub_pm_ops, - }, -}; - - /* -- SSAM platform/meta-hub driver. ---------------------------------------- */ static const struct acpi_device_id ssam_platform_hub_match[] = { @@ -597,7 +366,7 @@ static int ssam_platform_hub_probe(struct platform_device *pdev) set_secondary_fwnode(&pdev->dev, root); - status = ssam_hub_register_clients(&pdev->dev, ctrl, root); + status = __ssam_register_clients(&pdev->dev, ctrl, root); if (status) { set_secondary_fwnode(&pdev->dev, NULL); software_node_unregister_node_group(nodes); @@ -626,32 +395,7 @@ static struct platform_driver ssam_platform_hub_driver = { .probe_type = PROBE_PREFER_ASYNCHRONOUS, }, }; - - -/* -- Module initialization. ------------------------------------------------ */ - -static int __init ssam_device_hub_init(void) -{ - int status; - - status = platform_driver_register(&ssam_platform_hub_driver); - if (status) - return status; - - status = ssam_device_driver_register(&ssam_base_hub_driver); - if (status) - platform_driver_unregister(&ssam_platform_hub_driver); - - return status; -} -module_init(ssam_device_hub_init); - -static void __exit ssam_device_hub_exit(void) -{ - ssam_device_driver_unregister(&ssam_base_hub_driver); - platform_driver_unregister(&ssam_platform_hub_driver); -} -module_exit(ssam_device_hub_exit); +module_platform_driver(ssam_platform_hub_driver); MODULE_AUTHOR("Maximilian Luz "); MODULE_DESCRIPTION("Device-registry for Surface System Aggregator Module"); diff --git a/drivers/platform/surface/surface_aggregator_tabletsw.c b/drivers/platform/surface/surface_aggregator_tabletsw.c new file mode 100644 index 000000000000..27d95a6a7851 --- /dev/null +++ b/drivers/platform/surface/surface_aggregator_tabletsw.c @@ -0,0 +1,533 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Surface System Aggregator Module (SSAM) tablet mode switch driver. + * + * Copyright (C) 2022 Maximilian Luz + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + + +/* -- SSAM generic tablet switch driver framework. -------------------------- */ + +struct ssam_tablet_sw; + +struct ssam_tablet_sw_ops { + int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); + const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); + bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); +}; + +struct ssam_tablet_sw { + struct ssam_device *sdev; + + u32 state; + struct work_struct update_work; + struct input_dev *mode_switch; + + struct ssam_tablet_sw_ops ops; + struct ssam_event_notifier notif; +}; + +struct ssam_tablet_sw_desc { + struct { + const char *name; + const char *phys; + } dev; + + struct { + u32 (*notify)(struct ssam_event_notifier *nf, const struct ssam_event *event); + int (*get_state)(struct ssam_tablet_sw *sw, u32 *state); + const char *(*state_name)(struct ssam_tablet_sw *sw, u32 state); + bool (*state_is_tablet_mode)(struct ssam_tablet_sw *sw, u32 state); + } ops; + + struct { + struct ssam_event_registry reg; + struct ssam_event_id id; + enum ssam_event_mask mask; + u8 flags; + } event; +}; + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct ssam_tablet_sw *sw = dev_get_drvdata(dev); + const char *state = sw->ops.state_name(sw, sw->state); + + return sysfs_emit(buf, "%s\n", state); +} +static DEVICE_ATTR_RO(state); + +static struct attribute *ssam_tablet_sw_attrs[] = { + &dev_attr_state.attr, + NULL, +}; + +static const struct attribute_group ssam_tablet_sw_group = { + .attrs = ssam_tablet_sw_attrs, +}; + +static void ssam_tablet_sw_update_workfn(struct work_struct *work) +{ + struct ssam_tablet_sw *sw = container_of(work, struct ssam_tablet_sw, update_work); + int tablet, status; + u32 state; + + status = sw->ops.get_state(sw, &state); + if (status) + return; + + if (sw->state == state) + return; + sw->state = state; + + /* Send SW_TABLET_MODE event. */ + tablet = sw->ops.state_is_tablet_mode(sw, state); + input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); + input_sync(sw->mode_switch); +} + +static int __maybe_unused ssam_tablet_sw_resume(struct device *dev) +{ + struct ssam_tablet_sw *sw = dev_get_drvdata(dev); + + schedule_work(&sw->update_work); + return 0; +} +static SIMPLE_DEV_PM_OPS(ssam_tablet_sw_pm_ops, NULL, ssam_tablet_sw_resume); + +static int ssam_tablet_sw_probe(struct ssam_device *sdev) +{ + const struct ssam_tablet_sw_desc *desc; + struct ssam_tablet_sw *sw; + int tablet, status; + + desc = ssam_device_get_match_data(sdev); + if (!desc) { + WARN(1, "no driver match data specified"); + return -EINVAL; + } + + sw = devm_kzalloc(&sdev->dev, sizeof(*sw), GFP_KERNEL); + if (!sw) + return -ENOMEM; + + sw->sdev = sdev; + + sw->ops.get_state = desc->ops.get_state; + sw->ops.state_name = desc->ops.state_name; + sw->ops.state_is_tablet_mode = desc->ops.state_is_tablet_mode; + + INIT_WORK(&sw->update_work, ssam_tablet_sw_update_workfn); + + ssam_device_set_drvdata(sdev, sw); + + /* Get initial state. */ + status = sw->ops.get_state(sw, &sw->state); + if (status) + return status; + + /* Set up tablet mode switch. */ + sw->mode_switch = devm_input_allocate_device(&sdev->dev); + if (!sw->mode_switch) + return -ENOMEM; + + sw->mode_switch->name = desc->dev.name; + sw->mode_switch->phys = desc->dev.phys; + sw->mode_switch->id.bustype = BUS_HOST; + sw->mode_switch->dev.parent = &sdev->dev; + + tablet = sw->ops.state_is_tablet_mode(sw, sw->state); + input_set_capability(sw->mode_switch, EV_SW, SW_TABLET_MODE); + input_report_switch(sw->mode_switch, SW_TABLET_MODE, tablet); + + status = input_register_device(sw->mode_switch); + if (status) + return status; + + /* Set up notifier. */ + sw->notif.base.priority = 0; + sw->notif.base.fn = desc->ops.notify; + sw->notif.event.reg = desc->event.reg; + sw->notif.event.id = desc->event.id; + sw->notif.event.mask = desc->event.mask; + sw->notif.event.flags = SSAM_EVENT_SEQUENCED; + + status = ssam_device_notifier_register(sdev, &sw->notif); + if (status) + return status; + + status = sysfs_create_group(&sdev->dev.kobj, &ssam_tablet_sw_group); + if (status) + goto err; + + /* We might have missed events during setup, so check again. */ + schedule_work(&sw->update_work); + return 0; + +err: + ssam_device_notifier_unregister(sdev, &sw->notif); + cancel_work_sync(&sw->update_work); + return status; +} + +static void ssam_tablet_sw_remove(struct ssam_device *sdev) +{ + struct ssam_tablet_sw *sw = ssam_device_get_drvdata(sdev); + + sysfs_remove_group(&sdev->dev.kobj, &ssam_tablet_sw_group); + + ssam_device_notifier_unregister(sdev, &sw->notif); + cancel_work_sync(&sw->update_work); +} + + +/* -- SSAM KIP tablet switch implementation. -------------------------------- */ + +#define SSAM_EVENT_KIP_CID_COVER_STATE_CHANGED 0x1d + +enum ssam_kip_cover_state { + SSAM_KIP_COVER_STATE_DISCONNECTED = 0x01, + SSAM_KIP_COVER_STATE_CLOSED = 0x02, + SSAM_KIP_COVER_STATE_LAPTOP = 0x03, + SSAM_KIP_COVER_STATE_FOLDED_CANVAS = 0x04, + SSAM_KIP_COVER_STATE_FOLDED_BACK = 0x05, +}; + +static const char *ssam_kip_cover_state_name(struct ssam_tablet_sw *sw, u32 state) +{ + switch (state) { + case SSAM_KIP_COVER_STATE_DISCONNECTED: + return "disconnected"; + + case SSAM_KIP_COVER_STATE_CLOSED: + return "closed"; + + case SSAM_KIP_COVER_STATE_LAPTOP: + return "laptop"; + + case SSAM_KIP_COVER_STATE_FOLDED_CANVAS: + return "folded-canvas"; + + case SSAM_KIP_COVER_STATE_FOLDED_BACK: + return "folded-back"; + + default: + dev_warn(&sw->sdev->dev, "unknown KIP cover state: %u\n", state); + return ""; + } +} + +static bool ssam_kip_cover_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) +{ + switch (state) { + case SSAM_KIP_COVER_STATE_DISCONNECTED: + case SSAM_KIP_COVER_STATE_FOLDED_CANVAS: + case SSAM_KIP_COVER_STATE_FOLDED_BACK: + return true; + + case SSAM_KIP_COVER_STATE_CLOSED: + case SSAM_KIP_COVER_STATE_LAPTOP: + return false; + + default: + dev_warn(&sw->sdev->dev, "unknown KIP cover state: %d\n", sw->state); + return true; + } +} + +SSAM_DEFINE_SYNC_REQUEST_R(__ssam_kip_get_cover_state, u8, { + .target_category = SSAM_SSH_TC_KIP, + .target_id = 0x01, + .command_id = 0x1d, + .instance_id = 0x00, +}); + +static int ssam_kip_get_cover_state(struct ssam_tablet_sw *sw, u32 *state) +{ + int status; + u8 raw; + + status = ssam_retry(__ssam_kip_get_cover_state, sw->sdev->ctrl, &raw); + if (status < 0) { + dev_err(&sw->sdev->dev, "failed to query KIP lid state: %d\n", status); + return status; + } + + *state = raw; + return 0; +} + +static u32 ssam_kip_sw_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ + struct ssam_tablet_sw *sw = container_of(nf, struct ssam_tablet_sw, notif); + + if (event->command_id != SSAM_EVENT_KIP_CID_COVER_STATE_CHANGED) + return 0; /* Return "unhandled". */ + + if (event->length < 1) + dev_warn(&sw->sdev->dev, "unexpected payload size: %u\n", event->length); + + schedule_work(&sw->update_work); + return SSAM_NOTIF_HANDLED; +} + +static const struct ssam_tablet_sw_desc ssam_kip_sw_desc = { + .dev = { + .name = "Microsoft Surface KIP Tablet Mode Switch", + .phys = "ssam/01:0e:01:00:01/input0", + }, + .ops = { + .notify = ssam_kip_sw_notif, + .get_state = ssam_kip_get_cover_state, + .state_name = ssam_kip_cover_state_name, + .state_is_tablet_mode = ssam_kip_cover_state_is_tablet_mode, + }, + .event = { + .reg = SSAM_EVENT_REGISTRY_SAM, + .id = { + .target_category = SSAM_SSH_TC_KIP, + .instance = 0, + }, + .mask = SSAM_EVENT_MASK_TARGET, + }, +}; + + +/* -- SSAM POS tablet switch implementation. -------------------------------- */ + +static bool tablet_mode_in_slate_state = true; +module_param(tablet_mode_in_slate_state, bool, 0644); +MODULE_PARM_DESC(tablet_mode_in_slate_state, "Enable tablet mode in slate device posture, default is 'true'"); + +#define SSAM_EVENT_POS_CID_POSTURE_CHANGED 0x03 +#define SSAM_POS_MAX_SOURCES 4 + +enum ssam_pos_state { + SSAM_POS_POSTURE_LID_CLOSED = 0x00, + SSAM_POS_POSTURE_LAPTOP = 0x01, + SSAM_POS_POSTURE_SLATE = 0x02, + SSAM_POS_POSTURE_TABLET = 0x03, +}; + +struct ssam_sources_list { + __le32 count; + __le32 id[SSAM_POS_MAX_SOURCES]; +} __packed; + +static const char *ssam_pos_state_name(struct ssam_tablet_sw *sw, u32 state) +{ + switch (state) { + case SSAM_POS_POSTURE_LID_CLOSED: + return "closed"; + + case SSAM_POS_POSTURE_LAPTOP: + return "laptop"; + + case SSAM_POS_POSTURE_SLATE: + return "slate"; + + case SSAM_POS_POSTURE_TABLET: + return "tablet"; + + default: + dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); + return ""; + } +} + +static bool ssam_pos_state_is_tablet_mode(struct ssam_tablet_sw *sw, u32 state) +{ + switch (state) { + case SSAM_POS_POSTURE_LAPTOP: + case SSAM_POS_POSTURE_LID_CLOSED: + return false; + + case SSAM_POS_POSTURE_SLATE: + return tablet_mode_in_slate_state; + + case SSAM_POS_POSTURE_TABLET: + return true; + + default: + dev_warn(&sw->sdev->dev, "unknown device posture: %u\n", state); + return true; + } +} + +static int ssam_pos_get_sources_list(struct ssam_tablet_sw *sw, struct ssam_sources_list *sources) +{ + struct ssam_request rqst; + struct ssam_response rsp; + int status; + + rqst.target_category = SSAM_SSH_TC_POS; + rqst.target_id = 0x01; + rqst.command_id = 0x01; + rqst.instance_id = 0x00; + rqst.flags = SSAM_REQUEST_HAS_RESPONSE; + rqst.length = 0; + rqst.payload = NULL; + + rsp.capacity = sizeof(*sources); + rsp.length = 0; + rsp.pointer = (u8 *)sources; + + status = ssam_retry(ssam_request_sync_onstack, sw->sdev->ctrl, &rqst, &rsp, 0); + if (status) + return status; + + /* We need at least the 'sources->count' field. */ + if (rsp.length < sizeof(__le32)) { + dev_err(&sw->sdev->dev, "received source list response is too small\n"); + return -EPROTO; + } + + /* Make sure 'sources->count' matches with the response length. */ + if (get_unaligned_le32(&sources->count) * sizeof(__le32) + sizeof(__le32) != rsp.length) { + dev_err(&sw->sdev->dev, "mismatch between number of sources and response size\n"); + return -EPROTO; + } + + return 0; +} + +static int ssam_pos_get_source(struct ssam_tablet_sw *sw, u32 *source_id) +{ + struct ssam_sources_list sources = {}; + int status; + + status = ssam_pos_get_sources_list(sw, &sources); + if (status) + return status; + + if (get_unaligned_le32(&sources.count) == 0) { + dev_err(&sw->sdev->dev, "no posture sources found\n"); + return -ENODEV; + } + + /* + * We currently don't know what to do with more than one posture + * source. At the moment, only one source seems to be used/provided. + * The WARN_ON() here should hopefully let us know quickly once there + * is a device that provides multiple sources, at which point we can + * then try to figure out how to handle them. + */ + WARN_ON(get_unaligned_le32(&sources.count) > 1); + + *source_id = get_unaligned_le32(&sources.id[0]); + return 0; +} + +SSAM_DEFINE_SYNC_REQUEST_WR(__ssam_pos_get_posture_for_source, __le32, __le32, { + .target_category = SSAM_SSH_TC_POS, + .target_id = 0x01, + .command_id = 0x02, + .instance_id = 0x00, +}); + +static int ssam_pos_get_posture_for_source(struct ssam_tablet_sw *sw, u32 source_id, u32 *posture) +{ + __le32 source_le = cpu_to_le32(source_id); + __le32 rspval_le = 0; + int status; + + status = ssam_retry(__ssam_pos_get_posture_for_source, sw->sdev->ctrl, + &source_le, &rspval_le); + if (status) + return status; + + *posture = le32_to_cpu(rspval_le); + return 0; +} + +static int ssam_pos_get_posture(struct ssam_tablet_sw *sw, u32 *state) +{ + u32 source_id; + int status; + + status = ssam_pos_get_source(sw, &source_id); + if (status) { + dev_err(&sw->sdev->dev, "failed to get posture source ID: %d\n", status); + return status; + } + + status = ssam_pos_get_posture_for_source(sw, source_id, state); + if (status) { + dev_err(&sw->sdev->dev, "failed to get posture value for source %u: %d\n", + source_id, status); + return status; + } + + return 0; +} + +static u32 ssam_pos_sw_notif(struct ssam_event_notifier *nf, const struct ssam_event *event) +{ + struct ssam_tablet_sw *sw = container_of(nf, struct ssam_tablet_sw, notif); + + if (event->command_id != SSAM_EVENT_POS_CID_POSTURE_CHANGED) + return 0; /* Return "unhandled". */ + + if (event->length != sizeof(__le32) * 3) + dev_warn(&sw->sdev->dev, "unexpected payload size: %u\n", event->length); + + schedule_work(&sw->update_work); + return SSAM_NOTIF_HANDLED; +} + +static const struct ssam_tablet_sw_desc ssam_pos_sw_desc = { + .dev = { + .name = "Microsoft Surface POS Tablet Mode Switch", + .phys = "ssam/01:26:01:00:01/input0", + }, + .ops = { + .notify = ssam_pos_sw_notif, + .get_state = ssam_pos_get_posture, + .state_name = ssam_pos_state_name, + .state_is_tablet_mode = ssam_pos_state_is_tablet_mode, + }, + .event = { + .reg = SSAM_EVENT_REGISTRY_SAM, + .id = { + .target_category = SSAM_SSH_TC_POS, + .instance = 0, + }, + .mask = SSAM_EVENT_MASK_TARGET, + }, +}; + + +/* -- Driver registration. -------------------------------------------------- */ + +static const struct ssam_device_id ssam_tablet_sw_match[] = { + { SSAM_SDEV(KIP, 0x01, 0x00, 0x01), (unsigned long)&ssam_kip_sw_desc }, + { SSAM_SDEV(POS, 0x01, 0x00, 0x01), (unsigned long)&ssam_pos_sw_desc }, + { }, +}; +MODULE_DEVICE_TABLE(ssam, ssam_tablet_sw_match); + +static struct ssam_device_driver ssam_tablet_sw_driver = { + .probe = ssam_tablet_sw_probe, + .remove = ssam_tablet_sw_remove, + .match_table = ssam_tablet_sw_match, + .driver = { + .name = "surface_aggregator_tablet_mode_switch", + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + .pm = &ssam_tablet_sw_pm_ops, + }, +}; +module_ssam_device_driver(ssam_tablet_sw_driver); + +MODULE_AUTHOR("Maximilian Luz "); +MODULE_DESCRIPTION("Tablet mode switch driver for Surface devices using the Surface Aggregator Module"); +MODULE_LICENSE("GPL"); diff --git a/drivers/platform/surface/surface_dtx.c b/drivers/platform/surface/surface_dtx.c index 1203b9a82993..ed36944467f9 100644 --- a/drivers/platform/surface/surface_dtx.c +++ b/drivers/platform/surface/surface_dtx.c @@ -8,7 +8,7 @@ * acknowledge (to speed things up), abort (e.g. in case the dGPU is still in * use), or request detachment via user-space. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/surface_gpe.c b/drivers/platform/surface/surface_gpe.c index ec66fde28e75..c219b840d491 100644 --- a/drivers/platform/surface/surface_gpe.c +++ b/drivers/platform/surface/surface_gpe.c @@ -4,7 +4,7 @@ * properly configuring the respective GPEs. Required for wakeup via lid on * newer Intel-based Microsoft Surface devices. * - * Copyright (C) 2020 Maximilian Luz + * Copyright (C) 2020-2022 Maximilian Luz */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt @@ -171,6 +171,18 @@ static const struct dmi_system_id dmi_lid_device_table[] = { }, .driver_data = (void *)lid_device_props_l4D, }, + { + .ident = "Surface Laptop 4 (Intel 13\")", + .matches = { + /* + * We match for SKU here due to different variants: The + * AMD (15") version does not rely on GPEs. + */ + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "Surface_Laptop_4_1950:1951"), + }, + .driver_data = (void *)lid_device_props_l4B, + }, { .ident = "Surface Laptop Studio", .matches = { diff --git a/drivers/platform/surface/surface_hotplug.c b/drivers/platform/surface/surface_hotplug.c index cfcc15cfbacb..f004a2495201 100644 --- a/drivers/platform/surface/surface_hotplug.c +++ b/drivers/platform/surface/surface_hotplug.c @@ -10,7 +10,7 @@ * Event signaling is handled via ACPI, which will generate the appropriate * device-check notifications to be picked up by the PCIe hot-plug driver. * - * Copyright (C) 2019-2021 Maximilian Luz + * Copyright (C) 2019-2022 Maximilian Luz */ #include diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c index 6373d3b5eb7f..fbf2e11fd6ce 100644 --- a/drivers/platform/surface/surface_platform_profile.c +++ b/drivers/platform/surface/surface_platform_profile.c @@ -3,7 +3,7 @@ * Surface Platform Profile / Performance Mode driver for Surface System * Aggregator Module (thermal subsystem). * - * Copyright (C) 2021 Maximilian Luz + * Copyright (C) 2021-2022 Maximilian Luz */ #include diff --git a/drivers/platform/x86/Kconfig b/drivers/platform/x86/Kconfig index bc4013e950ed..f2f98e942cf2 100644 --- a/drivers/platform/x86/Kconfig +++ b/drivers/platform/x86/Kconfig @@ -177,17 +177,15 @@ config ACER_WIRELESS config ACER_WMI tristate "Acer WMI Laptop Extras" - depends on ACPI - select LEDS_CLASS - select NEW_LEDS depends on BACKLIGHT_CLASS_DEVICE depends on SERIO_I8042 depends on INPUT depends on RFKILL || RFKILL = n depends on ACPI_WMI + select ACPI_VIDEO select INPUT_SPARSEKMAP - # Acer WMI depends on ACPI_VIDEO when ACPI is enabled - select ACPI_VIDEO if ACPI + select LEDS_CLASS + select NEW_LEDS help This is a driver for newer Acer (and Wistron) laptops. It adds wireless radio and bluetooth control, and on some laptops, @@ -196,32 +194,7 @@ config ACER_WMI If you have an ACPI-WMI compatible Acer/ Wistron laptop, say Y or M here. -config AMD_PMC - tristate "AMD SoC PMC driver" - depends on ACPI && PCI && RTC_CLASS - help - The driver provides support for AMD Power Management Controller - primarily responsible for S2Idle transactions that are driven from - a platform firmware running on SMU. This driver also provides a debug - mechanism to investigate the S2Idle transactions and failures. - - Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. - - If you choose to compile this driver as a module the module will be - called amd-pmc. - -config AMD_HSMP - tristate "AMD HSMP Driver" - depends on AMD_NB && X86_64 - help - The driver provides a way for user space tools to monitor and manage - system management functionality on EPYC server CPUs from AMD. - - Host System Management Port (HSMP) interface is a mailbox interface - between the x86 core and the System Management Unit (SMU) firmware. - - If you choose to compile this driver as a module the module will be - called amd_hsmp. +source "drivers/platform/x86/amd/Kconfig" config ADV_SWBUTTON tristate "Advantech ACPI Software Button Driver" @@ -300,6 +273,8 @@ config ASUS_WMI select INPUT_SPARSEKMAP select LEDS_CLASS select NEW_LEDS + select LEDS_TRIGGERS + select LEDS_TRIGGER_AUDIO select ACPI_PLATFORM_PROFILE help Say Y here if you have a WMI aware Asus laptop (like Eee PCs or new @@ -1164,7 +1139,14 @@ config WINMATE_FM07_KEYS endif # X86_PLATFORM_DEVICES -config PMC_ATOM - def_bool y - depends on PCI - select COMMON_CLK +config P2SB + bool "Primary to Sideband (P2SB) bridge access support" + depends on PCI && X86 + help + The Primary to Sideband (P2SB) bridge is an interface to some + PCI devices connected through it. In particular, SPI NOR controller + in Intel Apollo Lake SoC is one of such devices. + + The main purpose of this library is to unhide P2SB device in case + firmware kept it hidden on some platforms in order to access devices + behind it. diff --git a/drivers/platform/x86/Makefile b/drivers/platform/x86/Makefile index 4a59f47a46e2..5a428caa654a 100644 --- a/drivers/platform/x86/Makefile +++ b/drivers/platform/x86/Makefile @@ -23,8 +23,7 @@ obj-$(CONFIG_ACER_WIRELESS) += acer-wireless.o obj-$(CONFIG_ACER_WMI) += acer-wmi.o # AMD -obj-$(CONFIG_AMD_PMC) += amd-pmc.o -obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o +obj-y += amd/ # Advantech obj-$(CONFIG_ADV_SWBUTTON) += adv_swbutton.o @@ -120,13 +119,17 @@ obj-$(CONFIG_X86_ANDROID_TABLETS) += x86-android-tablets.o # Intel uncore drivers obj-$(CONFIG_INTEL_IPS) += intel_ips.o +# Intel miscellaneous drivers +intel_p2sb-y := p2sb.o +obj-$(CONFIG_P2SB) += intel_p2sb.o + # Intel PMIC / PMC / P-Unit devices obj-$(CONFIG_INTEL_SCU_IPC) += intel_scu_ipc.o obj-$(CONFIG_INTEL_SCU_PCI) += intel_scu_pcidrv.o obj-$(CONFIG_INTEL_SCU_PLATFORM) += intel_scu_pltdrv.o obj-$(CONFIG_INTEL_SCU_WDT) += intel_scu_wdt.o obj-$(CONFIG_INTEL_SCU_IPC_UTIL) += intel_scu_ipcutil.o -obj-$(CONFIG_PMC_ATOM) += pmc_atom.o +obj-$(CONFIG_X86_INTEL_LPSS) += pmc_atom.o # Siemens Simatic Industrial PCs obj-$(CONFIG_SIEMENS_SIMATIC_IPC) += simatic-ipc.o diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c index 9c6943e401a6..e0230ea0cb7e 100644 --- a/drivers/platform/x86/acer-wmi.c +++ b/drivers/platform/x86/acer-wmi.c @@ -1615,12 +1615,7 @@ static int read_brightness(struct backlight_device *bd) static int update_bl_status(struct backlight_device *bd) { - int intensity = bd->props.brightness; - - if (bd->props.power != FB_BLANK_UNBLANK) - intensity = 0; - if (bd->props.fb_blank != FB_BLANK_UNBLANK) - intensity = 0; + int intensity = backlight_get_brightness(bd); set_u32(intensity, ACER_CAP_BRIGHTNESS); diff --git a/drivers/platform/x86/amd/Kconfig b/drivers/platform/x86/amd/Kconfig new file mode 100644 index 000000000000..c0d0a3c5170c --- /dev/null +++ b/drivers/platform/x86/amd/Kconfig @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# AMD x86 Platform Specific Drivers +# + +config AMD_PMC + tristate "AMD SoC PMC driver" + depends on ACPI && PCI && RTC_CLASS + help + The driver provides support for AMD Power Management Controller + primarily responsible for S2Idle transactions that are driven from + a platform firmware running on SMU. This driver also provides a debug + mechanism to investigate the S2Idle transactions and failures. + + Say Y or M here if you have a notebook powered by AMD RYZEN CPU/APU. + + If you choose to compile this driver as a module the module will be + called amd-pmc. + +config AMD_HSMP + tristate "AMD HSMP Driver" + depends on AMD_NB && X86_64 + help + The driver provides a way for user space tools to monitor and manage + system management functionality on EPYC server CPUs from AMD. + + Host System Management Port (HSMP) interface is a mailbox interface + between the x86 core and the System Management Unit (SMU) firmware. + + If you choose to compile this driver as a module the module will be + called amd_hsmp. diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile new file mode 100644 index 000000000000..a03fbb08e808 --- /dev/null +++ b/drivers/platform/x86/amd/Makefile @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for drivers/platform/x86/amd +# AMD x86 Platform-Specific Drivers +# + +amd-pmc-y := pmc.o +obj-$(CONFIG_AMD_PMC) += amd-pmc.o +amd_hsmp-y := hsmp.o +obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o diff --git a/drivers/platform/x86/amd_hsmp.c b/drivers/platform/x86/amd/hsmp.c similarity index 100% rename from drivers/platform/x86/amd_hsmp.c rename to drivers/platform/x86/amd/hsmp.c diff --git a/drivers/platform/x86/amd-pmc.c b/drivers/platform/x86/amd/pmc.c similarity index 100% rename from drivers/platform/x86/amd-pmc.c rename to drivers/platform/x86/amd/pmc.c diff --git a/drivers/platform/x86/apple-gmux.c b/drivers/platform/x86/apple-gmux.c index 57553f9b4d1d..ffe98a18440b 100644 --- a/drivers/platform/x86/apple-gmux.c +++ b/drivers/platform/x86/apple-gmux.c @@ -291,10 +291,7 @@ static int gmux_get_brightness(struct backlight_device *bd) static int gmux_update_status(struct backlight_device *bd) { struct apple_gmux_data *gmux_data = bl_get_data(bd); - u32 brightness = bd->props.brightness; - - if (bd->props.state & BL_CORE_SUSPENDED) - return 0; + u32 brightness = backlight_get_brightness(bd); gmux_write32(gmux_data, GMUX_PORT_BRIGHTNESS, brightness); diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 62ce198a3463..89b604e04d7f 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -208,6 +208,7 @@ struct asus_wmi { int kbd_led_wk; struct led_classdev lightbar_led; int lightbar_led_wk; + struct led_classdev micmute_led; struct workqueue_struct *led_workqueue; struct work_struct tpd_led_work; struct work_struct wlan_led_work; @@ -1028,12 +1029,23 @@ static enum led_brightness lightbar_led_get(struct led_classdev *led_cdev) return result & ASUS_WMI_DSTS_LIGHTBAR_MASK; } +static int micmute_led_set(struct led_classdev *led_cdev, + enum led_brightness brightness) +{ + int state = brightness != LED_OFF; + int err; + + err = asus_wmi_set_devstate(ASUS_WMI_DEVID_MICMUTE_LED, state, NULL); + return err < 0 ? err : 0; +} + static void asus_wmi_led_exit(struct asus_wmi *asus) { led_classdev_unregister(&asus->kbd_led); led_classdev_unregister(&asus->tpd_led); led_classdev_unregister(&asus->wlan_led); led_classdev_unregister(&asus->lightbar_led); + led_classdev_unregister(&asus->micmute_led); if (asus->led_workqueue) destroy_workqueue(asus->led_workqueue); @@ -1105,6 +1117,19 @@ static int asus_wmi_led_init(struct asus_wmi *asus) &asus->lightbar_led); } + if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_MICMUTE_LED)) { + asus->micmute_led.name = "asus::micmute"; + asus->micmute_led.max_brightness = 1; + asus->micmute_led.brightness = ledtrig_audio_get(LED_AUDIO_MICMUTE); + asus->micmute_led.brightness_set_blocking = micmute_led_set; + asus->micmute_led.default_trigger = "audio-micmute"; + + rv = led_classdev_register(&asus->platform_device->dev, + &asus->micmute_led); + if (rv) + goto error; + } + error: if (rv) asus_wmi_led_exit(asus); diff --git a/drivers/platform/x86/compal-laptop.c b/drivers/platform/x86/compal-laptop.c index ab610376fdad..0942f50bd793 100644 --- a/drivers/platform/x86/compal-laptop.c +++ b/drivers/platform/x86/compal-laptop.c @@ -324,9 +324,7 @@ static int bl_update_status(struct backlight_device *b) if (ret) return ret; - set_backlight_state((b->props.power == FB_BLANK_UNBLANK) - && !(b->props.state & BL_CORE_SUSPENDED) - && !(b->props.state & BL_CORE_FBBLANK)); + set_backlight_state(!backlight_is_blank(b)); return 0; } diff --git a/drivers/platform/x86/dell/Kconfig b/drivers/platform/x86/dell/Kconfig index fe224a54f24c..25421e061c47 100644 --- a/drivers/platform/x86/dell/Kconfig +++ b/drivers/platform/x86/dell/Kconfig @@ -5,7 +5,6 @@ menuconfig X86_PLATFORM_DRIVERS_DELL bool "Dell X86 Platform Specific Device Drivers" - depends on X86_PLATFORM_DEVICES help Say Y here to get to see options for device drivers for various Dell x86 platforms, including vendor-specific laptop extension drivers. diff --git a/drivers/platform/x86/intel/pmt/class.c b/drivers/platform/x86/intel/pmt/class.c index 1c9e3f3ea41c..53d7fd2943b4 100644 --- a/drivers/platform/x86/intel/pmt/class.c +++ b/drivers/platform/x86/intel/pmt/class.c @@ -20,25 +20,16 @@ #define PMT_XA_MAX INT_MAX #define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) -/* - * Early implementations of PMT on client platforms have some - * differences from the server platforms (which use the Out Of Band - * Management Services Module OOBMSM). This list tracks those - * platforms as needed to handle those differences. Newer client - * platforms are expected to be fully compatible with server. - */ -static const struct pci_device_id pmt_telem_early_client_pci_ids[] = { - { PCI_VDEVICE(INTEL, 0x467d) }, /* ADL */ - { PCI_VDEVICE(INTEL, 0x490e) }, /* DG1 */ - { PCI_VDEVICE(INTEL, 0x9a0d) }, /* TGL */ - { } -}; - bool intel_pmt_is_early_client_hw(struct device *dev) { - struct pci_dev *parent = to_pci_dev(dev->parent); + struct intel_vsec_device *ivdev = dev_to_ivdev(dev); - return !!pci_match_id(pmt_telem_early_client_pci_ids, parent); + /* + * Early implementations of PMT on client platforms have some + * differences from the server platforms (which use the Out Of Band + * Management Services Module OOBMSM). + */ + return !!(ivdev->info->quirks & VSEC_QUIRK_EARLY_HW); } EXPORT_SYMBOL_GPL(intel_pmt_is_early_client_hw); diff --git a/drivers/platform/x86/intel/pmt/telemetry.c b/drivers/platform/x86/intel/pmt/telemetry.c index f73ecfd4a309..5e4009c05ecf 100644 --- a/drivers/platform/x86/intel/pmt/telemetry.c +++ b/drivers/platform/x86/intel/pmt/telemetry.c @@ -23,12 +23,19 @@ #define TELEM_GUID_OFFSET 0x4 #define TELEM_BASE_OFFSET 0x8 #define TELEM_ACCESS(v) ((v) & GENMASK(3, 0)) +#define TELEM_TYPE(v) (((v) & GENMASK(7, 4)) >> 4) /* size is in bytes */ #define TELEM_SIZE(v) (((v) & GENMASK(27, 12)) >> 10) /* Used by client hardware to identify a fixed telemetry entry*/ #define TELEM_CLIENT_FIXED_BLOCK_GUID 0x10000000 +enum telem_type { + TELEM_TYPE_PUNIT = 0, + TELEM_TYPE_CRASHLOG, + TELEM_TYPE_PUNIT_FIXED, +}; + struct pmt_telem_priv { int num_entries; struct intel_pmt_entry entry[]; @@ -39,10 +46,15 @@ static bool pmt_telem_region_overlaps(struct intel_pmt_entry *entry, { u32 guid = readl(entry->disc_table + TELEM_GUID_OFFSET); - if (guid != TELEM_CLIENT_FIXED_BLOCK_GUID) - return false; + if (intel_pmt_is_early_client_hw(dev)) { + u32 type = TELEM_TYPE(readl(entry->disc_table)); - return intel_pmt_is_early_client_hw(dev); + if ((type == TELEM_TYPE_PUNIT_FIXED) || + (guid == TELEM_CLIENT_FIXED_BLOCK_GUID)) + return true; + } + + return false; } static int pmt_telem_header_decode(struct intel_pmt_entry *entry, diff --git a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c index e8424e70d81d..fd102678c75f 100644 --- a/drivers/platform/x86/intel/speed_select_if/isst_if_common.c +++ b/drivers/platform/x86/intel/speed_select_if/isst_if_common.c @@ -277,29 +277,38 @@ static int isst_if_get_platform_info(void __user *argp) return 0; } +#define ISST_MAX_BUS_NUMBER 2 struct isst_if_cpu_info { /* For BUS 0 and BUS 1 only, which we need for PUNIT interface */ - int bus_info[2]; - struct pci_dev *pci_dev[2]; + int bus_info[ISST_MAX_BUS_NUMBER]; + struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER]; int punit_cpu_id; int numa_node; }; +struct isst_if_pkg_info { + struct pci_dev *pci_dev[ISST_MAX_BUS_NUMBER]; +}; + static struct isst_if_cpu_info *isst_cpu_info; +static struct isst_if_pkg_info *isst_pkg_info; + #define ISST_MAX_PCI_DOMAINS 8 static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn) { struct pci_dev *matched_pci_dev = NULL; struct pci_dev *pci_dev = NULL; - int no_matches = 0; + int no_matches = 0, pkg_id; int i, bus_number; - if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids || - cpu >= num_possible_cpus()) + if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 || + cpu >= nr_cpu_ids || cpu >= num_possible_cpus()) return NULL; + pkg_id = topology_physical_package_id(cpu); + bus_number = isst_cpu_info[cpu].bus_info[bus_no]; if (bus_number < 0) return NULL; @@ -324,6 +333,8 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn } if (node == isst_cpu_info[cpu].numa_node) { + isst_pkg_info[pkg_id].pci_dev[bus_no] = _pci_dev; + pci_dev = _pci_dev; break; } @@ -342,6 +353,10 @@ static struct pci_dev *_isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn if (!pci_dev && no_matches == 1) pci_dev = matched_pci_dev; + /* Return pci_dev pointer for any matched CPU in the package */ + if (!pci_dev) + pci_dev = isst_pkg_info[pkg_id].pci_dev[bus_no]; + return pci_dev; } @@ -361,8 +376,8 @@ struct pci_dev *isst_if_get_pci_dev(int cpu, int bus_no, int dev, int fn) { struct pci_dev *pci_dev; - if (bus_no < 0 || bus_no > 1 || cpu < 0 || cpu >= nr_cpu_ids || - cpu >= num_possible_cpus()) + if (bus_no < 0 || bus_no >= ISST_MAX_BUS_NUMBER || cpu < 0 || + cpu >= nr_cpu_ids || cpu >= num_possible_cpus()) return NULL; pci_dev = isst_cpu_info[cpu].pci_dev[bus_no]; @@ -417,10 +432,19 @@ static int isst_if_cpu_info_init(void) if (!isst_cpu_info) return -ENOMEM; + isst_pkg_info = kcalloc(topology_max_packages(), + sizeof(*isst_pkg_info), + GFP_KERNEL); + if (!isst_pkg_info) { + kfree(isst_cpu_info); + return -ENOMEM; + } + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "platform/x86/isst-if:online", isst_if_cpu_online, NULL); if (ret < 0) { + kfree(isst_pkg_info); kfree(isst_cpu_info); return ret; } @@ -433,6 +457,7 @@ static int isst_if_cpu_info_init(void) static void isst_if_cpu_info_exit(void) { cpuhp_remove_state(isst_if_online_id); + kfree(isst_pkg_info); kfree(isst_cpu_info); }; diff --git a/drivers/platform/x86/intel/vsec.c b/drivers/platform/x86/intel/vsec.c index bed436bf181f..bb81b8b1f7e9 100644 --- a/drivers/platform/x86/intel/vsec.c +++ b/drivers/platform/x86/intel/vsec.c @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -30,9 +31,13 @@ #define INTEL_DVSEC_TABLE_BAR(x) ((x) & GENMASK(2, 0)) #define INTEL_DVSEC_TABLE_OFFSET(x) ((x) & GENMASK(31, 3)) #define TABLE_OFFSET_SHIFT 3 +#define PMT_XA_START 0 +#define PMT_XA_MAX INT_MAX +#define PMT_XA_LIMIT XA_LIMIT(PMT_XA_START, PMT_XA_MAX) static DEFINE_IDA(intel_vsec_ida); static DEFINE_IDA(intel_vsec_sdsi_ida); +static DEFINE_XARRAY_ALLOC(auxdev_array); /** * struct intel_vsec_header - Common fields of Intel VSEC and DVSEC registers. @@ -54,12 +59,6 @@ struct intel_vsec_header { u32 offset; }; -/* Platform specific data */ -struct intel_vsec_platform_info { - struct intel_vsec_header **capabilities; - unsigned long quirks; -}; - enum intel_vsec_id { VSEC_ID_TELEMETRY = 2, VSEC_ID_WATCHER = 3, @@ -138,7 +137,7 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in const char *name) { struct auxiliary_device *auxdev = &intel_vsec_dev->auxdev; - int ret; + int ret, id; ret = ida_alloc(intel_vsec_dev->ida, GFP_KERNEL); if (ret < 0) { @@ -165,14 +164,26 @@ static int intel_vsec_add_aux(struct pci_dev *pdev, struct intel_vsec_device *in return ret; } - return devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, auxdev); + ret = devm_add_action_or_reset(&pdev->dev, intel_vsec_remove_aux, + auxdev); + if (ret < 0) + return ret; + + /* Add auxdev to list */ + ret = xa_alloc(&auxdev_array, &id, intel_vsec_dev, PMT_XA_LIMIT, + GFP_KERNEL); + if (ret) + return ret; + + return 0; } static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *header, - unsigned long quirks) + struct intel_vsec_platform_info *info) { struct intel_vsec_device *intel_vsec_dev; struct resource *res, *tmp; + unsigned long quirks = info->quirks; int i; if (!intel_vsec_allowed(header->id) || intel_vsec_disabled(header->id, quirks)) @@ -216,7 +227,7 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he intel_vsec_dev->pcidev = pdev; intel_vsec_dev->resource = res; intel_vsec_dev->num_resources = header->num_entries; - intel_vsec_dev->quirks = quirks; + intel_vsec_dev->info = info; if (header->id == VSEC_ID_SDSI) intel_vsec_dev->ida = &intel_vsec_sdsi_ida; @@ -226,14 +237,15 @@ static int intel_vsec_add_dev(struct pci_dev *pdev, struct intel_vsec_header *he return intel_vsec_add_aux(pdev, intel_vsec_dev, intel_vsec_name(header->id)); } -static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, - struct intel_vsec_header **header) +static bool intel_vsec_walk_header(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) { + struct intel_vsec_header **header = info->capabilities; bool have_devices = false; int ret; for ( ; *header; header++) { - ret = intel_vsec_add_dev(pdev, *header, quirks); + ret = intel_vsec_add_dev(pdev, *header, info); if (ret) dev_info(&pdev->dev, "Could not add device for DVSEC id %d\n", (*header)->id); @@ -244,7 +256,8 @@ static bool intel_vsec_walk_header(struct pci_dev *pdev, unsigned long quirks, return have_devices; } -static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) +static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -283,7 +296,7 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) pci_read_config_dword(pdev, pos + PCI_DVSEC_HEADER2, &hdr); header.id = PCI_DVSEC_HEADER2_ID(hdr); - ret = intel_vsec_add_dev(pdev, &header, quirks); + ret = intel_vsec_add_dev(pdev, &header, info); if (ret) continue; @@ -293,7 +306,8 @@ static bool intel_vsec_walk_dvsec(struct pci_dev *pdev, unsigned long quirks) return have_devices; } -static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) +static bool intel_vsec_walk_vsec(struct pci_dev *pdev, + struct intel_vsec_platform_info *info) { bool have_devices = false; int pos = 0; @@ -327,7 +341,7 @@ static bool intel_vsec_walk_vsec(struct pci_dev *pdev, unsigned long quirks) header.tbir = INTEL_DVSEC_TABLE_BAR(table); header.offset = INTEL_DVSEC_TABLE_OFFSET(table); - ret = intel_vsec_add_dev(pdev, &header, quirks); + ret = intel_vsec_add_dev(pdev, &header, info); if (ret) continue; @@ -341,25 +355,25 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id { struct intel_vsec_platform_info *info; bool have_devices = false; - unsigned long quirks = 0; int ret; ret = pcim_enable_device(pdev); if (ret) return ret; + pci_save_state(pdev); info = (struct intel_vsec_platform_info *)id->driver_data; - if (info) - quirks = info->quirks; + if (!info) + return -EINVAL; - if (intel_vsec_walk_dvsec(pdev, quirks)) + if (intel_vsec_walk_dvsec(pdev, info)) have_devices = true; - if (intel_vsec_walk_vsec(pdev, quirks)) + if (intel_vsec_walk_vsec(pdev, info)) have_devices = true; if (info && (info->quirks & VSEC_QUIRK_NO_DVSEC) && - intel_vsec_walk_header(pdev, quirks, info->capabilities)) + intel_vsec_walk_header(pdev, info)) have_devices = true; if (!have_devices) @@ -370,7 +384,8 @@ static int intel_vsec_pci_probe(struct pci_dev *pdev, const struct pci_device_id /* TGL info */ static const struct intel_vsec_platform_info tgl_info = { - .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | VSEC_QUIRK_TABLE_SHIFT, + .quirks = VSEC_QUIRK_NO_WATCHER | VSEC_QUIRK_NO_CRASHLOG | + VSEC_QUIRK_TABLE_SHIFT | VSEC_QUIRK_EARLY_HW, }; /* DG1 info */ @@ -390,26 +405,89 @@ static struct intel_vsec_header *dg1_capabilities[] = { static const struct intel_vsec_platform_info dg1_info = { .capabilities = dg1_capabilities, - .quirks = VSEC_QUIRK_NO_DVSEC, + .quirks = VSEC_QUIRK_NO_DVSEC | VSEC_QUIRK_EARLY_HW, }; #define PCI_DEVICE_ID_INTEL_VSEC_ADL 0x467d #define PCI_DEVICE_ID_INTEL_VSEC_DG1 0x490e #define PCI_DEVICE_ID_INTEL_VSEC_OOBMSM 0x09a7 +#define PCI_DEVICE_ID_INTEL_VSEC_RPL 0xa77d #define PCI_DEVICE_ID_INTEL_VSEC_TGL 0x9a0d static const struct pci_device_id intel_vsec_pci_ids[] = { { PCI_DEVICE_DATA(INTEL, VSEC_ADL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_DG1, &dg1_info) }, - { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, NULL) }, + { PCI_DEVICE_DATA(INTEL, VSEC_OOBMSM, &(struct intel_vsec_platform_info) {}) }, + { PCI_DEVICE_DATA(INTEL, VSEC_RPL, &tgl_info) }, { PCI_DEVICE_DATA(INTEL, VSEC_TGL, &tgl_info) }, { } }; MODULE_DEVICE_TABLE(pci, intel_vsec_pci_ids); +static pci_ers_result_t intel_vsec_pci_error_detected(struct pci_dev *pdev, + pci_channel_state_t state) +{ + pci_ers_result_t status = PCI_ERS_RESULT_NEED_RESET; + + dev_info(&pdev->dev, "PCI error detected, state %d", state); + + if (state == pci_channel_io_perm_failure) + status = PCI_ERS_RESULT_DISCONNECT; + else + pci_disable_device(pdev); + + return status; +} + +static pci_ers_result_t intel_vsec_pci_slot_reset(struct pci_dev *pdev) +{ + struct intel_vsec_device *intel_vsec_dev; + pci_ers_result_t status = PCI_ERS_RESULT_DISCONNECT; + const struct pci_device_id *pci_dev_id; + unsigned long index; + + dev_info(&pdev->dev, "Resetting PCI slot\n"); + + msleep(2000); + if (pci_enable_device(pdev)) { + dev_info(&pdev->dev, + "Failed to re-enable PCI device after reset.\n"); + goto out; + } + + status = PCI_ERS_RESULT_RECOVERED; + + xa_for_each(&auxdev_array, index, intel_vsec_dev) { + /* check if pdev doesn't match */ + if (pdev != intel_vsec_dev->pcidev) + continue; + devm_release_action(&pdev->dev, intel_vsec_remove_aux, + &intel_vsec_dev->auxdev); + } + pci_disable_device(pdev); + pci_restore_state(pdev); + pci_dev_id = pci_match_id(intel_vsec_pci_ids, pdev); + intel_vsec_pci_probe(pdev, pci_dev_id); + +out: + return status; +} + +static void intel_vsec_pci_resume(struct pci_dev *pdev) +{ + dev_info(&pdev->dev, "Done resuming PCI device\n"); +} + +static const struct pci_error_handlers intel_vsec_pci_err_handlers = { + .error_detected = intel_vsec_pci_error_detected, + .slot_reset = intel_vsec_pci_slot_reset, + .resume = intel_vsec_pci_resume, +}; + static struct pci_driver intel_vsec_pci_driver = { .name = "intel_vsec", .id_table = intel_vsec_pci_ids, .probe = intel_vsec_pci_probe, + .err_handler = &intel_vsec_pci_err_handlers, }; module_pci_driver(intel_vsec_pci_driver); diff --git a/drivers/platform/x86/intel/vsec.h b/drivers/platform/x86/intel/vsec.h index 4cc36678e8c5..3deeb05cf394 100644 --- a/drivers/platform/x86/intel/vsec.h +++ b/drivers/platform/x86/intel/vsec.h @@ -20,6 +20,15 @@ enum intel_vsec_quirks { /* DVSEC not present (provided in driver data) */ VSEC_QUIRK_NO_DVSEC = BIT(3), + + /* Platforms requiring quirk in the auxiliary driver */ + VSEC_QUIRK_EARLY_HW = BIT(4), +}; + +/* Platform specific data */ +struct intel_vsec_platform_info { + struct intel_vsec_header **capabilities; + unsigned long quirks; }; struct intel_vsec_device { @@ -27,7 +36,7 @@ struct intel_vsec_device { struct pci_dev *pcidev; struct resource *resource; struct ida *ida; - unsigned long quirks; + struct intel_vsec_platform_info *info; int num_resources; }; diff --git a/drivers/platform/x86/mlx-platform.c b/drivers/platform/x86/mlx-platform.c index 447044fdcb77..5e072a0666f4 100644 --- a/drivers/platform/x86/mlx-platform.c +++ b/drivers/platform/x86/mlx-platform.c @@ -34,6 +34,7 @@ #define MLXPLAT_CPLD_LPC_REG_CPLD3_PN1_OFFSET 0x09 #define MLXPLAT_CPLD_LPC_REG_CPLD4_PN_OFFSET 0x0a #define MLXPLAT_CPLD_LPC_REG_CPLD4_PN1_OFFSET 0x0b +#define MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET 0x19 #define MLXPLAT_CPLD_LPC_REG_RESET_GP4_OFFSET 0x1c #define MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET 0x1d #define MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET 0x1e @@ -66,9 +67,15 @@ #define MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET 0x43 #define MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET 0x44 #define MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET 0x45 +#define MLXPLAT_CPLD_LPC_REG_GWP_OFFSET 0x4a +#define MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET 0x4b +#define MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET 0x4c #define MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET 0x50 #define MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET 0x51 #define MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET 0x52 +#define MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET 0x53 +#define MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET 0x54 +#define MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET 0x55 #define MLXPLAT_CPLD_LPC_REG_AGGRLC_OFFSET 0x56 #define MLXPLAT_CPLD_LPC_REG_AGGRLC_MASK_OFFSET 0x57 #define MLXPLAT_CPLD_LPC_REG_PSU_OFFSET 0x58 @@ -143,6 +150,7 @@ #define MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET 0xfa #define MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET 0xfb #define MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET 0xfc +#define MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET 0xfd #define MLXPLAT_CPLD_LPC_IO_RANGE 0x100 #define MLXPLAT_CPLD_LPC_I2C_CH1_OFF 0xdb #define MLXPLAT_CPLD_LPC_I2C_CH2_OFF 0xda @@ -193,6 +201,7 @@ MLXPLAT_CPLD_AGGR_MASK_LC_ACT | \ MLXPLAT_CPLD_AGGR_MASK_LC_SDWN) #define MLXPLAT_CPLD_LOW_AGGR_MASK_LOW 0xc1 +#define MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2 BIT(2) #define MLXPLAT_CPLD_LOW_AGGR_MASK_I2C BIT(6) #define MLXPLAT_CPLD_PSU_MASK GENMASK(1, 0) #define MLXPLAT_CPLD_PWR_MASK GENMASK(1, 0) @@ -204,6 +213,7 @@ #define MLXPLAT_CPLD_LED_LO_NIBBLE_MASK GENMASK(7, 4) #define MLXPLAT_CPLD_LED_HI_NIBBLE_MASK GENMASK(3, 0) #define MLXPLAT_CPLD_VOLTREG_UPD_MASK GENMASK(5, 4) +#define MLXPLAT_CPLD_GWP_MASK GENMASK(0, 0) #define MLXPLAT_CPLD_I2C_CAP_BIT 0x04 #define MLXPLAT_CPLD_I2C_CAP_MASK GENMASK(5, MLXPLAT_CPLD_I2C_CAP_BIT) @@ -588,6 +598,15 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_asic_items_data[] = { }, }; +static struct mlxreg_core_data mlxplat_mlxcpld_default_asic2_items_data[] = { + { + .label = "asic2", + .reg = MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET, + .mask = MLXPLAT_CPLD_ASIC_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + static struct mlxreg_core_item mlxplat_mlxcpld_default_items[] = { { .data = mlxplat_mlxcpld_default_psu_items_data, @@ -1151,6 +1170,15 @@ static struct mlxreg_core_item mlxplat_mlxcpld_ext_items[] = { .inversed = 0, .health = true, }, + { + .data = mlxplat_mlxcpld_default_asic2_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET, + .mask = MLXPLAT_CPLD_ASIC_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_default_asic2_items_data), + .inversed = 0, + .health = true, + } }; static @@ -1160,7 +1188,7 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_ext_data = { .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, .mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF | MLXPLAT_CPLD_AGGR_MASK_COMEX, .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, - .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW | MLXPLAT_CPLD_LOW_AGGR_MASK_ASIC2, }; static struct mlxreg_core_data mlxplat_mlxcpld_modular_pwr_items_data[] = { @@ -2004,6 +2032,38 @@ struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_modular_data = { .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, }; +/* Platform hotplug for NVLink blade systems family data */ +static struct mlxreg_core_data mlxplat_mlxcpld_global_wp_items_data[] = { + { + .label = "global_wp_grant", + .reg = MLXPLAT_CPLD_LPC_REG_GWP_OFFSET, + .mask = MLXPLAT_CPLD_GWP_MASK, + .hpdev.nr = MLXPLAT_CPLD_NR_NONE, + }, +}; + +static struct mlxreg_core_item mlxplat_mlxcpld_nvlink_blade_items[] = { + { + .data = mlxplat_mlxcpld_global_wp_items_data, + .aggr_mask = MLXPLAT_CPLD_AGGR_MASK_NG_DEF, + .reg = MLXPLAT_CPLD_LPC_REG_GWP_OFFSET, + .mask = MLXPLAT_CPLD_GWP_MASK, + .count = ARRAY_SIZE(mlxplat_mlxcpld_global_wp_items_data), + .inversed = 0, + .health = false, + }, +}; + +static +struct mlxreg_core_hotplug_platform_data mlxplat_mlxcpld_nvlink_blade_data = { + .items = mlxplat_mlxcpld_nvlink_blade_items, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_nvlink_blade_items), + .cell = MLXPLAT_CPLD_LPC_REG_AGGR_OFFSET, + .mask = MLXPLAT_CPLD_AGGR_MASK_COMEX, + .cell_low = MLXPLAT_CPLD_LPC_REG_AGGRLO_OFFSET, + .mask_low = MLXPLAT_CPLD_LOW_AGGR_MASK_LOW, +}; + /* Platform led default data */ static struct mlxreg_core_data mlxplat_mlxcpld_default_led_data[] = { { @@ -2102,6 +2162,25 @@ static struct mlxreg_core_platform_data mlxplat_default_led_wc_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_wc_data), }; +/* Platform led default data for water cooling Ethernet switch blade */ +static struct mlxreg_core_data mlxplat_mlxcpld_default_led_eth_wc_blade_data[] = { + { + .label = "status:green", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK, + }, + { + .label = "status:red", + .reg = MLXPLAT_CPLD_LPC_REG_LED1_OFFSET, + .mask = MLXPLAT_CPLD_LED_LO_NIBBLE_MASK + }, +}; + +static struct mlxreg_core_platform_data mlxplat_default_led_eth_wc_blade_data = { + .data = mlxplat_mlxcpld_default_led_eth_wc_blade_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_default_led_eth_wc_blade_data), +}; + /* Platform led MSN21xx system family data */ static struct mlxreg_core_data mlxplat_mlxcpld_msn21xx_led_data[] = { { @@ -2856,6 +2935,18 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .bit = GENMASK(7, 0), .mode = 0444, }, + { + .label = "asic_reset", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "asic2_reset", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0200, + }, { .label = "reset_long_pb", .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, @@ -2995,6 +3086,13 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .bit = 1, .mode = 0444, }, + { + .label = "asic2_health", + .reg = MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET, + .mask = MLXPLAT_CPLD_ASIC_MASK, + .bit = 1, + .mode = 0444, + }, { .label = "fan_dir", .reg = MLXPLAT_CPLD_LPC_REG_FAN_DIRECTION, @@ -3056,6 +3154,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_default_ng_regs_io_data[] = { .bit = GENMASK(7, 0), .mode = 0444, }, + { + .label = "config3", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, { .label = "ufm_version", .reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET, @@ -3534,6 +3638,12 @@ static struct mlxreg_core_data mlxplat_mlxcpld_modular_regs_io_data[] = { .bit = GENMASK(7, 0), .mode = 0444, }, + { + .label = "config3", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, { .label = "ufm_version", .reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET, @@ -3547,6 +3657,209 @@ static struct mlxreg_core_platform_data mlxplat_modular_regs_io_data = { .counter = ARRAY_SIZE(mlxplat_mlxcpld_modular_regs_io_data), }; +/* Platform register access for NVLink blade systems family data */ +static struct mlxreg_core_data mlxplat_mlxcpld_nvlink_blade_regs_io_data[] = { + { + .label = "cpld1_version", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_VER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "cpld1_pn", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_PN_OFFSET, + .bit = GENMASK(15, 0), + .mode = 0444, + .regnum = 2, + }, + { + .label = "cpld1_version_min", + .reg = MLXPLAT_CPLD_LPC_REG_CPLD1_MVER_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "reset_aux_pwr_or_ref", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_from_comex", + .reg = MLXPLAT_CPLD_LPC_REG_RESET_CAUSE_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_comex_pwr_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_platform", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "reset_soc", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_comex_wd", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "reset_voltmon_upgrade_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "reset_system", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(1), + .mode = 0444, + }, + { + .label = "reset_sw_pwr_off", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0444, + }, + { + .label = "reset_comex_thermal", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0444, + }, + { + .label = "reset_reload_bios", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "reset_ac_pwr_fail", + .reg = MLXPLAT_CPLD_LPC_REG_RST_CAUSE2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "pwr_cycle", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(2), + .mode = 0200, + }, + { + .label = "pwr_down", + .reg = MLXPLAT_CPLD_LPC_REG_GP1_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0200, + }, + { + .label = "global_wp_request", + .reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0644, + }, + { + .label = "jtag_enable", + .reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "comm_chnl_ready", + .reg = MLXPLAT_CPLD_LPC_REG_GP2_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0200, + }, + { + .label = "bios_safe_mode", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0444, + }, + { + .label = "bios_active_image", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(5), + .mode = 0444, + }, + { + .label = "bios_auth_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(6), + .mode = 0444, + }, + { + .label = "bios_upgrade_fail", + .reg = MLXPLAT_CPLD_LPC_REG_GPCOM0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(7), + .mode = 0444, + }, + { + .label = "voltreg_update_status", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_RO_OFFSET, + .mask = MLXPLAT_CPLD_VOLTREG_UPD_MASK, + .bit = 5, + .mode = 0444, + }, + { + .label = "vpd_wp", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(3), + .mode = 0644, + }, + { + .label = "pcie_asic_reset_dis", + .reg = MLXPLAT_CPLD_LPC_REG_GP0_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(4), + .mode = 0644, + }, + { + .label = "global_wp_response", + .reg = MLXPLAT_CPLD_LPC_REG_GWP_OFFSET, + .mask = GENMASK(7, 0) & ~BIT(0), + .mode = 0444, + }, + { + .label = "config1", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "config2", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "config3", + .reg = MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, + { + .label = "ufm_version", + .reg = MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET, + .bit = GENMASK(7, 0), + .mode = 0444, + }, +}; + +static struct mlxreg_core_platform_data mlxplat_nvlink_blade_regs_io_data = { + .data = mlxplat_mlxcpld_nvlink_blade_regs_io_data, + .counter = ARRAY_SIZE(mlxplat_mlxcpld_nvlink_blade_regs_io_data), +}; + /* Platform FAN default */ static struct mlxreg_core_data mlxplat_mlxcpld_default_fan_data[] = { { @@ -3932,8 +4245,12 @@ static bool mlxplat_mlxcpld_writeable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_AGGRLO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_PWR_EVENT_OFFSET: @@ -4023,9 +4340,15 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET: @@ -4100,6 +4423,7 @@ static bool mlxplat_mlxcpld_readable_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET: case MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET: return true; } @@ -4150,9 +4474,15 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_AGGRCO_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCX_OFFSET: case MLXPLAT_CPLD_LPC_REG_AGGRCX_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_GWP_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_HEALTH_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_ASIC_MASK_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_HEALTH_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_EVENT_OFFSET: + case MLXPLAT_CPLD_LPC_REG_ASIC2_MASK_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_EVENT_OFFSET: case MLXPLAT_CPLD_LPC_REG_PSU_MASK_OFFSET: @@ -4221,6 +4551,7 @@ static bool mlxplat_mlxcpld_volatile_reg(struct device *dev, unsigned int reg) case MLXPLAT_CPLD_LPC_REG_SLOT_QTY_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG1_OFFSET: case MLXPLAT_CPLD_LPC_REG_CONFIG2_OFFSET: + case MLXPLAT_CPLD_LPC_REG_CONFIG3_OFFSET: case MLXPLAT_CPLD_LPC_REG_UFM_VERSION_OFFSET: return true; } @@ -4417,6 +4748,31 @@ static int __init mlxplat_dmi_default_wc_matched(const struct dmi_system_id *dmi return 1; } +static int __init mlxplat_dmi_default_eth_wc_blade_matched(const struct dmi_system_id *dmi) +{ + int i; + + mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; + mlxplat_mux_num = ARRAY_SIZE(mlxplat_default_mux_data); + mlxplat_mux_data = mlxplat_default_mux_data; + for (i = 0; i < mlxplat_mux_num; i++) { + mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; + mlxplat_mux_data[i].n_values = + ARRAY_SIZE(mlxplat_msn21xx_channels); + } + mlxplat_hotplug = &mlxplat_mlxcpld_default_wc_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + mlxplat_led = &mlxplat_default_led_eth_wc_blade_data; + mlxplat_regs_io = &mlxplat_default_ng_regs_io_data; + for (i = 0; i < ARRAY_SIZE(mlxplat_mlxcpld_wd_set_type2); i++) + mlxplat_wd_data[i] = &mlxplat_mlxcpld_wd_set_type2[i]; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; + mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng; + + return 1; +} + static int __init mlxplat_dmi_msn21xx_matched(const struct dmi_system_id *dmi) { int i; @@ -4579,6 +4935,28 @@ static int __init mlxplat_dmi_modular_matched(const struct dmi_system_id *dmi) return 1; } +static int __init mlxplat_dmi_nvlink_blade_matched(const struct dmi_system_id *dmi) +{ + int i; + + mlxplat_max_adap_num = MLXPLAT_CPLD_MAX_PHYS_ADAPTER_NUM; + mlxplat_mux_num = ARRAY_SIZE(mlxplat_default_mux_data); + mlxplat_mux_data = mlxplat_default_mux_data; + mlxplat_hotplug = &mlxplat_mlxcpld_nvlink_blade_data; + mlxplat_hotplug->deferred_nr = + mlxplat_msn21xx_channels[MLXPLAT_CPLD_GRP_CHNL_NUM - 1]; + for (i = 0; i < mlxplat_mux_num; i++) { + mlxplat_mux_data[i].values = mlxplat_msn21xx_channels; + mlxplat_mux_data[i].n_values = + ARRAY_SIZE(mlxplat_msn21xx_channels); + } + mlxplat_regs_io = &mlxplat_nvlink_blade_regs_io_data; + mlxplat_i2c = &mlxplat_mlxcpld_i2c_ng_data; + mlxplat_regmap_config = &mlxplat_mlxcpld_regmap_config_ng400; + + return 1; +} + static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { { .callback = mlxplat_dmi_default_wc_matched, @@ -4611,6 +4989,13 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { DMI_MATCH(DMI_BOARD_NAME, "VMOD0004"), }, }, + { + .callback = mlxplat_dmi_default_eth_wc_blade_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0005"), + DMI_EXACT_MATCH(DMI_PRODUCT_SKU, "HI139"), + }, + }, { .callback = mlxplat_dmi_qmb7xx_matched, .matches = { @@ -4641,6 +5026,12 @@ static const struct dmi_system_id mlxplat_dmi_table[] __initconst = { DMI_MATCH(DMI_BOARD_NAME, "VMOD0011"), }, }, + { + .callback = mlxplat_dmi_nvlink_blade_matched, + .matches = { + DMI_MATCH(DMI_BOARD_NAME, "VMOD0015"), + }, + }, { .callback = mlxplat_dmi_msn274x_matched, .matches = { @@ -4830,22 +5221,20 @@ static int __init mlxplat_init(void) nr = (nr == mlxplat_max_adap_num) ? -1 : nr; if (mlxplat_i2c) mlxplat_i2c->regmap = priv->regmap; - priv->pdev_i2c = platform_device_register_resndata( - &mlxplat_dev->dev, "i2c_mlxcpld", - nr, mlxplat_mlxcpld_resources, - ARRAY_SIZE(mlxplat_mlxcpld_resources), - mlxplat_i2c, sizeof(*mlxplat_i2c)); + priv->pdev_i2c = platform_device_register_resndata(&mlxplat_dev->dev, "i2c_mlxcpld", + nr, mlxplat_mlxcpld_resources, + ARRAY_SIZE(mlxplat_mlxcpld_resources), + mlxplat_i2c, sizeof(*mlxplat_i2c)); if (IS_ERR(priv->pdev_i2c)) { err = PTR_ERR(priv->pdev_i2c); goto fail_alloc; } for (i = 0; i < mlxplat_mux_num; i++) { - priv->pdev_mux[i] = platform_device_register_resndata( - &priv->pdev_i2c->dev, - "i2c-mux-reg", i, NULL, - 0, &mlxplat_mux_data[i], - sizeof(mlxplat_mux_data[i])); + priv->pdev_mux[i] = platform_device_register_resndata(&priv->pdev_i2c->dev, + "i2c-mux-reg", i, NULL, 0, + &mlxplat_mux_data[i], + sizeof(mlxplat_mux_data[i])); if (IS_ERR(priv->pdev_mux[i])) { err = PTR_ERR(priv->pdev_mux[i]); goto fail_platform_mux_register; @@ -4853,16 +5242,18 @@ static int __init mlxplat_init(void) } /* Add hotplug driver */ - mlxplat_hotplug->regmap = priv->regmap; - priv->pdev_hotplug = platform_device_register_resndata( - &mlxplat_dev->dev, "mlxreg-hotplug", - PLATFORM_DEVID_NONE, - mlxplat_mlxcpld_resources, - ARRAY_SIZE(mlxplat_mlxcpld_resources), - mlxplat_hotplug, sizeof(*mlxplat_hotplug)); - if (IS_ERR(priv->pdev_hotplug)) { - err = PTR_ERR(priv->pdev_hotplug); - goto fail_platform_mux_register; + if (mlxplat_hotplug) { + mlxplat_hotplug->regmap = priv->regmap; + priv->pdev_hotplug = + platform_device_register_resndata(&mlxplat_dev->dev, + "mlxreg-hotplug", PLATFORM_DEVID_NONE, + mlxplat_mlxcpld_resources, + ARRAY_SIZE(mlxplat_mlxcpld_resources), + mlxplat_hotplug, sizeof(*mlxplat_hotplug)); + if (IS_ERR(priv->pdev_hotplug)) { + err = PTR_ERR(priv->pdev_hotplug); + goto fail_platform_mux_register; + } } /* Set default registers. */ @@ -4875,24 +5266,26 @@ static int __init mlxplat_init(void) } /* Add LED driver. */ - mlxplat_led->regmap = priv->regmap; - priv->pdev_led = platform_device_register_resndata( - &mlxplat_dev->dev, "leds-mlxreg", - PLATFORM_DEVID_NONE, NULL, 0, - mlxplat_led, sizeof(*mlxplat_led)); - if (IS_ERR(priv->pdev_led)) { - err = PTR_ERR(priv->pdev_led); - goto fail_platform_hotplug_register; + if (mlxplat_led) { + mlxplat_led->regmap = priv->regmap; + priv->pdev_led = + platform_device_register_resndata(&mlxplat_dev->dev, "leds-mlxreg", + PLATFORM_DEVID_NONE, NULL, 0, mlxplat_led, + sizeof(*mlxplat_led)); + if (IS_ERR(priv->pdev_led)) { + err = PTR_ERR(priv->pdev_led); + goto fail_platform_hotplug_register; + } } /* Add registers io access driver. */ if (mlxplat_regs_io) { mlxplat_regs_io->regmap = priv->regmap; - priv->pdev_io_regs = platform_device_register_resndata( - &mlxplat_dev->dev, "mlxreg-io", - PLATFORM_DEVID_NONE, NULL, 0, - mlxplat_regs_io, - sizeof(*mlxplat_regs_io)); + priv->pdev_io_regs = platform_device_register_resndata(&mlxplat_dev->dev, + "mlxreg-io", + PLATFORM_DEVID_NONE, NULL, + 0, mlxplat_regs_io, + sizeof(*mlxplat_regs_io)); if (IS_ERR(priv->pdev_io_regs)) { err = PTR_ERR(priv->pdev_io_regs); goto fail_platform_led_register; @@ -4902,11 +5295,10 @@ static int __init mlxplat_init(void) /* Add FAN driver. */ if (mlxplat_fan) { mlxplat_fan->regmap = priv->regmap; - priv->pdev_fan = platform_device_register_resndata( - &mlxplat_dev->dev, "mlxreg-fan", - PLATFORM_DEVID_NONE, NULL, 0, - mlxplat_fan, - sizeof(*mlxplat_fan)); + priv->pdev_fan = platform_device_register_resndata(&mlxplat_dev->dev, "mlxreg-fan", + PLATFORM_DEVID_NONE, NULL, 0, + mlxplat_fan, + sizeof(*mlxplat_fan)); if (IS_ERR(priv->pdev_fan)) { err = PTR_ERR(priv->pdev_fan); goto fail_platform_io_regs_register; @@ -4920,11 +5312,10 @@ static int __init mlxplat_init(void) for (j = 0; j < MLXPLAT_CPLD_WD_MAX_DEVS; j++) { if (mlxplat_wd_data[j]) { mlxplat_wd_data[j]->regmap = priv->regmap; - priv->pdev_wd[j] = platform_device_register_resndata( - &mlxplat_dev->dev, "mlx-wdt", - j, NULL, 0, - mlxplat_wd_data[j], - sizeof(*mlxplat_wd_data[j])); + priv->pdev_wd[j] = + platform_device_register_resndata(&mlxplat_dev->dev, "mlx-wdt", j, + NULL, 0, mlxplat_wd_data[j], + sizeof(*mlxplat_wd_data[j])); if (IS_ERR(priv->pdev_wd[j])) { err = PTR_ERR(priv->pdev_wd[j]); goto fail_platform_wd_register; @@ -4949,9 +5340,11 @@ fail_platform_io_regs_register: if (mlxplat_regs_io) platform_device_unregister(priv->pdev_io_regs); fail_platform_led_register: - platform_device_unregister(priv->pdev_led); + if (mlxplat_led) + platform_device_unregister(priv->pdev_led); fail_platform_hotplug_register: - platform_device_unregister(priv->pdev_hotplug); + if (mlxplat_hotplug) + platform_device_unregister(priv->pdev_hotplug); fail_platform_mux_register: while (--i >= 0) platform_device_unregister(priv->pdev_mux[i]); @@ -4974,8 +5367,10 @@ static void __exit mlxplat_exit(void) platform_device_unregister(priv->pdev_fan); if (priv->pdev_io_regs) platform_device_unregister(priv->pdev_io_regs); - platform_device_unregister(priv->pdev_led); - platform_device_unregister(priv->pdev_hotplug); + if (priv->pdev_led) + platform_device_unregister(priv->pdev_led); + if (priv->pdev_hotplug) + platform_device_unregister(priv->pdev_hotplug); for (i = mlxplat_mux_num - 1; i >= 0 ; i--) platform_device_unregister(priv->pdev_mux[i]); diff --git a/drivers/platform/x86/p2sb.c b/drivers/platform/x86/p2sb.c new file mode 100644 index 000000000000..fb2e141f3eb8 --- /dev/null +++ b/drivers/platform/x86/p2sb.c @@ -0,0 +1,133 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Primary to Sideband (P2SB) bridge access support + * + * Copyright (c) 2017, 2021-2022 Intel Corporation. + * + * Authors: Andy Shevchenko + * Jonathan Yong + */ + +#include +#include +#include +#include + +#include +#include + +#define P2SBC 0xe0 +#define P2SBC_HIDE BIT(8) + +static const struct x86_cpu_id p2sb_cpu_ids[] = { + X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT, PCI_DEVFN(13, 0)), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_GOLDMONT_D, PCI_DEVFN(31, 1)), + X86_MATCH_INTEL_FAM6_MODEL(ATOM_SILVERMONT_D, PCI_DEVFN(31, 1)), + X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE, PCI_DEVFN(31, 1)), + X86_MATCH_INTEL_FAM6_MODEL(KABYLAKE_L, PCI_DEVFN(31, 1)), + X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE, PCI_DEVFN(31, 1)), + X86_MATCH_INTEL_FAM6_MODEL(SKYLAKE_L, PCI_DEVFN(31, 1)), + {} +}; + +static int p2sb_get_devfn(unsigned int *devfn) +{ + const struct x86_cpu_id *id; + + id = x86_match_cpu(p2sb_cpu_ids); + if (!id) + return -ENODEV; + + *devfn = (unsigned int)id->driver_data; + return 0; +} + +static int p2sb_read_bar0(struct pci_dev *pdev, struct resource *mem) +{ + /* Copy resource from the first BAR of the device in question */ + *mem = pdev->resource[0]; + return 0; +} + +static int p2sb_scan_and_read(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +{ + struct pci_dev *pdev; + int ret; + + pdev = pci_scan_single_device(bus, devfn); + if (!pdev) + return -ENODEV; + + ret = p2sb_read_bar0(pdev, mem); + + pci_stop_and_remove_bus_device(pdev); + return ret; +} + +/** + * p2sb_bar - Get Primary to Sideband (P2SB) bridge device BAR + * @bus: PCI bus to communicate with + * @devfn: PCI slot and function to communicate with + * @mem: memory resource to be filled in + * + * The BIOS prevents the P2SB device from being enumerated by the PCI + * subsystem, so we need to unhide and hide it back to lookup the BAR. + * + * if @bus is NULL, the bus 0 in domain 0 will be used. + * If @devfn is 0, it will be replaced by devfn of the P2SB device. + * + * Caller must provide a valid pointer to @mem. + * + * Locking is handled by pci_rescan_remove_lock mutex. + * + * Return: + * 0 on success or appropriate errno value on error. + */ +int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +{ + struct pci_dev *pdev_p2sb; + unsigned int devfn_p2sb; + u32 value = P2SBC_HIDE; + int ret; + + /* Get devfn for P2SB device itself */ + ret = p2sb_get_devfn(&devfn_p2sb); + if (ret) + return ret; + + /* if @bus is NULL, use bus 0 in domain 0 */ + bus = bus ?: pci_find_bus(0, 0); + + /* + * Prevent concurrent PCI bus scan from seeing the P2SB device and + * removing via sysfs while it is temporarily exposed. + */ + pci_lock_rescan_remove(); + + /* Unhide the P2SB device, if needed */ + pci_bus_read_config_dword(bus, devfn_p2sb, P2SBC, &value); + if (value & P2SBC_HIDE) + pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, 0); + + pdev_p2sb = pci_scan_single_device(bus, devfn_p2sb); + if (devfn) + ret = p2sb_scan_and_read(bus, devfn, mem); + else + ret = p2sb_read_bar0(pdev_p2sb, mem); + pci_stop_and_remove_bus_device(pdev_p2sb); + + /* Hide the P2SB device, if it was hidden */ + if (value & P2SBC_HIDE) + pci_bus_write_config_dword(bus, devfn_p2sb, P2SBC, P2SBC_HIDE); + + pci_unlock_rescan_remove(); + + if (ret) + return ret; + + if (mem->flags == 0) + return -ENODEV; + + return 0; +} +EXPORT_SYMBOL_GPL(p2sb_bar); diff --git a/drivers/platform/x86/panasonic-laptop.c b/drivers/platform/x86/panasonic-laptop.c index 615e39cbbbf1..d9a095d2c0eb 100644 --- a/drivers/platform/x86/panasonic-laptop.c +++ b/drivers/platform/x86/panasonic-laptop.c @@ -998,19 +998,23 @@ static int acpi_pcc_hotkey_add(struct acpi_device *device) pr_err("Couldn't retrieve BIOS data\n"); goto out_input; } - /* initialize backlight */ - memset(&props, 0, sizeof(struct backlight_properties)); - props.type = BACKLIGHT_PLATFORM; - props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; - pcc->backlight = backlight_device_register("panasonic", NULL, pcc, - &pcc_backlight_ops, &props); - if (IS_ERR(pcc->backlight)) { - result = PTR_ERR(pcc->backlight); - goto out_input; - } - /* read the initial brightness setting from the hardware */ - pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) { + /* initialize backlight */ + memset(&props, 0, sizeof(struct backlight_properties)); + props.type = BACKLIGHT_PLATFORM; + props.max_brightness = pcc->sinf[SINF_AC_MAX_BRIGHT]; + + pcc->backlight = backlight_device_register("panasonic", NULL, pcc, + &pcc_backlight_ops, &props); + if (IS_ERR(pcc->backlight)) { + result = PTR_ERR(pcc->backlight); + goto out_input; + } + + /* read the initial brightness setting from the hardware */ + pcc->backlight->props.brightness = pcc->sinf[SINF_AC_CUR_BRIGHT]; + } /* Reset initial sticky key mode since the hardware register state is not consistent */ acpi_pcc_write_sset(pcc, SINF_STICKY_KEY, 0); diff --git a/drivers/platform/x86/pmc_atom.c b/drivers/platform/x86/pmc_atom.c index b8b1ed1406de..154317e9910d 100644 --- a/drivers/platform/x86/pmc_atom.c +++ b/drivers/platform/x86/pmc_atom.c @@ -389,21 +389,16 @@ static const struct dmi_system_id critclk_systems[] = { }, }, { - /* pmc_plt_clk0 - 3 are used for the 4 ethernet controllers */ - .ident = "Lex 3I380D", + /* + * Lex System / Lex Computech Co. makes a lot of Bay Trail + * based embedded boards which often come with multiple + * ethernet controllers using multiple pmc_plt_clks. See: + * https://www.lex.com.tw/products/embedded-ipc-board/ + */ + .ident = "Lex BayTrail", .callback = dmi_callback, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"), - DMI_MATCH(DMI_PRODUCT_NAME, "3I380D"), - }, - }, - { - /* pmc_plt_clk* - are used for ethernet controllers */ - .ident = "Lex 2I385SW", - .callback = dmi_callback, - .matches = { - DMI_MATCH(DMI_SYS_VENDOR, "Lex BayTrail"), - DMI_MATCH(DMI_PRODUCT_NAME, "2I385SW"), }, }, { diff --git a/drivers/platform/x86/serial-multi-instantiate.c b/drivers/platform/x86/serial-multi-instantiate.c index 1e8063b7c169..67feed25c9db 100644 --- a/drivers/platform/x86/serial-multi-instantiate.c +++ b/drivers/platform/x86/serial-multi-instantiate.c @@ -61,36 +61,35 @@ static int smi_get_irq(struct platform_device *pdev, struct acpi_device *adev, default: return 0; } - if (ret < 0) - dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d: %d\n", - inst->irq_idx, ret); + return dev_err_probe(&pdev->dev, ret, "Error requesting irq at index %d\n", + inst->irq_idx); return ret; } static void smi_devs_unregister(struct smi *smi) { - while (smi->i2c_num > 0) - i2c_unregister_device(smi->i2c_devs[--smi->i2c_num]); + while (smi->i2c_num--) + i2c_unregister_device(smi->i2c_devs[smi->i2c_num]); - while (smi->spi_num > 0) - spi_unregister_device(smi->spi_devs[--smi->spi_num]); + while (smi->spi_num--) + spi_unregister_device(smi->spi_devs[smi->spi_num]); } /** * smi_spi_probe - Instantiate multiple SPI devices from inst array * @pdev: Platform device - * @adev: ACPI device * @smi: Internal struct for Serial multi instantiate driver * @inst_array: Array of instances to probe * * Returns the number of SPI devices instantiate, Zero if none is found or a negative error code. */ -static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi, +static int smi_spi_probe(struct platform_device *pdev, struct smi *smi, const struct smi_instance *inst_array) { struct device *dev = &pdev->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); struct spi_controller *ctlr; struct spi_device *spi_dev; char name[50]; @@ -99,8 +98,8 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, ret = acpi_spi_count_resources(adev); if (ret < 0) return ret; - else if (!ret) - return -ENODEV; + if (!ret) + return -ENOENT; count = ret; @@ -112,9 +111,8 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, spi_dev = acpi_spi_device_alloc(NULL, adev, i); if (IS_ERR(spi_dev)) { - ret = PTR_ERR(spi_dev); - dev_err_probe(dev, ret, "failed to allocate SPI device %s from ACPI: %d\n", - dev_name(&adev->dev), ret); + ret = dev_err_probe(dev, PTR_ERR(spi_dev), "failed to allocate SPI device %s from ACPI\n", + dev_name(&adev->dev)); goto error; } @@ -135,9 +133,8 @@ static int smi_spi_probe(struct platform_device *pdev, struct acpi_device *adev, ret = spi_add_device(spi_dev); if (ret) { - dev_err_probe(&ctlr->dev, ret, - "failed to add SPI device %s from ACPI: %d\n", - dev_name(&adev->dev), ret); + dev_err_probe(&ctlr->dev, ret, "failed to add SPI device %s from ACPI\n", + dev_name(&adev->dev)); spi_dev_put(spi_dev); goto error; } @@ -166,25 +163,25 @@ error: /** * smi_i2c_probe - Instantiate multiple I2C devices from inst array * @pdev: Platform device - * @adev: ACPI device * @smi: Internal struct for Serial multi instantiate driver * @inst_array: Array of instances to probe * * Returns the number of I2C devices instantiate, Zero if none is found or a negative error code. */ -static int smi_i2c_probe(struct platform_device *pdev, struct acpi_device *adev, struct smi *smi, +static int smi_i2c_probe(struct platform_device *pdev, struct smi *smi, const struct smi_instance *inst_array) { struct i2c_board_info board_info = {}; struct device *dev = &pdev->dev; + struct acpi_device *adev = ACPI_COMPANION(dev); char name[32]; int i, ret, count; ret = i2c_acpi_client_count(adev); if (ret < 0) return ret; - else if (!ret) - return -ENODEV; + if (!ret) + return -ENOENT; count = ret; @@ -230,12 +227,8 @@ static int smi_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; const struct smi_node *node; - struct acpi_device *adev; struct smi *smi; - - adev = ACPI_COMPANION(dev); - if (!adev) - return -ENODEV; + int ret; node = device_get_match_data(dev); if (!node) { @@ -251,19 +244,25 @@ static int smi_probe(struct platform_device *pdev) switch (node->bus_type) { case SMI_I2C: - return smi_i2c_probe(pdev, adev, smi, node->instances); + return smi_i2c_probe(pdev, smi, node->instances); case SMI_SPI: - return smi_spi_probe(pdev, adev, smi, node->instances); + return smi_spi_probe(pdev, smi, node->instances); case SMI_AUTO_DETECT: - if (i2c_acpi_client_count(adev) > 0) - return smi_i2c_probe(pdev, adev, smi, node->instances); - else - return smi_spi_probe(pdev, adev, smi, node->instances); + /* + * For backwards-compatibility with the existing nodes I2C + * is checked first and if such entries are found ONLY I2C + * devices are created. Since some existing nodes that were + * already handled by this driver could also contain unrelated + * SpiSerialBus nodes that were previously ignored, and this + * preserves that behavior. + */ + ret = smi_i2c_probe(pdev, smi, node->instances); + if (ret != -ENOENT) + return ret; + return smi_spi_probe(pdev, smi, node->instances); default: return -EINVAL; } - - return 0; /* never reached */ } static int smi_remove(struct platform_device *pdev) @@ -325,8 +324,8 @@ static const struct smi_node cs35l41_hda = { static const struct acpi_device_id smi_acpi_ids[] = { { "BSG1160", (unsigned long)&bsg1160_data }, { "BSG2150", (unsigned long)&bsg2150_data }, - { "INT3515", (unsigned long)&int3515_data }, { "CSC3551", (unsigned long)&cs35l41_hda }, + { "INT3515", (unsigned long)&int3515_data }, /* Non-conforming _HID for Cirrus Logic already released */ { "CLSA0100", (unsigned long)&cs35l41_hda }, { } diff --git a/drivers/platform/x86/simatic-ipc.c b/drivers/platform/x86/simatic-ipc.c index b599cda5ba3c..ca3647b751d5 100644 --- a/drivers/platform/x86/simatic-ipc.c +++ b/drivers/platform/x86/simatic-ipc.c @@ -51,6 +51,7 @@ static int register_platform_devices(u32 station_id) { u8 ledmode = SIMATIC_IPC_DEVICE_NONE; u8 wdtmode = SIMATIC_IPC_DEVICE_NONE; + char *pdevname = KBUILD_MODNAME "_leds"; int i; platform_data.devmode = SIMATIC_IPC_DEVICE_NONE; @@ -64,10 +65,12 @@ static int register_platform_devices(u32 station_id) } if (ledmode != SIMATIC_IPC_DEVICE_NONE) { + if (ledmode == SIMATIC_IPC_DEVICE_127E) + pdevname = KBUILD_MODNAME "_leds_gpio"; platform_data.devmode = ledmode; ipc_led_platform_device = platform_device_register_data(NULL, - KBUILD_MODNAME "_leds", PLATFORM_DEVID_NONE, + pdevname, PLATFORM_DEVID_NONE, &platform_data, sizeof(struct simatic_ipc_platform)); if (IS_ERR(ipc_led_platform_device)) @@ -101,44 +104,6 @@ static int register_platform_devices(u32 station_id) return 0; } -/* FIXME: this should eventually be done with generic P2SB discovery code - * the individual drivers for watchdogs and LEDs access memory that implements - * GPIO, but pinctrl will not come up because of missing ACPI entries - * - * While there is no conflict a cleaner solution would be to somehow bring up - * pinctrl even with these ACPI entries missing, and base the drivers on pinctrl. - * After which the following function could be dropped, together with the code - * poking the memory. - */ -/* - * Get membase address from PCI, used in leds and wdt module. Here we read - * the bar0. The final address calculation is done in the appropriate modules - */ -u32 simatic_ipc_get_membase0(unsigned int p2sb) -{ - struct pci_bus *bus; - u32 bar0 = 0; - /* - * The GPIO memory is in bar0 of the hidden P2SB device. - * Unhide the device to have a quick look at it, before we hide it - * again. - * Also grab the pci rescan lock so that device does not get discovered - * and remapped while it is visible. - * This code is inspired by drivers/mfd/lpc_ich.c - */ - bus = pci_find_bus(0, 0); - pci_lock_rescan_remove(); - pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x0); - pci_bus_read_config_dword(bus, p2sb, PCI_BASE_ADDRESS_0, &bar0); - - bar0 &= ~0xf; - pci_bus_write_config_byte(bus, p2sb, 0xE1, 0x1); - pci_unlock_rescan_remove(); - - return bar0; -} -EXPORT_SYMBOL(simatic_ipc_get_membase0); - static int __init simatic_ipc_init_module(void) { const struct dmi_system_id *match; diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index d8d0c0bed5e9..07ef05f727a2 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c @@ -4341,7 +4341,7 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) { struct acpi_resource_irq *p = &resource->data.irq; struct sony_pic_irq *interrupt = NULL; - if (!p || !p->interrupt_count) { + if (!p->interrupt_count) { /* * IRQ descriptors may have no IRQ# bits set, * particularly those those w/ _STA disabled @@ -4374,11 +4374,6 @@ sony_pic_read_possible_resource(struct acpi_resource *resource, void *context) struct acpi_resource_io *io = &resource->data.io; struct sony_pic_ioport *ioport = list_first_entry(&dev->ioports, struct sony_pic_ioport, list); - if (!io) { - dprintk("Blank IO resource\n"); - return AE_OK; - } - if (!ioport->io1.minimum) { memcpy(&ioport->io1, io, sizeof(*io)); dprintk("IO1 at 0x%.4x (0x%.2x)\n", ioport->io1.minimum, diff --git a/drivers/platform/x86/system76_acpi.c b/drivers/platform/x86/system76_acpi.c index 7299ad08c838..958df41ad509 100644 --- a/drivers/platform/x86/system76_acpi.c +++ b/drivers/platform/x86/system76_acpi.c @@ -339,7 +339,7 @@ static ssize_t kb_led_color_show( struct led_classdev *led; struct system76_data *data; - led = (struct led_classdev *)dev->driver_data; + led = dev_get_drvdata(dev); data = container_of(led, struct system76_data, kb_led); return sysfs_emit(buf, "%06X\n", data->kb_color); } @@ -356,7 +356,7 @@ static ssize_t kb_led_color_store( unsigned int val; int ret; - led = (struct led_classdev *)dev->driver_data; + led = dev_get_drvdata(dev); data = container_of(led, struct system76_data, kb_led); ret = kstrtouint(buf, 16, &val); if (ret) diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 502dcd1c33b7..22d4e8633e30 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -34,46 +34,51 @@ * thanks to Chris Wright */ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include #include #include +#include +#include #include -#include +#include #include #include +#include #include -#include -#include -#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include -#include +#include #include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include + #include #include + #include + +#include +#include +#include + #include "dual_accel_detect.h" /* ThinkPad CMOS commands */ @@ -159,6 +164,7 @@ enum tpacpi_hkey_event_t { TP_HKEY_EV_VOL_DOWN = 0x1016, /* Volume down or unmute */ TP_HKEY_EV_VOL_MUTE = 0x1017, /* Mixer output mute */ TP_HKEY_EV_PRIVACYGUARD_TOGGLE = 0x130f, /* Toggle priv.guard on/off */ + TP_HKEY_EV_AMT_TOGGLE = 0x131a, /* Toggle AMT on/off */ /* Reasons for waking up from S3/S4 */ TP_HKEY_EV_WKUP_S3_UNDOCK = 0x2304, /* undock requested, S3 */ @@ -257,8 +263,6 @@ enum tpacpi_hkey_event_t { #define TPACPI_DBG_BRGHT 0x0020 #define TPACPI_DBG_MIXER 0x0040 -#define onoff(status, bit) ((status) & (1 << (bit)) ? "on" : "off") -#define enabled(status, bit) ((status) & (1 << (bit)) ? "enabled" : "disabled") #define strlencmp(a, b) (strncmp((a), (b), strlen(b))) @@ -1312,9 +1316,7 @@ static int tpacpi_rfk_procfs_read(const enum tpacpi_rfk_id id, struct seq_file * return status; } - seq_printf(m, "status:\t\t%s\n", - (status == TPACPI_RFK_RADIO_ON) ? - "enabled" : "disabled"); + seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status == TPACPI_RFK_RADIO_ON)); seq_printf(m, "commands:\tenable, disable\n"); } @@ -1341,8 +1343,7 @@ static int tpacpi_rfk_procfs_write(const enum tpacpi_rfk_id id, char *buf) if (status != -1) { tpacpi_disclose_usertask("procfs", "attempt to %s %s\n", - (status == TPACPI_RFK_RADIO_ON) ? - "enable" : "disable", + str_enable_disable(status == TPACPI_RFK_RADIO_ON), tpacpi_rfkill_names[id]); res = (tpacpi_rfkill_switches[id]->ops->set_status)(status); tpacpi_rfk_update_swstate(tpacpi_rfkill_switches[id]); @@ -3499,8 +3500,7 @@ static int __init hotkey_init(struct ibm_init_struct *iibm) if (acpi_evalf(hkey_handle, &status, "WLSW", "qd")) { tp_features.hotkey_wlsw = 1; radiosw_state = !!status; - pr_info("radio switch found; radios are %s\n", - enabled(status, 0)); + pr_info("radio switch found; radios are %s\n", str_enabled_disabled(status & BIT(0))); } tabletsw_state = hotkey_init_tablet_mode(); @@ -3735,6 +3735,7 @@ static bool hotkey_notify_extended_hotkey(const u32 hkey) switch (hkey) { case TP_HKEY_EV_PRIVACYGUARD_TOGGLE: + case TP_HKEY_EV_AMT_TOGGLE: tpacpi_driver_event(hkey); return true; } @@ -4159,7 +4160,7 @@ static int hotkey_read(struct seq_file *m) if (res) return res; - seq_printf(m, "status:\t\t%s\n", enabled(status, 0)); + seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status & BIT(0))); if (hotkey_all_mask) { seq_printf(m, "mask:\t\t0x%08x\n", hotkey_user_mask); seq_printf(m, "commands:\tenable, disable, reset, \n"); @@ -4292,9 +4293,8 @@ static int bluetooth_set_status(enum tpacpi_rfkill_state state) { int status; - vdbg_printk(TPACPI_DBG_RFKILL, - "will attempt to %s bluetooth\n", - (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); + vdbg_printk(TPACPI_DBG_RFKILL, "will attempt to %s bluetooth\n", + str_enable_disable(state == TPACPI_RFK_RADIO_ON)); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_bluetoothemul) { @@ -4659,9 +4659,8 @@ static int wan_set_status(enum tpacpi_rfkill_state state) { int status; - vdbg_printk(TPACPI_DBG_RFKILL, - "will attempt to %s wwan\n", - (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); + vdbg_printk(TPACPI_DBG_RFKILL, "will attempt to %s wwan\n", + str_enable_disable(state == TPACPI_RFK_RADIO_ON)); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_wwanemul) { @@ -4837,9 +4836,8 @@ static int uwb_set_status(enum tpacpi_rfkill_state state) { int status; - vdbg_printk(TPACPI_DBG_RFKILL, - "will attempt to %s UWB\n", - (state == TPACPI_RFK_RADIO_ON) ? "enable" : "disable"); + vdbg_printk(TPACPI_DBG_RFKILL, "will attempt to %s UWB\n", + str_enable_disable(state == TPACPI_RFK_RADIO_ON)); #ifdef CONFIG_THINKPAD_ACPI_DEBUGFACILITIES if (dbg_uwbemul) { @@ -5193,11 +5191,11 @@ static int video_read(struct seq_file *m) return autosw; seq_printf(m, "status:\t\tsupported\n"); - seq_printf(m, "lcd:\t\t%s\n", enabled(status, 0)); - seq_printf(m, "crt:\t\t%s\n", enabled(status, 1)); + seq_printf(m, "lcd:\t\t%s\n", str_enabled_disabled(status & BIT(0))); + seq_printf(m, "crt:\t\t%s\n", str_enabled_disabled(status & BIT(1))); if (video_supported == TPACPI_VIDEO_NEW) - seq_printf(m, "dvi:\t\t%s\n", enabled(status, 3)); - seq_printf(m, "auto:\t\t%s\n", enabled(autosw, 0)); + seq_printf(m, "dvi:\t\t%s\n", str_enabled_disabled(status & BIT(3))); + seq_printf(m, "auto:\t\t%s\n", str_enabled_disabled(autosw & BIT(0))); seq_printf(m, "commands:\tlcd_enable, lcd_disable\n"); seq_printf(m, "commands:\tcrt_enable, crt_disable\n"); if (video_supported == TPACPI_VIDEO_NEW) @@ -5628,7 +5626,7 @@ static int light_read(struct seq_file *m) status = light_get_status(); if (status < 0) return status; - seq_printf(m, "status:\t\t%s\n", onoff(status, 0)); + seq_printf(m, "status:\t\t%s\n", str_on_off(status & BIT(0))); seq_printf(m, "commands:\ton, off\n"); } @@ -6084,9 +6082,7 @@ static int __init led_init(struct ibm_init_struct *iibm) return 0; } -#define str_led_status(s) \ - ((s) == TPACPI_LED_OFF ? "off" : \ - ((s) == TPACPI_LED_ON ? "on" : "blinking")) +#define str_led_status(s) ((s) >= TPACPI_LED_BLINK ? "blinking" : str_on_off(s)) static int led_read(struct seq_file *m) { @@ -6103,8 +6099,7 @@ static int led_read(struct seq_file *m) status = led_get_status(i); if (status < 0) return -EIO; - seq_printf(m, "%d:\t\t%s\n", - i, str_led_status(status)); + seq_printf(m, "%d:\t\t%s\n", i, str_led_status(status)); } } @@ -6797,10 +6792,7 @@ static int brightness_set(unsigned int value) static int brightness_update_status(struct backlight_device *bd) { - unsigned int level = - (bd->props.fb_blank == FB_BLANK_UNBLANK && - bd->props.power == FB_BLANK_UNBLANK) ? - bd->props.brightness : 0; + int level = backlight_get_brightness(bd); dbg_printk(TPACPI_DBG_BRGHT, "backlight: attempt to set level to %d\n", @@ -7830,8 +7822,7 @@ static int volume_read(struct seq_file *m) seq_printf(m, "level:\t\t%d\n", status & TP_EC_AUDIO_LVL_MSK); - seq_printf(m, "mute:\t\t%s\n", - onoff(status, TP_EC_AUDIO_MUTESW)); + seq_printf(m, "mute:\t\t%s\n", str_on_off(status & BIT(TP_EC_AUDIO_MUTESW))); if (volume_control_allowed) { seq_printf(m, "commands:\tunmute, mute\n"); @@ -9060,7 +9051,7 @@ static int fan_read(struct seq_file *m) seq_printf(m, "status:\t\t%s\n" "level:\t\t%d\n", - (status != 0) ? "enabled" : "disabled", status); + str_enabled_disabled(status), status); break; case TPACPI_FAN_RD_TPEC: @@ -9069,8 +9060,7 @@ static int fan_read(struct seq_file *m) if (rc) return rc; - seq_printf(m, "status:\t\t%s\n", - (status != 0) ? "enabled" : "disabled"); + seq_printf(m, "status:\t\t%s\n", str_enabled_disabled(status)); rc = fan_get_speed(&speed); if (rc < 0) @@ -10268,6 +10258,7 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_CMD_FUNC_CAP 3 /* To get DYTC capabilities */ #define DYTC_FC_MMC 27 /* MMC Mode supported */ #define DYTC_FC_PSC 29 /* PSC Mode supported */ +#define DYTC_FC_AMT 31 /* AMT mode supported */ #define DYTC_GET_FUNCTION_BIT 8 /* Bits 8-11 - function setting */ #define DYTC_GET_MODE_BIT 12 /* Bits 12-15 - mode setting */ @@ -10280,6 +10271,10 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_FUNCTION_CQL 1 /* Function = 1, lap mode */ #define DYTC_FUNCTION_MMC 11 /* Function = 11, MMC mode */ #define DYTC_FUNCTION_PSC 13 /* Function = 13, PSC mode */ +#define DYTC_FUNCTION_AMT 15 /* Function = 15, AMT mode */ + +#define DYTC_MODE_AMT_ENABLE 0x1 /* Enable AMT (in balanced mode) */ +#define DYTC_MODE_AMT_DISABLE 0xF /* Disable AMT (in other modes) */ #define DYTC_MODE_MMC_PERFORM 2 /* High power mode aka performance */ #define DYTC_MODE_MMC_LOWPOWER 3 /* Low power mode */ @@ -10300,6 +10295,8 @@ static struct ibm_struct proxsensor_driver_data = { #define DYTC_DISABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_MMC_BALANCE, 0) #define DYTC_ENABLE_CQL DYTC_SET_COMMAND(DYTC_FUNCTION_CQL, DYTC_MODE_MMC_BALANCE, 1) +static int dytc_control_amt(bool enable); +static bool dytc_amt_active; static enum platform_profile_option dytc_current_profile; static atomic_t dytc_ignore_event = ATOMIC_INIT(0); @@ -10382,6 +10379,30 @@ static int dytc_profile_get(struct platform_profile_handler *pprof, return 0; } +static int dytc_control_amt(bool enable) +{ + int dummy; + int err; + int cmd; + + if (!(dytc_capabilities & BIT(DYTC_FC_AMT))) { + pr_warn("Attempting to toggle AMT on a system that doesn't advertise support\n"); + return -ENODEV; + } + + if (enable) + cmd = DYTC_SET_COMMAND(DYTC_FUNCTION_AMT, DYTC_MODE_AMT_ENABLE, enable); + else + cmd = DYTC_SET_COMMAND(DYTC_FUNCTION_AMT, DYTC_MODE_AMT_DISABLE, enable); + + pr_debug("%sabling AMT (cmd 0x%x)", enable ? "en":"dis", cmd); + err = dytc_command(cmd, &dummy); + if (err) + return err; + dytc_amt_active = enable; + return 0; +} + /* * Helper function - check if we are in CQL mode and if we are * - disable CQL, @@ -10464,6 +10485,9 @@ static int dytc_profile_set(struct platform_profile_handler *pprof, err = dytc_command(DYTC_SET_COMMAND(DYTC_FUNCTION_PSC, perfmode, 1), &output); if (err) goto unlock; + /* system supports AMT, activate it when on balanced */ + if (dytc_capabilities & BIT(DYTC_FC_AMT)) + dytc_control_amt(profile == PLATFORM_PROFILE_BALANCED); } /* Success - update current profile */ dytc_current_profile = profile; @@ -10568,6 +10592,11 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm) /* Ensure initial values are correct */ dytc_profile_refresh(); + /* Set AMT correctly now we know current profile */ + if ((dytc_capabilities & BIT(DYTC_FC_PSC)) && + (dytc_capabilities & BIT(DYTC_FC_AMT))) + dytc_control_amt(dytc_current_profile == PLATFORM_PROFILE_BALANCED); + return 0; } @@ -11011,6 +11040,15 @@ static void tpacpi_driver_event(const unsigned int hkey_event) if (changed) drm_privacy_screen_call_notifier_chain(lcdshadow_dev); } + if (hkey_event == TP_HKEY_EV_AMT_TOGGLE) { + /* If we're enabling AMT we need to force balanced mode */ + if (!dytc_amt_active) + /* This will also set AMT mode enabled */ + dytc_profile_set(NULL, PLATFORM_PROFILE_BALANCED); + else + dytc_control_amt(!dytc_amt_active); + } + } static void hotkey_driver_event(const unsigned int scancode) diff --git a/drivers/power/reset/brcm-kona-reset.c b/drivers/power/reset/brcm-kona-reset.c index 8eaa959d8be6..3de024e3ceb7 100644 --- a/drivers/power/reset/brcm-kona-reset.c +++ b/drivers/power/reset/brcm-kona-reset.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include #include diff --git a/drivers/power/reset/brcmstb-reboot.c b/drivers/power/reset/brcmstb-reboot.c index 884b53c483c0..0f2944dc9355 100644 --- a/drivers/power/reset/brcmstb-reboot.c +++ b/drivers/power/reset/brcmstb-reboot.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2013 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2013 Broadcom Corporation #include #include diff --git a/drivers/power/supply/cpcap-battery.c b/drivers/power/supply/cpcap-battery.c index ae284bdd6cc3..d98d9244e394 100644 --- a/drivers/power/supply/cpcap-battery.c +++ b/drivers/power/supply/cpcap-battery.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Battery driver for CPCAP PMIC * @@ -7,15 +8,6 @@ * drivers: * * Copyright (C) 2009-2010 Motorola, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/power/supply/surface_battery.c b/drivers/power/supply/surface_battery.c index 5ec2e6bb2465..540707882bb0 100644 --- a/drivers/power/supply/surface_battery.c +++ b/drivers/power/supply/surface_battery.c @@ -802,7 +802,7 @@ static int spwr_battery_register(struct spwr_battery_device *bat) if (IS_ERR(bat->psy)) return PTR_ERR(bat->psy); - return ssam_notifier_register(bat->sdev->ctrl, &bat->notif); + return ssam_device_notifier_register(bat->sdev, &bat->notif); } @@ -837,7 +837,7 @@ static void surface_battery_remove(struct ssam_device *sdev) { struct spwr_battery_device *bat = ssam_device_get_drvdata(sdev); - ssam_notifier_unregister(sdev->ctrl, &bat->notif); + ssam_device_notifier_unregister(sdev, &bat->notif); cancel_delayed_work_sync(&bat->update_work); } diff --git a/drivers/power/supply/surface_charger.c b/drivers/power/supply/surface_charger.c index a060c36c7766..59182d55742d 100644 --- a/drivers/power/supply/surface_charger.c +++ b/drivers/power/supply/surface_charger.c @@ -216,7 +216,7 @@ static int spwr_ac_register(struct spwr_ac_device *ac) if (IS_ERR(ac->psy)) return PTR_ERR(ac->psy); - return ssam_notifier_register(ac->sdev->ctrl, &ac->notif); + return ssam_device_notifier_register(ac->sdev, &ac->notif); } @@ -251,7 +251,7 @@ static void surface_ac_remove(struct ssam_device *sdev) { struct spwr_ac_device *ac = ssam_device_get_drvdata(sdev); - ssam_notifier_unregister(sdev->ctrl, &ac->notif); + ssam_device_notifier_unregister(sdev, &ac->notif); } static const struct spwr_psy_properties spwr_psy_props_adp1 = { diff --git a/drivers/ptp/ptp_dte.c b/drivers/ptp/ptp_dte.c index 82d31ba32690..8641fd060491 100644 --- a/drivers/ptp/ptp_dte.c +++ b/drivers/ptp/ptp_dte.c @@ -1,15 +1,5 @@ -/* - * Copyright 2017 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2017 Broadcom #include #include diff --git a/drivers/pwm/pwm-bcm-iproc.c b/drivers/pwm/pwm-bcm-iproc.c index 0226bf697f09..7251037d4dd5 100644 --- a/drivers/pwm/pwm-bcm-iproc.c +++ b/drivers/pwm/pwm-bcm-iproc.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2016 Broadcom - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2016 Broadcom #include #include diff --git a/drivers/pwm/pwm-bcm-kona.c b/drivers/pwm/pwm-bcm-kona.c index f171169c1c1f..4fa6e249e4cf 100644 --- a/drivers/pwm/pwm-bcm-kona.c +++ b/drivers/pwm/pwm-bcm-kona.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014 Broadcom Corporation #include #include diff --git a/drivers/regulator/cpcap-regulator.c b/drivers/regulator/cpcap-regulator.c index 79b3eb3222c6..b0c225d98631 100644 --- a/drivers/regulator/cpcap-regulator.c +++ b/drivers/regulator/cpcap-regulator.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Motorola CPCAP PMIC regulator driver * @@ -6,15 +7,6 @@ * * Rewritten for mainline kernel to use device tree and regmap * Copyright (C) 2017 Tony Lindgren - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/regulator/cros-ec-regulator.c b/drivers/regulator/cros-ec-regulator.c index c4754f3cf233..1591636f86c3 100644 --- a/drivers/regulator/cros-ec-regulator.c +++ b/drivers/regulator/cros-ec-regulator.c @@ -22,36 +22,6 @@ struct cros_ec_regulator_data { u16 num_voltages; }; -static int cros_ec_cmd(struct cros_ec_device *ec, u32 version, u32 command, - void *outdata, u32 outsize, void *indata, u32 insize) -{ - struct cros_ec_command *msg; - int ret; - - msg = kzalloc(sizeof(*msg) + max(outsize, insize), GFP_KERNEL); - if (!msg) - return -ENOMEM; - - msg->version = version; - msg->command = command; - msg->outsize = outsize; - msg->insize = insize; - - if (outdata && outsize > 0) - memcpy(msg->data, outdata, outsize); - - ret = cros_ec_cmd_xfer_status(ec, msg); - if (ret < 0) - goto cleanup; - - if (insize) - memcpy(indata, msg->data, insize); - -cleanup: - kfree(msg); - return ret; -} - static int cros_ec_regulator_enable(struct regulator_dev *dev) { struct cros_ec_regulator_data *data = rdev_get_drvdata(dev); @@ -61,7 +31,7 @@ static int cros_ec_regulator_enable(struct regulator_dev *dev) }; return cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_ENABLE, &cmd, - sizeof(cmd), NULL, 0); + sizeof(cmd), NULL, 0); } static int cros_ec_regulator_disable(struct regulator_dev *dev) @@ -73,7 +43,7 @@ static int cros_ec_regulator_disable(struct regulator_dev *dev) }; return cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_ENABLE, &cmd, - sizeof(cmd), NULL, 0); + sizeof(cmd), NULL, 0); } static int cros_ec_regulator_is_enabled(struct regulator_dev *dev) @@ -161,7 +131,7 @@ static int cros_ec_regulator_init_info(struct device *dev, int ret; ret = cros_ec_cmd(data->ec_dev, 0, EC_CMD_REGULATOR_GET_INFO, &cmd, - sizeof(cmd), &resp, sizeof(resp)); + sizeof(cmd), &resp, sizeof(resp)); if (ret < 0) return ret; diff --git a/drivers/regulator/isl6271a-regulator.c b/drivers/regulator/isl6271a-regulator.c index 6f28bba81d13..591a64e1ca61 100644 --- a/drivers/regulator/isl6271a-regulator.c +++ b/drivers/regulator/isl6271a-regulator.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * isl6271a-regulator.c * * Support for Intersil ISL6271A voltage regulator * * Copyright (C) 2010 Marek Vasut - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. */ #include diff --git a/drivers/regulator/lp873x-regulator.c b/drivers/regulator/lp873x-regulator.c index c38387e0fbb2..d6e597922cb5 100644 --- a/drivers/regulator/lp873x-regulator.c +++ b/drivers/regulator/lp873x-regulator.c @@ -1,16 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Regulator driver for LP873X PMIC * * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include diff --git a/drivers/regulator/max8973-regulator.c b/drivers/regulator/max8973-regulator.c index cb7e50003f70..fdcb0f508984 100644 --- a/drivers/regulator/max8973-regulator.c +++ b/drivers/regulator/max8973-regulator.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * max8973-regulator.c -- Maxim max8973A * @@ -6,20 +7,6 @@ * Copyright (c) 2012, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #include diff --git a/drivers/regulator/ti-abb-regulator.c b/drivers/regulator/ti-abb-regulator.c index afa336be1cc9..ce00db27589a 100644 --- a/drivers/regulator/ti-abb-regulator.c +++ b/drivers/regulator/ti-abb-regulator.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instruments SoC Adaptive Body Bias(ABB) Regulator * @@ -7,15 +8,6 @@ * Copyright (C) 2012-2013 Texas Instruments, Inc. * Andrii Tseglytskyi * Nishanth Menon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/regulator/tps51632-regulator.c b/drivers/regulator/tps51632-regulator.c index a15e415e61d5..85e3326b99eb 100644 --- a/drivers/regulator/tps51632-regulator.c +++ b/drivers/regulator/tps51632-regulator.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps51632-regulator.c -- TI TPS51632 * @@ -7,20 +8,6 @@ * Copyright (c) 2012, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #include diff --git a/drivers/regulator/tps62360-regulator.c b/drivers/regulator/tps62360-regulator.c index 574958690ace..7c697bdf344e 100644 --- a/drivers/regulator/tps62360-regulator.c +++ b/drivers/regulator/tps62360-regulator.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps62360.c -- TI tps62360 * @@ -6,20 +7,6 @@ * Copyright (c) 2012, NVIDIA Corporation. * * Author: Laxman Dewangan - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA - * 02111-1307, USA */ #include diff --git a/drivers/regulator/tps65023-regulator.c b/drivers/regulator/tps65023-regulator.c index f25806531c7e..d24333344f93 100644 --- a/drivers/regulator/tps65023-regulator.c +++ b/drivers/regulator/tps65023-regulator.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps65023-regulator.c * * Supports TPS65023 Regulator * * Copyright (C) 2009 Texas Instrument Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. */ #include diff --git a/drivers/regulator/tps6507x-regulator.c b/drivers/regulator/tps6507x-regulator.c index eafbc2bb4b57..b83816ee6867 100644 --- a/drivers/regulator/tps6507x-regulator.c +++ b/drivers/regulator/tps6507x-regulator.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps6507x-regulator.c * * Regulator driver for TPS65073 PMIC * * Copyright (C) 2009 Texas Instrument Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any kind, - * whether express or implied; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. */ #include diff --git a/drivers/regulator/tps65086-regulator.c b/drivers/regulator/tps65086-regulator.c index 070c956216b0..f1bc54c825dd 100644 --- a/drivers/regulator/tps65086-regulator.c +++ b/drivers/regulator/tps65086-regulator.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * * Author: Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65912 driver */ diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index e88ed96f4744..6bb5b02e19e2 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps65217-regulator.c * * Regulator driver for TPS65217 PMIC * * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/regulator/tps65218-regulator.c b/drivers/regulator/tps65218-regulator.c index fa263545a70e..48809c3b3abc 100644 --- a/drivers/regulator/tps65218-regulator.c +++ b/drivers/regulator/tps65218-regulator.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps65218-regulator.c * * Regulator driver for TPS65218 PMIC * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #include diff --git a/drivers/regulator/tps65912-regulator.c b/drivers/regulator/tps65912-regulator.c index b52d4f2874b7..76f90202ae09 100644 --- a/drivers/regulator/tps65912-regulator.c +++ b/drivers/regulator/tps65912-regulator.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Regulator driver for TI TPS65912x PMICs * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera */ diff --git a/drivers/reset/reset-ti-sci.c b/drivers/reset/reset-ti-sci.c index b799aefad547..cc01fa5b0bea 100644 --- a/drivers/reset/reset-ti-sci.c +++ b/drivers/reset/reset-ti-sci.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Texas Instrument's System Control Interface (TI-SCI) reset driver * * Copyright (C) 2015-2017 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/reset/reset-ti-syscon.c b/drivers/reset/reset-ti-syscon.c index 2b92775d58f0..f0dd7ffc3b72 100644 --- a/drivers/reset/reset-ti-syscon.c +++ b/drivers/reset/reset-ti-syscon.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * TI SYSCON regmap reset driver * * Copyright (C) 2015-2016 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * Suman Anna - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 4df8bf6505fc..ea82821599f6 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1725,7 +1725,7 @@ void dasd_int_handler(struct ccw_device *cdev, unsigned long intparm, dasd_put_device(device); } - /* check for for attention message */ + /* check for attention message */ if (scsw_dstat(&irb->scsw) & DEV_STAT_ATTENTION) { device = dasd_device_from_cdev_locked(cdev); if (!IS_ERR(device)) { diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index e9edf3b6ed7c..94ee59864971 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -639,6 +639,7 @@ static void dasd_diag_setup_blk_queue(struct dasd_block *block) /* With page sized segments each segment can be translated into one idaw/tidaw */ blk_queue_max_segment_size(q, PAGE_SIZE); blk_queue_segment_boundary(q, PAGE_SIZE - 1); + blk_queue_dma_alignment(q, PAGE_SIZE - 1); } static int dasd_diag_pe_handler(struct dasd_device *device, diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 836838f7d686..3cc93e2e4e15 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -6626,6 +6626,7 @@ static void dasd_eckd_setup_blk_queue(struct dasd_block *block) /* With page sized segments each segment can be translated into one idaw/tidaw */ blk_queue_max_segment_size(q, PAGE_SIZE); blk_queue_segment_boundary(q, PAGE_SIZE - 1); + blk_queue_dma_alignment(q, PAGE_SIZE - 1); } static struct ccw_driver dasd_eckd_driver = { diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 4d8d1759775a..5187705bd0f3 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -863,7 +863,7 @@ dcssblk_submit_bio(struct bio *bio) unsigned long source_addr; unsigned long bytes_done; - blk_queue_split(&bio); + bio = bio_split_to_limits(bio); bytes_done = 0; dev_info = bio->bi_bdev->bd_disk->private_data; diff --git a/drivers/s390/char/sclp_early.c b/drivers/s390/char/sclp_early.c index dd313ff57df3..d15b0d541de3 100644 --- a/drivers/s390/char/sclp_early.c +++ b/drivers/s390/char/sclp_early.c @@ -45,6 +45,10 @@ static void __init sclp_early_facilities_detect(void) sclp.has_gisaf = !!(sccb->fac118 & 0x08); sclp.has_hvs = !!(sccb->fac119 & 0x80); sclp.has_kss = !!(sccb->fac98 & 0x01); + sclp.has_aisii = !!(sccb->fac118 & 0x40); + sclp.has_aeni = !!(sccb->fac118 & 0x20); + sclp.has_aisi = !!(sccb->fac118 & 0x10); + sclp.has_zpci_lsi = !!(sccb->fac118 & 0x01); if (sccb->fac85 & 0x02) S390_lowcore.machine_flags |= MACHINE_FLAG_ESOP; if (sccb->fac91 & 0x40) diff --git a/drivers/s390/cio/airq.c b/drivers/s390/cio/airq.c index c0ed364bf446..34967e67249e 100644 --- a/drivers/s390/cio/airq.c +++ b/drivers/s390/cio/airq.c @@ -99,7 +99,7 @@ static irqreturn_t do_airq_interrupt(int irq, void *dummy) rcu_read_lock(); hlist_for_each_entry_rcu(airq, head, list) if ((*airq->lsi_ptr & airq->lsi_mask) != 0) - airq->handler(airq, !tpi_info->directed_irq); + airq->handler(airq, tpi_info); rcu_read_unlock(); return IRQ_HANDLED; @@ -122,10 +122,12 @@ static inline unsigned long iv_size(unsigned long bits) * airq_iv_create - create an interrupt vector * @bits: number of bits in the interrupt vector * @flags: allocation flags + * @vec: pointer to pinned guest memory if AIRQ_IV_GUESTVEC * * Returns a pointer to an interrupt vector structure */ -struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags) +struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags, + unsigned long *vec) { struct airq_iv *iv; unsigned long size; @@ -146,6 +148,8 @@ struct airq_iv *airq_iv_create(unsigned long bits, unsigned long flags) &iv->vector_dma); if (!iv->vector) goto out_free; + } else if (flags & AIRQ_IV_GUESTVEC) { + iv->vector = vec; } else { iv->vector = cio_dma_zalloc(size); if (!iv->vector) @@ -185,7 +189,7 @@ out_free: kfree(iv->avail); if (iv->flags & AIRQ_IV_CACHELINE && iv->vector) dma_pool_free(airq_iv_cache, iv->vector, iv->vector_dma); - else + else if (!(iv->flags & AIRQ_IV_GUESTVEC)) cio_dma_free(iv->vector, size); kfree(iv); out: @@ -204,7 +208,7 @@ void airq_iv_release(struct airq_iv *iv) kfree(iv->bitlock); if (iv->flags & AIRQ_IV_CACHELINE) dma_pool_free(airq_iv_cache, iv->vector, iv->vector_dma); - else + else if (!(iv->flags & AIRQ_IV_GUESTVEC)) cio_dma_free(iv->vector, iv_size(iv->bits)); kfree(iv->avail); kfree(iv); diff --git a/drivers/s390/cio/qdio_thinint.c b/drivers/s390/cio/qdio_thinint.c index 8e09bf3a2fcd..9b9335dd06db 100644 --- a/drivers/s390/cio/qdio_thinint.c +++ b/drivers/s390/cio/qdio_thinint.c @@ -15,6 +15,7 @@ #include #include #include +#include #include "cio.h" #include "ioasm.h" @@ -93,9 +94,10 @@ static inline u32 clear_shared_ind(void) /** * tiqdio_thinint_handler - thin interrupt handler for qdio * @airq: pointer to adapter interrupt descriptor - * @floating: flag to recognize floating vs. directed interrupts (unused) + * @tpi_info: interrupt information (e.g. floating vs directed -- unused) */ -static void tiqdio_thinint_handler(struct airq_struct *airq, bool floating) +static void tiqdio_thinint_handler(struct airq_struct *airq, + struct tpi_info *tpi_info) { u64 irq_time = S390_lowcore.int_clock; u32 si_used = clear_shared_ind(); diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 0a9045b49c50..95cfdeab5baf 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -131,7 +132,8 @@ static int ap_max_adapter_id = 63; static struct bus_type ap_bus_type; /* Adapter interrupt definitions */ -static void ap_interrupt_handler(struct airq_struct *airq, bool floating); +static void ap_interrupt_handler(struct airq_struct *airq, + struct tpi_info *tpi_info); static bool ap_irq_flag; @@ -452,9 +454,10 @@ static enum hrtimer_restart ap_poll_timeout(struct hrtimer *unused) /** * ap_interrupt_handler() - Schedule ap_tasklet on interrupt * @airq: pointer to adapter interrupt descriptor - * @floating: ignored + * @tpi_info: ignored */ -static void ap_interrupt_handler(struct airq_struct *airq, bool floating) +static void ap_interrupt_handler(struct airq_struct *airq, + struct tpi_info *tpi_info) { inc_irq_stat(IRQIO_APB); tasklet_schedule(&ap_tasklet); diff --git a/drivers/s390/scsi/zfcp_diag.h b/drivers/s390/scsi/zfcp_diag.h index da55133da8fe..15c25fefe91a 100644 --- a/drivers/s390/scsi/zfcp_diag.h +++ b/drivers/s390/scsi/zfcp_diag.h @@ -2,7 +2,7 @@ /* * zfcp device driver * - * Definitions for handling diagnostics in the the zfcp device driver. + * Definitions for handling diagnostics in the zfcp device driver. * * Copyright IBM Corp. 2018, 2020 */ diff --git a/drivers/s390/scsi/zfcp_sysfs.c b/drivers/s390/scsi/zfcp_sysfs.c index dbf3e50444e6..cb67fa80fb12 100644 --- a/drivers/s390/scsi/zfcp_sysfs.c +++ b/drivers/s390/scsi/zfcp_sysfs.c @@ -672,7 +672,7 @@ ZFCP_DEFINE_SCSI_ATTR(zfcp_in_recovery, "%d\n", ZFCP_DEFINE_SCSI_ATTR(zfcp_status, "0x%08x\n", atomic_read(&zfcp_sdev->status)); -struct attribute *zfcp_sdev_attrs[] = { +static struct attribute *zfcp_sdev_attrs[] = { &dev_attr_fcp_lun.attr, &dev_attr_wwpn.attr, &dev_attr_hba_id.attr, diff --git a/drivers/s390/virtio/virtio_ccw.c b/drivers/s390/virtio/virtio_ccw.c index 161d3b141f0d..aa96f67dd0b1 100644 --- a/drivers/s390/virtio/virtio_ccw.c +++ b/drivers/s390/virtio/virtio_ccw.c @@ -33,6 +33,7 @@ #include #include #include +#include /* * virtio related functions @@ -204,7 +205,8 @@ static void drop_airq_indicator(struct virtqueue *vq, struct airq_info *info) write_unlock_irqrestore(&info->lock, flags); } -static void virtio_airq_handler(struct airq_struct *airq, bool floating) +static void virtio_airq_handler(struct airq_struct *airq, + struct tpi_info *tpi_info) { struct airq_info *info = container_of(airq, struct airq_info, airq); unsigned long ai; @@ -240,7 +242,7 @@ static struct airq_info *new_airq_info(int index) return NULL; rwlock_init(&info->lock); info->aiv = airq_iv_create(VIRTIO_IV_BITS, AIRQ_IV_ALLOC | AIRQ_IV_PTR - | AIRQ_IV_CACHELINE); + | AIRQ_IV_CACHELINE, NULL); if (!info->aiv) { kfree(info); return NULL; diff --git a/drivers/scsi/BusLogic.c b/drivers/scsi/BusLogic.c index a897c8f914cf..f2abffce2659 100644 --- a/drivers/scsi/BusLogic.c +++ b/drivers/scsi/BusLogic.c @@ -2515,12 +2515,26 @@ static int blogic_resultcode(struct blogic_adapter *adapter, return (hoststatus << 16) | tgt_status; } +/* + * turn the dma address from an inbox into a ccb pointer + * This is rather inefficient. + */ +static struct blogic_ccb * +blogic_inbox_to_ccb(struct blogic_adapter *adapter, struct blogic_inbox *inbox) +{ + struct blogic_ccb *ccb; + + for (ccb = adapter->all_ccbs; ccb; ccb = ccb->next_all) + if (inbox->ccb == ccb->dma_handle) + break; + + return ccb; +} /* blogic_scan_inbox scans the Incoming Mailboxes saving any Incoming Mailbox entries for completion processing. */ - static void blogic_scan_inbox(struct blogic_adapter *adapter) { /* @@ -2540,17 +2554,14 @@ static void blogic_scan_inbox(struct blogic_adapter *adapter) enum blogic_cmplt_code comp_code; while ((comp_code = next_inbox->comp_code) != BLOGIC_INBOX_FREE) { - /* - We are only allowed to do this because we limit our - architectures we run on to machines where bus_to_virt( - actually works. There *needs* to be a dma_addr_to_virt() - in the new PCI DMA mapping interface to replace - bus_to_virt() or else this code is going to become very - innefficient. - */ - struct blogic_ccb *ccb = - (struct blogic_ccb *) bus_to_virt(next_inbox->ccb); - if (comp_code != BLOGIC_CMD_NOTFOUND) { + struct blogic_ccb *ccb = blogic_inbox_to_ccb(adapter, next_inbox); + if (!ccb) { + /* + * This should never happen, unless the CCB list is + * corrupted in memory. + */ + blogic_warn("Could not find CCB for dma address %x\n", adapter, next_inbox->ccb); + } else if (comp_code != BLOGIC_CMD_NOTFOUND) { if (ccb->status == BLOGIC_CCB_ACTIVE || ccb->status == BLOGIC_CCB_RESET) { /* diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig index a9fe5152addd..955cb69a5418 100644 --- a/drivers/scsi/Kconfig +++ b/drivers/scsi/Kconfig @@ -458,17 +458,6 @@ config SCSI_MVUMI To compile this driver as a module, choose M here: the module will be called mvumi. -config SCSI_DPT_I2O - tristate "Adaptec I2O RAID support " - depends on SCSI && PCI && VIRT_TO_BUS - help - This driver supports all of Adaptec's I2O based RAID controllers as - well as the DPT SmartRaid V cards. This is an Adaptec maintained - driver by Deanna Bonds. See . - - To compile this driver as a module, choose M here: the - module will be called dpt_i2o. - config SCSI_ADVANSYS tristate "AdvanSys SCSI support" depends on SCSI @@ -513,7 +502,7 @@ config SCSI_HPTIOP config SCSI_BUSLOGIC tristate "BusLogic SCSI support" - depends on PCI && SCSI && VIRT_TO_BUS + depends on PCI && SCSI help This is support for BusLogic MultiMaster and FlashPoint SCSI Host Adapters. Consult the SCSI-HOWTO, available from diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile index 2ad3bc052531..f055bfd54a68 100644 --- a/drivers/scsi/Makefile +++ b/drivers/scsi/Makefile @@ -63,7 +63,6 @@ obj-$(CONFIG_BVME6000_SCSI) += 53c700.o bvme6000_scsi.o obj-$(CONFIG_SCSI_SIM710) += 53c700.o sim710.o obj-$(CONFIG_SCSI_ADVANSYS) += advansys.o obj-$(CONFIG_SCSI_BUSLOGIC) += BusLogic.o -obj-$(CONFIG_SCSI_DPT_I2O) += dpt_i2o.o obj-$(CONFIG_SCSI_ARCMSR) += arcmsr/ obj-$(CONFIG_SCSI_AHA152X) += aha152x.o obj-$(CONFIG_SCSI_AHA1542) += aha1542.o diff --git a/drivers/scsi/a2091.c b/drivers/scsi/a2091.c index cf703a1ecdda..74312400468b 100644 --- a/drivers/scsi/a2091.c +++ b/drivers/scsi/a2091.c @@ -24,8 +24,11 @@ struct a2091_hostdata { struct WD33C93_hostdata wh; struct a2091_scsiregs *regs; + struct device *dev; }; +#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE) + static irqreturn_t a2091_intr(int irq, void *data) { struct Scsi_Host *instance = data; @@ -45,15 +48,31 @@ static irqreturn_t a2091_intr(int irq, void *data) static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd); + unsigned long len = scsi_pointer->this_residual; struct Scsi_Host *instance = cmd->device->host; struct a2091_hostdata *hdata = shost_priv(instance); struct WD33C93_hostdata *wh = &hdata->wh; struct a2091_scsiregs *regs = hdata->regs; unsigned short cntr = CNTR_PDMD | CNTR_INTEN; - unsigned long addr = virt_to_bus(scsi_pointer->ptr); + dma_addr_t addr; + + addr = dma_map_single(hdata->dev, scsi_pointer->ptr, + len, DMA_DIR(dir_in)); + if (dma_mapping_error(hdata->dev, addr)) { + dev_warn(hdata->dev, "cannot map SCSI data block %p\n", + scsi_pointer->ptr); + return 1; + } + scsi_pointer->dma_handle = addr; /* don't allow DMA if the physical address is bad */ if (addr & A2091_XFER_MASK) { + /* drop useless mapping */ + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(dir_in)); + scsi_pointer->dma_handle = (dma_addr_t) NULL; + wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff; wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len, GFP_KERNEL); @@ -64,8 +83,21 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) return 1; } - /* get the physical address of the bounce buffer */ - addr = virt_to_bus(wh->dma_bounce_buffer); + if (!dir_in) { + /* copy to bounce buffer for a write */ + memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, + scsi_pointer->this_residual); + } + + /* will flush/invalidate cache for us */ + addr = dma_map_single(hdata->dev, wh->dma_bounce_buffer, + wh->dma_bounce_len, DMA_DIR(dir_in)); + /* can't map buffer; use PIO */ + if (dma_mapping_error(hdata->dev, addr)) { + dev_warn(hdata->dev, "cannot map bounce buffer %p\n", + wh->dma_bounce_buffer); + return 1; + } /* the bounce buffer may not be in the first 16M of physmem */ if (addr & A2091_XFER_MASK) { @@ -76,11 +108,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) return 1; } - if (!dir_in) { - /* copy to bounce buffer for a write */ - memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, - scsi_pointer->this_residual); - } + scsi_pointer->dma_handle = addr; } /* setup dma direction */ @@ -95,13 +123,8 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) /* setup DMA *physical* address */ regs->ACR = addr; - if (dir_in) { - /* invalidate any cache */ - cache_clear(addr, scsi_pointer->this_residual); - } else { - /* push any dirty cache */ - cache_push(addr, scsi_pointer->this_residual); - } + /* no more cache flush here - dma_map_single() takes care */ + /* start DMA */ regs->ST_DMA = 1; @@ -142,6 +165,10 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, /* restore the CONTROL bits (minus the direction flag) */ regs->CNTR = CNTR_PDMD | CNTR_INTEN; + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(wh->dma_dir)); + /* copy from a bounce buffer, if necessary */ if (status && wh->dma_bounce_buffer) { if (wh->dma_dir) @@ -178,6 +205,11 @@ static int a2091_probe(struct zorro_dev *z, const struct zorro_device_id *ent) wd33c93_regs wdregs; struct a2091_hostdata *hdata; + if (dma_set_mask_and_coherent(&z->dev, DMA_BIT_MASK(24))) { + dev_warn(&z->dev, "cannot use 24 bit DMA\n"); + return -ENODEV; + } + if (!request_mem_region(z->resource.start, 256, "wd33c93")) return -EBUSY; @@ -198,6 +230,7 @@ static int a2091_probe(struct zorro_dev *z, const struct zorro_device_id *ent) wdregs.SCMD = ®s->SCMD; hdata = shost_priv(instance); + hdata->dev = &z->dev; hdata->wh.no_sync = 0xff; hdata->wh.fast = 0; hdata->wh.dma_mode = CTRL_DMA; diff --git a/drivers/scsi/a3000.c b/drivers/scsi/a3000.c index dd161885eed1..2c5cb1a02e86 100644 --- a/drivers/scsi/a3000.c +++ b/drivers/scsi/a3000.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -25,8 +26,11 @@ struct a3000_hostdata { struct WD33C93_hostdata wh; struct a3000_scsiregs *regs; + struct device *dev; }; +#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE) + static irqreturn_t a3000_intr(int irq, void *data) { struct Scsi_Host *instance = data; @@ -49,20 +53,38 @@ static irqreturn_t a3000_intr(int irq, void *data) static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd); + unsigned long len = scsi_pointer->this_residual; struct Scsi_Host *instance = cmd->device->host; struct a3000_hostdata *hdata = shost_priv(instance); struct WD33C93_hostdata *wh = &hdata->wh; struct a3000_scsiregs *regs = hdata->regs; unsigned short cntr = CNTR_PDMD | CNTR_INTEN; - unsigned long addr = virt_to_bus(scsi_pointer->ptr); + dma_addr_t addr; + + addr = dma_map_single(hdata->dev, scsi_pointer->ptr, + len, DMA_DIR(dir_in)); + if (dma_mapping_error(hdata->dev, addr)) { + dev_warn(hdata->dev, "cannot map SCSI data block %p\n", + scsi_pointer->ptr); + return 1; + } + scsi_pointer->dma_handle = addr; /* * if the physical address has the wrong alignment, or if * physical address is bad, or if it is a write and at the * end of a physical memory chunk, then allocate a bounce * buffer + * MSch 20220629 - only wrong alignment tested - bounce + * buffer returned by kmalloc is guaranteed to be aligned */ if (addr & A3000_XFER_MASK) { + WARN_ONCE(1, "Invalid alignment for DMA!"); + /* drop useless mapping */ + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(dir_in)); + wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff; wh->dma_bounce_buffer = kmalloc(wh->dma_bounce_len, GFP_KERNEL); @@ -70,6 +92,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) /* can't allocate memory; use PIO */ if (!wh->dma_bounce_buffer) { wh->dma_bounce_len = 0; + scsi_pointer->dma_handle = (dma_addr_t) NULL; return 1; } @@ -79,7 +102,15 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) scsi_pointer->this_residual); } - addr = virt_to_bus(wh->dma_bounce_buffer); + addr = dma_map_single(hdata->dev, scsi_pointer->ptr, + len, DMA_DIR(dir_in)); + if (dma_mapping_error(hdata->dev, addr)) { + dev_warn(hdata->dev, + "cannot map SCSI data block %p\n", + scsi_pointer->ptr); + return 1; + } + scsi_pointer->dma_handle = addr; } /* setup dma direction */ @@ -94,13 +125,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) /* setup DMA *physical* address */ regs->ACR = addr; - if (dir_in) { - /* invalidate any cache */ - cache_clear(addr, scsi_pointer->this_residual); - } else { - /* push any dirty cache */ - cache_push(addr, scsi_pointer->this_residual); - } + /* no more cache flush here - dma_map_single() takes care */ /* start DMA */ mb(); /* make sure setup is completed */ @@ -151,6 +176,10 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, regs->CNTR = CNTR_PDMD | CNTR_INTEN; mb(); /* make sure CNTR is updated before next IO */ + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(wh->dma_dir)); + /* copy from a bounce buffer, if necessary */ if (status && wh->dma_bounce_buffer) { if (SCpnt) { @@ -193,6 +222,11 @@ static int __init amiga_a3000_scsi_probe(struct platform_device *pdev) wd33c93_regs wdregs; struct a3000_hostdata *hdata; + if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32))) { + dev_warn(&pdev->dev, "cannot use 32 bit DMA\n"); + return -ENODEV; + } + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) return -ENODEV; @@ -216,6 +250,7 @@ static int __init amiga_a3000_scsi_probe(struct platform_device *pdev) wdregs.SCMD = ®s->SCMD; hdata = shost_priv(instance); + hdata->dev = &pdev->dev; hdata->wh.no_sync = 0xff; hdata->wh.fast = 0; hdata->wh.dma_mode = CTRL_DMA; diff --git a/drivers/scsi/aacraid/aachba.c b/drivers/scsi/aacraid/aachba.c index 81462f4ddb90..4d4cb47b3846 100644 --- a/drivers/scsi/aacraid/aachba.c +++ b/drivers/scsi/aacraid/aachba.c @@ -1050,7 +1050,7 @@ static void get_container_serial_callback(void *context, struct fib * fibptr) vpdpage83data.type1.productid)); /* Convert to ascii based serial number. - * The LSB is the the end. + * The LSB is the end. */ for (i = 0; i < 8; i++) { u8 temp = diff --git a/drivers/scsi/aic94xx/aic94xx_dev.c b/drivers/scsi/aic94xx/aic94xx_dev.c index 73506a459bf8..91d196f26b76 100644 --- a/drivers/scsi/aic94xx/aic94xx_dev.c +++ b/drivers/scsi/aic94xx/aic94xx_dev.c @@ -159,7 +159,7 @@ static int asd_init_target_ddb(struct domain_device *dev) flags |= OPEN_REQUIRED; if ((dev->dev_type == SAS_SATA_DEV) || (dev->tproto & SAS_PROTOCOL_STP)) { - struct smp_resp *rps_resp = &dev->sata_dev.rps_resp; + struct smp_rps_resp *rps_resp = &dev->sata_dev.rps_resp; if (rps_resp->frame_type == SMP_RESPONSE && rps_resp->function == SMP_REPORT_PHY_SATA && rps_resp->result == SMP_RESP_FUNC_ACC) { diff --git a/drivers/scsi/be2iscsi/be_main.c b/drivers/scsi/be2iscsi/be_main.c index 3bb0adefbe06..50a577ac3bb4 100644 --- a/drivers/scsi/be2iscsi/be_main.c +++ b/drivers/scsi/be2iscsi/be_main.c @@ -231,6 +231,7 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc) cls_session = starget_to_session(scsi_target(sc->device)); session = cls_session->dd_data; +completion_check: /* check if we raced, task just got cleaned up under us */ spin_lock_bh(&session->back_lock); if (!abrt_task || !abrt_task->sc) { @@ -238,7 +239,13 @@ static int beiscsi_eh_abort(struct scsi_cmnd *sc) return SUCCESS; } /* get a task ref till FW processes the req for the ICD used */ - __iscsi_get_task(abrt_task); + if (!iscsi_get_task(abrt_task)) { + spin_unlock(&session->back_lock); + /* We are just about to call iscsi_free_task so wait for it. */ + udelay(5); + goto completion_check; + } + abrt_io_task = abrt_task->dd_data; conn = abrt_task->conn; beiscsi_conn = conn->dd_data; @@ -323,7 +330,15 @@ static int beiscsi_eh_device_reset(struct scsi_cmnd *sc) } /* get a task ref till FW processes the req for the ICD used */ - __iscsi_get_task(task); + if (!iscsi_get_task(task)) { + /* + * The task has completed in the driver and is + * completing in libiscsi. Just ignore it here. When we + * call iscsi_eh_device_reset, it will wait for us. + */ + continue; + } + io_task = task->dd_data; /* mark WRB invalid which have been not processed by FW yet */ if (is_chip_be2_be3r(phba)) { @@ -5745,7 +5760,7 @@ static void beiscsi_remove(struct pci_dev *pcidev) cancel_work_sync(&phba->sess_work); beiscsi_iface_destroy_default(phba); - iscsi_host_remove(phba->shost); + iscsi_host_remove(phba->shost, false); beiscsi_disable_port(phba, 1); /* after cancelling boot_work */ diff --git a/drivers/scsi/bnx2i/bnx2i_iscsi.c b/drivers/scsi/bnx2i/bnx2i_iscsi.c index 15fbd09baa94..a3c800e04a2e 100644 --- a/drivers/scsi/bnx2i/bnx2i_iscsi.c +++ b/drivers/scsi/bnx2i/bnx2i_iscsi.c @@ -909,7 +909,7 @@ void bnx2i_free_hba(struct bnx2i_hba *hba) { struct Scsi_Host *shost = hba->shost; - iscsi_host_remove(shost); + iscsi_host_remove(shost, false); INIT_LIST_HEAD(&hba->ep_ofld_list); INIT_LIST_HEAD(&hba->ep_active_list); INIT_LIST_HEAD(&hba->ep_destroy_list); diff --git a/drivers/scsi/ch.c b/drivers/scsi/ch.c index 908854869864..7ab29eaec6f3 100644 --- a/drivers/scsi/ch.c +++ b/drivers/scsi/ch.c @@ -63,7 +63,7 @@ static int verbose = 1; module_param(verbose, int, 0644); MODULE_PARM_DESC(verbose,"be verbose (default: on)"); -static int debug = 0; +static int debug; module_param(debug, int, 0644); MODULE_PARM_DESC(debug,"enable/disable debug messages, also prints more " "detailed sense codes on scsi errors (default: off)"); diff --git a/drivers/scsi/cxgbi/libcxgbi.c b/drivers/scsi/cxgbi/libcxgbi.c index 4365d52c6430..af281e271f88 100644 --- a/drivers/scsi/cxgbi/libcxgbi.c +++ b/drivers/scsi/cxgbi/libcxgbi.c @@ -328,7 +328,7 @@ void cxgbi_hbas_remove(struct cxgbi_device *cdev) chba = cdev->hbas[i]; if (chba) { cdev->hbas[i] = NULL; - iscsi_host_remove(chba->shost); + iscsi_host_remove(chba->shost, false); pci_dev_put(cdev->pdev); iscsi_host_free(chba->shost); } @@ -1455,7 +1455,7 @@ void cxgbi_conn_tx_open(struct cxgbi_sock *csk) if (conn) { log_debug(1 << CXGBI_DBG_SOCK, "csk 0x%p, cid %d.\n", csk, conn->id); - iscsi_conn_queue_work(conn); + iscsi_conn_queue_xmit(conn); } } EXPORT_SYMBOL_GPL(cxgbi_conn_tx_open); diff --git a/drivers/scsi/dpt/dpti_i2o.h b/drivers/scsi/dpt/dpti_i2o.h deleted file mode 100644 index e1fbbf55c09d..000000000000 --- a/drivers/scsi/dpt/dpti_i2o.h +++ /dev/null @@ -1,441 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -#ifndef _SCSI_I2O_H -#define _SCSI_I2O_H - -/* I2O kernel space accessible structures/APIs - * - * (c) Copyright 1999, 2000 Red Hat Software - * - ************************************************************************* - * - * This header file defined the I2O APIs/structures for use by - * the I2O kernel modules. - */ - -#ifdef __KERNEL__ /* This file to be included by kernel only */ - -#include - -#include -#include - - -/* - * Tunable parameters first - */ - -/* How many different OSM's are we allowing */ -#define MAX_I2O_MODULES 64 - -#define I2O_EVT_CAPABILITY_OTHER 0x01 -#define I2O_EVT_CAPABILITY_CHANGED 0x02 - -#define I2O_EVT_SENSOR_STATE_CHANGED 0x01 - -//#ifdef __KERNEL__ /* ioctl stuff only thing exported to users */ - -#define I2O_MAX_MANAGERS 4 - -/* - * I2O Interface Objects - */ - -#include -typedef wait_queue_head_t adpt_wait_queue_head_t; -#define ADPT_DECLARE_WAIT_QUEUE_HEAD(wait) DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wait) -typedef wait_queue_entry_t adpt_wait_queue_entry_t; - -/* - * message structures - */ - -struct i2o_message -{ - u8 version_offset; - u8 flags; - u16 size; - u32 target_tid:12; - u32 init_tid:12; - u32 function:8; - u32 initiator_context; - /* List follows */ -}; - -struct adpt_device; -struct _adpt_hba; -struct i2o_device -{ - struct i2o_device *next; /* Chain */ - struct i2o_device *prev; - - char dev_name[8]; /* linux /dev name if available */ - i2o_lct_entry lct_data;/* Device LCT information */ - u32 flags; - struct proc_dir_entry* proc_entry; /* /proc dir */ - struct adpt_device *owner; - struct _adpt_hba *controller; /* Controlling IOP */ -}; - -/* - * Each I2O controller has one of these objects - */ - -struct i2o_controller -{ - char name[16]; - int unit; - int type; - int enabled; - - struct notifier_block *event_notifer; /* Events */ - atomic_t users; - struct i2o_device *devices; /* I2O device chain */ - struct i2o_controller *next; /* Controller chain */ - -}; - -/* - * I2O System table entry - */ -struct i2o_sys_tbl_entry -{ - u16 org_id; - u16 reserved1; - u32 iop_id:12; - u32 reserved2:20; - u16 seg_num:12; - u16 i2o_version:4; - u8 iop_state; - u8 msg_type; - u16 frame_size; - u16 reserved3; - u32 last_changed; - u32 iop_capabilities; - u32 inbound_low; - u32 inbound_high; -}; - -struct i2o_sys_tbl -{ - u8 num_entries; - u8 version; - u16 reserved1; - u32 change_ind; - u32 reserved2; - u32 reserved3; - struct i2o_sys_tbl_entry iops[]; -}; - -/* - * I2O classes / subclasses - */ - -/* Class ID and Code Assignments - * (LCT.ClassID.Version field) - */ -#define I2O_CLASS_VERSION_10 0x00 -#define I2O_CLASS_VERSION_11 0x01 - -/* Class code names - * (from v1.5 Table 6-1 Class Code Assignments.) - */ - -#define I2O_CLASS_EXECUTIVE 0x000 -#define I2O_CLASS_DDM 0x001 -#define I2O_CLASS_RANDOM_BLOCK_STORAGE 0x010 -#define I2O_CLASS_SEQUENTIAL_STORAGE 0x011 -#define I2O_CLASS_LAN 0x020 -#define I2O_CLASS_WAN 0x030 -#define I2O_CLASS_FIBRE_CHANNEL_PORT 0x040 -#define I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL 0x041 -#define I2O_CLASS_SCSI_PERIPHERAL 0x051 -#define I2O_CLASS_ATE_PORT 0x060 -#define I2O_CLASS_ATE_PERIPHERAL 0x061 -#define I2O_CLASS_FLOPPY_CONTROLLER 0x070 -#define I2O_CLASS_FLOPPY_DEVICE 0x071 -#define I2O_CLASS_BUS_ADAPTER_PORT 0x080 -#define I2O_CLASS_PEER_TRANSPORT_AGENT 0x090 -#define I2O_CLASS_PEER_TRANSPORT 0x091 - -/* Rest of 0x092 - 0x09f reserved for peer-to-peer classes - */ - -#define I2O_CLASS_MATCH_ANYCLASS 0xffffffff - -/* Subclasses - */ - -#define I2O_SUBCLASS_i960 0x001 -#define I2O_SUBCLASS_HDM 0x020 -#define I2O_SUBCLASS_ISM 0x021 - -/* Operation functions */ - -#define I2O_PARAMS_FIELD_GET 0x0001 -#define I2O_PARAMS_LIST_GET 0x0002 -#define I2O_PARAMS_MORE_GET 0x0003 -#define I2O_PARAMS_SIZE_GET 0x0004 -#define I2O_PARAMS_TABLE_GET 0x0005 -#define I2O_PARAMS_FIELD_SET 0x0006 -#define I2O_PARAMS_LIST_SET 0x0007 -#define I2O_PARAMS_ROW_ADD 0x0008 -#define I2O_PARAMS_ROW_DELETE 0x0009 -#define I2O_PARAMS_TABLE_CLEAR 0x000A - -/* - * I2O serial number conventions / formats - * (circa v1.5) - */ - -#define I2O_SNFORMAT_UNKNOWN 0 -#define I2O_SNFORMAT_BINARY 1 -#define I2O_SNFORMAT_ASCII 2 -#define I2O_SNFORMAT_UNICODE 3 -#define I2O_SNFORMAT_LAN48_MAC 4 -#define I2O_SNFORMAT_WAN 5 - -/* Plus new in v2.0 (Yellowstone pdf doc) - */ - -#define I2O_SNFORMAT_LAN64_MAC 6 -#define I2O_SNFORMAT_DDM 7 -#define I2O_SNFORMAT_IEEE_REG64 8 -#define I2O_SNFORMAT_IEEE_REG128 9 -#define I2O_SNFORMAT_UNKNOWN2 0xff - -/* Transaction Reply Lists (TRL) Control Word structure */ - -#define TRL_SINGLE_FIXED_LENGTH 0x00 -#define TRL_SINGLE_VARIABLE_LENGTH 0x40 -#define TRL_MULTIPLE_FIXED_LENGTH 0x80 - -/* - * Messaging API values - */ - -#define I2O_CMD_ADAPTER_ASSIGN 0xB3 -#define I2O_CMD_ADAPTER_READ 0xB2 -#define I2O_CMD_ADAPTER_RELEASE 0xB5 -#define I2O_CMD_BIOS_INFO_SET 0xA5 -#define I2O_CMD_BOOT_DEVICE_SET 0xA7 -#define I2O_CMD_CONFIG_VALIDATE 0xBB -#define I2O_CMD_CONN_SETUP 0xCA -#define I2O_CMD_DDM_DESTROY 0xB1 -#define I2O_CMD_DDM_ENABLE 0xD5 -#define I2O_CMD_DDM_QUIESCE 0xC7 -#define I2O_CMD_DDM_RESET 0xD9 -#define I2O_CMD_DDM_SUSPEND 0xAF -#define I2O_CMD_DEVICE_ASSIGN 0xB7 -#define I2O_CMD_DEVICE_RELEASE 0xB9 -#define I2O_CMD_HRT_GET 0xA8 -#define I2O_CMD_ADAPTER_CLEAR 0xBE -#define I2O_CMD_ADAPTER_CONNECT 0xC9 -#define I2O_CMD_ADAPTER_RESET 0xBD -#define I2O_CMD_LCT_NOTIFY 0xA2 -#define I2O_CMD_OUTBOUND_INIT 0xA1 -#define I2O_CMD_PATH_ENABLE 0xD3 -#define I2O_CMD_PATH_QUIESCE 0xC5 -#define I2O_CMD_PATH_RESET 0xD7 -#define I2O_CMD_STATIC_MF_CREATE 0xDD -#define I2O_CMD_STATIC_MF_RELEASE 0xDF -#define I2O_CMD_STATUS_GET 0xA0 -#define I2O_CMD_SW_DOWNLOAD 0xA9 -#define I2O_CMD_SW_UPLOAD 0xAB -#define I2O_CMD_SW_REMOVE 0xAD -#define I2O_CMD_SYS_ENABLE 0xD1 -#define I2O_CMD_SYS_MODIFY 0xC1 -#define I2O_CMD_SYS_QUIESCE 0xC3 -#define I2O_CMD_SYS_TAB_SET 0xA3 - -#define I2O_CMD_UTIL_NOP 0x00 -#define I2O_CMD_UTIL_ABORT 0x01 -#define I2O_CMD_UTIL_CLAIM 0x09 -#define I2O_CMD_UTIL_RELEASE 0x0B -#define I2O_CMD_UTIL_PARAMS_GET 0x06 -#define I2O_CMD_UTIL_PARAMS_SET 0x05 -#define I2O_CMD_UTIL_EVT_REGISTER 0x13 -#define I2O_CMD_UTIL_EVT_ACK 0x14 -#define I2O_CMD_UTIL_CONFIG_DIALOG 0x10 -#define I2O_CMD_UTIL_DEVICE_RESERVE 0x0D -#define I2O_CMD_UTIL_DEVICE_RELEASE 0x0F -#define I2O_CMD_UTIL_LOCK 0x17 -#define I2O_CMD_UTIL_LOCK_RELEASE 0x19 -#define I2O_CMD_UTIL_REPLY_FAULT_NOTIFY 0x15 - -#define I2O_CMD_SCSI_EXEC 0x81 -#define I2O_CMD_SCSI_ABORT 0x83 -#define I2O_CMD_SCSI_BUSRESET 0x27 - -#define I2O_CMD_BLOCK_READ 0x30 -#define I2O_CMD_BLOCK_WRITE 0x31 -#define I2O_CMD_BLOCK_CFLUSH 0x37 -#define I2O_CMD_BLOCK_MLOCK 0x49 -#define I2O_CMD_BLOCK_MUNLOCK 0x4B -#define I2O_CMD_BLOCK_MMOUNT 0x41 -#define I2O_CMD_BLOCK_MEJECT 0x43 - -#define I2O_PRIVATE_MSG 0xFF - -/* - * Init Outbound Q status - */ - -#define I2O_CMD_OUTBOUND_INIT_IN_PROGRESS 0x01 -#define I2O_CMD_OUTBOUND_INIT_REJECTED 0x02 -#define I2O_CMD_OUTBOUND_INIT_FAILED 0x03 -#define I2O_CMD_OUTBOUND_INIT_COMPLETE 0x04 - -/* - * I2O Get Status State values - */ - -#define ADAPTER_STATE_INITIALIZING 0x01 -#define ADAPTER_STATE_RESET 0x02 -#define ADAPTER_STATE_HOLD 0x04 -#define ADAPTER_STATE_READY 0x05 -#define ADAPTER_STATE_OPERATIONAL 0x08 -#define ADAPTER_STATE_FAILED 0x10 -#define ADAPTER_STATE_FAULTED 0x11 - -/* I2O API function return values */ - -#define I2O_RTN_NO_ERROR 0 -#define I2O_RTN_NOT_INIT 1 -#define I2O_RTN_FREE_Q_EMPTY 2 -#define I2O_RTN_TCB_ERROR 3 -#define I2O_RTN_TRANSACTION_ERROR 4 -#define I2O_RTN_ADAPTER_ALREADY_INIT 5 -#define I2O_RTN_MALLOC_ERROR 6 -#define I2O_RTN_ADPTR_NOT_REGISTERED 7 -#define I2O_RTN_MSG_REPLY_TIMEOUT 8 -#define I2O_RTN_NO_STATUS 9 -#define I2O_RTN_NO_FIRM_VER 10 -#define I2O_RTN_NO_LINK_SPEED 11 - -/* Reply message status defines for all messages */ - -#define I2O_REPLY_STATUS_SUCCESS 0x00 -#define I2O_REPLY_STATUS_ABORT_DIRTY 0x01 -#define I2O_REPLY_STATUS_ABORT_NO_DATA_TRANSFER 0x02 -#define I2O_REPLY_STATUS_ABORT_PARTIAL_TRANSFER 0x03 -#define I2O_REPLY_STATUS_ERROR_DIRTY 0x04 -#define I2O_REPLY_STATUS_ERROR_NO_DATA_TRANSFER 0x05 -#define I2O_REPLY_STATUS_ERROR_PARTIAL_TRANSFER 0x06 -#define I2O_REPLY_STATUS_PROCESS_ABORT_DIRTY 0x08 -#define I2O_REPLY_STATUS_PROCESS_ABORT_NO_DATA_TRANSFER 0x09 -#define I2O_REPLY_STATUS_PROCESS_ABORT_PARTIAL_TRANSFER 0x0A -#define I2O_REPLY_STATUS_TRANSACTION_ERROR 0x0B -#define I2O_REPLY_STATUS_PROGRESS_REPORT 0x80 - -/* Status codes and Error Information for Parameter functions */ - -#define I2O_PARAMS_STATUS_SUCCESS 0x00 -#define I2O_PARAMS_STATUS_BAD_KEY_ABORT 0x01 -#define I2O_PARAMS_STATUS_BAD_KEY_CONTINUE 0x02 -#define I2O_PARAMS_STATUS_BUFFER_FULL 0x03 -#define I2O_PARAMS_STATUS_BUFFER_TOO_SMALL 0x04 -#define I2O_PARAMS_STATUS_FIELD_UNREADABLE 0x05 -#define I2O_PARAMS_STATUS_FIELD_UNWRITEABLE 0x06 -#define I2O_PARAMS_STATUS_INSUFFICIENT_FIELDS 0x07 -#define I2O_PARAMS_STATUS_INVALID_GROUP_ID 0x08 -#define I2O_PARAMS_STATUS_INVALID_OPERATION 0x09 -#define I2O_PARAMS_STATUS_NO_KEY_FIELD 0x0A -#define I2O_PARAMS_STATUS_NO_SUCH_FIELD 0x0B -#define I2O_PARAMS_STATUS_NON_DYNAMIC_GROUP 0x0C -#define I2O_PARAMS_STATUS_OPERATION_ERROR 0x0D -#define I2O_PARAMS_STATUS_SCALAR_ERROR 0x0E -#define I2O_PARAMS_STATUS_TABLE_ERROR 0x0F -#define I2O_PARAMS_STATUS_WRONG_GROUP_TYPE 0x10 - -/* DetailedStatusCode defines for Executive, DDM, Util and Transaction error - * messages: Table 3-2 Detailed Status Codes.*/ - -#define I2O_DSC_SUCCESS 0x0000 -#define I2O_DSC_BAD_KEY 0x0002 -#define I2O_DSC_TCL_ERROR 0x0003 -#define I2O_DSC_REPLY_BUFFER_FULL 0x0004 -#define I2O_DSC_NO_SUCH_PAGE 0x0005 -#define I2O_DSC_INSUFFICIENT_RESOURCE_SOFT 0x0006 -#define I2O_DSC_INSUFFICIENT_RESOURCE_HARD 0x0007 -#define I2O_DSC_CHAIN_BUFFER_TOO_LARGE 0x0009 -#define I2O_DSC_UNSUPPORTED_FUNCTION 0x000A -#define I2O_DSC_DEVICE_LOCKED 0x000B -#define I2O_DSC_DEVICE_RESET 0x000C -#define I2O_DSC_INAPPROPRIATE_FUNCTION 0x000D -#define I2O_DSC_INVALID_INITIATOR_ADDRESS 0x000E -#define I2O_DSC_INVALID_MESSAGE_FLAGS 0x000F -#define I2O_DSC_INVALID_OFFSET 0x0010 -#define I2O_DSC_INVALID_PARAMETER 0x0011 -#define I2O_DSC_INVALID_REQUEST 0x0012 -#define I2O_DSC_INVALID_TARGET_ADDRESS 0x0013 -#define I2O_DSC_MESSAGE_TOO_LARGE 0x0014 -#define I2O_DSC_MESSAGE_TOO_SMALL 0x0015 -#define I2O_DSC_MISSING_PARAMETER 0x0016 -#define I2O_DSC_TIMEOUT 0x0017 -#define I2O_DSC_UNKNOWN_ERROR 0x0018 -#define I2O_DSC_UNKNOWN_FUNCTION 0x0019 -#define I2O_DSC_UNSUPPORTED_VERSION 0x001A -#define I2O_DSC_DEVICE_BUSY 0x001B -#define I2O_DSC_DEVICE_NOT_AVAILABLE 0x001C - -/* Device Claim Types */ -#define I2O_CLAIM_PRIMARY 0x01000000 -#define I2O_CLAIM_MANAGEMENT 0x02000000 -#define I2O_CLAIM_AUTHORIZED 0x03000000 -#define I2O_CLAIM_SECONDARY 0x04000000 - -/* Message header defines for VersionOffset */ -#define I2OVER15 0x0001 -#define I2OVER20 0x0002 -/* Default is 1.5, FIXME: Need support for both 1.5 and 2.0 */ -#define I2OVERSION I2OVER15 -#define SGL_OFFSET_0 I2OVERSION -#define SGL_OFFSET_4 (0x0040 | I2OVERSION) -#define SGL_OFFSET_5 (0x0050 | I2OVERSION) -#define SGL_OFFSET_6 (0x0060 | I2OVERSION) -#define SGL_OFFSET_7 (0x0070 | I2OVERSION) -#define SGL_OFFSET_8 (0x0080 | I2OVERSION) -#define SGL_OFFSET_9 (0x0090 | I2OVERSION) -#define SGL_OFFSET_10 (0x00A0 | I2OVERSION) -#define SGL_OFFSET_12 (0x00C0 | I2OVERSION) - -#define TRL_OFFSET_5 (0x0050 | I2OVERSION) -#define TRL_OFFSET_6 (0x0060 | I2OVERSION) - - /* msg header defines for MsgFlags */ -#define MSG_STATIC 0x0100 -#define MSG_64BIT_CNTXT 0x0200 -#define MSG_MULTI_TRANS 0x1000 -#define MSG_FAIL 0x2000 -#define MSG_LAST 0x4000 -#define MSG_REPLY 0x8000 - - /* minimum size msg */ -#define THREE_WORD_MSG_SIZE 0x00030000 -#define FOUR_WORD_MSG_SIZE 0x00040000 -#define FIVE_WORD_MSG_SIZE 0x00050000 -#define SIX_WORD_MSG_SIZE 0x00060000 -#define SEVEN_WORD_MSG_SIZE 0x00070000 -#define EIGHT_WORD_MSG_SIZE 0x00080000 -#define NINE_WORD_MSG_SIZE 0x00090000 -#define TEN_WORD_MSG_SIZE 0x000A0000 -#define I2O_MESSAGE_SIZE(x) ((x)<<16) - - -/* Special TID Assignments */ - -#define ADAPTER_TID 0 -#define HOST_TID 1 - -#define MSG_FRAME_SIZE 128 -#define NMBR_MSG_FRAMES 128 - -#define MSG_POOL_SIZE 16384 - -#define I2O_POST_WAIT_OK 0 -#define I2O_POST_WAIT_TIMEOUT -ETIMEDOUT - - -#endif /* __KERNEL__ */ - -#endif /* _SCSI_I2O_H */ diff --git a/drivers/scsi/dpt/dpti_ioctl.h b/drivers/scsi/dpt/dpti_ioctl.h deleted file mode 100644 index 25e9251f8c78..000000000000 --- a/drivers/scsi/dpt/dpti_ioctl.h +++ /dev/null @@ -1,136 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/*************************************************************************** - dpti_ioctl.h - description - ------------------- - begin : Thu Sep 7 2000 - copyright : (C) 2001 by Adaptec - - See Documentation/scsi/dpti.rst for history, notes, license info - and credits - ***************************************************************************/ - -/*************************************************************************** - * * - * * - ***************************************************************************/ - -/*************************************************************************** - * This file is generated from osd_unix.h * - * *************************************************************************/ - -#ifndef _dpti_ioctl_h -#define _dpti_ioctl_h - -// IOCTL interface commands - -#ifndef _IOWR -# define _IOWR(x,y,z) (((x)<<8)|y) -#endif -#ifndef _IOW -# define _IOW(x,y,z) (((x)<<8)|y) -#endif -#ifndef _IOR -# define _IOR(x,y,z) (((x)<<8)|y) -#endif -#ifndef _IO -# define _IO(x,y) (((x)<<8)|y) -#endif -/* EATA PassThrough Command */ -#define EATAUSRCMD _IOWR('D',65,EATA_CP) -/* Set Debug Level If Enabled */ -#define DPT_DEBUG _IOW('D',66,int) -/* Get Signature Structure */ -#define DPT_SIGNATURE _IOR('D',67,dpt_sig_S) -#if defined __bsdi__ -#define DPT_SIGNATURE_PACKED _IOR('D',67,dpt_sig_S_Packed) -#endif -/* Get Number Of DPT Adapters */ -#define DPT_NUMCTRLS _IOR('D',68,int) -/* Get Adapter Info Structure */ -#define DPT_CTRLINFO _IOR('D',69,CtrlInfo) -/* Get Statistics If Enabled */ -#define DPT_STATINFO _IO('D',70) -/* Clear Stats If Enabled */ -#define DPT_CLRSTAT _IO('D',71) -/* Get System Info Structure */ -#define DPT_SYSINFO _IOR('D',72,sysInfo_S) -/* Set Timeout Value */ -#define DPT_TIMEOUT _IO('D',73) -/* Get config Data */ -#define DPT_CONFIG _IO('D',74) -/* Get Blink LED Code */ -#define DPT_BLINKLED _IOR('D',75,int) -/* Get Statistical information (if available) */ -#define DPT_STATS_INFO _IOR('D',80,STATS_DATA) -/* Clear the statistical information */ -#define DPT_STATS_CLEAR _IO('D',81) -/* Get Performance metrics */ -#define DPT_PERF_INFO _IOR('D',82,dpt_perf_t) -/* Send an I2O command */ -#define I2OUSRCMD _IO('D',76) -/* Inform driver to re-acquire LCT information */ -#define I2ORESCANCMD _IO('D',77) -/* Inform driver to reset adapter */ -#define I2ORESETCMD _IO('D',78) -/* See if the target is mounted */ -#define DPT_TARGET_BUSY _IOR('D',79, TARGET_BUSY_T) - - - /* Structure Returned From Get Controller Info */ - -typedef struct { - uCHAR state; /* Operational state */ - uCHAR id; /* Host adapter SCSI id */ - int vect; /* Interrupt vector number */ - int base; /* Base I/O address */ - int njobs; /* # of jobs sent to HA */ - int qdepth; /* Controller queue depth. */ - int wakebase; /* mpx wakeup base index. */ - uINT SGsize; /* Scatter/Gather list size. */ - unsigned heads; /* heads for drives on cntlr. */ - unsigned sectors; /* sectors for drives on cntlr. */ - uCHAR do_drive32; /* Flag for Above 16 MB Ability */ - uCHAR BusQuiet; /* SCSI Bus Quiet Flag */ - char idPAL[4]; /* 4 Bytes Of The ID Pal */ - uCHAR primary; /* 1 For Primary, 0 For Secondary */ - uCHAR eataVersion; /* EATA Version */ - uINT cpLength; /* EATA Command Packet Length */ - uINT spLength; /* EATA Status Packet Length */ - uCHAR drqNum; /* DRQ Index (0,5,6,7) */ - uCHAR flag1; /* EATA Flags 1 (Byte 9) */ - uCHAR flag2; /* EATA Flags 2 (Byte 30) */ -} CtrlInfo; - -typedef struct { - uSHORT length; // Remaining length of this - uSHORT drvrHBAnum; // Relative HBA # used by the driver - uINT baseAddr; // Base I/O address - uSHORT blinkState; // Blink LED state (0=Not in blink LED) - uCHAR pciBusNum; // PCI Bus # (Optional) - uCHAR pciDeviceNum; // PCI Device # (Optional) - uSHORT hbaFlags; // Miscellaneous HBA flags - uSHORT Interrupt; // Interrupt set for this device. -# if (defined(_DPT_ARC)) - uINT baseLength; - ADAPTER_OBJECT *AdapterObject; - LARGE_INTEGER DmaLogicalAddress; - PVOID DmaVirtualAddress; - LARGE_INTEGER ReplyLogicalAddress; - PVOID ReplyVirtualAddress; -# else - uINT reserved1; // Reserved for future expansion - uINT reserved2; // Reserved for future expansion - uINT reserved3; // Reserved for future expansion -# endif -} drvrHBAinfo_S; - -typedef struct TARGET_BUSY -{ - uLONG channel; - uLONG id; - uLONG lun; - uLONG isBusy; -} TARGET_BUSY_T; - -#endif - diff --git a/drivers/scsi/dpt/dptsig.h b/drivers/scsi/dpt/dptsig.h deleted file mode 100644 index a6644b332b53..000000000000 --- a/drivers/scsi/dpt/dptsig.h +++ /dev/null @@ -1,336 +0,0 @@ -/* BSDI dptsig.h,v 1.7 1998/06/03 19:15:00 karels Exp */ - -/* - * Copyright (c) 1996-1999 Distributed Processing Technology Corporation - * All rights reserved. - * - * Redistribution and use in source form, with or without modification, are - * permitted provided that redistributions of source code must retain the - * above copyright notice, this list of conditions and the following disclaimer. - * - * This software is provided `as is' by Distributed Processing Technology and - * any express or implied warranties, including, but not limited to, the - * implied warranties of merchantability and fitness for a particular purpose, - * are disclaimed. In no event shall Distributed Processing Technology be - * liable for any direct, indirect, incidental, special, exemplary or - * consequential damages (including, but not limited to, procurement of - * substitute goods or services; loss of use, data, or profits; or business - * interruptions) however caused and on any theory of liability, whether in - * contract, strict liability, or tort (including negligence or otherwise) - * arising in any way out of the use of this driver software, even if advised - * of the possibility of such damage. - * - */ - -#ifndef __DPTSIG_H_ -#define __DPTSIG_H_ -#ifdef _SINIX_ADDON -#include "dpt.h" -#endif -/* DPT SIGNATURE SPEC AND HEADER FILE */ -/* Signature Version 1 (sorry no 'A') */ - -/* to make sure we are talking the same size under all OS's */ -typedef unsigned char sigBYTE; -typedef unsigned short sigWORD; -typedef unsigned int sigINT; - -/* - * use sigWORDLittleEndian for: - * dsCapabilities - * dsDeviceSupp - * dsAdapterSupp - * dsApplication - * use sigLONGLittleEndian for: - * dsOS - * so that the sig can be standardised to Little Endian - */ -#if (defined(_DPT_BIG_ENDIAN)) -# define sigWORDLittleEndian(x) ((((x)&0xFF)<<8)|(((x)>>8)&0xFF)) -# define sigLONGLittleEndian(x) \ - ((((x)&0xFF)<<24) | \ - (((x)&0xFF00)<<8) | \ - (((x)&0xFF0000L)>>8) | \ - (((x)&0xFF000000L)>>24)) -#else -# define sigWORDLittleEndian(x) (x) -# define sigLONGLittleEndian(x) (x) -#endif - -/* must make sure the structure is not word or double-word aligned */ -/* --------------------------------------------------------------- */ -/* Borland will ignore the following pragma: */ -/* Word alignment is OFF by default. If in the, IDE make */ -/* sure that Options | Compiler | Code Generation | Word Alignment */ -/* is not checked. If using BCC, do not use the -a option. */ - -#ifndef NO_PACK -#if defined (_DPT_AIX) -#pragma options align=packed -#else -#pragma pack(1) -#endif /* aix */ -#endif -/* For the Macintosh */ -#ifdef STRUCTALIGNMENTSUPPORTED -#pragma options align=mac68k -#endif - - -/* Current Signature Version - sigBYTE dsSigVersion; */ -/* ------------------------------------------------------------------ */ -#define SIG_VERSION 1 - -/* Processor Family - sigBYTE dsProcessorFamily; DISTINCT VALUES */ -/* ------------------------------------------------------------------ */ -/* What type of processor the file is meant to run on. */ -/* This will let us know whether to read sigWORDs as high/low or low/high. */ -#define PROC_INTEL 0x00 /* Intel 80x86/ia64 */ -#define PROC_MOTOROLA 0x01 /* Motorola 68K */ -#define PROC_MIPS4000 0x02 /* MIPS RISC 4000 */ -#define PROC_ALPHA 0x03 /* DEC Alpha */ -#define PROC_POWERPC 0x04 /* IBM Power PC */ -#define PROC_i960 0x05 /* Intel i960 */ -#define PROC_ULTRASPARC 0x06 /* SPARC processor */ - -/* Specific Minimim Processor - sigBYTE dsProcessor; FLAG BITS */ -/* ------------------------------------------------------------------ */ -/* Different bit definitions dependent on processor_family */ - -/* PROC_INTEL: */ -#define PROC_8086 0x01 /* Intel 8086 */ -#define PROC_286 0x02 /* Intel 80286 */ -#define PROC_386 0x04 /* Intel 80386 */ -#define PROC_486 0x08 /* Intel 80486 */ -#define PROC_PENTIUM 0x10 /* Intel 586 aka P5 aka Pentium */ -#define PROC_SEXIUM 0x20 /* Intel 686 aka P6 aka Pentium Pro or MMX */ -#define PROC_IA64 0x40 /* Intel IA64 processor */ - -/* PROC_i960: */ -#define PROC_960RX 0x01 /* Intel 80960RC/RD */ -#define PROC_960HX 0x02 /* Intel 80960HA/HD/HT */ - -/* PROC_MOTOROLA: */ -#define PROC_68000 0x01 /* Motorola 68000 */ -#define PROC_68010 0x02 /* Motorola 68010 */ -#define PROC_68020 0x04 /* Motorola 68020 */ -#define PROC_68030 0x08 /* Motorola 68030 */ -#define PROC_68040 0x10 /* Motorola 68040 */ - -/* PROC_POWERPC */ -#define PROC_PPC601 0x01 /* PowerPC 601 */ -#define PROC_PPC603 0x02 /* PowerPC 603 */ -#define PROC_PPC604 0x04 /* PowerPC 604 */ - -/* PROC_MIPS4000: */ -#define PROC_R4000 0x01 /* MIPS R4000 */ - -/* Filetype - sigBYTE dsFiletype; DISTINCT VALUES */ -/* ------------------------------------------------------------------ */ -#define FT_EXECUTABLE 0 /* Executable Program */ -#define FT_SCRIPT 1 /* Script/Batch File??? */ -#define FT_HBADRVR 2 /* HBA Driver */ -#define FT_OTHERDRVR 3 /* Other Driver */ -#define FT_IFS 4 /* Installable Filesystem Driver */ -#define FT_ENGINE 5 /* DPT Engine */ -#define FT_COMPDRVR 6 /* Compressed Driver Disk */ -#define FT_LANGUAGE 7 /* Foreign Language file */ -#define FT_FIRMWARE 8 /* Downloadable or actual Firmware */ -#define FT_COMMMODL 9 /* Communications Module */ -#define FT_INT13 10 /* INT 13 style HBA Driver */ -#define FT_HELPFILE 11 /* Help file */ -#define FT_LOGGER 12 /* Event Logger */ -#define FT_INSTALL 13 /* An Install Program */ -#define FT_LIBRARY 14 /* Storage Manager Real-Mode Calls */ -#define FT_RESOURCE 15 /* Storage Manager Resource File */ -#define FT_MODEM_DB 16 /* Storage Manager Modem Database */ - -/* Filetype flags - sigBYTE dsFiletypeFlags; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define FTF_DLL 0x01 /* Dynamic Link Library */ -#define FTF_NLM 0x02 /* Netware Loadable Module */ -#define FTF_OVERLAYS 0x04 /* Uses overlays */ -#define FTF_DEBUG 0x08 /* Debug version */ -#define FTF_TSR 0x10 /* TSR */ -#define FTF_SYS 0x20 /* DOS Loadable driver */ -#define FTF_PROTECTED 0x40 /* Runs in protected mode */ -#define FTF_APP_SPEC 0x80 /* Application Specific */ -#define FTF_ROM (FTF_SYS|FTF_TSR) /* Special Case */ - -/* OEM - sigBYTE dsOEM; DISTINCT VALUES */ -/* ------------------------------------------------------------------ */ -#define OEM_DPT 0 /* DPT */ -#define OEM_ATT 1 /* ATT */ -#define OEM_NEC 2 /* NEC */ -#define OEM_ALPHA 3 /* Alphatronix */ -#define OEM_AST 4 /* AST */ -#define OEM_OLIVETTI 5 /* Olivetti */ -#define OEM_SNI 6 /* Siemens/Nixdorf */ -#define OEM_SUN 7 /* SUN Microsystems */ - -/* Operating System - sigLONG dsOS; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define OS_DOS 0x00000001 /* PC/MS-DOS */ -#define OS_WINDOWS 0x00000002 /* Microsoft Windows 3.x */ -#define OS_WINDOWS_NT 0x00000004 /* Microsoft Windows NT */ -#define OS_OS2M 0x00000008 /* OS/2 1.2.x,MS 1.3.0,IBM 1.3.x - Monolithic */ -#define OS_OS2L 0x00000010 /* Microsoft OS/2 1.301 - LADDR */ -#define OS_OS22x 0x00000020 /* IBM OS/2 2.x */ -#define OS_NW286 0x00000040 /* Novell NetWare 286 */ -#define OS_NW386 0x00000080 /* Novell NetWare 386 */ -#define OS_GEN_UNIX 0x00000100 /* Generic Unix */ -#define OS_SCO_UNIX 0x00000200 /* SCO Unix */ -#define OS_ATT_UNIX 0x00000400 /* ATT Unix */ -#define OS_UNIXWARE 0x00000800 /* USL Unix */ -#define OS_INT_UNIX 0x00001000 /* Interactive Unix */ -#define OS_SOLARIS 0x00002000 /* SunSoft Solaris */ -#define OS_QNX 0x00004000 /* QNX for Tom Moch */ -#define OS_NEXTSTEP 0x00008000 /* NeXTSTEP/OPENSTEP/MACH */ -#define OS_BANYAN 0x00010000 /* Banyan Vines */ -#define OS_OLIVETTI_UNIX 0x00020000/* Olivetti Unix */ -#define OS_MAC_OS 0x00040000 /* Mac OS */ -#define OS_WINDOWS_95 0x00080000 /* Microsoft Windows '95 */ -#define OS_NW4x 0x00100000 /* Novell Netware 4.x */ -#define OS_BSDI_UNIX 0x00200000 /* BSDi Unix BSD/OS 2.0 and up */ -#define OS_AIX_UNIX 0x00400000 /* AIX Unix */ -#define OS_FREE_BSD 0x00800000 /* FreeBSD Unix */ -#define OS_LINUX 0x01000000 /* Linux */ -#define OS_DGUX_UNIX 0x02000000 /* Data General Unix */ -#define OS_SINIX_N 0x04000000 /* SNI SINIX-N */ -#define OS_PLAN9 0x08000000 /* ATT Plan 9 */ -#define OS_TSX 0x10000000 /* SNH TSX-32 */ - -#define OS_OTHER 0x80000000 /* Other */ - -/* Capabilities - sigWORD dsCapabilities; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define CAP_RAID0 0x0001 /* RAID-0 */ -#define CAP_RAID1 0x0002 /* RAID-1 */ -#define CAP_RAID3 0x0004 /* RAID-3 */ -#define CAP_RAID5 0x0008 /* RAID-5 */ -#define CAP_SPAN 0x0010 /* Spanning */ -#define CAP_PASS 0x0020 /* Provides passthrough */ -#define CAP_OVERLAP 0x0040 /* Passthrough supports overlapped commands */ -#define CAP_ASPI 0x0080 /* Supports ASPI Command Requests */ -#define CAP_ABOVE16MB 0x0100 /* ISA Driver supports greater than 16MB */ -#define CAP_EXTEND 0x8000 /* Extended info appears after description */ -#ifdef SNI_MIPS -#define CAP_CACHEMODE 0x1000 /* dpt_force_cache is set in driver */ -#endif - -/* Devices Supported - sigWORD dsDeviceSupp; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define DEV_DASD 0x0001 /* DASD (hard drives) */ -#define DEV_TAPE 0x0002 /* Tape drives */ -#define DEV_PRINTER 0x0004 /* Printers */ -#define DEV_PROC 0x0008 /* Processors */ -#define DEV_WORM 0x0010 /* WORM drives */ -#define DEV_CDROM 0x0020 /* CD-ROM drives */ -#define DEV_SCANNER 0x0040 /* Scanners */ -#define DEV_OPTICAL 0x0080 /* Optical Drives */ -#define DEV_JUKEBOX 0x0100 /* Jukebox */ -#define DEV_COMM 0x0200 /* Communications Devices */ -#define DEV_OTHER 0x0400 /* Other Devices */ -#define DEV_ALL 0xFFFF /* All SCSI Devices */ - -/* Adapters Families Supported - sigWORD dsAdapterSupp; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define ADF_2001 0x0001 /* PM2001 */ -#define ADF_2012A 0x0002 /* PM2012A */ -#define ADF_PLUS_ISA 0x0004 /* PM2011,PM2021 */ -#define ADF_PLUS_EISA 0x0008 /* PM2012B,PM2022 */ -#define ADF_SC3_ISA 0x0010 /* PM2021 */ -#define ADF_SC3_EISA 0x0020 /* PM2022,PM2122, etc */ -#define ADF_SC3_PCI 0x0040 /* SmartCache III PCI */ -#define ADF_SC4_ISA 0x0080 /* SmartCache IV ISA */ -#define ADF_SC4_EISA 0x0100 /* SmartCache IV EISA */ -#define ADF_SC4_PCI 0x0200 /* SmartCache IV PCI */ -#define ADF_SC5_PCI 0x0400 /* Fifth Generation I2O products */ -/* - * Combinations of products - */ -#define ADF_ALL_2000 (ADF_2001|ADF_2012A) -#define ADF_ALL_PLUS (ADF_PLUS_ISA|ADF_PLUS_EISA) -#define ADF_ALL_SC3 (ADF_SC3_ISA|ADF_SC3_EISA|ADF_SC3_PCI) -#define ADF_ALL_SC4 (ADF_SC4_ISA|ADF_SC4_EISA|ADF_SC4_PCI) -#define ADF_ALL_SC5 (ADF_SC5_PCI) -/* All EATA Cacheing Products */ -#define ADF_ALL_CACHE (ADF_ALL_PLUS|ADF_ALL_SC3|ADF_ALL_SC4) -/* All EATA Bus Mastering Products */ -#define ADF_ALL_MASTER (ADF_2012A|ADF_ALL_CACHE) -/* All EATA Adapter Products */ -#define ADF_ALL_EATA (ADF_2001|ADF_ALL_MASTER) -#define ADF_ALL ADF_ALL_EATA - -/* Application - sigWORD dsApplication; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define APP_DPTMGR 0x0001 /* DPT Storage Manager */ -#define APP_ENGINE 0x0002 /* DPT Engine */ -#define APP_SYTOS 0x0004 /* Sytron Sytos Plus */ -#define APP_CHEYENNE 0x0008 /* Cheyenne ARCServe + ARCSolo */ -#define APP_MSCDEX 0x0010 /* Microsoft CD-ROM extensions */ -#define APP_NOVABACK 0x0020 /* NovaStor Novaback */ -#define APP_AIM 0x0040 /* Archive Information Manager */ - -/* Requirements - sigBYTE dsRequirements; FLAG BITS */ -/* ------------------------------------------------------------------ */ -#define REQ_SMARTROM 0x01 /* Requires SmartROM to be present */ -#define REQ_DPTDDL 0x02 /* Requires DPTDDL.SYS to be loaded */ -#define REQ_HBA_DRIVER 0x04 /* Requires an HBA driver to be loaded */ -#define REQ_ASPI_TRAN 0x08 /* Requires an ASPI Transport Modules */ -#define REQ_ENGINE 0x10 /* Requires a DPT Engine to be loaded */ -#define REQ_COMM_ENG 0x20 /* Requires a DPT Communications Engine */ - -/* - * You may adjust dsDescription_size with an override to a value less than - * 50 so that the structure allocates less real space. - */ -#if (!defined(dsDescription_size)) -# define dsDescription_size 50 -#endif - -typedef struct dpt_sig { - char dsSignature[6]; /* ALWAYS "dPtSiG" */ - sigBYTE dsSigVersion; /* signature version (currently 1) */ - sigBYTE dsProcessorFamily; /* what type of processor */ - sigBYTE dsProcessor; /* precise processor */ - sigBYTE dsFiletype; /* type of file */ - sigBYTE dsFiletypeFlags; /* flags to specify load type, etc. */ - sigBYTE dsOEM; /* OEM file was created for */ - sigINT dsOS; /* which Operating systems */ - sigWORD dsCapabilities; /* RAID levels, etc. */ - sigWORD dsDeviceSupp; /* Types of SCSI devices supported */ - sigWORD dsAdapterSupp; /* DPT adapter families supported */ - sigWORD dsApplication; /* applications file is for */ - sigBYTE dsRequirements; /* Other driver dependencies */ - sigBYTE dsVersion; /* 1 */ - sigBYTE dsRevision; /* 'J' */ - sigBYTE dsSubRevision; /* '9' ' ' if N/A */ - sigBYTE dsMonth; /* creation month */ - sigBYTE dsDay; /* creation day */ - sigBYTE dsYear; /* creation year since 1980 (1993=13) */ - /* description (NULL terminated) */ - char dsDescription[dsDescription_size]; -} dpt_sig_S; -/* 32 bytes minimum - with no description. Put NULL at description[0] */ -/* 81 bytes maximum - with 49 character description plus NULL. */ - -/* This line added at Roycroft's request */ -/* Microsoft's NT compiler gets confused if you do a pack and don't */ -/* restore it. */ - -#ifndef NO_UNPACK -#if defined (_DPT_AIX) -#pragma options align=reset -#elif defined (UNPACK_FOUR) -#pragma pack(4) -#else -#pragma pack() -#endif /* aix */ -#endif -/* For the Macintosh */ -#ifdef STRUCTALIGNMENTSUPPORTED -#pragma options align=reset -#endif - -#endif diff --git a/drivers/scsi/dpt/osd_defs.h b/drivers/scsi/dpt/osd_defs.h deleted file mode 100644 index de3ae5722982..000000000000 --- a/drivers/scsi/dpt/osd_defs.h +++ /dev/null @@ -1,79 +0,0 @@ -/* BSDI osd_defs.h,v 1.4 1998/06/03 19:14:58 karels Exp */ -/* - * Copyright (c) 1996-1999 Distributed Processing Technology Corporation - * All rights reserved. - * - * Redistribution and use in source form, with or without modification, are - * permitted provided that redistributions of source code must retain the - * above copyright notice, this list of conditions and the following disclaimer. - * - * This software is provided `as is' by Distributed Processing Technology and - * any express or implied warranties, including, but not limited to, the - * implied warranties of merchantability and fitness for a particular purpose, - * are disclaimed. In no event shall Distributed Processing Technology be - * liable for any direct, indirect, incidental, special, exemplary or - * consequential damages (including, but not limited to, procurement of - * substitute goods or services; loss of use, data, or profits; or business - * interruptions) however caused and on any theory of liability, whether in - * contract, strict liability, or tort (including negligence or otherwise) - * arising in any way out of the use of this driver software, even if advised - * of the possibility of such damage. - * - */ - -#ifndef _OSD_DEFS_H -#define _OSD_DEFS_H - -/*File - OSD_DEFS.H - **************************************************************************** - * - *Description: - * - * This file contains the OS dependent defines. This file is included - *in osd_util.h and provides the OS specific defines for that file. - * - *Copyright Distributed Processing Technology, Corp. - * 140 Candace Dr. - * Maitland, Fl. 32751 USA - * Phone: (407) 830-5522 Fax: (407) 260-5366 - * All Rights Reserved - * - *Author: Doug Anderson - *Date: 1/31/94 - * - *Editors: - * - *Remarks: - * - * - *****************************************************************************/ - - -/*Definitions - Defines & Constants ----------------------------------------- */ - - /* Define the operating system */ -#if (defined(__linux__)) -# define _DPT_LINUX -#elif (defined(__bsdi__)) -# define _DPT_BSDI -#elif (defined(__FreeBSD__)) -# define _DPT_FREE_BSD -#else -# define _DPT_SCO -#endif - -#if defined (ZIL_CURSES) -#define _DPT_CURSES -#else -#define _DPT_MOTIF -#endif - - /* Redefine 'far' to nothing - no far pointer type required in UNIX */ -#define far - - /* Define the mutually exclusive semaphore type */ -#define SEMAPHORE_T unsigned int * - /* Define a handle to a DLL */ -#define DLL_HANDLE_T unsigned int * - -#endif diff --git a/drivers/scsi/dpt/osd_util.h b/drivers/scsi/dpt/osd_util.h deleted file mode 100644 index b2613c2eaac7..000000000000 --- a/drivers/scsi/dpt/osd_util.h +++ /dev/null @@ -1,358 +0,0 @@ -/* BSDI osd_util.h,v 1.8 1998/06/03 19:14:58 karels Exp */ - -/* - * Copyright (c) 1996-1999 Distributed Processing Technology Corporation - * All rights reserved. - * - * Redistribution and use in source form, with or without modification, are - * permitted provided that redistributions of source code must retain the - * above copyright notice, this list of conditions and the following disclaimer. - * - * This software is provided `as is' by Distributed Processing Technology and - * any express or implied warranties, including, but not limited to, the - * implied warranties of merchantability and fitness for a particular purpose, - * are disclaimed. In no event shall Distributed Processing Technology be - * liable for any direct, indirect, incidental, special, exemplary or - * consequential damages (including, but not limited to, procurement of - * substitute goods or services; loss of use, data, or profits; or business - * interruptions) however caused and on any theory of liability, whether in - * contract, strict liability, or tort (including negligence or otherwise) - * arising in any way out of the use of this driver software, even if advised - * of the possibility of such damage. - * - */ - -#ifndef __OSD_UTIL_H -#define __OSD_UTIL_H - -/*File - OSD_UTIL.H - **************************************************************************** - * - *Description: - * - * This file contains defines and function prototypes that are - *operating system dependent. The resources defined in this file - *are not specific to any particular application. - * - *Copyright Distributed Processing Technology, Corp. - * 140 Candace Dr. - * Maitland, Fl. 32751 USA - * Phone: (407) 830-5522 Fax: (407) 260-5366 - * All Rights Reserved - * - *Author: Doug Anderson - *Date: 1/7/94 - * - *Editors: - * - *Remarks: - * - * - *****************************************************************************/ - - -/*Definitions - Defines & Constants ----------------------------------------- */ - -/*----------------------------- */ -/* Operating system selections: */ -/*----------------------------- */ - -/*#define _DPT_MSDOS */ -/*#define _DPT_WIN_3X */ -/*#define _DPT_WIN_4X */ -/*#define _DPT_WIN_NT */ -/*#define _DPT_NETWARE */ -/*#define _DPT_OS2 */ -/*#define _DPT_SCO */ -/*#define _DPT_UNIXWARE */ -/*#define _DPT_SOLARIS */ -/*#define _DPT_NEXTSTEP */ -/*#define _DPT_BANYAN */ - -/*-------------------------------- */ -/* Include the OS specific defines */ -/*-------------------------------- */ - -/*#define OS_SELECTION From Above List */ -/*#define SEMAPHORE_T ??? */ -/*#define DLL_HANDLE_T ??? */ - -#if (defined(KERNEL) && (defined(__FreeBSD__) || defined(__bsdi__))) -# include "i386/isa/dpt_osd_defs.h" -#else -# include "osd_defs.h" -#endif - -#ifndef DPT_UNALIGNED - #define DPT_UNALIGNED -#endif - -#ifndef DPT_EXPORT - #define DPT_EXPORT -#endif - -#ifndef DPT_IMPORT - #define DPT_IMPORT -#endif - -#ifndef DPT_RUNTIME_IMPORT - #define DPT_RUNTIME_IMPORT DPT_IMPORT -#endif - -/*--------------------- */ -/* OS dependent defines */ -/*--------------------- */ - -#if defined (_DPT_MSDOS) || defined (_DPT_WIN_3X) - #define _DPT_16_BIT -#else - #define _DPT_32_BIT -#endif - -#if defined (_DPT_SCO) || defined (_DPT_UNIXWARE) || defined (_DPT_SOLARIS) || defined (_DPT_AIX) || defined (SNI_MIPS) || defined (_DPT_BSDI) || defined (_DPT_FREE_BSD) || defined(_DPT_LINUX) - #define _DPT_UNIX -#endif - -#if defined (_DPT_WIN_3x) || defined (_DPT_WIN_4X) || defined (_DPT_WIN_NT) \ - || defined (_DPT_OS2) - #define _DPT_DLL_SUPPORT -#endif - -#if !defined (_DPT_MSDOS) && !defined (_DPT_WIN_3X) && !defined (_DPT_NETWARE) - #define _DPT_PREEMPTIVE -#endif - -#if !defined (_DPT_MSDOS) && !defined (_DPT_WIN_3X) - #define _DPT_MULTI_THREADED -#endif - -#if !defined (_DPT_MSDOS) - #define _DPT_MULTI_TASKING -#endif - - /* These exist for platforms that */ - /* chunk when accessing mis-aligned */ - /* data */ -#if defined (SNI_MIPS) || defined (_DPT_SOLARIS) - #if defined (_DPT_BIG_ENDIAN) - #if !defined (_DPT_STRICT_ALIGN) - #define _DPT_STRICT_ALIGN - #endif - #endif -#endif - - /* Determine if in C or C++ mode */ -#ifdef __cplusplus - #define _DPT_CPP -#else - #define _DPT_C -#endif - -/*-------------------------------------------------------------------*/ -/* Under Solaris the compiler refuses to accept code like: */ -/* { {"DPT"}, 0, NULL .... }, */ -/* and complains about the {"DPT"} part by saying "cannot use { } */ -/* to initialize char*". */ -/* */ -/* By defining these ugly macros we can get around this and also */ -/* not have to copy and #ifdef large sections of code. I know that */ -/* these macros are *really* ugly, but they should help reduce */ -/* maintenance in the long run. */ -/* */ -/*-------------------------------------------------------------------*/ -#if !defined (DPTSQO) - #if defined (_DPT_SOLARIS) - #define DPTSQO - #define DPTSQC - #else - #define DPTSQO { - #define DPTSQC } - #endif /* solaris */ -#endif /* DPTSQO */ - - -/*---------------------- */ -/* OS dependent typedefs */ -/*---------------------- */ - -#if defined (_DPT_MSDOS) || defined (_DPT_SCO) - #define BYTE unsigned char - #define WORD unsigned short -#endif - -#ifndef _DPT_TYPEDEFS - #define _DPT_TYPEDEFS - typedef unsigned char uCHAR; - typedef unsigned short uSHORT; - typedef unsigned int uINT; - typedef unsigned long uLONG; - - typedef union { - uCHAR u8[4]; - uSHORT u16[2]; - uLONG u32; - } access_U; -#endif - -#if !defined (NULL) - #define NULL 0 -#endif - - -/*Prototypes - function ----------------------------------------------------- */ - -#ifdef __cplusplus - extern "C" { /* Declare all these functions as "C" functions */ -#endif - -/*------------------------ */ -/* Byte reversal functions */ -/*------------------------ */ - - /* Reverses the byte ordering of a 2 byte variable */ -#if (!defined(osdSwap2)) - uSHORT osdSwap2(DPT_UNALIGNED uSHORT *); -#endif // !osdSwap2 - - /* Reverses the byte ordering of a 4 byte variable and shifts left 8 bits */ -#if (!defined(osdSwap3)) - uLONG osdSwap3(DPT_UNALIGNED uLONG *); -#endif // !osdSwap3 - - -#ifdef _DPT_NETWARE - #include "novpass.h" /* For DPT_Bswapl() prototype */ - /* Inline the byte swap */ - #ifdef __cplusplus - inline uLONG osdSwap4(uLONG *inLong) { - return *inLong = DPT_Bswapl(*inLong); - } - #else - #define osdSwap4(inLong) DPT_Bswapl(inLong) - #endif // cplusplus -#else - /* Reverses the byte ordering of a 4 byte variable */ -# if (!defined(osdSwap4)) - uLONG osdSwap4(DPT_UNALIGNED uLONG *); -# endif // !osdSwap4 - - /* The following functions ALWAYS swap regardless of the * - * presence of DPT_BIG_ENDIAN */ - - uSHORT trueSwap2(DPT_UNALIGNED uSHORT *); - uLONG trueSwap4(DPT_UNALIGNED uLONG *); - -#endif // netware - - -/*-------------------------------------* - * Network order swap functions * - * * - * These functions/macros will be used * - * by the structure insert()/extract() * - * functions. * - * - * We will enclose all structure * - * portability modifications inside * - * #ifdefs. When we are ready, we * - * will #define DPT_PORTABLE to begin * - * using the modifications. * - *-------------------------------------*/ -uLONG netSwap4(uLONG val); - -#if defined (_DPT_BIG_ENDIAN) - -// for big-endian we need to swap - -#ifndef NET_SWAP_2 -#define NET_SWAP_2(x) (((x) >> 8) | ((x) << 8)) -#endif // NET_SWAP_2 - -#ifndef NET_SWAP_4 -#define NET_SWAP_4(x) netSwap4((x)) -#endif // NET_SWAP_4 - -#else - -// for little-endian we don't need to do anything - -#ifndef NET_SWAP_2 -#define NET_SWAP_2(x) (x) -#endif // NET_SWAP_2 - -#ifndef NET_SWAP_4 -#define NET_SWAP_4(x) (x) -#endif // NET_SWAP_4 - -#endif // big endian - - - -/*----------------------------------- */ -/* Run-time loadable module functions */ -/*----------------------------------- */ - - /* Loads the specified run-time loadable DLL */ -DLL_HANDLE_T osdLoadModule(uCHAR *); - /* Unloads the specified run-time loadable DLL */ -uSHORT osdUnloadModule(DLL_HANDLE_T); - /* Returns a pointer to a function inside a run-time loadable DLL */ -void * osdGetFnAddr(DLL_HANDLE_T,uCHAR *); - -/*--------------------------------------- */ -/* Mutually exclusive semaphore functions */ -/*--------------------------------------- */ - - /* Create a named semaphore */ -SEMAPHORE_T osdCreateNamedSemaphore(char *); - /* Create a mutually exlusive semaphore */ -SEMAPHORE_T osdCreateSemaphore(void); - /* create an event semaphore */ -SEMAPHORE_T osdCreateEventSemaphore(void); - /* create a named event semaphore */ -SEMAPHORE_T osdCreateNamedEventSemaphore(char *); - - /* Destroy the specified mutually exclusive semaphore object */ -uSHORT osdDestroySemaphore(SEMAPHORE_T); - /* Request access to the specified mutually exclusive semaphore */ -uLONG osdRequestSemaphore(SEMAPHORE_T,uLONG); - /* Release access to the specified mutually exclusive semaphore */ -uSHORT osdReleaseSemaphore(SEMAPHORE_T); - /* wait for a event to happen */ -uLONG osdWaitForEventSemaphore(SEMAPHORE_T, uLONG); - /* signal an event */ -uLONG osdSignalEventSemaphore(SEMAPHORE_T); - /* reset the event */ -uLONG osdResetEventSemaphore(SEMAPHORE_T); - -/*----------------- */ -/* Thread functions */ -/*----------------- */ - - /* Releases control to the task switcher in non-preemptive */ - /* multitasking operating systems. */ -void osdSwitchThreads(void); - - /* Starts a thread function */ -uLONG osdStartThread(void *,void *); - -/* what is my thread id */ -uLONG osdGetThreadID(void); - -/* wakes up the specifed thread */ -void osdWakeThread(uLONG); - -/* osd sleep for x milliseconds */ -void osdSleep(uLONG); - -#define DPT_THREAD_PRIORITY_LOWEST 0x00 -#define DPT_THREAD_PRIORITY_NORMAL 0x01 -#define DPT_THREAD_PRIORITY_HIGHEST 0x02 - -uCHAR osdSetThreadPriority(uLONG tid, uCHAR priority); - -#ifdef __cplusplus - } /* end the xtern "C" declaration */ -#endif - -#endif /* osd_util_h */ diff --git a/drivers/scsi/dpt/sys_info.h b/drivers/scsi/dpt/sys_info.h deleted file mode 100644 index a4aa1c31ff72..000000000000 --- a/drivers/scsi/dpt/sys_info.h +++ /dev/null @@ -1,417 +0,0 @@ -/* BSDI sys_info.h,v 1.6 1998/06/03 19:14:59 karels Exp */ - -/* - * Copyright (c) 1996-1999 Distributed Processing Technology Corporation - * All rights reserved. - * - * Redistribution and use in source form, with or without modification, are - * permitted provided that redistributions of source code must retain the - * above copyright notice, this list of conditions and the following disclaimer. - * - * This software is provided `as is' by Distributed Processing Technology and - * any express or implied warranties, including, but not limited to, the - * implied warranties of merchantability and fitness for a particular purpose, - * are disclaimed. In no event shall Distributed Processing Technology be - * liable for any direct, indirect, incidental, special, exemplary or - * consequential damages (including, but not limited to, procurement of - * substitute goods or services; loss of use, data, or profits; or business - * interruptions) however caused and on any theory of liability, whether in - * contract, strict liability, or tort (including negligence or otherwise) - * arising in any way out of the use of this driver software, even if advised - * of the possibility of such damage. - * - */ - -#ifndef __SYS_INFO_H -#define __SYS_INFO_H - -/*File - SYS_INFO.H - **************************************************************************** - * - *Description: - * - * This file contains structure definitions for the OS dependent - *layer system information buffers. - * - *Copyright Distributed Processing Technology, Corp. - * 140 Candace Dr. - * Maitland, Fl. 32751 USA - * Phone: (407) 830-5522 Fax: (407) 260-5366 - * All Rights Reserved - * - *Author: Don Kemper - *Date: 5/10/94 - * - *Editors: - * - *Remarks: - * - * - *****************************************************************************/ - - -/*Include Files ------------------------------------------------------------- */ - -#include "osd_util.h" - -#ifndef NO_PACK -#if defined (_DPT_AIX) -#pragma options align=packed -#else -#pragma pack(1) -#endif /* aix */ -#endif // no unpack - - -/*struct - driveParam_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the drive parameters seen during - *booting. - * - *---------------------------------------------------------------------------*/ - -#ifdef __cplusplus - struct driveParam_S { -#else - typedef struct { -#endif - - uSHORT cylinders; /* Up to 1024 */ - uCHAR heads; /* Up to 255 */ - uCHAR sectors; /* Up to 63 */ - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } driveParam_S; -#endif -/*driveParam_S - end */ - - -/*struct - sysInfo_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the command system information that - *should be returned by every OS dependent layer. - * - *---------------------------------------------------------------------------*/ - -/*flags - bit definitions */ -#define SI_CMOS_Valid 0x0001 -#define SI_NumDrivesValid 0x0002 -#define SI_ProcessorValid 0x0004 -#define SI_MemorySizeValid 0x0008 -#define SI_DriveParamsValid 0x0010 -#define SI_SmartROMverValid 0x0020 -#define SI_OSversionValid 0x0040 -#define SI_OSspecificValid 0x0080 /* 1 if OS structure returned */ -#define SI_BusTypeValid 0x0100 - -#define SI_ALL_VALID 0x0FFF /* All Std SysInfo is valid */ -#define SI_NO_SmartROM 0x8000 - -/*busType - definitions */ -#define SI_ISA_BUS 0x00 -#define SI_MCA_BUS 0x01 -#define SI_EISA_BUS 0x02 -#define SI_PCI_BUS 0x04 - -#ifdef __cplusplus - struct sysInfo_S { -#else - typedef struct { -#endif - - uCHAR drive0CMOS; /* CMOS Drive 0 Type */ - uCHAR drive1CMOS; /* CMOS Drive 1 Type */ - uCHAR numDrives; /* 0040:0075 contents */ - uCHAR processorFamily; /* Same as DPTSIG's definition */ - uCHAR processorType; /* Same as DPTSIG's definition */ - uCHAR smartROMMajorVersion; - uCHAR smartROMMinorVersion; /* SmartROM version */ - uCHAR smartROMRevision; - uSHORT flags; /* See bit definitions above */ - uSHORT conventionalMemSize; /* in KB */ - uINT extendedMemSize; /* in KB */ - uINT osType; /* Same as DPTSIG's definition */ - uCHAR osMajorVersion; - uCHAR osMinorVersion; /* The OS version */ - uCHAR osRevision; -#ifdef _SINIX_ADDON - uCHAR busType; /* See defininitions above */ - uSHORT osSubRevision; - uCHAR pad[2]; /* For alignment */ -#else - uCHAR osSubRevision; - uCHAR busType; /* See defininitions above */ - uCHAR pad[3]; /* For alignment */ -#endif - driveParam_S drives[16]; /* SmartROM Logical Drives */ - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } sysInfo_S; -#endif -/*sysInfo_S - end */ - - -/*struct - DOS_Info_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the system information specific to a - *DOS workstation. - * - *---------------------------------------------------------------------------*/ - -/*flags - bit definitions */ -#define DI_DOS_HIGH 0x01 /* DOS is loaded high */ -#define DI_DPMI_VALID 0x02 /* DPMI version is valid */ - -#ifdef __cplusplus - struct DOS_Info_S { -#else - typedef struct { -#endif - - uCHAR flags; /* See bit definitions above */ - uSHORT driverLocation; /* SmartROM BIOS address */ - uSHORT DOS_version; - uSHORT DPMI_version; - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } DOS_Info_S; -#endif -/*DOS_Info_S - end */ - - -/*struct - Netware_Info_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the system information specific to a - *Netware machine. - * - *---------------------------------------------------------------------------*/ - -#ifdef __cplusplus - struct Netware_Info_S { -#else - typedef struct { -#endif - - uCHAR driverName[13]; /* ie PM12NW31.DSK */ - uCHAR serverName[48]; - uCHAR netwareVersion; /* The Netware OS version */ - uCHAR netwareSubVersion; - uCHAR netwareRevision; - uSHORT maxConnections; /* Probably 250 or 1000 */ - uSHORT connectionsInUse; - uSHORT maxVolumes; - uCHAR unused; - uCHAR SFTlevel; - uCHAR TTSlevel; - - uCHAR clibMajorVersion; /* The CLIB.NLM version */ - uCHAR clibMinorVersion; - uCHAR clibRevision; - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } Netware_Info_S; -#endif -/*Netware_Info_S - end */ - - -/*struct - OS2_Info_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the system information specific to an - *OS/2 machine. - * - *---------------------------------------------------------------------------*/ - -#ifdef __cplusplus - struct OS2_Info_S { -#else - typedef struct { -#endif - - uCHAR something; - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } OS2_Info_S; -#endif -/*OS2_Info_S - end */ - - -/*struct - WinNT_Info_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the system information specific to a - *Windows NT machine. - * - *---------------------------------------------------------------------------*/ - -#ifdef __cplusplus - struct WinNT_Info_S { -#else - typedef struct { -#endif - - uCHAR something; - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } WinNT_Info_S; -#endif -/*WinNT_Info_S - end */ - - -/*struct - SCO_Info_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the system information specific to an - *SCO UNIX machine. - * - *---------------------------------------------------------------------------*/ - -#ifdef __cplusplus - struct SCO_Info_S { -#else - typedef struct { -#endif - - uCHAR something; - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } SCO_Info_S; -#endif -/*SCO_Info_S - end */ - - -/*struct - USL_Info_S - start - *=========================================================================== - * - *Description: - * - * This structure defines the system information specific to a - *USL UNIX machine. - * - *---------------------------------------------------------------------------*/ - -#ifdef __cplusplus - struct USL_Info_S { -#else - typedef struct { -#endif - - uCHAR something; - -#ifdef __cplusplus - -//---------- Portability Additions ----------- in sp_sinfo.cpp -#ifdef DPT_PORTABLE - uSHORT netInsert(dptBuffer_S *buffer); - uSHORT netExtract(dptBuffer_S *buffer); -#endif // DPT PORTABLE -//-------------------------------------------- - - }; -#else - } USL_Info_S; -#endif -/*USL_Info_S - end */ - - - /* Restore default structure packing */ -#ifndef NO_UNPACK -#if defined (_DPT_AIX) -#pragma options align=reset -#elif defined (UNPACK_FOUR) -#pragma pack(4) -#else -#pragma pack() -#endif /* aix */ -#endif // no unpack - -#endif // __SYS_INFO_H - diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c deleted file mode 100644 index 2e9155ba7408..000000000000 --- a/drivers/scsi/dpt_i2o.c +++ /dev/null @@ -1,3545 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/*************************************************************************** - dpti.c - description - ------------------- - begin : Thu Sep 7 2000 - copyright : (C) 2000 by Adaptec - - July 30, 2001 First version being submitted - for inclusion in the kernel. V2.4 - - See Documentation/scsi/dpti.rst for history, notes, license info - and credits - ***************************************************************************/ - -/*************************************************************************** - * * - * * - ***************************************************************************/ -/*************************************************************************** - * Sat Dec 20 2003 Go Taniguchi - - Support 2.6 kernel and DMA-mapping - - ioctl fix for raid tools - - use schedule_timeout in long long loop - **************************************************************************/ - -/*#define DEBUG 1 */ -/*#define UARTDELAY 1 */ - -#include -#include - -MODULE_AUTHOR("Deanna Bonds, with _lots_ of help from Mark Salyzyn"); -MODULE_DESCRIPTION("Adaptec I2O RAID Driver"); - -//////////////////////////////////////////////////////////////// - -#include /* For SCSI-Passthrough */ -#include - -#include -#include /* for kmalloc() */ -#include /* for PCI support */ -#include -#include -#include /* for udelay */ -#include -#include /* for printk */ -#include -#include -#include -#include - -#include -#include -#include -#include - -#include /* for boot_cpu_data */ -#include /* for virt_to_bus, etc. */ - -#include -#include -#include -#include -#include - -#include "dpt/dptsig.h" -#include "dpti.h" - -/*============================================================================ - * Create a binary signature - this is read by dptsig - * Needed for our management apps - *============================================================================ - */ -static DEFINE_MUTEX(adpt_mutex); -static dpt_sig_S DPTI_sig = { - {'d', 'P', 't', 'S', 'i', 'G'}, SIG_VERSION, -#ifdef __i386__ - PROC_INTEL, PROC_386 | PROC_486 | PROC_PENTIUM | PROC_SEXIUM, -#elif defined(__ia64__) - PROC_INTEL, PROC_IA64, -#elif defined(__sparc__) - PROC_ULTRASPARC, PROC_ULTRASPARC, -#elif defined(__alpha__) - PROC_ALPHA, PROC_ALPHA, -#else - (-1),(-1), -#endif - FT_HBADRVR, 0, OEM_DPT, OS_LINUX, CAP_OVERLAP, DEV_ALL, - ADF_ALL_SC5, 0, 0, DPT_VERSION, DPT_REVISION, DPT_SUBREVISION, - DPT_MONTH, DPT_DAY, DPT_YEAR, "Adaptec Linux I2O RAID Driver" -}; - - - - -/*============================================================================ - * Globals - *============================================================================ - */ - -static DEFINE_MUTEX(adpt_configuration_lock); - -static struct i2o_sys_tbl *sys_tbl; -static dma_addr_t sys_tbl_pa; -static int sys_tbl_ind; -static int sys_tbl_len; - -static adpt_hba* hba_chain = NULL; -static int hba_count = 0; - -static struct class *adpt_sysfs_class; - -static long adpt_unlocked_ioctl(struct file *, unsigned int, unsigned long); -#ifdef CONFIG_COMPAT -static long compat_adpt_ioctl(struct file *, unsigned int, unsigned long); -#endif - -static const struct file_operations adpt_fops = { - .unlocked_ioctl = adpt_unlocked_ioctl, - .open = adpt_open, - .release = adpt_close, -#ifdef CONFIG_COMPAT - .compat_ioctl = compat_adpt_ioctl, -#endif - .llseek = noop_llseek, -}; - -/* Structures and definitions for synchronous message posting. - * See adpt_i2o_post_wait() for description - * */ -struct adpt_i2o_post_wait_data -{ - int status; - u32 id; - adpt_wait_queue_head_t *wq; - struct adpt_i2o_post_wait_data *next; -}; - -static struct adpt_i2o_post_wait_data *adpt_post_wait_queue = NULL; -static u32 adpt_post_wait_id = 0; -static DEFINE_SPINLOCK(adpt_post_wait_lock); - - -/*============================================================================ - * Functions - *============================================================================ - */ - -static inline int dpt_dma64(adpt_hba *pHba) -{ - return (sizeof(dma_addr_t) > 4 && (pHba)->dma64); -} - -static inline u32 dma_high(dma_addr_t addr) -{ - return upper_32_bits(addr); -} - -static inline u32 dma_low(dma_addr_t addr) -{ - return (u32)addr; -} - -static u8 adpt_read_blink_led(adpt_hba* host) -{ - if (host->FwDebugBLEDflag_P) { - if( readb(host->FwDebugBLEDflag_P) == 0xbc ){ - return readb(host->FwDebugBLEDvalue_P); - } - } - return 0; -} - -/*============================================================================ - * Scsi host template interface functions - *============================================================================ - */ - -#ifdef MODULE -static struct pci_device_id dptids[] = { - { PCI_DPT_VENDOR_ID, PCI_DPT_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, - { PCI_DPT_VENDOR_ID, PCI_DPT_RAPTOR_DEVICE_ID, PCI_ANY_ID, PCI_ANY_ID,}, - { 0, } -}; -#endif - -MODULE_DEVICE_TABLE(pci,dptids); - -static int adpt_detect(struct scsi_host_template* sht) -{ - struct pci_dev *pDev = NULL; - adpt_hba *pHba; - adpt_hba *next; - - PINFO("Detecting Adaptec I2O RAID controllers...\n"); - - /* search for all Adatpec I2O RAID cards */ - while ((pDev = pci_get_device( PCI_DPT_VENDOR_ID, PCI_ANY_ID, pDev))) { - if(pDev->device == PCI_DPT_DEVICE_ID || - pDev->device == PCI_DPT_RAPTOR_DEVICE_ID){ - if(adpt_install_hba(sht, pDev) ){ - PERROR("Could not Init an I2O RAID device\n"); - PERROR("Will not try to detect others.\n"); - return hba_count-1; - } - pci_dev_get(pDev); - } - } - - /* In INIT state, Activate IOPs */ - for (pHba = hba_chain; pHba; pHba = next) { - next = pHba->next; - // Activate does get status , init outbound, and get hrt - if (adpt_i2o_activate_hba(pHba) < 0) { - adpt_i2o_delete_hba(pHba); - } - } - - - /* Active IOPs in HOLD state */ - -rebuild_sys_tab: - if (hba_chain == NULL) - return 0; - - /* - * If build_sys_table fails, we kill everything and bail - * as we can't init the IOPs w/o a system table - */ - if (adpt_i2o_build_sys_table() < 0) { - adpt_i2o_sys_shutdown(); - return 0; - } - - PDEBUG("HBA's in HOLD state\n"); - - /* If IOP don't get online, we need to rebuild the System table */ - for (pHba = hba_chain; pHba; pHba = pHba->next) { - if (adpt_i2o_online_hba(pHba) < 0) { - adpt_i2o_delete_hba(pHba); - goto rebuild_sys_tab; - } - } - - /* Active IOPs now in OPERATIONAL state */ - PDEBUG("HBA's in OPERATIONAL state\n"); - - printk("dpti: If you have a lot of devices this could take a few minutes.\n"); - for (pHba = hba_chain; pHba; pHba = next) { - next = pHba->next; - printk(KERN_INFO"%s: Reading the hardware resource table.\n", pHba->name); - if (adpt_i2o_lct_get(pHba) < 0){ - adpt_i2o_delete_hba(pHba); - continue; - } - - if (adpt_i2o_parse_lct(pHba) < 0){ - adpt_i2o_delete_hba(pHba); - continue; - } - adpt_inquiry(pHba); - } - - adpt_sysfs_class = class_create(THIS_MODULE, "dpt_i2o"); - if (IS_ERR(adpt_sysfs_class)) { - printk(KERN_WARNING"dpti: unable to create dpt_i2o class\n"); - adpt_sysfs_class = NULL; - } - - for (pHba = hba_chain; pHba; pHba = next) { - next = pHba->next; - if (adpt_scsi_host_alloc(pHba, sht) < 0){ - adpt_i2o_delete_hba(pHba); - continue; - } - pHba->initialized = TRUE; - pHba->state &= ~DPTI_STATE_RESET; - if (adpt_sysfs_class) { - struct device *dev = device_create(adpt_sysfs_class, - NULL, MKDEV(DPTI_I2O_MAJOR, pHba->unit), NULL, - "dpti%d", pHba->unit); - if (IS_ERR(dev)) { - printk(KERN_WARNING"dpti%d: unable to " - "create device in dpt_i2o class\n", - pHba->unit); - } - } - } - - // Register our control device node - // nodes will need to be created in /dev to access this - // the nodes can not be created from within the driver - if (hba_count && register_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER, &adpt_fops)) { - adpt_i2o_sys_shutdown(); - return 0; - } - return hba_count; -} - - -static void adpt_release(adpt_hba *pHba) -{ - struct Scsi_Host *shost = pHba->host; - - scsi_remove_host(shost); -// adpt_i2o_quiesce_hba(pHba); - adpt_i2o_delete_hba(pHba); - scsi_host_put(shost); -} - - -static void adpt_inquiry(adpt_hba* pHba) -{ - u32 msg[17]; - u32 *mptr; - u32 *lenptr; - int direction; - int scsidir; - u32 len; - u32 reqlen; - u8* buf; - dma_addr_t addr; - u8 scb[16]; - s32 rcode; - - memset(msg, 0, sizeof(msg)); - buf = dma_alloc_coherent(&pHba->pDev->dev, 80, &addr, GFP_KERNEL); - if(!buf){ - printk(KERN_ERR"%s: Could not allocate buffer\n",pHba->name); - return; - } - memset((void*)buf, 0, 36); - - len = 36; - direction = 0x00000000; - scsidir =0x40000000; // DATA IN (iop<--dev) - - if (dpt_dma64(pHba)) - reqlen = 17; // SINGLE SGE, 64 bit - else - reqlen = 14; // SINGLE SGE, 32 bit - /* Stick the headers on */ - msg[0] = reqlen<<16 | SGL_OFFSET_12; - msg[1] = (0xff<<24|HOST_TID<<12|ADAPTER_TID); - msg[2] = 0; - msg[3] = 0; - // Adaptec/DPT Private stuff - msg[4] = I2O_CMD_SCSI_EXEC|DPT_ORGANIZATION_ID<<16; - msg[5] = ADAPTER_TID | 1<<16 /* Interpret*/; - /* Direction, disconnect ok | sense data | simple queue , CDBLen */ - // I2O_SCB_FLAG_ENABLE_DISCONNECT | - // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | - // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE; - msg[6] = scsidir|0x20a00000| 6 /* cmd len*/; - - mptr=msg+7; - - memset(scb, 0, sizeof(scb)); - // Write SCSI command into the message - always 16 byte block - scb[0] = INQUIRY; - scb[1] = 0; - scb[2] = 0; - scb[3] = 0; - scb[4] = 36; - scb[5] = 0; - // Don't care about the rest of scb - - memcpy(mptr, scb, sizeof(scb)); - mptr+=4; - lenptr=mptr++; /* Remember me - fill in when we know */ - - /* Now fill in the SGList and command */ - *lenptr = len; - if (dpt_dma64(pHba)) { - *mptr++ = (0x7C<<24)+(2<<16)+0x02; /* Enable 64 bit */ - *mptr++ = 1 << PAGE_SHIFT; - *mptr++ = 0xD0000000|direction|len; - *mptr++ = dma_low(addr); - *mptr++ = dma_high(addr); - } else { - *mptr++ = 0xD0000000|direction|len; - *mptr++ = addr; - } - - // Send it on it's way - rcode = adpt_i2o_post_wait(pHba, msg, reqlen<<2, 120); - if (rcode != 0) { - sprintf(pHba->detail, "Adaptec I2O RAID"); - printk(KERN_INFO "%s: Inquiry Error (%d)\n",pHba->name,rcode); - if (rcode != -ETIME && rcode != -EINTR) - dma_free_coherent(&pHba->pDev->dev, 80, buf, addr); - } else { - memset(pHba->detail, 0, sizeof(pHba->detail)); - memcpy(&(pHba->detail), "Vendor: Adaptec ", 16); - memcpy(&(pHba->detail[16]), " Model: ", 8); - memcpy(&(pHba->detail[24]), (u8*) &buf[16], 16); - memcpy(&(pHba->detail[40]), " FW: ", 4); - memcpy(&(pHba->detail[44]), (u8*) &buf[32], 4); - pHba->detail[48] = '\0'; /* precautionary */ - dma_free_coherent(&pHba->pDev->dev, 80, buf, addr); - } - adpt_i2o_status_get(pHba); - return ; -} - - -static int adpt_slave_configure(struct scsi_device * device) -{ - struct Scsi_Host *host = device->host; - - if (host->can_queue && device->tagged_supported) { - scsi_change_queue_depth(device, - host->can_queue - 1); - } - return 0; -} - -static int adpt_queue_lck(struct scsi_cmnd *cmd) -{ - adpt_hba* pHba = NULL; - struct adpt_device* pDev = NULL; /* dpt per device information */ - - /* - * SCSI REQUEST_SENSE commands will be executed automatically by the - * Host Adapter for any errors, so they should not be executed - * explicitly unless the Sense Data is zero indicating that no error - * occurred. - */ - - if ((cmd->cmnd[0] == REQUEST_SENSE) && (cmd->sense_buffer[0] != 0)) { - cmd->result = (DID_OK << 16); - scsi_done(cmd); - return 0; - } - - pHba = (adpt_hba*)cmd->device->host->hostdata[0]; - if (!pHba) { - return FAILED; - } - - rmb(); - if ((pHba->state) & DPTI_STATE_RESET) - return SCSI_MLQUEUE_HOST_BUSY; - - // TODO if the cmd->device if offline then I may need to issue a bus rescan - // followed by a get_lct to see if the device is there anymore - if((pDev = (struct adpt_device*) (cmd->device->hostdata)) == NULL) { - /* - * First command request for this device. Set up a pointer - * to the device structure. This should be a TEST_UNIT_READY - * command from scan_scsis_single. - */ - if ((pDev = adpt_find_device(pHba, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun)) == NULL) { - // TODO: if any luns are at this bus, scsi id then fake a TEST_UNIT_READY and INQUIRY response - // with type 7F (for all luns less than the max for this bus,id) so the lun scan will continue. - cmd->result = (DID_NO_CONNECT << 16); - scsi_done(cmd); - return 0; - } - cmd->device->hostdata = pDev; - } - pDev->pScsi_dev = cmd->device; - - /* - * If we are being called from when the device is being reset, - * delay processing of the command until later. - */ - if (pDev->state & DPTI_DEV_RESET ) { - return FAILED; - } - return adpt_scsi_to_i2o(pHba, cmd, pDev); -} - -static DEF_SCSI_QCMD(adpt_queue) - -static int adpt_bios_param(struct scsi_device *sdev, struct block_device *dev, - sector_t capacity, int geom[]) -{ - int heads=-1; - int sectors=-1; - int cylinders=-1; - - // *** First lets set the default geometry **** - - // If the capacity is less than ox2000 - if (capacity < 0x2000 ) { // floppy - heads = 18; - sectors = 2; - } - // else if between 0x2000 and 0x20000 - else if (capacity < 0x20000) { - heads = 64; - sectors = 32; - } - // else if between 0x20000 and 0x40000 - else if (capacity < 0x40000) { - heads = 65; - sectors = 63; - } - // else if between 0x4000 and 0x80000 - else if (capacity < 0x80000) { - heads = 128; - sectors = 63; - } - // else if greater than 0x80000 - else { - heads = 255; - sectors = 63; - } - cylinders = sector_div(capacity, heads * sectors); - - // Special case if CDROM - if(sdev->type == 5) { // CDROM - heads = 252; - sectors = 63; - cylinders = 1111; - } - - geom[0] = heads; - geom[1] = sectors; - geom[2] = cylinders; - - PDEBUG("adpt_bios_param: exit\n"); - return 0; -} - - -static const char *adpt_info(struct Scsi_Host *host) -{ - adpt_hba* pHba; - - pHba = (adpt_hba *) host->hostdata[0]; - return (char *) (pHba->detail); -} - -static int adpt_show_info(struct seq_file *m, struct Scsi_Host *host) -{ - struct adpt_device* d; - int id; - int chan; - adpt_hba* pHba; - int unit; - - // Find HBA (host bus adapter) we are looking for - mutex_lock(&adpt_configuration_lock); - for (pHba = hba_chain; pHba; pHba = pHba->next) { - if (pHba->host == host) { - break; /* found adapter */ - } - } - mutex_unlock(&adpt_configuration_lock); - if (pHba == NULL) { - return 0; - } - host = pHba->host; - - seq_printf(m, "Adaptec I2O RAID Driver Version: %s\n\n", DPT_I2O_VERSION); - seq_printf(m, "%s\n", pHba->detail); - seq_printf(m, "SCSI Host=scsi%d Control Node=/dev/%s irq=%d\n", - pHba->host->host_no, pHba->name, host->irq); - seq_printf(m, "\tpost fifo size = %d\n\treply fifo size = %d\n\tsg table size = %d\n\n", - host->can_queue, (int) pHba->reply_fifo_size , host->sg_tablesize); - - seq_puts(m, "Devices:\n"); - for(chan = 0; chan < MAX_CHANNEL; chan++) { - for(id = 0; id < MAX_ID; id++) { - d = pHba->channel[chan].device[id]; - while(d) { - seq_printf(m,"\t%-24.24s", d->pScsi_dev->vendor); - seq_printf(m," Rev: %-8.8s\n", d->pScsi_dev->rev); - - unit = d->pI2o_dev->lct_data.tid; - seq_printf(m, "\tTID=%d, (Channel=%d, Target=%d, Lun=%llu) (%s)\n\n", - unit, (int)d->scsi_channel, (int)d->scsi_id, d->scsi_lun, - scsi_device_online(d->pScsi_dev)? "online":"offline"); - d = d->next_lun; - } - } - } - return 0; -} - -/* - * Turn a pointer to ioctl reply data into an u32 'context' - */ -static u32 adpt_ioctl_to_context(adpt_hba * pHba, void *reply) -{ -#if BITS_PER_LONG == 32 - return (u32)(unsigned long)reply; -#else - ulong flags = 0; - u32 nr, i; - - spin_lock_irqsave(pHba->host->host_lock, flags); - nr = ARRAY_SIZE(pHba->ioctl_reply_context); - for (i = 0; i < nr; i++) { - if (pHba->ioctl_reply_context[i] == NULL) { - pHba->ioctl_reply_context[i] = reply; - break; - } - } - spin_unlock_irqrestore(pHba->host->host_lock, flags); - if (i >= nr) { - printk(KERN_WARNING"%s: Too many outstanding " - "ioctl commands\n", pHba->name); - return (u32)-1; - } - - return i; -#endif -} - -/* - * Go from an u32 'context' to a pointer to ioctl reply data. - */ -static void *adpt_ioctl_from_context(adpt_hba *pHba, u32 context) -{ -#if BITS_PER_LONG == 32 - return (void *)(unsigned long)context; -#else - void *p = pHba->ioctl_reply_context[context]; - pHba->ioctl_reply_context[context] = NULL; - - return p; -#endif -} - -/*=========================================================================== - * Error Handling routines - *=========================================================================== - */ - -static int adpt_abort(struct scsi_cmnd * cmd) -{ - adpt_hba* pHba = NULL; /* host bus adapter structure */ - struct adpt_device* dptdevice; /* dpt per device information */ - u32 msg[5]; - int rcode; - - pHba = (adpt_hba*) cmd->device->host->hostdata[0]; - printk(KERN_INFO"%s: Trying to Abort\n",pHba->name); - if ((dptdevice = (void*) (cmd->device->hostdata)) == NULL) { - printk(KERN_ERR "%s: Unable to abort: No device in cmnd\n",pHba->name); - return FAILED; - } - - memset(msg, 0, sizeof(msg)); - msg[0] = FIVE_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_SCSI_ABORT<<24|HOST_TID<<12|dptdevice->tid; - msg[2] = 0; - msg[3]= 0; - /* Add 1 to avoid firmware treating it as invalid command */ - msg[4] = scsi_cmd_to_rq(cmd)->tag + 1; - if (pHba->host) - spin_lock_irq(pHba->host->host_lock); - rcode = adpt_i2o_post_wait(pHba, msg, sizeof(msg), FOREVER); - if (pHba->host) - spin_unlock_irq(pHba->host->host_lock); - if (rcode != 0) { - if(rcode == -EOPNOTSUPP ){ - printk(KERN_INFO"%s: Abort cmd not supported\n",pHba->name); - return FAILED; - } - printk(KERN_INFO"%s: Abort failed.\n",pHba->name); - return FAILED; - } - printk(KERN_INFO"%s: Abort complete.\n",pHba->name); - return SUCCESS; -} - - -#define I2O_DEVICE_RESET 0x27 -// This is the same for BLK and SCSI devices -// NOTE this is wrong in the i2o.h definitions -// This is not currently supported by our adapter but we issue it anyway -static int adpt_device_reset(struct scsi_cmnd* cmd) -{ - adpt_hba* pHba; - u32 msg[4]; - u32 rcode; - int old_state; - struct adpt_device* d = cmd->device->hostdata; - - pHba = (void*) cmd->device->host->hostdata[0]; - printk(KERN_INFO"%s: Trying to reset device\n",pHba->name); - if (!d) { - printk(KERN_INFO"%s: Reset Device: Device Not found\n",pHba->name); - return FAILED; - } - memset(msg, 0, sizeof(msg)); - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = (I2O_DEVICE_RESET<<24|HOST_TID<<12|d->tid); - msg[2] = 0; - msg[3] = 0; - - if (pHba->host) - spin_lock_irq(pHba->host->host_lock); - old_state = d->state; - d->state |= DPTI_DEV_RESET; - rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER); - d->state = old_state; - if (pHba->host) - spin_unlock_irq(pHba->host->host_lock); - if (rcode != 0) { - if(rcode == -EOPNOTSUPP ){ - printk(KERN_INFO"%s: Device reset not supported\n",pHba->name); - return FAILED; - } - printk(KERN_INFO"%s: Device reset failed\n",pHba->name); - return FAILED; - } else { - printk(KERN_INFO"%s: Device reset successful\n",pHba->name); - return SUCCESS; - } -} - - -#define I2O_HBA_BUS_RESET 0x87 -// This version of bus reset is called by the eh_error handler -static int adpt_bus_reset(struct scsi_cmnd* cmd) -{ - adpt_hba* pHba; - u32 msg[4]; - u32 rcode; - - pHba = (adpt_hba*)cmd->device->host->hostdata[0]; - memset(msg, 0, sizeof(msg)); - printk(KERN_WARNING"%s: Bus reset: SCSI Bus %d: tid: %d\n",pHba->name, cmd->device->channel,pHba->channel[cmd->device->channel].tid ); - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = (I2O_HBA_BUS_RESET<<24|HOST_TID<<12|pHba->channel[cmd->device->channel].tid); - msg[2] = 0; - msg[3] = 0; - if (pHba->host) - spin_lock_irq(pHba->host->host_lock); - rcode = adpt_i2o_post_wait(pHba, msg,sizeof(msg), FOREVER); - if (pHba->host) - spin_unlock_irq(pHba->host->host_lock); - if (rcode != 0) { - printk(KERN_WARNING"%s: Bus reset failed.\n",pHba->name); - return FAILED; - } else { - printk(KERN_WARNING"%s: Bus reset success.\n",pHba->name); - return SUCCESS; - } -} - -// This version of reset is called by the eh_error_handler -static int __adpt_reset(struct scsi_cmnd* cmd) -{ - adpt_hba* pHba; - int rcode; - char name[32]; - - pHba = (adpt_hba*)cmd->device->host->hostdata[0]; - strncpy(name, pHba->name, sizeof(name)); - printk(KERN_WARNING"%s: Hba Reset: scsi id %d: tid: %d\n", name, cmd->device->channel, pHba->channel[cmd->device->channel].tid); - rcode = adpt_hba_reset(pHba); - if(rcode == 0){ - printk(KERN_WARNING"%s: HBA reset complete\n", name); - return SUCCESS; - } else { - printk(KERN_WARNING"%s: HBA reset failed (%x)\n", name, rcode); - return FAILED; - } -} - -static int adpt_reset(struct scsi_cmnd* cmd) -{ - int rc; - - spin_lock_irq(cmd->device->host->host_lock); - rc = __adpt_reset(cmd); - spin_unlock_irq(cmd->device->host->host_lock); - - return rc; -} - -// This version of reset is called by the ioctls and indirectly from eh_error_handler via adpt_reset -static int adpt_hba_reset(adpt_hba* pHba) -{ - int rcode; - - pHba->state |= DPTI_STATE_RESET; - - // Activate does get status , init outbound, and get hrt - if ((rcode=adpt_i2o_activate_hba(pHba)) < 0) { - printk(KERN_ERR "%s: Could not activate\n", pHba->name); - adpt_i2o_delete_hba(pHba); - return rcode; - } - - if ((rcode=adpt_i2o_build_sys_table()) < 0) { - adpt_i2o_delete_hba(pHba); - return rcode; - } - PDEBUG("%s: in HOLD state\n",pHba->name); - - if ((rcode=adpt_i2o_online_hba(pHba)) < 0) { - adpt_i2o_delete_hba(pHba); - return rcode; - } - PDEBUG("%s: in OPERATIONAL state\n",pHba->name); - - if ((rcode=adpt_i2o_lct_get(pHba)) < 0){ - adpt_i2o_delete_hba(pHba); - return rcode; - } - - if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0){ - adpt_i2o_delete_hba(pHba); - return rcode; - } - pHba->state &= ~DPTI_STATE_RESET; - - scsi_host_complete_all_commands(pHba->host, DID_RESET); - return 0; /* return success */ -} - -/*=========================================================================== - * - *=========================================================================== - */ - - -static void adpt_i2o_sys_shutdown(void) -{ - adpt_hba *pHba, *pNext; - struct adpt_i2o_post_wait_data *p1, *old; - - printk(KERN_INFO "Shutting down Adaptec I2O controllers.\n"); - printk(KERN_INFO " This could take a few minutes if there are many devices attached\n"); - /* Delete all IOPs from the controller chain */ - /* They should have already been released by the - * scsi-core - */ - for (pHba = hba_chain; pHba; pHba = pNext) { - pNext = pHba->next; - adpt_i2o_delete_hba(pHba); - } - - /* Remove any timedout entries from the wait queue. */ -// spin_lock_irqsave(&adpt_post_wait_lock, flags); - /* Nothing should be outstanding at this point so just - * free them - */ - for(p1 = adpt_post_wait_queue; p1;) { - old = p1; - p1 = p1->next; - kfree(old); - } -// spin_unlock_irqrestore(&adpt_post_wait_lock, flags); - adpt_post_wait_queue = NULL; - - printk(KERN_INFO "Adaptec I2O controllers down.\n"); -} - -static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev) -{ - - adpt_hba* pHba = NULL; - adpt_hba* p = NULL; - ulong base_addr0_phys = 0; - ulong base_addr1_phys = 0; - u32 hba_map0_area_size = 0; - u32 hba_map1_area_size = 0; - void __iomem *base_addr_virt = NULL; - void __iomem *msg_addr_virt = NULL; - int dma64 = 0; - - int raptorFlag = FALSE; - - if(pci_enable_device(pDev)) { - return -EINVAL; - } - - if (pci_request_regions(pDev, "dpt_i2o")) { - PERROR("dpti: adpt_config_hba: pci request region failed\n"); - return -EINVAL; - } - - pci_set_master(pDev); - - /* - * See if we should enable dma64 mode. - */ - if (sizeof(dma_addr_t) > 4 && - dma_get_required_mask(&pDev->dev) > DMA_BIT_MASK(32) && - dma_set_mask(&pDev->dev, DMA_BIT_MASK(64)) == 0) - dma64 = 1; - - if (!dma64 && dma_set_mask(&pDev->dev, DMA_BIT_MASK(32)) != 0) - return -EINVAL; - - /* adapter only supports message blocks below 4GB */ - dma_set_coherent_mask(&pDev->dev, DMA_BIT_MASK(32)); - - base_addr0_phys = pci_resource_start(pDev,0); - hba_map0_area_size = pci_resource_len(pDev,0); - - // Check if standard PCI card or single BAR Raptor - if(pDev->device == PCI_DPT_DEVICE_ID){ - if(pDev->subsystem_device >=0xc032 && pDev->subsystem_device <= 0xc03b){ - // Raptor card with this device id needs 4M - hba_map0_area_size = 0x400000; - } else { // Not Raptor - it is a PCI card - if(hba_map0_area_size > 0x100000 ){ - hba_map0_area_size = 0x100000; - } - } - } else {// Raptor split BAR config - // Use BAR1 in this configuration - base_addr1_phys = pci_resource_start(pDev,1); - hba_map1_area_size = pci_resource_len(pDev,1); - raptorFlag = TRUE; - } - -#if BITS_PER_LONG == 64 - /* - * The original Adaptec 64 bit driver has this comment here: - * "x86_64 machines need more optimal mappings" - * - * I assume some HBAs report ridiculously large mappings - * and we need to limit them on platforms with IOMMUs. - */ - if (raptorFlag == TRUE) { - if (hba_map0_area_size > 128) - hba_map0_area_size = 128; - if (hba_map1_area_size > 524288) - hba_map1_area_size = 524288; - } else { - if (hba_map0_area_size > 524288) - hba_map0_area_size = 524288; - } -#endif - - base_addr_virt = ioremap(base_addr0_phys,hba_map0_area_size); - if (!base_addr_virt) { - pci_release_regions(pDev); - PERROR("dpti: adpt_config_hba: io remap failed\n"); - return -EINVAL; - } - - if(raptorFlag == TRUE) { - msg_addr_virt = ioremap(base_addr1_phys, hba_map1_area_size ); - if (!msg_addr_virt) { - PERROR("dpti: adpt_config_hba: io remap failed on BAR1\n"); - iounmap(base_addr_virt); - pci_release_regions(pDev); - return -EINVAL; - } - } else { - msg_addr_virt = base_addr_virt; - } - - // Allocate and zero the data structure - pHba = kzalloc(sizeof(adpt_hba), GFP_KERNEL); - if (!pHba) { - if (msg_addr_virt != base_addr_virt) - iounmap(msg_addr_virt); - iounmap(base_addr_virt); - pci_release_regions(pDev); - return -ENOMEM; - } - - mutex_lock(&adpt_configuration_lock); - - if(hba_chain != NULL){ - for(p = hba_chain; p->next; p = p->next); - p->next = pHba; - } else { - hba_chain = pHba; - } - pHba->next = NULL; - pHba->unit = hba_count; - sprintf(pHba->name, "dpti%d", hba_count); - hba_count++; - - mutex_unlock(&adpt_configuration_lock); - - pHba->pDev = pDev; - pHba->base_addr_phys = base_addr0_phys; - - // Set up the Virtual Base Address of the I2O Device - pHba->base_addr_virt = base_addr_virt; - pHba->msg_addr_virt = msg_addr_virt; - pHba->irq_mask = base_addr_virt+0x30; - pHba->post_port = base_addr_virt+0x40; - pHba->reply_port = base_addr_virt+0x44; - - pHba->hrt = NULL; - pHba->lct = NULL; - pHba->lct_size = 0; - pHba->status_block = NULL; - pHba->post_count = 0; - pHba->state = DPTI_STATE_RESET; - pHba->pDev = pDev; - pHba->devices = NULL; - pHba->dma64 = dma64; - - // Initializing the spinlocks - spin_lock_init(&pHba->state_lock); - - if(raptorFlag == 0){ - printk(KERN_INFO "Adaptec I2O RAID controller" - " %d at %p size=%x irq=%d%s\n", - hba_count-1, base_addr_virt, - hba_map0_area_size, pDev->irq, - dma64 ? " (64-bit DMA)" : ""); - } else { - printk(KERN_INFO"Adaptec I2O RAID controller %d irq=%d%s\n", - hba_count-1, pDev->irq, - dma64 ? " (64-bit DMA)" : ""); - printk(KERN_INFO" BAR0 %p - size= %x\n",base_addr_virt,hba_map0_area_size); - printk(KERN_INFO" BAR1 %p - size= %x\n",msg_addr_virt,hba_map1_area_size); - } - - if (request_irq (pDev->irq, adpt_isr, IRQF_SHARED, pHba->name, pHba)) { - printk(KERN_ERR"%s: Couldn't register IRQ %d\n", pHba->name, pDev->irq); - adpt_i2o_delete_hba(pHba); - return -EINVAL; - } - - return 0; -} - - -static void adpt_i2o_delete_hba(adpt_hba* pHba) -{ - adpt_hba* p1; - adpt_hba* p2; - struct i2o_device* d; - struct i2o_device* next; - int i; - int j; - struct adpt_device* pDev; - struct adpt_device* pNext; - - - mutex_lock(&adpt_configuration_lock); - if(pHba->host){ - free_irq(pHba->host->irq, pHba); - } - p2 = NULL; - for( p1 = hba_chain; p1; p2 = p1,p1=p1->next){ - if(p1 == pHba) { - if(p2) { - p2->next = p1->next; - } else { - hba_chain = p1->next; - } - break; - } - } - - hba_count--; - mutex_unlock(&adpt_configuration_lock); - - iounmap(pHba->base_addr_virt); - pci_release_regions(pHba->pDev); - if(pHba->msg_addr_virt != pHba->base_addr_virt){ - iounmap(pHba->msg_addr_virt); - } - if(pHba->FwDebugBuffer_P) - iounmap(pHba->FwDebugBuffer_P); - if(pHba->hrt) { - dma_free_coherent(&pHba->pDev->dev, - pHba->hrt->num_entries * pHba->hrt->entry_len << 2, - pHba->hrt, pHba->hrt_pa); - } - if(pHba->lct) { - dma_free_coherent(&pHba->pDev->dev, pHba->lct_size, - pHba->lct, pHba->lct_pa); - } - if(pHba->status_block) { - dma_free_coherent(&pHba->pDev->dev, sizeof(i2o_status_block), - pHba->status_block, pHba->status_block_pa); - } - if(pHba->reply_pool) { - dma_free_coherent(&pHba->pDev->dev, - pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, - pHba->reply_pool, pHba->reply_pool_pa); - } - - for(d = pHba->devices; d ; d = next){ - next = d->next; - kfree(d); - } - for(i = 0 ; i < pHba->top_scsi_channel ; i++){ - for(j = 0; j < MAX_ID; j++){ - if(pHba->channel[i].device[j] != NULL){ - for(pDev = pHba->channel[i].device[j]; pDev; pDev = pNext){ - pNext = pDev->next_lun; - kfree(pDev); - } - } - } - } - pci_dev_put(pHba->pDev); - if (adpt_sysfs_class) - device_destroy(adpt_sysfs_class, - MKDEV(DPTI_I2O_MAJOR, pHba->unit)); - kfree(pHba); - - if(hba_count <= 0){ - unregister_chrdev(DPTI_I2O_MAJOR, DPT_DRIVER); - if (adpt_sysfs_class) { - class_destroy(adpt_sysfs_class); - adpt_sysfs_class = NULL; - } - } -} - -static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u64 lun) -{ - struct adpt_device* d; - - if (chan >= MAX_CHANNEL) - return NULL; - - d = pHba->channel[chan].device[id]; - if(!d || d->tid == 0) { - return NULL; - } - - /* If it is the only lun at that address then this should match*/ - if(d->scsi_lun == lun){ - return d; - } - - /* else we need to look through all the luns */ - for(d=d->next_lun ; d ; d = d->next_lun){ - if(d->scsi_lun == lun){ - return d; - } - } - return NULL; -} - - -static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout) -{ - // I used my own version of the WAIT_QUEUE_HEAD - // to handle some version differences - // When embedded in the kernel this could go back to the vanilla one - ADPT_DECLARE_WAIT_QUEUE_HEAD(adpt_wq_i2o_post); - int status = 0; - ulong flags = 0; - struct adpt_i2o_post_wait_data *p1, *p2; - struct adpt_i2o_post_wait_data *wait_data = - kmalloc(sizeof(struct adpt_i2o_post_wait_data), GFP_ATOMIC); - DECLARE_WAITQUEUE(wait, current); - - if (!wait_data) - return -ENOMEM; - - /* - * The spin locking is needed to keep anyone from playing - * with the queue pointers and id while we do the same - */ - spin_lock_irqsave(&adpt_post_wait_lock, flags); - // TODO we need a MORE unique way of getting ids - // to support async LCT get - wait_data->next = adpt_post_wait_queue; - adpt_post_wait_queue = wait_data; - adpt_post_wait_id++; - adpt_post_wait_id &= 0x7fff; - wait_data->id = adpt_post_wait_id; - spin_unlock_irqrestore(&adpt_post_wait_lock, flags); - - wait_data->wq = &adpt_wq_i2o_post; - wait_data->status = -ETIMEDOUT; - - add_wait_queue(&adpt_wq_i2o_post, &wait); - - msg[2] |= 0x80000000 | ((u32)wait_data->id); - timeout *= HZ; - if((status = adpt_i2o_post_this(pHba, msg, len)) == 0){ - set_current_state(TASK_INTERRUPTIBLE); - if(pHba->host) - spin_unlock_irq(pHba->host->host_lock); - if (!timeout) - schedule(); - else{ - timeout = schedule_timeout(timeout); - if (timeout == 0) { - // I/O issued, but cannot get result in - // specified time. Freeing resorces is - // dangerous. - status = -ETIME; - } - } - if(pHba->host) - spin_lock_irq(pHba->host->host_lock); - } - remove_wait_queue(&adpt_wq_i2o_post, &wait); - - if(status == -ETIMEDOUT){ - printk(KERN_INFO"dpti%d: POST WAIT TIMEOUT\n",pHba->unit); - // We will have to free the wait_data memory during shutdown - return status; - } - - /* Remove the entry from the queue. */ - p2 = NULL; - spin_lock_irqsave(&adpt_post_wait_lock, flags); - for(p1 = adpt_post_wait_queue; p1; p2 = p1, p1 = p1->next) { - if(p1 == wait_data) { - if(p1->status == I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION ) { - status = -EOPNOTSUPP; - } - if(p2) { - p2->next = p1->next; - } else { - adpt_post_wait_queue = p1->next; - } - break; - } - } - spin_unlock_irqrestore(&adpt_post_wait_lock, flags); - - kfree(wait_data); - - return status; -} - - -static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len) -{ - - u32 m = EMPTY_QUEUE; - u32 __iomem *msg; - ulong timeout = jiffies + 30*HZ; - do { - rmb(); - m = readl(pHba->post_port); - if (m != EMPTY_QUEUE) { - break; - } - if(time_after(jiffies,timeout)){ - printk(KERN_WARNING"dpti%d: Timeout waiting for message frame!\n", pHba->unit); - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } while(m == EMPTY_QUEUE); - - msg = pHba->msg_addr_virt + m; - memcpy_toio(msg, data, len); - wmb(); - - //post message - writel(m, pHba->post_port); - wmb(); - - return 0; -} - - -static void adpt_i2o_post_wait_complete(u32 context, int status) -{ - struct adpt_i2o_post_wait_data *p1 = NULL; - /* - * We need to search through the adpt_post_wait - * queue to see if the given message is still - * outstanding. If not, it means that the IOP - * took longer to respond to the message than we - * had allowed and timer has already expired. - * Not much we can do about that except log - * it for debug purposes, increase timeout, and recompile - * - * Lock needed to keep anyone from moving queue pointers - * around while we're looking through them. - */ - - context &= 0x7fff; - - spin_lock(&adpt_post_wait_lock); - for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) { - if(p1->id == context) { - p1->status = status; - spin_unlock(&adpt_post_wait_lock); - wake_up_interruptible(p1->wq); - return; - } - } - spin_unlock(&adpt_post_wait_lock); - // If this happens we lose commands that probably really completed - printk(KERN_DEBUG"dpti: Could Not find task %d in wait queue\n",context); - printk(KERN_DEBUG" Tasks in wait queue:\n"); - for(p1 = adpt_post_wait_queue; p1; p1 = p1->next) { - printk(KERN_DEBUG" %d\n",p1->id); - } - return; -} - -static s32 adpt_i2o_reset_hba(adpt_hba* pHba) -{ - u32 msg[8]; - u8* status; - dma_addr_t addr; - u32 m = EMPTY_QUEUE ; - ulong timeout = jiffies + (TMOUT_IOPRESET*HZ); - - if(pHba->initialized == FALSE) { // First time reset should be quick - timeout = jiffies + (25*HZ); - } else { - adpt_i2o_quiesce_hba(pHba); - } - - do { - rmb(); - m = readl(pHba->post_port); - if (m != EMPTY_QUEUE) { - break; - } - if(time_after(jiffies,timeout)){ - printk(KERN_WARNING"Timeout waiting for message!\n"); - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } while (m == EMPTY_QUEUE); - - status = dma_alloc_coherent(&pHba->pDev->dev, 4, &addr, GFP_KERNEL); - if(status == NULL) { - adpt_send_nop(pHba, m); - printk(KERN_ERR"IOP reset failed - no free memory.\n"); - return -ENOMEM; - } - - msg[0]=EIGHT_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_ADAPTER_RESET<<24|HOST_TID<<12|ADAPTER_TID; - msg[2]=0; - msg[3]=0; - msg[4]=0; - msg[5]=0; - msg[6]=dma_low(addr); - msg[7]=dma_high(addr); - - memcpy_toio(pHba->msg_addr_virt+m, msg, sizeof(msg)); - wmb(); - writel(m, pHba->post_port); - wmb(); - - while(*status == 0){ - if(time_after(jiffies,timeout)){ - printk(KERN_WARNING"%s: IOP Reset Timeout\n",pHba->name); - /* We lose 4 bytes of "status" here, but we cannot - free these because controller may awake and corrupt - those bytes at any time */ - /* dma_free_coherent(&pHba->pDev->dev, 4, buf, addr); */ - return -ETIMEDOUT; - } - rmb(); - schedule_timeout_uninterruptible(1); - } - - if(*status == 0x01 /*I2O_EXEC_IOP_RESET_IN_PROGRESS*/) { - PDEBUG("%s: Reset in progress...\n", pHba->name); - // Here we wait for message frame to become available - // indicated that reset has finished - do { - rmb(); - m = readl(pHba->post_port); - if (m != EMPTY_QUEUE) { - break; - } - if(time_after(jiffies,timeout)){ - printk(KERN_ERR "%s:Timeout waiting for IOP Reset.\n",pHba->name); - /* We lose 4 bytes of "status" here, but we - cannot free these because controller may - awake and corrupt those bytes at any time */ - /* dma_free_coherent(&pHba->pDev->dev, 4, buf, addr); */ - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } while (m == EMPTY_QUEUE); - // Flush the offset - adpt_send_nop(pHba, m); - } - adpt_i2o_status_get(pHba); - if(*status == 0x02 || - pHba->status_block->iop_state != ADAPTER_STATE_RESET) { - printk(KERN_WARNING"%s: Reset reject, trying to clear\n", - pHba->name); - } else { - PDEBUG("%s: Reset completed.\n", pHba->name); - } - - dma_free_coherent(&pHba->pDev->dev, 4, status, addr); -#ifdef UARTDELAY - // This delay is to allow someone attached to the card through the debug UART to - // set up the dump levels that they want before the rest of the initialization sequence - adpt_delay(20000); -#endif - return 0; -} - - -static int adpt_i2o_parse_lct(adpt_hba* pHba) -{ - int i; - int max; - int tid; - struct i2o_device *d; - i2o_lct *lct = pHba->lct; - u8 bus_no = 0; - s16 scsi_id; - u64 scsi_lun; - u32 buf[10]; // larger than 7, or 8 ... - struct adpt_device* pDev; - - if (lct == NULL) { - printk(KERN_ERR "%s: LCT is empty???\n",pHba->name); - return -1; - } - - max = lct->table_size; - max -= 3; - max /= 9; - - for(i=0;ilct_entry[i].user_tid != 0xfff){ - /* - * If we have hidden devices, we need to inform the upper layers about - * the possible maximum id reference to handle device access when - * an array is disassembled. This code has no other purpose but to - * allow us future access to devices that are currently hidden - * behind arrays, hotspares or have not been configured (JBOD mode). - */ - if( lct->lct_entry[i].class_id != I2O_CLASS_RANDOM_BLOCK_STORAGE && - lct->lct_entry[i].class_id != I2O_CLASS_SCSI_PERIPHERAL && - lct->lct_entry[i].class_id != I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){ - continue; - } - tid = lct->lct_entry[i].tid; - // I2O_DPT_DEVICE_INFO_GROUP_NO; - if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) { - continue; - } - bus_no = buf[0]>>16; - scsi_id = buf[1]; - scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]); - if(bus_no >= MAX_CHANNEL) { // Something wrong skip it - printk(KERN_WARNING"%s: Channel number %d out of range \n", pHba->name, bus_no); - continue; - } - if (scsi_id >= MAX_ID){ - printk(KERN_WARNING"%s: SCSI ID %d out of range \n", pHba->name, bus_no); - continue; - } - if(bus_no > pHba->top_scsi_channel){ - pHba->top_scsi_channel = bus_no; - } - if(scsi_id > pHba->top_scsi_id){ - pHba->top_scsi_id = scsi_id; - } - if(scsi_lun > pHba->top_scsi_lun){ - pHba->top_scsi_lun = scsi_lun; - } - continue; - } - d = kmalloc(sizeof(struct i2o_device), GFP_KERNEL); - if(d==NULL) - { - printk(KERN_CRIT"%s: Out of memory for I2O device data.\n",pHba->name); - return -ENOMEM; - } - - d->controller = pHba; - d->next = NULL; - - memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); - - d->flags = 0; - tid = d->lct_data.tid; - adpt_i2o_report_hba_unit(pHba, d); - adpt_i2o_install_device(pHba, d); - } - bus_no = 0; - for(d = pHba->devices; d ; d = d->next) { - if(d->lct_data.class_id == I2O_CLASS_BUS_ADAPTER_PORT || - d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PORT){ - tid = d->lct_data.tid; - // TODO get the bus_no from hrt-but for now they are in order - //bus_no = - if(bus_no > pHba->top_scsi_channel){ - pHba->top_scsi_channel = bus_no; - } - pHba->channel[bus_no].type = d->lct_data.class_id; - pHba->channel[bus_no].tid = tid; - if(adpt_i2o_query_scalar(pHba, tid, 0x0200, -1, buf, 28)>=0) - { - pHba->channel[bus_no].scsi_id = buf[1]; - PDEBUG("Bus %d - SCSI ID %d.\n", bus_no, buf[1]); - } - // TODO remove - this is just until we get from hrt - bus_no++; - if(bus_no >= MAX_CHANNEL) { // Something wrong skip it - printk(KERN_WARNING"%s: Channel number %d out of range - LCT\n", pHba->name, bus_no); - break; - } - } - } - - // Setup adpt_device table - for(d = pHba->devices; d ; d = d->next) { - if(d->lct_data.class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE || - d->lct_data.class_id == I2O_CLASS_SCSI_PERIPHERAL || - d->lct_data.class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){ - - tid = d->lct_data.tid; - scsi_id = -1; - // I2O_DPT_DEVICE_INFO_GROUP_NO; - if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)>=0) { - bus_no = buf[0]>>16; - scsi_id = buf[1]; - scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]); - if(bus_no >= MAX_CHANNEL) { // Something wrong skip it - continue; - } - if (scsi_id >= MAX_ID) { - continue; - } - if( pHba->channel[bus_no].device[scsi_id] == NULL){ - pDev = kzalloc(sizeof(struct adpt_device),GFP_KERNEL); - if(pDev == NULL) { - return -ENOMEM; - } - pHba->channel[bus_no].device[scsi_id] = pDev; - } else { - for( pDev = pHba->channel[bus_no].device[scsi_id]; - pDev->next_lun; pDev = pDev->next_lun){ - } - pDev->next_lun = kzalloc(sizeof(struct adpt_device),GFP_KERNEL); - if(pDev->next_lun == NULL) { - return -ENOMEM; - } - pDev = pDev->next_lun; - } - pDev->tid = tid; - pDev->scsi_channel = bus_no; - pDev->scsi_id = scsi_id; - pDev->scsi_lun = scsi_lun; - pDev->pI2o_dev = d; - d->owner = pDev; - pDev->type = (buf[0])&0xff; - pDev->flags = (buf[0]>>8)&0xff; - if(scsi_id > pHba->top_scsi_id){ - pHba->top_scsi_id = scsi_id; - } - if(scsi_lun > pHba->top_scsi_lun){ - pHba->top_scsi_lun = scsi_lun; - } - } - if(scsi_id == -1){ - printk(KERN_WARNING"Could not find SCSI ID for %s\n", - d->lct_data.identity_tag); - } - } - } - return 0; -} - - -/* - * Each I2O controller has a chain of devices on it - these match - * the useful parts of the LCT of the board. - */ - -static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d) -{ - mutex_lock(&adpt_configuration_lock); - d->controller=pHba; - d->owner=NULL; - d->next=pHba->devices; - d->prev=NULL; - if (pHba->devices != NULL){ - pHba->devices->prev=d; - } - pHba->devices=d; - *d->dev_name = 0; - - mutex_unlock(&adpt_configuration_lock); - return 0; -} - -static int adpt_open(struct inode *inode, struct file *file) -{ - int minor; - adpt_hba* pHba; - - mutex_lock(&adpt_mutex); - //TODO check for root access - // - minor = iminor(inode); - if (minor >= hba_count) { - mutex_unlock(&adpt_mutex); - return -ENXIO; - } - mutex_lock(&adpt_configuration_lock); - for (pHba = hba_chain; pHba; pHba = pHba->next) { - if (pHba->unit == minor) { - break; /* found adapter */ - } - } - if (pHba == NULL) { - mutex_unlock(&adpt_configuration_lock); - mutex_unlock(&adpt_mutex); - return -ENXIO; - } - -// if(pHba->in_use){ - // mutex_unlock(&adpt_configuration_lock); -// return -EBUSY; -// } - - pHba->in_use = 1; - mutex_unlock(&adpt_configuration_lock); - mutex_unlock(&adpt_mutex); - - return 0; -} - -static int adpt_close(struct inode *inode, struct file *file) -{ - int minor; - adpt_hba* pHba; - - minor = iminor(inode); - if (minor >= hba_count) { - return -ENXIO; - } - mutex_lock(&adpt_configuration_lock); - for (pHba = hba_chain; pHba; pHba = pHba->next) { - if (pHba->unit == minor) { - break; /* found adapter */ - } - } - mutex_unlock(&adpt_configuration_lock); - if (pHba == NULL) { - return -ENXIO; - } - - pHba->in_use = 0; - - return 0; -} - - -static int adpt_i2o_passthru(adpt_hba* pHba, u32 __user *arg) -{ - u32 msg[MAX_MESSAGE_SIZE]; - u32* reply = NULL; - u32 size = 0; - u32 reply_size = 0; - u32 __user *user_msg = arg; - u32 __user * user_reply = NULL; - void **sg_list = NULL; - u32 sg_offset = 0; - u32 sg_count = 0; - int sg_index = 0; - u32 i = 0; - u32 rcode = 0; - void *p = NULL; - dma_addr_t addr; - ulong flags = 0; - - memset(&msg, 0, MAX_MESSAGE_SIZE*4); - // get user msg size in u32s - if(get_user(size, &user_msg[0])){ - return -EFAULT; - } - size = size>>16; - - user_reply = &user_msg[size]; - if(size > MAX_MESSAGE_SIZE){ - return -EFAULT; - } - size *= 4; // Convert to bytes - - /* Copy in the user's I2O command */ - if(copy_from_user(msg, user_msg, size)) { - return -EFAULT; - } - get_user(reply_size, &user_reply[0]); - reply_size = reply_size>>16; - if(reply_size > REPLY_FRAME_SIZE){ - reply_size = REPLY_FRAME_SIZE; - } - reply_size *= 4; - reply = kzalloc(REPLY_FRAME_SIZE*4, GFP_KERNEL); - if(reply == NULL) { - printk(KERN_WARNING"%s: Could not allocate reply buffer\n",pHba->name); - return -ENOMEM; - } - sg_offset = (msg[0]>>4)&0xf; - msg[2] = 0x40000000; // IOCTL context - msg[3] = adpt_ioctl_to_context(pHba, reply); - if (msg[3] == (u32)-1) { - rcode = -EBUSY; - goto free; - } - - sg_list = kcalloc(pHba->sg_tablesize, sizeof(*sg_list), GFP_KERNEL); - if (!sg_list) { - rcode = -ENOMEM; - goto free; - } - if(sg_offset) { - // TODO add 64 bit API - struct sg_simple_element *sg = (struct sg_simple_element*) (msg+sg_offset); - sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element); - if (sg_count > pHba->sg_tablesize){ - printk(KERN_DEBUG"%s:IOCTL SG List too large (%u)\n", pHba->name,sg_count); - rcode = -EINVAL; - goto free; - } - - for(i = 0; i < sg_count; i++) { - int sg_size; - - if (!(sg[i].flag_count & 0x10000000 /*I2O_SGL_FLAGS_SIMPLE_ADDRESS_ELEMENT*/)) { - printk(KERN_DEBUG"%s:Bad SG element %d - not simple (%x)\n",pHba->name,i, sg[i].flag_count); - rcode = -EINVAL; - goto cleanup; - } - sg_size = sg[i].flag_count & 0xffffff; - /* Allocate memory for the transfer */ - p = dma_alloc_coherent(&pHba->pDev->dev, sg_size, &addr, GFP_KERNEL); - if(!p) { - printk(KERN_DEBUG"%s: Could not allocate SG buffer - size = %d buffer number %d of %d\n", - pHba->name,sg_size,i,sg_count); - rcode = -ENOMEM; - goto cleanup; - } - sg_list[sg_index++] = p; // sglist indexed with input frame, not our internal frame. - /* Copy in the user's SG buffer if necessary */ - if(sg[i].flag_count & 0x04000000 /*I2O_SGL_FLAGS_DIR*/) { - // sg_simple_element API is 32 bit - if (copy_from_user(p,(void __user *)(ulong)sg[i].addr_bus, sg_size)) { - printk(KERN_DEBUG"%s: Could not copy SG buf %d FROM user\n",pHba->name,i); - rcode = -EFAULT; - goto cleanup; - } - } - /* sg_simple_element API is 32 bit, but addr < 4GB */ - sg[i].addr_bus = addr; - } - } - - do { - /* - * Stop any new commands from enterring the - * controller while processing the ioctl - */ - if (pHba->host) { - scsi_block_requests(pHba->host); - spin_lock_irqsave(pHba->host->host_lock, flags); - } - rcode = adpt_i2o_post_wait(pHba, msg, size, FOREVER); - if (rcode != 0) - printk("adpt_i2o_passthru: post wait failed %d %p\n", - rcode, reply); - if (pHba->host) { - spin_unlock_irqrestore(pHba->host->host_lock, flags); - scsi_unblock_requests(pHba->host); - } - } while (rcode == -ETIMEDOUT); - - if(rcode){ - goto cleanup; - } - - if(sg_offset) { - /* Copy back the Scatter Gather buffers back to user space */ - u32 j; - // TODO add 64 bit API - struct sg_simple_element* sg; - int sg_size; - - // re-acquire the original message to handle correctly the sg copy operation - memset(&msg, 0, MAX_MESSAGE_SIZE*4); - // get user msg size in u32s - if(get_user(size, &user_msg[0])){ - rcode = -EFAULT; - goto cleanup; - } - size = size>>16; - size *= 4; - if (size > MAX_MESSAGE_SIZE) { - rcode = -EINVAL; - goto cleanup; - } - /* Copy in the user's I2O command */ - if (copy_from_user (msg, user_msg, size)) { - rcode = -EFAULT; - goto cleanup; - } - sg_count = (size - sg_offset*4) / sizeof(struct sg_simple_element); - - // TODO add 64 bit API - sg = (struct sg_simple_element*)(msg + sg_offset); - for (j = 0; j < sg_count; j++) { - /* Copy out the SG list to user's buffer if necessary */ - if(! (sg[j].flag_count & 0x4000000 /*I2O_SGL_FLAGS_DIR*/)) { - sg_size = sg[j].flag_count & 0xffffff; - // sg_simple_element API is 32 bit - if (copy_to_user((void __user *)(ulong)sg[j].addr_bus,sg_list[j], sg_size)) { - printk(KERN_WARNING"%s: Could not copy %p TO user %x\n",pHba->name, sg_list[j], sg[j].addr_bus); - rcode = -EFAULT; - goto cleanup; - } - } - } - } - - /* Copy back the reply to user space */ - if (reply_size) { - // we wrote our own values for context - now restore the user supplied ones - if(copy_from_user(reply+2, user_msg+2, sizeof(u32)*2)) { - printk(KERN_WARNING"%s: Could not copy message context FROM user\n",pHba->name); - rcode = -EFAULT; - } - if(copy_to_user(user_reply, reply, reply_size)) { - printk(KERN_WARNING"%s: Could not copy reply TO user\n",pHba->name); - rcode = -EFAULT; - } - } - - -cleanup: - if (rcode != -ETIME && rcode != -EINTR) { - struct sg_simple_element *sg = - (struct sg_simple_element*) (msg +sg_offset); - while(sg_index) { - if(sg_list[--sg_index]) { - dma_free_coherent(&pHba->pDev->dev, - sg[sg_index].flag_count & 0xffffff, - sg_list[sg_index], - sg[sg_index].addr_bus); - } - } - } - -free: - kfree(sg_list); - kfree(reply); - return rcode; -} - -#if defined __ia64__ -static void adpt_ia64_info(sysInfo_S* si) -{ - // This is all the info we need for now - // We will add more info as our new - // managmenent utility requires it - si->processorType = PROC_IA64; -} -#endif - -#if defined __sparc__ -static void adpt_sparc_info(sysInfo_S* si) -{ - // This is all the info we need for now - // We will add more info as our new - // managmenent utility requires it - si->processorType = PROC_ULTRASPARC; -} -#endif -#if defined __alpha__ -static void adpt_alpha_info(sysInfo_S* si) -{ - // This is all the info we need for now - // We will add more info as our new - // managmenent utility requires it - si->processorType = PROC_ALPHA; -} -#endif - -#if defined __i386__ - -#include - -static void adpt_i386_info(sysInfo_S* si) -{ - // This is all the info we need for now - // We will add more info as our new - // managmenent utility requires it - switch (boot_cpu_data.x86) { - case CPU_386: - si->processorType = PROC_386; - break; - case CPU_486: - si->processorType = PROC_486; - break; - case CPU_586: - si->processorType = PROC_PENTIUM; - break; - default: // Just in case - si->processorType = PROC_PENTIUM; - break; - } -} -#endif - -/* - * This routine returns information about the system. This does not effect - * any logic and if the info is wrong - it doesn't matter. - */ - -/* Get all the info we can not get from kernel services */ -static int adpt_system_info(void __user *buffer) -{ - sysInfo_S si; - - memset(&si, 0, sizeof(si)); - - si.osType = OS_LINUX; - si.osMajorVersion = 0; - si.osMinorVersion = 0; - si.osRevision = 0; - si.busType = SI_PCI_BUS; - si.processorFamily = DPTI_sig.dsProcessorFamily; - -#if defined __i386__ - adpt_i386_info(&si); -#elif defined (__ia64__) - adpt_ia64_info(&si); -#elif defined(__sparc__) - adpt_sparc_info(&si); -#elif defined (__alpha__) - adpt_alpha_info(&si); -#else - si.processorType = 0xff ; -#endif - if (copy_to_user(buffer, &si, sizeof(si))){ - printk(KERN_WARNING"dpti: Could not copy buffer TO user\n"); - return -EFAULT; - } - - return 0; -} - -static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg) -{ - int minor; - int error = 0; - adpt_hba* pHba; - ulong flags = 0; - void __user *argp = (void __user *)arg; - - minor = iminor(inode); - if (minor >= DPTI_MAX_HBA){ - return -ENXIO; - } - mutex_lock(&adpt_configuration_lock); - for (pHba = hba_chain; pHba; pHba = pHba->next) { - if (pHba->unit == minor) { - break; /* found adapter */ - } - } - mutex_unlock(&adpt_configuration_lock); - if(pHba == NULL){ - return -ENXIO; - } - - while((volatile u32) pHba->state & DPTI_STATE_RESET ) - schedule_timeout_uninterruptible(2); - - switch (cmd) { - // TODO: handle 3 cases - case DPT_SIGNATURE: - if (copy_to_user(argp, &DPTI_sig, sizeof(DPTI_sig))) { - return -EFAULT; - } - break; - case I2OUSRCMD: - return adpt_i2o_passthru(pHba, argp); - - case DPT_CTRLINFO:{ - drvrHBAinfo_S HbaInfo; - -#define FLG_OSD_PCI_VALID 0x0001 -#define FLG_OSD_DMA 0x0002 -#define FLG_OSD_I2O 0x0004 - memset(&HbaInfo, 0, sizeof(HbaInfo)); - HbaInfo.drvrHBAnum = pHba->unit; - HbaInfo.baseAddr = (ulong) pHba->base_addr_phys; - HbaInfo.blinkState = adpt_read_blink_led(pHba); - HbaInfo.pciBusNum = pHba->pDev->bus->number; - HbaInfo.pciDeviceNum=PCI_SLOT(pHba->pDev->devfn); - HbaInfo.Interrupt = pHba->pDev->irq; - HbaInfo.hbaFlags = FLG_OSD_PCI_VALID | FLG_OSD_DMA | FLG_OSD_I2O; - if(copy_to_user(argp, &HbaInfo, sizeof(HbaInfo))){ - printk(KERN_WARNING"%s: Could not copy HbaInfo TO user\n",pHba->name); - return -EFAULT; - } - break; - } - case DPT_SYSINFO: - return adpt_system_info(argp); - case DPT_BLINKLED:{ - u32 value; - value = (u32)adpt_read_blink_led(pHba); - if (copy_to_user(argp, &value, sizeof(value))) { - return -EFAULT; - } - break; - } - case I2ORESETCMD: { - struct Scsi_Host *shost = pHba->host; - - if (shost) - spin_lock_irqsave(shost->host_lock, flags); - adpt_hba_reset(pHba); - if (shost) - spin_unlock_irqrestore(shost->host_lock, flags); - break; - } - case I2ORESCANCMD: - adpt_rescan(pHba); - break; - default: - return -EINVAL; - } - - return error; -} - -static long adpt_unlocked_ioctl(struct file *file, uint cmd, ulong arg) -{ - struct inode *inode; - long ret; - - inode = file_inode(file); - - mutex_lock(&adpt_mutex); - ret = adpt_ioctl(inode, file, cmd, arg); - mutex_unlock(&adpt_mutex); - - return ret; -} - -#ifdef CONFIG_COMPAT -static long compat_adpt_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - struct inode *inode; - long ret; - - inode = file_inode(file); - - mutex_lock(&adpt_mutex); - - switch(cmd) { - case DPT_SIGNATURE: - case I2OUSRCMD: - case DPT_CTRLINFO: - case DPT_SYSINFO: - case DPT_BLINKLED: - case I2ORESETCMD: - case I2ORESCANCMD: - case (DPT_TARGET_BUSY & 0xFFFF): - case DPT_TARGET_BUSY: - ret = adpt_ioctl(inode, file, cmd, arg); - break; - default: - ret = -ENOIOCTLCMD; - } - - mutex_unlock(&adpt_mutex); - - return ret; -} -#endif - -static irqreturn_t adpt_isr(int irq, void *dev_id) -{ - struct scsi_cmnd* cmd; - adpt_hba* pHba = dev_id; - u32 m; - void __iomem *reply; - u32 status=0; - u32 context; - ulong flags = 0; - int handled = 0; - - if (pHba == NULL){ - printk(KERN_WARNING"adpt_isr: NULL dev_id\n"); - return IRQ_NONE; - } - if(pHba->host) - spin_lock_irqsave(pHba->host->host_lock, flags); - - while( readl(pHba->irq_mask) & I2O_INTERRUPT_PENDING_B) { - m = readl(pHba->reply_port); - if(m == EMPTY_QUEUE){ - // Try twice then give up - rmb(); - m = readl(pHba->reply_port); - if(m == EMPTY_QUEUE){ - // This really should not happen - printk(KERN_ERR"dpti: Could not get reply frame\n"); - goto out; - } - } - if (pHba->reply_pool_pa <= m && - m < pHba->reply_pool_pa + - (pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4)) { - reply = (u8 *)pHba->reply_pool + - (m - pHba->reply_pool_pa); - } else { - /* Ick, we should *never* be here */ - printk(KERN_ERR "dpti: reply frame not from pool\n"); - reply = (u8 *)bus_to_virt(m); - } - - if (readl(reply) & MSG_FAIL) { - u32 old_m = readl(reply+28); - void __iomem *msg; - u32 old_context; - PDEBUG("%s: Failed message\n",pHba->name); - if(old_m >= 0x100000){ - printk(KERN_ERR"%s: Bad preserved MFA (%x)- dropping frame\n",pHba->name,old_m); - writel(m,pHba->reply_port); - continue; - } - // Transaction context is 0 in failed reply frame - msg = pHba->msg_addr_virt + old_m; - old_context = readl(msg+12); - writel(old_context, reply+12); - adpt_send_nop(pHba, old_m); - } - context = readl(reply+8); - if(context & 0x40000000){ // IOCTL - void *p = adpt_ioctl_from_context(pHba, readl(reply+12)); - if( p != NULL) { - memcpy_fromio(p, reply, REPLY_FRAME_SIZE * 4); - } - // All IOCTLs will also be post wait - } - if(context & 0x80000000){ // Post wait message - status = readl(reply+16); - if(status >> 24){ - status &= 0xffff; /* Get detail status */ - } else { - status = I2O_POST_WAIT_OK; - } - if(!(context & 0x40000000)) { - /* - * The request tag is one less than the command tag - * as the firmware might treat a 0 tag as invalid - */ - cmd = scsi_host_find_tag(pHba->host, - readl(reply + 12) - 1); - if(cmd != NULL) { - printk(KERN_WARNING"%s: Apparent SCSI cmd in Post Wait Context - cmd=%p context=%x\n", pHba->name, cmd, context); - } - } - adpt_i2o_post_wait_complete(context, status); - } else { // SCSI message - /* - * The request tag is one less than the command tag - * as the firmware might treat a 0 tag as invalid - */ - cmd = scsi_host_find_tag(pHba->host, - readl(reply + 12) - 1); - if(cmd != NULL){ - scsi_dma_unmap(cmd); - adpt_i2o_scsi_complete(reply, cmd); - } - } - writel(m, pHba->reply_port); - wmb(); - rmb(); - } - handled = 1; -out: if(pHba->host) - spin_unlock_irqrestore(pHba->host->host_lock, flags); - return IRQ_RETVAL(handled); -} - -static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* d) -{ - int i; - u32 msg[MAX_MESSAGE_SIZE]; - u32* mptr; - u32* lptr; - u32 *lenptr; - int direction; - int scsidir; - int nseg; - u32 len; - u32 reqlen; - s32 rcode; - dma_addr_t addr; - - memset(msg, 0 , sizeof(msg)); - len = scsi_bufflen(cmd); - direction = 0x00000000; - - scsidir = 0x00000000; // DATA NO XFER - if(len) { - /* - * Set SCBFlags to indicate if data is being transferred - * in or out, or no data transfer - * Note: Do not have to verify index is less than 0 since - * cmd->cmnd[0] is an unsigned char - */ - switch(cmd->sc_data_direction){ - case DMA_FROM_DEVICE: - scsidir =0x40000000; // DATA IN (iop<--dev) - break; - case DMA_TO_DEVICE: - direction=0x04000000; // SGL OUT - scsidir =0x80000000; // DATA OUT (iop-->dev) - break; - case DMA_NONE: - break; - case DMA_BIDIRECTIONAL: - scsidir =0x40000000; // DATA IN (iop<--dev) - // Assume In - and continue; - break; - default: - printk(KERN_WARNING"%s: scsi opcode 0x%x not supported.\n", - pHba->name, cmd->cmnd[0]); - cmd->result = (DID_ERROR <<16); - scsi_done(cmd); - return 0; - } - } - // msg[0] is set later - // I2O_CMD_SCSI_EXEC - msg[1] = ((0xff<<24)|(HOST_TID<<12)|d->tid); - msg[2] = 0; - /* Add 1 to avoid firmware treating it as invalid command */ - msg[3] = scsi_cmd_to_rq(cmd)->tag + 1; - // Our cards use the transaction context as the tag for queueing - // Adaptec/DPT Private stuff - msg[4] = I2O_CMD_SCSI_EXEC|(DPT_ORGANIZATION_ID<<16); - msg[5] = d->tid; - /* Direction, disconnect ok | sense data | simple queue , CDBLen */ - // I2O_SCB_FLAG_ENABLE_DISCONNECT | - // I2O_SCB_FLAG_SIMPLE_QUEUE_TAG | - // I2O_SCB_FLAG_SENSE_DATA_IN_MESSAGE; - msg[6] = scsidir|0x20a00000|cmd->cmd_len; - - mptr=msg+7; - - // Write SCSI command into the message - always 16 byte block - memset(mptr, 0, 16); - memcpy(mptr, cmd->cmnd, cmd->cmd_len); - mptr+=4; - lenptr=mptr++; /* Remember me - fill in when we know */ - if (dpt_dma64(pHba)) { - reqlen = 16; // SINGLE SGE - *mptr++ = (0x7C<<24)+(2<<16)+0x02; /* Enable 64 bit */ - *mptr++ = 1 << PAGE_SHIFT; - } else { - reqlen = 14; // SINGLE SGE - } - /* Now fill in the SGList and command */ - - nseg = scsi_dma_map(cmd); - BUG_ON(nseg < 0); - if (nseg) { - struct scatterlist *sg; - - len = 0; - scsi_for_each_sg(cmd, sg, nseg, i) { - lptr = mptr; - *mptr++ = direction|0x10000000|sg_dma_len(sg); - len+=sg_dma_len(sg); - addr = sg_dma_address(sg); - *mptr++ = dma_low(addr); - if (dpt_dma64(pHba)) - *mptr++ = dma_high(addr); - /* Make this an end of list */ - if (i == nseg - 1) - *lptr = direction|0xD0000000|sg_dma_len(sg); - } - reqlen = mptr - msg; - *lenptr = len; - - if(cmd->underflow && len != cmd->underflow){ - printk(KERN_WARNING"Cmd len %08X Cmd underflow %08X\n", - len, cmd->underflow); - } - } else { - *lenptr = len = 0; - reqlen = 12; - } - - /* Stick the headers on */ - msg[0] = reqlen<<16 | ((reqlen > 12) ? SGL_OFFSET_12 : SGL_OFFSET_0); - - // Send it on it's way - rcode = adpt_i2o_post_this(pHba, msg, reqlen<<2); - if (rcode == 0) { - return 0; - } - return rcode; -} - - -static s32 adpt_scsi_host_alloc(adpt_hba* pHba, struct scsi_host_template *sht) -{ - struct Scsi_Host *host; - - host = scsi_host_alloc(sht, sizeof(adpt_hba*)); - if (host == NULL) { - printk("%s: scsi_host_alloc returned NULL\n", pHba->name); - return -1; - } - host->hostdata[0] = (unsigned long)pHba; - pHba->host = host; - - host->irq = pHba->pDev->irq; - /* no IO ports, so don't have to set host->io_port and - * host->n_io_port - */ - host->io_port = 0; - host->n_io_port = 0; - /* see comments in scsi_host.h */ - host->max_id = 16; - host->max_lun = 256; - host->max_channel = pHba->top_scsi_channel + 1; - host->cmd_per_lun = 1; - host->unique_id = (u32)sys_tbl_pa + pHba->unit; - host->sg_tablesize = pHba->sg_tablesize; - host->can_queue = pHba->post_fifo_size; - - return 0; -} - - -static void adpt_i2o_scsi_complete(void __iomem *reply, struct scsi_cmnd *cmd) -{ - adpt_hba* pHba; - u32 hba_status; - u32 dev_status; - u32 reply_flags = readl(reply) & 0xff00; // Leave it shifted up 8 bits - // I know this would look cleaner if I just read bytes - // but the model I have been using for all the rest of the - // io is in 4 byte words - so I keep that model - u16 detailed_status = readl(reply+16) &0xffff; - dev_status = (detailed_status & 0xff); - hba_status = detailed_status >> 8; - - // calculate resid for sg - scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+20)); - - pHba = (adpt_hba*) cmd->device->host->hostdata[0]; - - cmd->sense_buffer[0] = '\0'; // initialize sense valid flag to false - - if(!(reply_flags & MSG_FAIL)) { - switch(detailed_status & I2O_SCSI_DSC_MASK) { - case I2O_SCSI_DSC_SUCCESS: - cmd->result = (DID_OK << 16); - // handle underflow - if (readl(reply+20) < cmd->underflow) { - cmd->result = (DID_ERROR <<16); - printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name); - } - break; - case I2O_SCSI_DSC_REQUEST_ABORTED: - cmd->result = (DID_ABORT << 16); - break; - case I2O_SCSI_DSC_PATH_INVALID: - case I2O_SCSI_DSC_DEVICE_NOT_PRESENT: - case I2O_SCSI_DSC_SELECTION_TIMEOUT: - case I2O_SCSI_DSC_COMMAND_TIMEOUT: - case I2O_SCSI_DSC_NO_ADAPTER: - case I2O_SCSI_DSC_RESOURCE_UNAVAILABLE: - printk(KERN_WARNING"%s: SCSI Timeout-Device (%d,%d,%llu) hba status=0x%x, dev status=0x%x, cmd=0x%x\n", - pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, hba_status, dev_status, cmd->cmnd[0]); - cmd->result = (DID_TIME_OUT << 16); - break; - case I2O_SCSI_DSC_ADAPTER_BUSY: - case I2O_SCSI_DSC_BUS_BUSY: - cmd->result = (DID_BUS_BUSY << 16); - break; - case I2O_SCSI_DSC_SCSI_BUS_RESET: - case I2O_SCSI_DSC_BDR_MESSAGE_SENT: - cmd->result = (DID_RESET << 16); - break; - case I2O_SCSI_DSC_PARITY_ERROR_FAILURE: - printk(KERN_WARNING"%s: SCSI CMD parity error\n",pHba->name); - cmd->result = (DID_PARITY << 16); - break; - case I2O_SCSI_DSC_UNABLE_TO_ABORT: - case I2O_SCSI_DSC_COMPLETE_WITH_ERROR: - case I2O_SCSI_DSC_UNABLE_TO_TERMINATE: - case I2O_SCSI_DSC_MR_MESSAGE_RECEIVED: - case I2O_SCSI_DSC_AUTOSENSE_FAILED: - case I2O_SCSI_DSC_DATA_OVERRUN: - case I2O_SCSI_DSC_UNEXPECTED_BUS_FREE: - case I2O_SCSI_DSC_SEQUENCE_FAILURE: - case I2O_SCSI_DSC_REQUEST_LENGTH_ERROR: - case I2O_SCSI_DSC_PROVIDE_FAILURE: - case I2O_SCSI_DSC_REQUEST_TERMINATED: - case I2O_SCSI_DSC_IDE_MESSAGE_SENT: - case I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT: - case I2O_SCSI_DSC_MESSAGE_RECEIVED: - case I2O_SCSI_DSC_INVALID_CDB: - case I2O_SCSI_DSC_LUN_INVALID: - case I2O_SCSI_DSC_SCSI_TID_INVALID: - case I2O_SCSI_DSC_FUNCTION_UNAVAILABLE: - case I2O_SCSI_DSC_NO_NEXUS: - case I2O_SCSI_DSC_CDB_RECEIVED: - case I2O_SCSI_DSC_LUN_ALREADY_ENABLED: - case I2O_SCSI_DSC_QUEUE_FROZEN: - case I2O_SCSI_DSC_REQUEST_INVALID: - default: - printk(KERN_WARNING"%s: SCSI error %0x-Device(%d,%d,%llu) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n", - pHba->name, detailed_status & I2O_SCSI_DSC_MASK, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, - hba_status, dev_status, cmd->cmnd[0]); - cmd->result = (DID_ERROR << 16); - break; - } - - // copy over the request sense data if it was a check - // condition status - if (dev_status == SAM_STAT_CHECK_CONDITION) { - u32 len = min(SCSI_SENSE_BUFFERSIZE, 40); - // Copy over the sense data - memcpy_fromio(cmd->sense_buffer, (reply+28) , len); - if(cmd->sense_buffer[0] == 0x70 /* class 7 */ && - cmd->sense_buffer[2] == DATA_PROTECT ){ - /* This is to handle an array failed */ - cmd->result = (DID_TIME_OUT << 16); - printk(KERN_WARNING"%s: SCSI Data Protect-Device (%d,%d,%llu) hba_status=0x%x, dev_status=0x%x, cmd=0x%x\n", - pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, - hba_status, dev_status, cmd->cmnd[0]); - - } - } - } else { - /* In this condtion we could not talk to the tid - * the card rejected it. We should signal a retry - * for a limitted number of retries. - */ - cmd->result = (DID_TIME_OUT << 16); - printk(KERN_WARNING"%s: I2O MSG_FAIL - Device (%d,%d,%llu) tid=%d, cmd=0x%x\n", - pHba->name, (u32)cmd->device->channel, (u32)cmd->device->id, cmd->device->lun, - ((struct adpt_device*)(cmd->device->hostdata))->tid, cmd->cmnd[0]); - } - - cmd->result |= (dev_status); - - scsi_done(cmd); -} - - -static s32 adpt_rescan(adpt_hba* pHba) -{ - s32 rcode; - ulong flags = 0; - - if(pHba->host) - spin_lock_irqsave(pHba->host->host_lock, flags); - if ((rcode=adpt_i2o_lct_get(pHba)) < 0) - goto out; - if ((rcode=adpt_i2o_reparse_lct(pHba)) < 0) - goto out; - rcode = 0; -out: if(pHba->host) - spin_unlock_irqrestore(pHba->host->host_lock, flags); - return rcode; -} - - -static s32 adpt_i2o_reparse_lct(adpt_hba* pHba) -{ - int i; - int max; - int tid; - struct i2o_device *d; - i2o_lct *lct = pHba->lct; - u8 bus_no = 0; - s16 scsi_id; - u64 scsi_lun; - u32 buf[10]; // at least 8 u32's - struct adpt_device* pDev = NULL; - struct i2o_device* pI2o_dev = NULL; - - if (lct == NULL) { - printk(KERN_ERR "%s: LCT is empty???\n",pHba->name); - return -1; - } - - max = lct->table_size; - max -= 3; - max /= 9; - - // Mark each drive as unscanned - for (d = pHba->devices; d; d = d->next) { - pDev =(struct adpt_device*) d->owner; - if(!pDev){ - continue; - } - pDev->state |= DPTI_DEV_UNSCANNED; - } - - printk(KERN_INFO "%s: LCT has %d entries.\n", pHba->name,max); - - for(i=0;ilct_entry[i].user_tid != 0xfff){ - continue; - } - - if( lct->lct_entry[i].class_id == I2O_CLASS_RANDOM_BLOCK_STORAGE || - lct->lct_entry[i].class_id == I2O_CLASS_SCSI_PERIPHERAL || - lct->lct_entry[i].class_id == I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL ){ - tid = lct->lct_entry[i].tid; - if(adpt_i2o_query_scalar(pHba, tid, 0x8000, -1, buf, 32)<0) { - printk(KERN_ERR"%s: Could not query device\n",pHba->name); - continue; - } - bus_no = buf[0]>>16; - if (bus_no >= MAX_CHANNEL) { /* Something wrong skip it */ - printk(KERN_WARNING - "%s: Channel number %d out of range\n", - pHba->name, bus_no); - continue; - } - - scsi_id = buf[1]; - scsi_lun = scsilun_to_int((struct scsi_lun *)&buf[2]); - pDev = pHba->channel[bus_no].device[scsi_id]; - /* da lun */ - while(pDev) { - if(pDev->scsi_lun == scsi_lun) { - break; - } - pDev = pDev->next_lun; - } - if(!pDev ) { // Something new add it - d = kmalloc(sizeof(struct i2o_device), - GFP_ATOMIC); - if(d==NULL) - { - printk(KERN_CRIT "Out of memory for I2O device data.\n"); - return -ENOMEM; - } - - d->controller = pHba; - d->next = NULL; - - memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); - - d->flags = 0; - adpt_i2o_report_hba_unit(pHba, d); - adpt_i2o_install_device(pHba, d); - - pDev = pHba->channel[bus_no].device[scsi_id]; - if( pDev == NULL){ - pDev = - kzalloc(sizeof(struct adpt_device), - GFP_ATOMIC); - if(pDev == NULL) { - return -ENOMEM; - } - pHba->channel[bus_no].device[scsi_id] = pDev; - } else { - while (pDev->next_lun) { - pDev = pDev->next_lun; - } - pDev = pDev->next_lun = - kzalloc(sizeof(struct adpt_device), - GFP_ATOMIC); - if(pDev == NULL) { - return -ENOMEM; - } - } - pDev->tid = d->lct_data.tid; - pDev->scsi_channel = bus_no; - pDev->scsi_id = scsi_id; - pDev->scsi_lun = scsi_lun; - pDev->pI2o_dev = d; - d->owner = pDev; - pDev->type = (buf[0])&0xff; - pDev->flags = (buf[0]>>8)&0xff; - // Too late, SCSI system has made up it's mind, but what the hey ... - if(scsi_id > pHba->top_scsi_id){ - pHba->top_scsi_id = scsi_id; - } - if(scsi_lun > pHba->top_scsi_lun){ - pHba->top_scsi_lun = scsi_lun; - } - continue; - } // end of new i2o device - - // We found an old device - check it - while(pDev) { - if(pDev->scsi_lun == scsi_lun) { - if(!scsi_device_online(pDev->pScsi_dev)) { - printk(KERN_WARNING"%s: Setting device (%d,%d,%llu) back online\n", - pHba->name,bus_no,scsi_id,scsi_lun); - if (pDev->pScsi_dev) { - scsi_device_set_state(pDev->pScsi_dev, SDEV_RUNNING); - } - } - d = pDev->pI2o_dev; - if(d->lct_data.tid != tid) { // something changed - pDev->tid = tid; - memcpy(&d->lct_data, &lct->lct_entry[i], sizeof(i2o_lct_entry)); - if (pDev->pScsi_dev) { - pDev->pScsi_dev->changed = TRUE; - pDev->pScsi_dev->removable = TRUE; - } - } - // Found it - mark it scanned - pDev->state = DPTI_DEV_ONLINE; - break; - } - pDev = pDev->next_lun; - } - } - } - for (pI2o_dev = pHba->devices; pI2o_dev; pI2o_dev = pI2o_dev->next) { - pDev =(struct adpt_device*) pI2o_dev->owner; - if(!pDev){ - continue; - } - // Drive offline drives that previously existed but could not be found - // in the LCT table - if (pDev->state & DPTI_DEV_UNSCANNED){ - pDev->state = DPTI_DEV_OFFLINE; - printk(KERN_WARNING"%s: Device (%d,%d,%llu) offline\n",pHba->name,pDev->scsi_channel,pDev->scsi_id,pDev->scsi_lun); - if (pDev->pScsi_dev) { - scsi_device_set_state(pDev->pScsi_dev, SDEV_OFFLINE); - } - } - } - return 0; -} - -/*============================================================================ - * Routines from i2o subsystem - *============================================================================ - */ - - - -/* - * Bring an I2O controller into HOLD state. See the spec. - */ -static int adpt_i2o_activate_hba(adpt_hba* pHba) -{ - int rcode; - - if(pHba->initialized ) { - if (adpt_i2o_status_get(pHba) < 0) { - if((rcode = adpt_i2o_reset_hba(pHba)) != 0){ - printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name); - return rcode; - } - if (adpt_i2o_status_get(pHba) < 0) { - printk(KERN_INFO "HBA not responding.\n"); - return -1; - } - } - - if(pHba->status_block->iop_state == ADAPTER_STATE_FAULTED) { - printk(KERN_CRIT "%s: hardware fault\n", pHba->name); - return -1; - } - - if (pHba->status_block->iop_state == ADAPTER_STATE_READY || - pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL || - pHba->status_block->iop_state == ADAPTER_STATE_HOLD || - pHba->status_block->iop_state == ADAPTER_STATE_FAILED) { - adpt_i2o_reset_hba(pHba); - if (adpt_i2o_status_get(pHba) < 0 || pHba->status_block->iop_state != ADAPTER_STATE_RESET) { - printk(KERN_ERR "%s: Failed to initialize.\n", pHba->name); - return -1; - } - } - } else { - if((rcode = adpt_i2o_reset_hba(pHba)) != 0){ - printk(KERN_WARNING"%s: Could NOT reset.\n", pHba->name); - return rcode; - } - - } - - if (adpt_i2o_init_outbound_q(pHba) < 0) { - return -1; - } - - /* In HOLD state */ - - if (adpt_i2o_hrt_get(pHba) < 0) { - return -1; - } - - return 0; -} - -/* - * Bring a controller online into OPERATIONAL state. - */ - -static int adpt_i2o_online_hba(adpt_hba* pHba) -{ - if (adpt_i2o_systab_send(pHba) < 0) - return -1; - /* In READY state */ - - if (adpt_i2o_enable_hba(pHba) < 0) - return -1; - - /* In OPERATIONAL state */ - return 0; -} - -static s32 adpt_send_nop(adpt_hba*pHba,u32 m) -{ - u32 __iomem *msg; - ulong timeout = jiffies + 5*HZ; - - while(m == EMPTY_QUEUE){ - rmb(); - m = readl(pHba->post_port); - if(m != EMPTY_QUEUE){ - break; - } - if(time_after(jiffies,timeout)){ - printk(KERN_ERR "%s: Timeout waiting for message frame!\n",pHba->name); - return 2; - } - schedule_timeout_uninterruptible(1); - } - msg = (u32 __iomem *)(pHba->msg_addr_virt + m); - writel( THREE_WORD_MSG_SIZE | SGL_OFFSET_0,&msg[0]); - writel( I2O_CMD_UTIL_NOP << 24 | HOST_TID << 12 | 0,&msg[1]); - writel( 0,&msg[2]); - wmb(); - - writel(m, pHba->post_port); - wmb(); - return 0; -} - -static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba) -{ - u8 *status; - dma_addr_t addr; - u32 __iomem *msg = NULL; - int i; - ulong timeout = jiffies + TMOUT_INITOUTBOUND*HZ; - u32 m; - - do { - rmb(); - m = readl(pHba->post_port); - if (m != EMPTY_QUEUE) { - break; - } - - if(time_after(jiffies,timeout)){ - printk(KERN_WARNING"%s: Timeout waiting for message frame\n",pHba->name); - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } while(m == EMPTY_QUEUE); - - msg=(u32 __iomem *)(pHba->msg_addr_virt+m); - - status = dma_alloc_coherent(&pHba->pDev->dev, 4, &addr, GFP_KERNEL); - if (!status) { - adpt_send_nop(pHba, m); - printk(KERN_WARNING"%s: IOP reset failed - no free memory.\n", - pHba->name); - return -ENOMEM; - } - - writel(EIGHT_WORD_MSG_SIZE| SGL_OFFSET_6, &msg[0]); - writel(I2O_CMD_OUTBOUND_INIT<<24 | HOST_TID<<12 | ADAPTER_TID, &msg[1]); - writel(0, &msg[2]); - writel(0x0106, &msg[3]); /* Transaction context */ - writel(4096, &msg[4]); /* Host page frame size */ - writel((REPLY_FRAME_SIZE)<<16|0x80, &msg[5]); /* Outbound msg frame size and Initcode */ - writel(0xD0000004, &msg[6]); /* Simple SG LE, EOB */ - writel((u32)addr, &msg[7]); - - writel(m, pHba->post_port); - wmb(); - - // Wait for the reply status to come back - do { - if (*status) { - if (*status != 0x01 /*I2O_EXEC_OUTBOUND_INIT_IN_PROGRESS*/) { - break; - } - } - rmb(); - if(time_after(jiffies,timeout)){ - printk(KERN_WARNING"%s: Timeout Initializing\n",pHba->name); - /* We lose 4 bytes of "status" here, but we - cannot free these because controller may - awake and corrupt those bytes at any time */ - /* dma_free_coherent(&pHba->pDev->dev, 4, status, addr); */ - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } while (1); - - // If the command was successful, fill the fifo with our reply - // message packets - if(*status != 0x04 /*I2O_EXEC_OUTBOUND_INIT_COMPLETE*/) { - dma_free_coherent(&pHba->pDev->dev, 4, status, addr); - return -2; - } - dma_free_coherent(&pHba->pDev->dev, 4, status, addr); - - if(pHba->reply_pool != NULL) { - dma_free_coherent(&pHba->pDev->dev, - pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, - pHba->reply_pool, pHba->reply_pool_pa); - } - - pHba->reply_pool = dma_alloc_coherent(&pHba->pDev->dev, - pHba->reply_fifo_size * REPLY_FRAME_SIZE * 4, - &pHba->reply_pool_pa, GFP_KERNEL); - if (!pHba->reply_pool) { - printk(KERN_ERR "%s: Could not allocate reply pool\n", pHba->name); - return -ENOMEM; - } - - for(i = 0; i < pHba->reply_fifo_size; i++) { - writel(pHba->reply_pool_pa + (i * REPLY_FRAME_SIZE * 4), - pHba->reply_port); - wmb(); - } - adpt_i2o_status_get(pHba); - return 0; -} - - -/* - * I2O System Table. Contains information about - * all the IOPs in the system. Used to inform IOPs - * about each other's existence. - * - * sys_tbl_ver is the CurrentChangeIndicator that is - * used by IOPs to track changes. - */ - - - -static s32 adpt_i2o_status_get(adpt_hba* pHba) -{ - ulong timeout; - u32 m; - u32 __iomem *msg; - u8 *status_block=NULL; - - if(pHba->status_block == NULL) { - pHba->status_block = dma_alloc_coherent(&pHba->pDev->dev, - sizeof(i2o_status_block), - &pHba->status_block_pa, GFP_KERNEL); - if(pHba->status_block == NULL) { - printk(KERN_ERR - "dpti%d: Get Status Block failed; Out of memory. \n", - pHba->unit); - return -ENOMEM; - } - } - memset(pHba->status_block, 0, sizeof(i2o_status_block)); - status_block = (u8*)(pHba->status_block); - timeout = jiffies+TMOUT_GETSTATUS*HZ; - do { - rmb(); - m = readl(pHba->post_port); - if (m != EMPTY_QUEUE) { - break; - } - if(time_after(jiffies,timeout)){ - printk(KERN_ERR "%s: Timeout waiting for message !\n", - pHba->name); - return -ETIMEDOUT; - } - schedule_timeout_uninterruptible(1); - } while(m==EMPTY_QUEUE); - - - msg=(u32 __iomem *)(pHba->msg_addr_virt+m); - - writel(NINE_WORD_MSG_SIZE|SGL_OFFSET_0, &msg[0]); - writel(I2O_CMD_STATUS_GET<<24|HOST_TID<<12|ADAPTER_TID, &msg[1]); - writel(1, &msg[2]); - writel(0, &msg[3]); - writel(0, &msg[4]); - writel(0, &msg[5]); - writel( dma_low(pHba->status_block_pa), &msg[6]); - writel( dma_high(pHba->status_block_pa), &msg[7]); - writel(sizeof(i2o_status_block), &msg[8]); // 88 bytes - - //post message - writel(m, pHba->post_port); - wmb(); - - while(status_block[87]!=0xff){ - if(time_after(jiffies,timeout)){ - printk(KERN_ERR"dpti%d: Get status timeout.\n", - pHba->unit); - return -ETIMEDOUT; - } - rmb(); - schedule_timeout_uninterruptible(1); - } - - // Set up our number of outbound and inbound messages - pHba->post_fifo_size = pHba->status_block->max_inbound_frames; - if (pHba->post_fifo_size > MAX_TO_IOP_MESSAGES) { - pHba->post_fifo_size = MAX_TO_IOP_MESSAGES; - } - - pHba->reply_fifo_size = pHba->status_block->max_outbound_frames; - if (pHba->reply_fifo_size > MAX_FROM_IOP_MESSAGES) { - pHba->reply_fifo_size = MAX_FROM_IOP_MESSAGES; - } - - // Calculate the Scatter Gather list size - if (dpt_dma64(pHba)) { - pHba->sg_tablesize - = ((pHba->status_block->inbound_frame_size * 4 - - 14 * sizeof(u32)) - / (sizeof(struct sg_simple_element) + sizeof(u32))); - } else { - pHba->sg_tablesize - = ((pHba->status_block->inbound_frame_size * 4 - - 12 * sizeof(u32)) - / sizeof(struct sg_simple_element)); - } - if (pHba->sg_tablesize > SG_LIST_ELEMENTS) { - pHba->sg_tablesize = SG_LIST_ELEMENTS; - } - - -#ifdef DEBUG - printk("dpti%d: State = ",pHba->unit); - switch(pHba->status_block->iop_state) { - case 0x01: - printk("INIT\n"); - break; - case 0x02: - printk("RESET\n"); - break; - case 0x04: - printk("HOLD\n"); - break; - case 0x05: - printk("READY\n"); - break; - case 0x08: - printk("OPERATIONAL\n"); - break; - case 0x10: - printk("FAILED\n"); - break; - case 0x11: - printk("FAULTED\n"); - break; - default: - printk("%x (unknown!!)\n",pHba->status_block->iop_state); - } -#endif - return 0; -} - -/* - * Get the IOP's Logical Configuration Table - */ -static int adpt_i2o_lct_get(adpt_hba* pHba) -{ - u32 msg[8]; - int ret; - u32 buf[16]; - - if ((pHba->lct_size == 0) || (pHba->lct == NULL)){ - pHba->lct_size = pHba->status_block->expected_lct_size; - } - do { - if (pHba->lct == NULL) { - pHba->lct = dma_alloc_coherent(&pHba->pDev->dev, - pHba->lct_size, &pHba->lct_pa, - GFP_ATOMIC); - if(pHba->lct == NULL) { - printk(KERN_CRIT "%s: Lct Get failed. Out of memory.\n", - pHba->name); - return -ENOMEM; - } - } - memset(pHba->lct, 0, pHba->lct_size); - - msg[0] = EIGHT_WORD_MSG_SIZE|SGL_OFFSET_6; - msg[1] = I2O_CMD_LCT_NOTIFY<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = 0; - msg[3] = 0; - msg[4] = 0xFFFFFFFF; /* All devices */ - msg[5] = 0x00000000; /* Report now */ - msg[6] = 0xD0000000|pHba->lct_size; - msg[7] = (u32)pHba->lct_pa; - - if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 360))) { - printk(KERN_ERR "%s: LCT Get failed (status=%#10x.\n", - pHba->name, ret); - printk(KERN_ERR"Adaptec: Error Reading Hardware.\n"); - return ret; - } - - if ((pHba->lct->table_size << 2) > pHba->lct_size) { - pHba->lct_size = pHba->lct->table_size << 2; - dma_free_coherent(&pHba->pDev->dev, pHba->lct_size, - pHba->lct, pHba->lct_pa); - pHba->lct = NULL; - } - } while (pHba->lct == NULL); - - PDEBUG("%s: Hardware resource table read.\n", pHba->name); - - - // I2O_DPT_EXEC_IOP_BUFFERS_GROUP_NO; - if(adpt_i2o_query_scalar(pHba, 0 , 0x8000, -1, buf, sizeof(buf))>=0) { - pHba->FwDebugBufferSize = buf[1]; - pHba->FwDebugBuffer_P = ioremap(pHba->base_addr_phys + buf[0], - pHba->FwDebugBufferSize); - if (pHba->FwDebugBuffer_P) { - pHba->FwDebugFlags_P = pHba->FwDebugBuffer_P + - FW_DEBUG_FLAGS_OFFSET; - pHba->FwDebugBLEDvalue_P = pHba->FwDebugBuffer_P + - FW_DEBUG_BLED_OFFSET; - pHba->FwDebugBLEDflag_P = pHba->FwDebugBLEDvalue_P + 1; - pHba->FwDebugStrLength_P = pHba->FwDebugBuffer_P + - FW_DEBUG_STR_LENGTH_OFFSET; - pHba->FwDebugBuffer_P += buf[2]; - pHba->FwDebugFlags = 0; - } - } - - return 0; -} - -static int adpt_i2o_build_sys_table(void) -{ - adpt_hba* pHba = hba_chain; - int count = 0; - - if (sys_tbl) - dma_free_coherent(&pHba->pDev->dev, sys_tbl_len, - sys_tbl, sys_tbl_pa); - - sys_tbl_len = sizeof(struct i2o_sys_tbl) + // Header + IOPs - (hba_count) * sizeof(struct i2o_sys_tbl_entry); - - sys_tbl = dma_alloc_coherent(&pHba->pDev->dev, - sys_tbl_len, &sys_tbl_pa, GFP_KERNEL); - if (!sys_tbl) { - printk(KERN_WARNING "SysTab Set failed. Out of memory.\n"); - return -ENOMEM; - } - - sys_tbl->num_entries = hba_count; - sys_tbl->version = I2OVERSION; - sys_tbl->change_ind = sys_tbl_ind++; - - for(pHba = hba_chain; pHba; pHba = pHba->next) { - u64 addr; - // Get updated Status Block so we have the latest information - if (adpt_i2o_status_get(pHba)) { - sys_tbl->num_entries--; - continue; // try next one - } - - sys_tbl->iops[count].org_id = pHba->status_block->org_id; - sys_tbl->iops[count].iop_id = pHba->unit + 2; - sys_tbl->iops[count].seg_num = 0; - sys_tbl->iops[count].i2o_version = pHba->status_block->i2o_version; - sys_tbl->iops[count].iop_state = pHba->status_block->iop_state; - sys_tbl->iops[count].msg_type = pHba->status_block->msg_type; - sys_tbl->iops[count].frame_size = pHba->status_block->inbound_frame_size; - sys_tbl->iops[count].last_changed = sys_tbl_ind - 1; // ?? - sys_tbl->iops[count].iop_capabilities = pHba->status_block->iop_capabilities; - addr = pHba->base_addr_phys + 0x40; - sys_tbl->iops[count].inbound_low = dma_low(addr); - sys_tbl->iops[count].inbound_high = dma_high(addr); - - count++; - } - -#ifdef DEBUG -{ - u32 *table = (u32*)sys_tbl; - printk(KERN_DEBUG"sys_tbl_len=%d in 32bit words\n",(sys_tbl_len >>2)); - for(count = 0; count < (sys_tbl_len >>2); count++) { - printk(KERN_INFO "sys_tbl[%d] = %0#10x\n", - count, table[count]); - } -} -#endif - - return 0; -} - - -/* - * Dump the information block associated with a given unit (TID) - */ - -static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d) -{ - char buf[64]; - int unit = d->lct_data.tid; - - printk(KERN_INFO "TID %3.3d ", unit); - - if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 3, buf, 16)>=0) - { - buf[16]=0; - printk(" Vendor: %-12.12s", buf); - } - if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 4, buf, 16)>=0) - { - buf[16]=0; - printk(" Device: %-12.12s", buf); - } - if(adpt_i2o_query_scalar(pHba, unit, 0xF100, 6, buf, 8)>=0) - { - buf[8]=0; - printk(" Rev: %-12.12s\n", buf); - } -#ifdef DEBUG - printk(KERN_INFO "\tClass: %.21s\n", adpt_i2o_get_class_name(d->lct_data.class_id)); - printk(KERN_INFO "\tSubclass: 0x%04X\n", d->lct_data.sub_class); - printk(KERN_INFO "\tFlags: "); - - if(d->lct_data.device_flags&(1<<0)) - printk("C"); // ConfigDialog requested - if(d->lct_data.device_flags&(1<<1)) - printk("U"); // Multi-user capable - if(!(d->lct_data.device_flags&(1<<4))) - printk("P"); // Peer service enabled! - if(!(d->lct_data.device_flags&(1<<5))) - printk("M"); // Mgmt service enabled! - printk("\n"); -#endif -} - -#ifdef DEBUG -/* - * Do i2o class name lookup - */ -static const char *adpt_i2o_get_class_name(int class) -{ - int idx = 16; - static char *i2o_class_name[] = { - "Executive", - "Device Driver Module", - "Block Device", - "Tape Device", - "LAN Interface", - "WAN Interface", - "Fibre Channel Port", - "Fibre Channel Device", - "SCSI Device", - "ATE Port", - "ATE Device", - "Floppy Controller", - "Floppy Device", - "Secondary Bus Port", - "Peer Transport Agent", - "Peer Transport", - "Unknown" - }; - - switch(class&0xFFF) { - case I2O_CLASS_EXECUTIVE: - idx = 0; break; - case I2O_CLASS_DDM: - idx = 1; break; - case I2O_CLASS_RANDOM_BLOCK_STORAGE: - idx = 2; break; - case I2O_CLASS_SEQUENTIAL_STORAGE: - idx = 3; break; - case I2O_CLASS_LAN: - idx = 4; break; - case I2O_CLASS_WAN: - idx = 5; break; - case I2O_CLASS_FIBRE_CHANNEL_PORT: - idx = 6; break; - case I2O_CLASS_FIBRE_CHANNEL_PERIPHERAL: - idx = 7; break; - case I2O_CLASS_SCSI_PERIPHERAL: - idx = 8; break; - case I2O_CLASS_ATE_PORT: - idx = 9; break; - case I2O_CLASS_ATE_PERIPHERAL: - idx = 10; break; - case I2O_CLASS_FLOPPY_CONTROLLER: - idx = 11; break; - case I2O_CLASS_FLOPPY_DEVICE: - idx = 12; break; - case I2O_CLASS_BUS_ADAPTER_PORT: - idx = 13; break; - case I2O_CLASS_PEER_TRANSPORT_AGENT: - idx = 14; break; - case I2O_CLASS_PEER_TRANSPORT: - idx = 15; break; - } - return i2o_class_name[idx]; -} -#endif - - -static s32 adpt_i2o_hrt_get(adpt_hba* pHba) -{ - u32 msg[6]; - int ret, size = sizeof(i2o_hrt); - - do { - if (pHba->hrt == NULL) { - pHba->hrt = dma_alloc_coherent(&pHba->pDev->dev, - size, &pHba->hrt_pa, GFP_KERNEL); - if (pHba->hrt == NULL) { - printk(KERN_CRIT "%s: Hrt Get failed; Out of memory.\n", pHba->name); - return -ENOMEM; - } - } - - msg[0]= SIX_WORD_MSG_SIZE| SGL_OFFSET_4; - msg[1]= I2O_CMD_HRT_GET<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2]= 0; - msg[3]= 0; - msg[4]= (0xD0000000 | size); /* Simple transaction */ - msg[5]= (u32)pHba->hrt_pa; /* Dump it here */ - - if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg),20))) { - printk(KERN_ERR "%s: Unable to get HRT (status=%#10x)\n", pHba->name, ret); - return ret; - } - - if (pHba->hrt->num_entries * pHba->hrt->entry_len << 2 > size) { - int newsize = pHba->hrt->num_entries * pHba->hrt->entry_len << 2; - dma_free_coherent(&pHba->pDev->dev, size, - pHba->hrt, pHba->hrt_pa); - size = newsize; - pHba->hrt = NULL; - } - } while(pHba->hrt == NULL); - return 0; -} - -/* - * Query one scalar group value or a whole scalar group. - */ -static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid, - int group, int field, void *buf, int buflen) -{ - u16 opblk[] = { 1, 0, I2O_PARAMS_FIELD_GET, group, 1, field }; - u8 *opblk_va; - dma_addr_t opblk_pa; - u8 *resblk_va; - dma_addr_t resblk_pa; - - int size; - - /* 8 bytes for header */ - resblk_va = dma_alloc_coherent(&pHba->pDev->dev, - sizeof(u8) * (8 + buflen), &resblk_pa, GFP_KERNEL); - if (resblk_va == NULL) { - printk(KERN_CRIT "%s: query scalar failed; Out of memory.\n", pHba->name); - return -ENOMEM; - } - - opblk_va = dma_alloc_coherent(&pHba->pDev->dev, - sizeof(opblk), &opblk_pa, GFP_KERNEL); - if (opblk_va == NULL) { - dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen), - resblk_va, resblk_pa); - printk(KERN_CRIT "%s: query operation failed; Out of memory.\n", - pHba->name); - return -ENOMEM; - } - if (field == -1) /* whole group */ - opblk[4] = -1; - - memcpy(opblk_va, opblk, sizeof(opblk)); - size = adpt_i2o_issue_params(I2O_CMD_UTIL_PARAMS_GET, pHba, tid, - opblk_va, opblk_pa, sizeof(opblk), - resblk_va, resblk_pa, sizeof(u8)*(8+buflen)); - dma_free_coherent(&pHba->pDev->dev, sizeof(opblk), opblk_va, opblk_pa); - if (size == -ETIME) { - dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen), - resblk_va, resblk_pa); - printk(KERN_WARNING "%s: issue params failed; Timed out.\n", pHba->name); - return -ETIME; - } else if (size == -EINTR) { - dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen), - resblk_va, resblk_pa); - printk(KERN_WARNING "%s: issue params failed; Interrupted.\n", pHba->name); - return -EINTR; - } - - memcpy(buf, resblk_va+8, buflen); /* cut off header */ - - dma_free_coherent(&pHba->pDev->dev, sizeof(u8) * (8+buflen), - resblk_va, resblk_pa); - if (size < 0) - return size; - - return buflen; -} - - -/* Issue UTIL_PARAMS_GET or UTIL_PARAMS_SET - * - * This function can be used for all UtilParamsGet/Set operations. - * The OperationBlock is given in opblk-buffer, - * and results are returned in resblk-buffer. - * Note that the minimum sized resblk is 8 bytes and contains - * ResultCount, ErrorInfoSize, BlockStatus and BlockSize. - */ -static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid, - void *opblk_va, dma_addr_t opblk_pa, int oplen, - void *resblk_va, dma_addr_t resblk_pa, int reslen) -{ - u32 msg[9]; - u32 *res = (u32 *)resblk_va; - int wait_status; - - msg[0] = NINE_WORD_MSG_SIZE | SGL_OFFSET_5; - msg[1] = cmd << 24 | HOST_TID << 12 | tid; - msg[2] = 0; - msg[3] = 0; - msg[4] = 0; - msg[5] = 0x54000000 | oplen; /* OperationBlock */ - msg[6] = (u32)opblk_pa; - msg[7] = 0xD0000000 | reslen; /* ResultBlock */ - msg[8] = (u32)resblk_pa; - - if ((wait_status = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 20))) { - printk("adpt_i2o_issue_params: post_wait failed (%p)\n", resblk_va); - return wait_status; /* -DetailedStatus */ - } - - if (res[1]&0x00FF0000) { /* BlockStatus != SUCCESS */ - printk(KERN_WARNING "%s: %s - Error:\n ErrorInfoSize = 0x%02x, " - "BlockStatus = 0x%02x, BlockSize = 0x%04x\n", - pHba->name, - (cmd == I2O_CMD_UTIL_PARAMS_SET) ? "PARAMS_SET" - : "PARAMS_GET", - res[1]>>24, (res[1]>>16)&0xFF, res[1]&0xFFFF); - return -((res[1] >> 16) & 0xFF); /* -BlockStatus */ - } - - return 4 + ((res[1] & 0x0000FFFF) << 2); /* bytes used in resblk */ -} - - -static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba) -{ - u32 msg[4]; - int ret; - - adpt_i2o_status_get(pHba); - - /* SysQuiesce discarded if IOP not in READY or OPERATIONAL state */ - - if((pHba->status_block->iop_state != ADAPTER_STATE_READY) && - (pHba->status_block->iop_state != ADAPTER_STATE_OPERATIONAL)){ - return 0; - } - - msg[0] = FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1] = I2O_CMD_SYS_QUIESCE<<24|HOST_TID<<12|ADAPTER_TID; - msg[2] = 0; - msg[3] = 0; - - if((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) { - printk(KERN_INFO"dpti%d: Unable to quiesce (status=%#x).\n", - pHba->unit, -ret); - } else { - printk(KERN_INFO"dpti%d: Quiesced.\n",pHba->unit); - } - - adpt_i2o_status_get(pHba); - return ret; -} - - -/* - * Enable IOP. Allows the IOP to resume external operations. - */ -static int adpt_i2o_enable_hba(adpt_hba* pHba) -{ - u32 msg[4]; - int ret; - - adpt_i2o_status_get(pHba); - if(!pHba->status_block){ - return -ENOMEM; - } - /* Enable only allowed on READY state */ - if(pHba->status_block->iop_state == ADAPTER_STATE_OPERATIONAL) - return 0; - - if(pHba->status_block->iop_state != ADAPTER_STATE_READY) - return -EINVAL; - - msg[0]=FOUR_WORD_MSG_SIZE|SGL_OFFSET_0; - msg[1]=I2O_CMD_SYS_ENABLE<<24|HOST_TID<<12|ADAPTER_TID; - msg[2]= 0; - msg[3]= 0; - - if ((ret = adpt_i2o_post_wait(pHba, msg, sizeof(msg), 240))) { - printk(KERN_WARNING"%s: Could not enable (status=%#10x).\n", - pHba->name, ret); - } else { - PDEBUG("%s: Enabled.\n", pHba->name); - } - - adpt_i2o_status_get(pHba); - return ret; -} - - -static int adpt_i2o_systab_send(adpt_hba* pHba) -{ - u32 msg[12]; - int ret; - - msg[0] = I2O_MESSAGE_SIZE(12) | SGL_OFFSET_6; - msg[1] = I2O_CMD_SYS_TAB_SET<<24 | HOST_TID<<12 | ADAPTER_TID; - msg[2] = 0; - msg[3] = 0; - msg[4] = (0<<16) | ((pHba->unit+2) << 12); /* Host 0 IOP ID (unit + 2) */ - msg[5] = 0; /* Segment 0 */ - - /* - * Provide three SGL-elements: - * System table (SysTab), Private memory space declaration and - * Private i/o space declaration - */ - msg[6] = 0x54000000 | sys_tbl_len; - msg[7] = (u32)sys_tbl_pa; - msg[8] = 0x54000000 | 0; - msg[9] = 0; - msg[10] = 0xD4000000 | 0; - msg[11] = 0; - - if ((ret=adpt_i2o_post_wait(pHba, msg, sizeof(msg), 120))) { - printk(KERN_INFO "%s: Unable to set SysTab (status=%#10x).\n", - pHba->name, ret); - } -#ifdef DEBUG - else { - PINFO("%s: SysTab set.\n", pHba->name); - } -#endif - - return ret; -} - - -/*============================================================================ - * - *============================================================================ - */ - - -#ifdef UARTDELAY - -static static void adpt_delay(int millisec) -{ - int i; - for (i = 0; i < millisec; i++) { - udelay(1000); /* delay for one millisecond */ - } -} - -#endif - -static struct scsi_host_template driver_template = { - .module = THIS_MODULE, - .name = "dpt_i2o", - .proc_name = "dpt_i2o", - .show_info = adpt_show_info, - .info = adpt_info, - .queuecommand = adpt_queue, - .eh_abort_handler = adpt_abort, - .eh_device_reset_handler = adpt_device_reset, - .eh_bus_reset_handler = adpt_bus_reset, - .eh_host_reset_handler = adpt_reset, - .bios_param = adpt_bios_param, - .slave_configure = adpt_slave_configure, - .can_queue = MAX_TO_IOP_MESSAGES, - .this_id = 7, -}; - -static int __init adpt_init(void) -{ - int error; - adpt_hba *pHba, *next; - - printk("Loading Adaptec I2O RAID: Version " DPT_I2O_VERSION "\n"); - - error = adpt_detect(&driver_template); - if (error < 0) - return error; - if (hba_chain == NULL) - return -ENODEV; - - for (pHba = hba_chain; pHba; pHba = pHba->next) { - error = scsi_add_host(pHba->host, &pHba->pDev->dev); - if (error) - goto fail; - scsi_scan_host(pHba->host); - } - return 0; -fail: - for (pHba = hba_chain; pHba; pHba = next) { - next = pHba->next; - scsi_remove_host(pHba->host); - } - return error; -} - -static void __exit adpt_exit(void) -{ - adpt_hba *pHba, *next; - - for (pHba = hba_chain; pHba; pHba = next) { - next = pHba->next; - adpt_release(pHba); - } -} - -module_init(adpt_init); -module_exit(adpt_exit); - -MODULE_LICENSE("GPL"); diff --git a/drivers/scsi/dpti.h b/drivers/scsi/dpti.h deleted file mode 100644 index 8a079e8d7f65..000000000000 --- a/drivers/scsi/dpti.h +++ /dev/null @@ -1,331 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/*************************************************************************** - dpti.h - description - ------------------- - begin : Thu Sep 7 2000 - copyright : (C) 2001 by Adaptec - - See Documentation/scsi/dpti.rst for history, notes, license info - and credits - ***************************************************************************/ - -/*************************************************************************** - * * - * * - ***************************************************************************/ - -#ifndef _DPT_H -#define _DPT_H - -#define MAX_TO_IOP_MESSAGES (255) -#define MAX_FROM_IOP_MESSAGES (255) - - -/* - * SCSI interface function Prototypes - */ - -static int adpt_detect(struct scsi_host_template * sht); -static int adpt_queue(struct Scsi_Host *h, struct scsi_cmnd * cmd); -static int adpt_abort(struct scsi_cmnd * cmd); -static int adpt_reset(struct scsi_cmnd* cmd); -static int adpt_slave_configure(struct scsi_device *); - -static const char *adpt_info(struct Scsi_Host *pSHost); -static int adpt_bios_param(struct scsi_device * sdev, struct block_device *dev, - sector_t, int geom[]); - -static int adpt_bus_reset(struct scsi_cmnd* cmd); -static int adpt_device_reset(struct scsi_cmnd* cmd); - - -/* - * struct scsi_host_template (see scsi/scsi_host.h) - */ - -#define DPT_DRIVER_NAME "Adaptec I2O RAID" - -#ifndef HOSTS_C - -#include "dpt/sys_info.h" -#include -#include "dpt/dpti_i2o.h" -#include "dpt/dpti_ioctl.h" - -#define DPT_I2O_VERSION "2.4 Build 5go" -#define DPT_VERSION 2 -#define DPT_REVISION '4' -#define DPT_SUBREVISION '5' -#define DPT_BETA "" -#define DPT_MONTH 8 -#define DPT_DAY 7 -#define DPT_YEAR (2001-1980) - -#define DPT_DRIVER "dpt_i2o" -#define DPTI_I2O_MAJOR (151) -#define DPT_ORGANIZATION_ID (0x1B) /* For Private Messages */ -#define DPTI_MAX_HBA (16) -#define MAX_CHANNEL (5) // Maximum Channel # Supported -#define MAX_ID (128) // Maximum Target ID Supported - -/* Sizes in 4 byte words */ -#define REPLY_FRAME_SIZE (17) -#define MAX_MESSAGE_SIZE (128) -#define SG_LIST_ELEMENTS (56) - -#define EMPTY_QUEUE 0xffffffff -#define I2O_INTERRUPT_PENDING_B (0x08) - -#define PCI_DPT_VENDOR_ID (0x1044) // DPT PCI Vendor ID -#define PCI_DPT_DEVICE_ID (0xA501) // DPT PCI I2O Device ID -#define PCI_DPT_RAPTOR_DEVICE_ID (0xA511) - -/* Debugging macro from Linux Device Drivers - Rubini */ -#undef PDEBUG -#ifdef DEBUG -//TODO add debug level switch -# define PDEBUG(fmt, args...) printk(KERN_DEBUG "dpti: " fmt, ##args) -# define PDEBUGV(fmt, args...) printk(KERN_DEBUG "dpti: " fmt, ##args) -#else -# define PDEBUG(fmt, args...) /* not debugging: nothing */ -# define PDEBUGV(fmt, args...) /* not debugging: nothing */ -#endif - -#define PERROR(fmt, args...) printk(KERN_ERR fmt, ##args) -#define PWARN(fmt, args...) printk(KERN_WARNING fmt, ##args) -#define PINFO(fmt, args...) printk(KERN_INFO fmt, ##args) -#define PCRIT(fmt, args...) printk(KERN_CRIT fmt, ##args) - -#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM)) - -// Command timeouts -#define FOREVER (0) -#define TMOUT_INQUIRY (20) -#define TMOUT_FLUSH (360/45) -#define TMOUT_ABORT (30) -#define TMOUT_SCSI (300) -#define TMOUT_IOPRESET (360) -#define TMOUT_GETSTATUS (15) -#define TMOUT_INITOUTBOUND (15) -#define TMOUT_LCT (360) - - -#define I2O_SCSI_DEVICE_DSC_MASK 0x00FF - -#define I2O_DETAIL_STATUS_UNSUPPORTED_FUNCTION 0x000A - -#define I2O_SCSI_DSC_MASK 0xFF00 -#define I2O_SCSI_DSC_SUCCESS 0x0000 -#define I2O_SCSI_DSC_REQUEST_ABORTED 0x0200 -#define I2O_SCSI_DSC_UNABLE_TO_ABORT 0x0300 -#define I2O_SCSI_DSC_COMPLETE_WITH_ERROR 0x0400 -#define I2O_SCSI_DSC_ADAPTER_BUSY 0x0500 -#define I2O_SCSI_DSC_REQUEST_INVALID 0x0600 -#define I2O_SCSI_DSC_PATH_INVALID 0x0700 -#define I2O_SCSI_DSC_DEVICE_NOT_PRESENT 0x0800 -#define I2O_SCSI_DSC_UNABLE_TO_TERMINATE 0x0900 -#define I2O_SCSI_DSC_SELECTION_TIMEOUT 0x0A00 -#define I2O_SCSI_DSC_COMMAND_TIMEOUT 0x0B00 -#define I2O_SCSI_DSC_MR_MESSAGE_RECEIVED 0x0D00 -#define I2O_SCSI_DSC_SCSI_BUS_RESET 0x0E00 -#define I2O_SCSI_DSC_PARITY_ERROR_FAILURE 0x0F00 -#define I2O_SCSI_DSC_AUTOSENSE_FAILED 0x1000 -#define I2O_SCSI_DSC_NO_ADAPTER 0x1100 -#define I2O_SCSI_DSC_DATA_OVERRUN 0x1200 -#define I2O_SCSI_DSC_UNEXPECTED_BUS_FREE 0x1300 -#define I2O_SCSI_DSC_SEQUENCE_FAILURE 0x1400 -#define I2O_SCSI_DSC_REQUEST_LENGTH_ERROR 0x1500 -#define I2O_SCSI_DSC_PROVIDE_FAILURE 0x1600 -#define I2O_SCSI_DSC_BDR_MESSAGE_SENT 0x1700 -#define I2O_SCSI_DSC_REQUEST_TERMINATED 0x1800 -#define I2O_SCSI_DSC_IDE_MESSAGE_SENT 0x3300 -#define I2O_SCSI_DSC_RESOURCE_UNAVAILABLE 0x3400 -#define I2O_SCSI_DSC_UNACKNOWLEDGED_EVENT 0x3500 -#define I2O_SCSI_DSC_MESSAGE_RECEIVED 0x3600 -#define I2O_SCSI_DSC_INVALID_CDB 0x3700 -#define I2O_SCSI_DSC_LUN_INVALID 0x3800 -#define I2O_SCSI_DSC_SCSI_TID_INVALID 0x3900 -#define I2O_SCSI_DSC_FUNCTION_UNAVAILABLE 0x3A00 -#define I2O_SCSI_DSC_NO_NEXUS 0x3B00 -#define I2O_SCSI_DSC_SCSI_IID_INVALID 0x3C00 -#define I2O_SCSI_DSC_CDB_RECEIVED 0x3D00 -#define I2O_SCSI_DSC_LUN_ALREADY_ENABLED 0x3E00 -#define I2O_SCSI_DSC_BUS_BUSY 0x3F00 -#define I2O_SCSI_DSC_QUEUE_FROZEN 0x4000 - - -#ifndef TRUE -#define TRUE 1 -#define FALSE 0 -#endif - -#define HBA_FLAGS_INSTALLED_B 0x00000001 // Adapter Was Installed -#define HBA_FLAGS_BLINKLED_B 0x00000002 // Adapter In Blink LED State -#define HBA_FLAGS_IN_RESET 0x00000040 /* in reset */ -#define HBA_HOSTRESET_FAILED 0x00000080 /* adpt_resethost failed */ - - -// Device state flags -#define DPTI_DEV_ONLINE 0x00 -#define DPTI_DEV_UNSCANNED 0x01 -#define DPTI_DEV_RESET 0x02 -#define DPTI_DEV_OFFLINE 0x04 - - -struct adpt_device { - struct adpt_device* next_lun; - u32 flags; - u32 type; - u32 capacity; - u32 block_size; - u8 scsi_channel; - u8 scsi_id; - u64 scsi_lun; - u8 state; - u16 tid; - struct i2o_device* pI2o_dev; - struct scsi_device *pScsi_dev; -}; - -struct adpt_channel { - struct adpt_device* device[MAX_ID]; /* used as an array of 128 scsi ids */ - u8 scsi_id; - u8 type; - u16 tid; - u32 state; - struct i2o_device* pI2o_dev; -}; - -// HBA state flags -#define DPTI_STATE_RESET (0x01) - -typedef struct _adpt_hba { - struct _adpt_hba *next; - struct pci_dev *pDev; - struct Scsi_Host *host; - u32 state; - spinlock_t state_lock; - int unit; - int host_no; /* SCSI host number */ - u8 initialized; - u8 in_use; /* is the management node open*/ - - char name[32]; - char detail[55]; - - void __iomem *base_addr_virt; - void __iomem *msg_addr_virt; - ulong base_addr_phys; - void __iomem *post_port; - void __iomem *reply_port; - void __iomem *irq_mask; - u16 post_count; - u32 post_fifo_size; - u32 reply_fifo_size; - u32* reply_pool; - dma_addr_t reply_pool_pa; - u32 sg_tablesize; // Scatter/Gather List Size. - u8 top_scsi_channel; - u8 top_scsi_id; - u64 top_scsi_lun; - u8 dma64; - - i2o_status_block* status_block; - dma_addr_t status_block_pa; - i2o_hrt* hrt; - dma_addr_t hrt_pa; - i2o_lct* lct; - dma_addr_t lct_pa; - uint lct_size; - struct i2o_device* devices; - struct adpt_channel channel[MAX_CHANNEL]; - struct proc_dir_entry* proc_entry; /* /proc dir */ - - void __iomem *FwDebugBuffer_P; // Virtual Address Of FW Debug Buffer - u32 FwDebugBufferSize; // FW Debug Buffer Size In Bytes - void __iomem *FwDebugStrLength_P;// Virtual Addr Of FW Debug String Len - void __iomem *FwDebugFlags_P; // Virtual Address Of FW Debug Flags - void __iomem *FwDebugBLEDflag_P;// Virtual Addr Of FW Debug BLED - void __iomem *FwDebugBLEDvalue_P;// Virtual Addr Of FW Debug BLED - u32 FwDebugFlags; - u32 *ioctl_reply_context[4]; -} adpt_hba; - -struct sg_simple_element { - u32 flag_count; - u32 addr_bus; -}; - -/* - * Function Prototypes - */ - -static void adpt_i2o_sys_shutdown(void); -static int adpt_init(void); -static int adpt_i2o_build_sys_table(void); -static irqreturn_t adpt_isr(int irq, void *dev_id); - -static void adpt_i2o_report_hba_unit(adpt_hba* pHba, struct i2o_device *d); -static int adpt_i2o_query_scalar(adpt_hba* pHba, int tid, - int group, int field, void *buf, int buflen); -#ifdef DEBUG -static const char *adpt_i2o_get_class_name(int class); -#endif -static int adpt_i2o_issue_params(int cmd, adpt_hba* pHba, int tid, - void *opblk, dma_addr_t opblk_pa, int oplen, - void *resblk, dma_addr_t resblk_pa, int reslen); -static int adpt_i2o_post_wait(adpt_hba* pHba, u32* msg, int len, int timeout); -static int adpt_i2o_lct_get(adpt_hba* pHba); -static int adpt_i2o_parse_lct(adpt_hba* pHba); -static int adpt_i2o_activate_hba(adpt_hba* pHba); -static int adpt_i2o_enable_hba(adpt_hba* pHba); -static int adpt_i2o_install_device(adpt_hba* pHba, struct i2o_device *d); -static s32 adpt_i2o_post_this(adpt_hba* pHba, u32* data, int len); -static s32 adpt_i2o_quiesce_hba(adpt_hba* pHba); -static s32 adpt_i2o_status_get(adpt_hba* pHba); -static s32 adpt_i2o_init_outbound_q(adpt_hba* pHba); -static s32 adpt_i2o_hrt_get(adpt_hba* pHba); -static s32 adpt_scsi_to_i2o(adpt_hba* pHba, struct scsi_cmnd* cmd, struct adpt_device* dptdevice); -static void adpt_i2o_scsi_complete(void __iomem *reply, struct scsi_cmnd *cmd); -static s32 adpt_scsi_host_alloc(adpt_hba* pHba,struct scsi_host_template * sht); -static s32 adpt_hba_reset(adpt_hba* pHba); -static s32 adpt_i2o_reset_hba(adpt_hba* pHba); -static s32 adpt_rescan(adpt_hba* pHba); -static s32 adpt_i2o_reparse_lct(adpt_hba* pHba); -static s32 adpt_send_nop(adpt_hba*pHba,u32 m); -static void adpt_i2o_delete_hba(adpt_hba* pHba); -static void adpt_inquiry(adpt_hba* pHba); -static struct adpt_device* adpt_find_device(adpt_hba* pHba, u32 chan, u32 id, u64 lun); -static int adpt_install_hba(struct scsi_host_template* sht, struct pci_dev* pDev) ; -static int adpt_i2o_online_hba(adpt_hba* pHba); -static void adpt_i2o_post_wait_complete(u32, int); -static int adpt_i2o_systab_send(adpt_hba* pHba); - -static int adpt_ioctl(struct inode *inode, struct file *file, uint cmd, ulong arg); -static int adpt_open(struct inode *inode, struct file *file); -static int adpt_close(struct inode *inode, struct file *file); - - -#ifdef UARTDELAY -static void adpt_delay(int millisec); -#endif - -#define PRINT_BUFFER_SIZE 512 - -#define HBA_FLAGS_DBG_FLAGS_MASK 0xffff0000 // Mask for debug flags -#define HBA_FLAGS_DBG_KERNEL_PRINT_B 0x00010000 // Kernel Debugger Print -#define HBA_FLAGS_DBG_FW_PRINT_B 0x00020000 // Firmware Debugger Print -#define HBA_FLAGS_DBG_FUNCTION_ENTRY_B 0x00040000 // Function Entry Point -#define HBA_FLAGS_DBG_FUNCTION_EXIT_B 0x00080000 // Function Exit -#define HBA_FLAGS_DBG_ERROR_B 0x00100000 // Error Conditions -#define HBA_FLAGS_DBG_INIT_B 0x00200000 // Init Prints -#define HBA_FLAGS_DBG_OS_COMMANDS_B 0x00400000 // OS Command Info -#define HBA_FLAGS_DBG_SCAN_B 0x00800000 // Device Scan - -#define FW_DEBUG_STR_LENGTH_OFFSET 0 -#define FW_DEBUG_FLAGS_OFFSET 4 -#define FW_DEBUG_BLED_OFFSET 8 - -#define FW_DEBUG_FLAGS_NO_HEADERS_B 0x01 -#endif /* !HOSTS_C */ -#endif /* _DPT_H */ diff --git a/drivers/scsi/fcoe/fcoe.c b/drivers/scsi/fcoe/fcoe.c index c2a59109857a..6ec296321ffc 100644 --- a/drivers/scsi/fcoe/fcoe.c +++ b/drivers/scsi/fcoe/fcoe.c @@ -1488,7 +1488,6 @@ static int fcoe_xmit(struct fc_lport *lport, struct fc_frame *fp) fh = fc_frame_header_get(fp); skb = fp_skb(fp); - wlen = skb->len / FCOE_WORD_TO_BYTE; if (!lport->link_up) { kfree_skb(skb); diff --git a/drivers/scsi/fnic/cq_desc.h b/drivers/scsi/fnic/cq_desc.h index d1225cf6320e..0eb4ba277264 100644 --- a/drivers/scsi/fnic/cq_desc.h +++ b/drivers/scsi/fnic/cq_desc.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _CQ_DESC_H_ #define _CQ_DESC_H_ diff --git a/drivers/scsi/fnic/cq_enet_desc.h b/drivers/scsi/fnic/cq_enet_desc.h index a9fa26f82ddd..b6113291cf68 100644 --- a/drivers/scsi/fnic/cq_enet_desc.h +++ b/drivers/scsi/fnic/cq_enet_desc.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _CQ_ENET_DESC_H_ #define _CQ_ENET_DESC_H_ diff --git a/drivers/scsi/fnic/cq_exch_desc.h b/drivers/scsi/fnic/cq_exch_desc.h index 501660cfe228..4d94329c8ef5 100644 --- a/drivers/scsi/fnic/cq_exch_desc.h +++ b/drivers/scsi/fnic/cq_exch_desc.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _CQ_EXCH_DESC_H_ #define _CQ_EXCH_DESC_H_ diff --git a/drivers/scsi/fnic/fcpio.h b/drivers/scsi/fnic/fcpio.h index 12d770d885c5..54a0b2ba8f6f 100644 --- a/drivers/scsi/fnic/fcpio.h +++ b/drivers/scsi/fnic/fcpio.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _FCPIO_H_ #define _FCPIO_H_ diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 85ec6163ddab..d82de34f6fd7 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _FNIC_H_ #define _FNIC_H_ diff --git a/drivers/scsi/fnic/fnic_attrs.c b/drivers/scsi/fnic/fnic_attrs.c index bbe2ca4971b2..a61e0c5e6506 100644 --- a/drivers/scsi/fnic/fnic_attrs.c +++ b/drivers/scsi/fnic/fnic_attrs.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include diff --git a/drivers/scsi/fnic/fnic_debugfs.c b/drivers/scsi/fnic/fnic_debugfs.c index 866b4c983ace..6fedc3b7d1ab 100644 --- a/drivers/scsi/fnic/fnic_debugfs.c +++ b/drivers/scsi/fnic/fnic_debugfs.c @@ -1,19 +1,5 @@ -/* - * Copyright 2012 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2012 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/fnic/fnic_fcs.c b/drivers/scsi/fnic/fnic_fcs.c index 1885218f9d15..79ddfaaf71a4 100644 --- a/drivers/scsi/fnic/fnic_fcs.c +++ b/drivers/scsi/fnic/fnic_fcs.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include diff --git a/drivers/scsi/fnic/fnic_fip.h b/drivers/scsi/fnic/fnic_fip.h index 7761f33ab5d4..79f53029737b 100644 --- a/drivers/scsi/fnic/fnic_fip.h +++ b/drivers/scsi/fnic/fnic_fip.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _FNIC_FIP_H_ diff --git a/drivers/scsi/fnic/fnic_io.h b/drivers/scsi/fnic/fnic_io.h index 1cb6a68c8e4e..f4c8769df312 100644 --- a/drivers/scsi/fnic/fnic_io.h +++ b/drivers/scsi/fnic/fnic_io.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _FNIC_IO_H_ #define _FNIC_IO_H_ diff --git a/drivers/scsi/fnic/fnic_isr.c b/drivers/scsi/fnic/fnic_isr.c index 2fb2731f50fb..8896758fed8c 100644 --- a/drivers/scsi/fnic/fnic_isr.c +++ b/drivers/scsi/fnic/fnic_isr.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include @@ -332,4 +320,3 @@ void fnic_clear_intr_mode(struct fnic *fnic) pci_free_irq_vectors(fnic->pdev); vnic_dev_set_intr_mode(fnic->vdev, VNIC_DEV_INTR_MODE_INTX); } - diff --git a/drivers/scsi/fnic/fnic_main.c b/drivers/scsi/fnic/fnic_main.c index 51e7c344ddc3..1077110ab273 100644 --- a/drivers/scsi/fnic/fnic_main.c +++ b/drivers/scsi/fnic/fnic_main.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include @@ -556,6 +544,39 @@ static void fnic_set_vlan(struct fnic *fnic, u16 vlan_id) vnic_dev_set_default_vlan(fnic->vdev, vlan_id); } +static int fnic_scsi_drv_init(struct fnic *fnic) +{ + struct Scsi_Host *host = fnic->lport->host; + + /* Configure maximum outstanding IO reqs*/ + if (fnic->config.io_throttle_count != FNIC_UCSM_DFLT_THROTTLE_CNT_BLD) + host->can_queue = min_t(u32, FNIC_MAX_IO_REQ, + max_t(u32, FNIC_MIN_IO_REQ, + fnic->config.io_throttle_count)); + + fnic->fnic_max_tag_id = host->can_queue; + host->max_lun = fnic->config.luns_per_tgt; + host->max_id = FNIC_MAX_FCP_TARGET; + host->max_cmd_len = FCOE_MAX_CMD_LEN; + + host->nr_hw_queues = fnic->wq_copy_count; + if (host->nr_hw_queues > 1) + shost_printk(KERN_ERR, host, + "fnic: blk-mq is not supported"); + + host->nr_hw_queues = fnic->wq_copy_count = 1; + + shost_printk(KERN_INFO, host, + "fnic: can_queue: %d max_lun: %llu", + host->can_queue, host->max_lun); + + shost_printk(KERN_INFO, host, + "fnic: max_id: %d max_cmd_len: %d nr_hw_queues: %d", + host->max_id, host->max_cmd_len, host->nr_hw_queues); + + return 0; +} + static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { struct Scsi_Host *host; @@ -696,17 +717,7 @@ static int fnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent) goto err_out_dev_close; } - /* Configure Maximum Outstanding IO reqs*/ - if (fnic->config.io_throttle_count != FNIC_UCSM_DFLT_THROTTLE_CNT_BLD) { - host->can_queue = min_t(u32, FNIC_MAX_IO_REQ, - max_t(u32, FNIC_MIN_IO_REQ, - fnic->config.io_throttle_count)); - } - fnic->fnic_max_tag_id = host->can_queue; - - host->max_lun = fnic->config.luns_per_tgt; - host->max_id = FNIC_MAX_FCP_TARGET; - host->max_cmd_len = FCOE_MAX_CMD_LEN; + fnic_scsi_drv_init(fnic); fnic_get_res_counts(fnic); @@ -1159,4 +1170,3 @@ static void __exit fnic_cleanup_module(void) module_init(fnic_init_module); module_exit(fnic_cleanup_module); - diff --git a/drivers/scsi/fnic/fnic_res.c b/drivers/scsi/fnic/fnic_res.c index 50488f8e169d..a1c9cfcace7f 100644 --- a/drivers/scsi/fnic/fnic_res.c +++ b/drivers/scsi/fnic/fnic_res.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include diff --git a/drivers/scsi/fnic/fnic_res.h b/drivers/scsi/fnic/fnic_res.h index ef8aaf2156dd..92a2fcfd3ea9 100644 --- a/drivers/scsi/fnic/fnic_res.h +++ b/drivers/scsi/fnic/fnic_res.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _FNIC_RES_H_ #define _FNIC_RES_H_ diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 77a4d9f8aa83..26dbd347156e 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include diff --git a/drivers/scsi/fnic/fnic_stats.h b/drivers/scsi/fnic/fnic_stats.h index 086f729f3c46..bdf639eef8cf 100644 --- a/drivers/scsi/fnic/fnic_stats.h +++ b/drivers/scsi/fnic/fnic_stats.h @@ -1,19 +1,5 @@ -/* - * Copyright 2013 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2013 Cisco Systems, Inc. All rights reserved. */ #ifndef _FNIC_STATS_H_ #define _FNIC_STATS_H_ diff --git a/drivers/scsi/fnic/fnic_trace.c b/drivers/scsi/fnic/fnic_trace.c index 4a7536bb0ab3..e03967463561 100644 --- a/drivers/scsi/fnic/fnic_trace.c +++ b/drivers/scsi/fnic/fnic_trace.c @@ -1,19 +1,5 @@ -/* - * Copyright 2012 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2012 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/fnic/fnic_trace.h b/drivers/scsi/fnic/fnic_trace.h index 8aa55c1e2025..d1c301bf3fde 100644 --- a/drivers/scsi/fnic/fnic_trace.h +++ b/drivers/scsi/fnic/fnic_trace.h @@ -1,19 +1,5 @@ -/* - * Copyright 2012 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2012 Cisco Systems, Inc. All rights reserved. */ #ifndef __FNIC_TRACE_H__ #define __FNIC_TRACE_H__ diff --git a/drivers/scsi/fnic/rq_enet_desc.h b/drivers/scsi/fnic/rq_enet_desc.h index 92e80ae6b725..9bc509d355c4 100644 --- a/drivers/scsi/fnic/rq_enet_desc.h +++ b/drivers/scsi/fnic/rq_enet_desc.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _RQ_ENET_DESC_H_ #define _RQ_ENET_DESC_H_ diff --git a/drivers/scsi/fnic/vnic_cq.c b/drivers/scsi/fnic/vnic_cq.c index c5db32eda5ef..ed3dd443fe3e 100644 --- a/drivers/scsi/fnic/vnic_cq.c +++ b/drivers/scsi/fnic/vnic_cq.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include #include diff --git a/drivers/scsi/fnic/vnic_cq.h b/drivers/scsi/fnic/vnic_cq.h index 4ede6809fb1e..e7cc1f165390 100644 --- a/drivers/scsi/fnic/vnic_cq.h +++ b/drivers/scsi/fnic/vnic_cq.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_CQ_H_ #define _VNIC_CQ_H_ diff --git a/drivers/scsi/fnic/vnic_cq_copy.h b/drivers/scsi/fnic/vnic_cq_copy.h index 7901ce255a81..1b198ee59dd6 100644 --- a/drivers/scsi/fnic/vnic_cq_copy.h +++ b/drivers/scsi/fnic/vnic_cq_copy.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_CQ_COPY_H_ #define _VNIC_CQ_COPY_H_ diff --git a/drivers/scsi/fnic/vnic_dev.c b/drivers/scsi/fnic/vnic_dev.c index 5988c300cc82..3e5b437c0492 100644 --- a/drivers/scsi/fnic/vnic_dev.c +++ b/drivers/scsi/fnic/vnic_dev.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include diff --git a/drivers/scsi/fnic/vnic_dev.h b/drivers/scsi/fnic/vnic_dev.h index ef5309a5df5d..7a568d141cde 100644 --- a/drivers/scsi/fnic/vnic_dev.h +++ b/drivers/scsi/fnic/vnic_dev.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_DEV_H_ #define _VNIC_DEV_H_ diff --git a/drivers/scsi/fnic/vnic_devcmd.h b/drivers/scsi/fnic/vnic_devcmd.h index c20d30e36dfc..f876d223b2b4 100644 --- a/drivers/scsi/fnic/vnic_devcmd.h +++ b/drivers/scsi/fnic/vnic_devcmd.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_DEVCMD_H_ #define _VNIC_DEVCMD_H_ diff --git a/drivers/scsi/fnic/vnic_intr.c b/drivers/scsi/fnic/vnic_intr.c index 4f4dc8793d23..df7f63acd879 100644 --- a/drivers/scsi/fnic/vnic_intr.c +++ b/drivers/scsi/fnic/vnic_intr.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include diff --git a/drivers/scsi/fnic/vnic_intr.h b/drivers/scsi/fnic/vnic_intr.h index d5fb40e7c98e..acc194c0f522 100644 --- a/drivers/scsi/fnic/vnic_intr.h +++ b/drivers/scsi/fnic/vnic_intr.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_INTR_H_ #define _VNIC_INTR_H_ diff --git a/drivers/scsi/fnic/vnic_nic.h b/drivers/scsi/fnic/vnic_nic.h index f15b83eeaced..6896f16d564b 100644 --- a/drivers/scsi/fnic/vnic_nic.h +++ b/drivers/scsi/fnic/vnic_nic.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_NIC_H_ #define _VNIC_NIC_H_ diff --git a/drivers/scsi/fnic/vnic_resource.h b/drivers/scsi/fnic/vnic_resource.h index 7c6163f73bd3..3d260b831fc5 100644 --- a/drivers/scsi/fnic/vnic_resource.h +++ b/drivers/scsi/fnic/vnic_resource.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_RESOURCE_H_ #define _VNIC_RESOURCE_H_ diff --git a/drivers/scsi/fnic/vnic_rq.c b/drivers/scsi/fnic/vnic_rq.c index 6a35b1be0032..350607d13c9a 100644 --- a/drivers/scsi/fnic/vnic_rq.c +++ b/drivers/scsi/fnic/vnic_rq.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include @@ -191,4 +179,3 @@ void vnic_rq_clean(struct vnic_rq *rq, vnic_dev_clear_desc_ring(&rq->ring); } - diff --git a/drivers/scsi/fnic/vnic_rq.h b/drivers/scsi/fnic/vnic_rq.h index aebdfbd6ad3c..1066255de808 100644 --- a/drivers/scsi/fnic/vnic_rq.h +++ b/drivers/scsi/fnic/vnic_rq.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_RQ_H_ #define _VNIC_RQ_H_ diff --git a/drivers/scsi/fnic/vnic_scsi.h b/drivers/scsi/fnic/vnic_scsi.h index e343e1d0f801..4e12f7b32d9d 100644 --- a/drivers/scsi/fnic/vnic_scsi.h +++ b/drivers/scsi/fnic/vnic_scsi.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_SCSI_H_ #define _VNIC_SCSI_H_ diff --git a/drivers/scsi/fnic/vnic_stats.h b/drivers/scsi/fnic/vnic_stats.h index 5372e23c1cb3..4396397b089d 100644 --- a/drivers/scsi/fnic/vnic_stats.h +++ b/drivers/scsi/fnic/vnic_stats.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_STATS_H_ #define _VNIC_STATS_H_ diff --git a/drivers/scsi/fnic/vnic_wq.c b/drivers/scsi/fnic/vnic_wq.c index 442972c04e65..426b901c80f3 100644 --- a/drivers/scsi/fnic/vnic_wq.c +++ b/drivers/scsi/fnic/vnic_wq.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include diff --git a/drivers/scsi/fnic/vnic_wq.h b/drivers/scsi/fnic/vnic_wq.h index 5d1e0a44d94a..041618e13ce2 100644 --- a/drivers/scsi/fnic/vnic_wq.h +++ b/drivers/scsi/fnic/vnic_wq.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_WQ_H_ #define _VNIC_WQ_H_ diff --git a/drivers/scsi/fnic/vnic_wq_copy.c b/drivers/scsi/fnic/vnic_wq_copy.c index 7b18635df7e6..96569d4ccc58 100644 --- a/drivers/scsi/fnic/vnic_wq_copy.c +++ b/drivers/scsi/fnic/vnic_wq_copy.c @@ -1,19 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include @@ -108,4 +96,3 @@ void vnic_wq_copy_init(struct vnic_wq_copy *wq, unsigned int cq_index, iowrite32(error_interrupt_enable, &wq->ctrl->error_interrupt_enable); iowrite32(error_interrupt_offset, &wq->ctrl->error_interrupt_offset); } - diff --git a/drivers/scsi/fnic/vnic_wq_copy.h b/drivers/scsi/fnic/vnic_wq_copy.h index 6aff9740c3df..2f8340144e79 100644 --- a/drivers/scsi/fnic/vnic_wq_copy.h +++ b/drivers/scsi/fnic/vnic_wq_copy.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _VNIC_WQ_COPY_H_ #define _VNIC_WQ_COPY_H_ diff --git a/drivers/scsi/fnic/wq_enet_desc.h b/drivers/scsi/fnic/wq_enet_desc.h index b121cbad18b8..9a933a5dee79 100644 --- a/drivers/scsi/fnic/wq_enet_desc.h +++ b/drivers/scsi/fnic/wq_enet_desc.h @@ -1,19 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright 2008 Cisco Systems, Inc. All rights reserved. * Copyright 2007 Nuova Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef _WQ_ENET_DESC_H_ #define _WQ_ENET_DESC_H_ diff --git a/drivers/scsi/gvp11.c b/drivers/scsi/gvp11.c index 2f6c56aabe1d..7d56a236a011 100644 --- a/drivers/scsi/gvp11.c +++ b/drivers/scsi/gvp11.c @@ -26,8 +26,12 @@ struct gvp11_hostdata { struct WD33C93_hostdata wh; struct gvp11_scsiregs *regs; + struct device *dev; }; +#define DMA_DIR(d) ((d == DATA_OUT_DIR) ? DMA_TO_DEVICE : DMA_FROM_DEVICE) +#define TO_DMA_MASK(m) (~((unsigned long long)m & 0xffffffff)) + static irqreturn_t gvp11_intr(int irq, void *data) { struct Scsi_Host *instance = data; @@ -54,17 +58,33 @@ void gvp11_setup(char *str, int *ints) static int dma_setup(struct scsi_cmnd *cmd, int dir_in) { struct scsi_pointer *scsi_pointer = WD33C93_scsi_pointer(cmd); + unsigned long len = scsi_pointer->this_residual; struct Scsi_Host *instance = cmd->device->host; struct gvp11_hostdata *hdata = shost_priv(instance); struct WD33C93_hostdata *wh = &hdata->wh; struct gvp11_scsiregs *regs = hdata->regs; unsigned short cntr = GVP11_DMAC_INT_ENABLE; - unsigned long addr = virt_to_bus(scsi_pointer->ptr); + dma_addr_t addr; int bank_mask; static int scsi_alloc_out_of_range = 0; + addr = dma_map_single(hdata->dev, scsi_pointer->ptr, + len, DMA_DIR(dir_in)); + if (dma_mapping_error(hdata->dev, addr)) { + dev_warn(hdata->dev, "cannot map SCSI data block %p\n", + scsi_pointer->ptr); + return 1; + } + scsi_pointer->dma_handle = addr; + /* use bounce buffer if the physical address is bad */ if (addr & wh->dma_xfer_mask) { + /* drop useless mapping */ + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(dir_in)); + scsi_pointer->dma_handle = (dma_addr_t) NULL; + wh->dma_bounce_len = (scsi_pointer->this_residual + 511) & ~0x1ff; if (!scsi_alloc_out_of_range) { @@ -87,10 +107,32 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) wh->dma_buffer_pool = BUF_CHIP_ALLOCED; } - /* check if the address of the bounce buffer is OK */ - addr = virt_to_bus(wh->dma_bounce_buffer); + if (!dir_in) { + /* copy to bounce buffer for a write */ + memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, + scsi_pointer->this_residual); + } + + if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED) { + /* will flush/invalidate cache for us */ + addr = dma_map_single(hdata->dev, + wh->dma_bounce_buffer, + wh->dma_bounce_len, + DMA_DIR(dir_in)); + /* can't map buffer; use PIO */ + if (dma_mapping_error(hdata->dev, addr)) { + dev_warn(hdata->dev, + "cannot map bounce buffer %p\n", + wh->dma_bounce_buffer); + return 1; + } + } if (addr & wh->dma_xfer_mask) { + /* drop useless mapping */ + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(dir_in)); /* fall back to Chip RAM if address out of range */ if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED) { kfree(wh->dma_bounce_buffer); @@ -108,15 +150,19 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) return 1; } - addr = virt_to_bus(wh->dma_bounce_buffer); + if (!dir_in) { + /* copy to bounce buffer for a write */ + memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, + scsi_pointer->this_residual); + } + /* chip RAM can be mapped to phys. address directly */ + addr = virt_to_phys(wh->dma_bounce_buffer); + /* no need to flush/invalidate cache */ wh->dma_buffer_pool = BUF_CHIP_ALLOCED; } + /* finally, have OK mapping (punted for PIO else) */ + scsi_pointer->dma_handle = addr; - if (!dir_in) { - /* copy to bounce buffer for a write */ - memcpy(wh->dma_bounce_buffer, scsi_pointer->ptr, - scsi_pointer->this_residual); - } } /* setup dma direction */ @@ -129,13 +175,7 @@ static int dma_setup(struct scsi_cmnd *cmd, int dir_in) /* setup DMA *physical* address */ regs->ACR = addr; - if (dir_in) { - /* invalidate any cache */ - cache_clear(addr, scsi_pointer->this_residual); - } else { - /* push any dirty cache */ - cache_push(addr, scsi_pointer->this_residual); - } + /* no more cache flush here - dma_map_single() takes care */ bank_mask = (~wh->dma_xfer_mask >> 18) & 0x01c0; if (bank_mask) @@ -161,6 +201,11 @@ static void dma_stop(struct Scsi_Host *instance, struct scsi_cmnd *SCpnt, /* remove write bit from CONTROL bits */ regs->CNTR = GVP11_DMAC_INT_ENABLE; + if (wh->dma_buffer_pool == BUF_SCSI_ALLOCED) + dma_unmap_single(hdata->dev, scsi_pointer->dma_handle, + scsi_pointer->this_residual, + DMA_DIR(wh->dma_dir)); + /* copy from a bounce buffer, if necessary */ if (status && wh->dma_bounce_buffer) { if (wh->dma_dir && SCpnt) @@ -287,6 +332,13 @@ static int gvp11_probe(struct zorro_dev *z, const struct zorro_device_id *ent) default_dma_xfer_mask = ent->driver_data; + if (dma_set_mask_and_coherent(&z->dev, + TO_DMA_MASK(default_dma_xfer_mask))) { + dev_warn(&z->dev, "cannot use DMA mask %llx\n", + TO_DMA_MASK(default_dma_xfer_mask)); + return -ENODEV; + } + /* * Rumors state that some GVP ram boards use the same product * code as the SCSI controllers. Therefore if the board-size @@ -327,9 +379,16 @@ static int gvp11_probe(struct zorro_dev *z, const struct zorro_device_id *ent) wdregs.SCMD = ®s->SCMD; hdata = shost_priv(instance); - if (gvp11_xfer_mask) + if (gvp11_xfer_mask) { hdata->wh.dma_xfer_mask = gvp11_xfer_mask; - else + if (dma_set_mask_and_coherent(&z->dev, + TO_DMA_MASK(gvp11_xfer_mask))) { + dev_warn(&z->dev, "cannot use DMA mask %llx\n", + TO_DMA_MASK(gvp11_xfer_mask)); + error = -ENODEV; + goto fail_check_or_alloc; + } + } else hdata->wh.dma_xfer_mask = default_dma_xfer_mask; hdata->wh.no_sync = 0xff; diff --git a/drivers/scsi/hisi_sas/hisi_sas_main.c b/drivers/scsi/hisi_sas/hisi_sas_main.c index 764e859d0106..33af5b8dede2 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_main.c +++ b/drivers/scsi/hisi_sas/hisi_sas_main.c @@ -219,10 +219,15 @@ void hisi_sas_slot_task_free(struct hisi_hba *hisi_hba, struct sas_task *task, task->lldd_task = NULL; if (!sas_protocol_ata(task->task_proto)) { - if (slot->n_elem) - dma_unmap_sg(dev, task->scatter, - task->num_scatter, - task->data_dir); + if (slot->n_elem) { + if (task->task_proto & SAS_PROTOCOL_SSP) + dma_unmap_sg(dev, task->scatter, + task->num_scatter, + task->data_dir); + else + dma_unmap_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); + } if (slot->n_elem_dif) { struct sas_ssp_task *ssp_task = &task->ssp_task; struct scsi_cmnd *scsi_cmnd = ssp_task->cmd; @@ -269,28 +274,23 @@ static void hisi_sas_task_prep_abort(struct hisi_hba *hisi_hba, } static void hisi_sas_dma_unmap(struct hisi_hba *hisi_hba, - struct sas_task *task, int n_elem, - int n_elem_req) + struct sas_task *task, int n_elem) { struct device *dev = hisi_hba->dev; - if (!sas_protocol_ata(task->task_proto)) { + if (!sas_protocol_ata(task->task_proto) && n_elem) { if (task->num_scatter) { - if (n_elem) - dma_unmap_sg(dev, task->scatter, - task->num_scatter, - task->data_dir); + dma_unmap_sg(dev, task->scatter, task->num_scatter, + task->data_dir); } else if (task->task_proto & SAS_PROTOCOL_SMP) { - if (n_elem_req) - dma_unmap_sg(dev, &task->smp_task.smp_req, - 1, DMA_TO_DEVICE); + dma_unmap_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); } } } static int hisi_sas_dma_map(struct hisi_hba *hisi_hba, - struct sas_task *task, int *n_elem, - int *n_elem_req) + struct sas_task *task, int *n_elem) { struct device *dev = hisi_hba->dev; int rc; @@ -308,9 +308,9 @@ static int hisi_sas_dma_map(struct hisi_hba *hisi_hba, goto prep_out; } } else if (task->task_proto & SAS_PROTOCOL_SMP) { - *n_elem_req = dma_map_sg(dev, &task->smp_task.smp_req, - 1, DMA_TO_DEVICE); - if (!*n_elem_req) { + *n_elem = dma_map_sg(dev, &task->smp_task.smp_req, + 1, DMA_TO_DEVICE); + if (!*n_elem) { rc = -ENOMEM; goto prep_out; } @@ -332,8 +332,7 @@ static int hisi_sas_dma_map(struct hisi_hba *hisi_hba, err_out_dma_unmap: /* It would be better to call dma_unmap_sg() here, but it's messy */ - hisi_sas_dma_unmap(hisi_hba, task, *n_elem, - *n_elem_req); + hisi_sas_dma_unmap(hisi_hba, task, *n_elem); prep_out: return rc; } @@ -457,7 +456,7 @@ void hisi_sas_task_deliver(struct hisi_hba *hisi_hba, static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) { - int n_elem = 0, n_elem_dif = 0, n_elem_req = 0; + int n_elem = 0, n_elem_dif = 0; struct domain_device *device = task->dev; struct asd_sas_port *sas_port = device->port; struct hisi_sas_device *sas_dev = device->lldd_dev; @@ -568,8 +567,7 @@ static int hisi_sas_queue_command(struct sas_task *task, gfp_t gfp_flags) return -EINVAL; } - rc = hisi_sas_dma_map(hisi_hba, task, &n_elem, - &n_elem_req); + rc = hisi_sas_dma_map(hisi_hba, task, &n_elem); if (rc < 0) goto prep_out; @@ -605,8 +603,7 @@ err_out_dif_dma_unmap: if (!sas_protocol_ata(task->task_proto)) hisi_sas_dif_dma_unmap(hisi_hba, task, n_elem_dif); err_out_dma_unmap: - hisi_sas_dma_unmap(hisi_hba, task, n_elem, - n_elem_req); + hisi_sas_dma_unmap(hisi_hba, task, n_elem); prep_out: dev_err(dev, "task exec: failed[%d]!\n", rc); return rc; diff --git a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c index 4582791def32..349546bacb2b 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v1_hw.c @@ -1282,8 +1282,6 @@ static void slot_complete_v1_hw(struct hisi_hba *hisi_hba, ts->stat = SAS_SAM_STAT_GOOD; - dma_unmap_sg(dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); memcpy(to + sg_resp->offset, hisi_sas_status_buf_addr_mem(slot) + sizeof(struct hisi_sas_err_record), diff --git a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c index 455d49299ddf..70e401fd432a 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v2_hw.c @@ -805,8 +805,8 @@ slot_index_alloc_quirk_v2_hw(struct hisi_hba *hisi_hba, return -SAS_QUEUE_FULL; } /* - * SAS IPTT bit0 should be 1, and SATA IPTT bit0 should be 0. - */ + * SAS IPTT bit0 should be 1, and SATA IPTT bit0 should be 0. + */ if (sata_dev ^ (start & 1)) break; start++; @@ -2428,8 +2428,6 @@ static void slot_complete_v2_hw(struct hisi_hba *hisi_hba, ts->stat = SAS_SAM_STAT_GOOD; - dma_unmap_sg(dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); memcpy(to + sg_resp->offset, hisi_sas_status_buf_addr_mem(slot) + sizeof(struct hisi_sas_err_record), diff --git a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c index eb86afb21aab..efe8c5be5870 100644 --- a/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c +++ b/drivers/scsi/hisi_sas/hisi_sas_v3_hw.c @@ -481,6 +481,9 @@ struct hisi_sas_err_record_v3 { #define RX_DATA_LEN_UNDERFLOW_OFF 6 #define RX_DATA_LEN_UNDERFLOW_MSK (1 << RX_DATA_LEN_UNDERFLOW_OFF) +#define RX_FIS_STATUS_ERR_OFF 0 +#define RX_FIS_STATUS_ERR_MSK (1 << RX_FIS_STATUS_ERR_OFF) + #define HISI_SAS_COMMAND_ENTRIES_V3_HW 4096 #define HISI_SAS_MSI_COUNT_V3_HW 32 @@ -2161,6 +2164,7 @@ slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task, hisi_sas_status_buf_addr_mem(slot); u32 dma_rx_err_type = le32_to_cpu(record->dma_rx_err_type); u32 trans_tx_fail_type = le32_to_cpu(record->trans_tx_fail_type); + u16 sipc_rx_err_type = le16_to_cpu(record->sipc_rx_err_type); u32 dw3 = le32_to_cpu(complete_hdr->dw3); switch (task->task_proto) { @@ -2188,7 +2192,10 @@ slot_err_v3_hw(struct hisi_hba *hisi_hba, struct sas_task *task, case SAS_PROTOCOL_SATA: case SAS_PROTOCOL_STP: case SAS_PROTOCOL_SATA | SAS_PROTOCOL_STP: - if (dma_rx_err_type & RX_DATA_LEN_UNDERFLOW_MSK) { + if ((complete_hdr->dw0 & CMPLT_HDR_RSPNS_XFRD_MSK) && + (sipc_rx_err_type & RX_FIS_STATUS_ERR_MSK)) { + ts->stat = SAS_PROTO_RESPONSE; + } else if (dma_rx_err_type & RX_DATA_LEN_UNDERFLOW_MSK) { ts->residual = trans_tx_fail_type; ts->stat = SAS_DATA_UNDERRUN; } else if (dw3 & CMPLT_HDR_IO_IN_TARGET_MSK) { @@ -2311,8 +2318,6 @@ static void slot_complete_v3_hw(struct hisi_hba *hisi_hba, ts->stat = SAS_SAM_STAT_GOOD; - dma_unmap_sg(dev, &task->smp_task.smp_req, 1, - DMA_TO_DEVICE); memcpy(to + sg_resp->offset, hisi_sas_status_buf_addr_mem(slot) + sizeof(struct hisi_sas_err_record), @@ -2778,16 +2783,13 @@ static DEVICE_ATTR_RW(intr_coal_count_v3_hw); static int slave_configure_v3_hw(struct scsi_device *sdev) { struct Scsi_Host *shost = dev_to_shost(&sdev->sdev_gendev); - struct domain_device *ddev = sdev_to_domain_dev(sdev); struct hisi_hba *hisi_hba = shost_priv(shost); + int ret = hisi_sas_slave_configure(sdev); struct device *dev = hisi_hba->dev; - int ret = sas_slave_configure(sdev); unsigned int max_sectors; if (ret) return ret; - if (!dev_is_sata(ddev)) - sas_change_queue_depth(sdev, 64); if (sdev->type == TYPE_ENCLOSURE) return 0; diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c index 315c7ac730e9..bbcb901f6926 100644 --- a/drivers/scsi/hosts.c +++ b/drivers/scsi/hosts.c @@ -350,7 +350,7 @@ static void scsi_host_dev_release(struct device *dev) kfree(shost->shost_data); - ida_simple_remove(&host_index_ida, shost->host_no); + ida_free(&host_index_ida, shost->host_no); if (shost->shost_state != SHOST_CREATED) put_device(parent); @@ -395,7 +395,7 @@ struct Scsi_Host *scsi_host_alloc(struct scsi_host_template *sht, int privsize) init_waitqueue_head(&shost->host_wait); mutex_init(&shost->scan_mutex); - index = ida_simple_get(&host_index_ida, 0, 0, GFP_KERNEL); + index = ida_alloc(&host_index_ida, GFP_KERNEL); if (index < 0) { kfree(shost); return NULL; diff --git a/drivers/scsi/iscsi_tcp.c b/drivers/scsi/iscsi_tcp.c index 9fee70d6434a..29b1bd755afe 100644 --- a/drivers/scsi/iscsi_tcp.c +++ b/drivers/scsi/iscsi_tcp.c @@ -52,6 +52,10 @@ static struct iscsi_transport iscsi_sw_tcp_transport; static unsigned int iscsi_max_lun = ~0; module_param_named(max_lun, iscsi_max_lun, uint, S_IRUGO); +static bool iscsi_recv_from_iscsi_q; +module_param_named(recv_from_iscsi_q, iscsi_recv_from_iscsi_q, bool, 0644); +MODULE_PARM_DESC(recv_from_iscsi_q, "Set to true to read iSCSI data/headers from the iscsi_q workqueue. The default is false which will perform reads from the network softirq context."); + static int iscsi_sw_tcp_dbg; module_param_named(debug_iscsi_tcp, iscsi_sw_tcp_dbg, int, S_IRUGO | S_IWUSR); @@ -122,20 +126,13 @@ static inline int iscsi_sw_sk_state_check(struct sock *sk) return 0; } -static void iscsi_sw_tcp_data_ready(struct sock *sk) +static void iscsi_sw_tcp_recv_data(struct iscsi_conn *conn) { - struct iscsi_conn *conn; - struct iscsi_tcp_conn *tcp_conn; + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; + struct sock *sk = tcp_sw_conn->sock->sk; read_descriptor_t rd_desc; - read_lock_bh(&sk->sk_callback_lock); - conn = sk->sk_user_data; - if (!conn) { - read_unlock_bh(&sk->sk_callback_lock); - return; - } - tcp_conn = conn->dd_data; - /* * Use rd_desc to pass 'conn' to iscsi_tcp_recv. * We set count to 1 because we want the network layer to @@ -144,13 +141,48 @@ static void iscsi_sw_tcp_data_ready(struct sock *sk) */ rd_desc.arg.data = conn; rd_desc.count = 1; - tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv); - iscsi_sw_sk_state_check(sk); + tcp_read_sock(sk, &rd_desc, iscsi_sw_tcp_recv); /* If we had to (atomically) map a highmem page, * unmap it now. */ iscsi_tcp_segment_unmap(&tcp_conn->in.segment); + + iscsi_sw_sk_state_check(sk); +} + +static void iscsi_sw_tcp_recv_data_work(struct work_struct *work) +{ + struct iscsi_conn *conn = container_of(work, struct iscsi_conn, + recvwork); + struct iscsi_tcp_conn *tcp_conn = conn->dd_data; + struct iscsi_sw_tcp_conn *tcp_sw_conn = tcp_conn->dd_data; + struct sock *sk = tcp_sw_conn->sock->sk; + + lock_sock(sk); + iscsi_sw_tcp_recv_data(conn); + release_sock(sk); +} + +static void iscsi_sw_tcp_data_ready(struct sock *sk) +{ + struct iscsi_sw_tcp_conn *tcp_sw_conn; + struct iscsi_tcp_conn *tcp_conn; + struct iscsi_conn *conn; + + read_lock_bh(&sk->sk_callback_lock); + conn = sk->sk_user_data; + if (!conn) { + read_unlock_bh(&sk->sk_callback_lock); + return; + } + tcp_conn = conn->dd_data; + tcp_sw_conn = tcp_conn->dd_data; + + if (tcp_sw_conn->queue_recv) + iscsi_conn_queue_recv(conn); + else + iscsi_sw_tcp_recv_data(conn); read_unlock_bh(&sk->sk_callback_lock); } @@ -205,7 +237,7 @@ static void iscsi_sw_tcp_write_space(struct sock *sk) old_write_space(sk); ISCSI_SW_TCP_DBG(conn, "iscsi_write_space\n"); - iscsi_conn_queue_work(conn); + iscsi_conn_queue_xmit(conn); } static void iscsi_sw_tcp_conn_set_callbacks(struct iscsi_conn *conn) @@ -274,7 +306,10 @@ static int iscsi_sw_tcp_xmit_segment(struct iscsi_tcp_conn *tcp_conn, copy = segment->size - offset; if (segment->total_copied + segment->size < segment->total_size) - flags |= MSG_MORE; + flags |= MSG_MORE | MSG_SENDPAGE_NOTLAST; + + if (tcp_sw_conn->queue_recv) + flags |= MSG_DONTWAIT; /* Use sendpage if we can; else fall back to sendmsg */ if (!segment->data) { @@ -557,6 +592,8 @@ iscsi_sw_tcp_conn_create(struct iscsi_cls_session *cls_session, conn = cls_conn->dd_data; tcp_conn = conn->dd_data; tcp_sw_conn = tcp_conn->dd_data; + INIT_WORK(&conn->recvwork, iscsi_sw_tcp_recv_data_work); + tcp_sw_conn->queue_recv = iscsi_recv_from_iscsi_q; tfm = crypto_alloc_ahash("crc32c", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(tfm)) @@ -610,6 +647,8 @@ static void iscsi_sw_tcp_release_conn(struct iscsi_conn *conn) iscsi_sw_tcp_conn_restore_callbacks(conn); sock_put(sock->sk); + iscsi_suspend_rx(conn); + spin_lock_bh(&session->frwd_lock); tcp_sw_conn->sock = NULL; spin_unlock_bh(&session->frwd_lock); @@ -898,7 +937,7 @@ iscsi_sw_tcp_session_create(struct iscsi_endpoint *ep, uint16_t cmds_max, remove_session: iscsi_session_teardown(cls_session); remove_host: - iscsi_host_remove(shost); + iscsi_host_remove(shost, false); free_host: iscsi_host_free(shost); return NULL; @@ -915,7 +954,7 @@ static void iscsi_sw_tcp_session_destroy(struct iscsi_cls_session *cls_session) iscsi_tcp_r2tpool_free(cls_session->dd_data); iscsi_session_teardown(cls_session); - iscsi_host_remove(shost); + iscsi_host_remove(shost, false); iscsi_host_free(shost); } @@ -1003,7 +1042,6 @@ static struct scsi_host_template iscsi_sw_tcp_sht = { .eh_target_reset_handler = iscsi_eh_recover_target, .dma_boundary = PAGE_SIZE - 1, .slave_configure = iscsi_sw_tcp_slave_configure, - .target_alloc = iscsi_target_alloc, .proc_name = "iscsi_tcp", .this_id = -1, .track_queue_depth = 1, diff --git a/drivers/scsi/iscsi_tcp.h b/drivers/scsi/iscsi_tcp.h index 791453195099..850a018aefb9 100644 --- a/drivers/scsi/iscsi_tcp.h +++ b/drivers/scsi/iscsi_tcp.h @@ -28,6 +28,8 @@ struct iscsi_sw_tcp_send { struct iscsi_sw_tcp_conn { struct socket *sock; + struct work_struct recvwork; + bool queue_recv; struct iscsi_sw_tcp_send out; /* old values for socket callbacks */ diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 797abf4f5399..d95f4bcdeb2e 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -83,7 +83,9 @@ MODULE_PARM_DESC(debug_libiscsi_eh, "%s " dbg_fmt, __func__, ##arg); \ } while (0); -inline void iscsi_conn_queue_work(struct iscsi_conn *conn) +#define ISCSI_CMD_COMPL_WAIT 5 + +inline void iscsi_conn_queue_xmit(struct iscsi_conn *conn) { struct Scsi_Host *shost = conn->session->host; struct iscsi_host *ihost = shost_priv(shost); @@ -91,7 +93,17 @@ inline void iscsi_conn_queue_work(struct iscsi_conn *conn) if (ihost->workq) queue_work(ihost->workq, &conn->xmitwork); } -EXPORT_SYMBOL_GPL(iscsi_conn_queue_work); +EXPORT_SYMBOL_GPL(iscsi_conn_queue_xmit); + +inline void iscsi_conn_queue_recv(struct iscsi_conn *conn) +{ + struct Scsi_Host *shost = conn->session->host; + struct iscsi_host *ihost = shost_priv(shost); + + if (ihost->workq && !test_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags)) + queue_work(ihost->workq, &conn->recvwork); +} +EXPORT_SYMBOL_GPL(iscsi_conn_queue_recv); static void __iscsi_update_cmdsn(struct iscsi_session *session, uint32_t exp_cmdsn, uint32_t max_cmdsn) @@ -472,12 +484,18 @@ static void iscsi_free_task(struct iscsi_task *task) } } -void __iscsi_get_task(struct iscsi_task *task) +bool iscsi_get_task(struct iscsi_task *task) { - refcount_inc(&task->refcount); + return refcount_inc_not_zero(&task->refcount); } -EXPORT_SYMBOL_GPL(__iscsi_get_task); +EXPORT_SYMBOL_GPL(iscsi_get_task); +/** + * __iscsi_put_task - drop the refcount on a task + * @task: iscsi_task to drop the refcount on + * + * The back_lock must be held when calling in case it frees the task. + */ void __iscsi_put_task(struct iscsi_task *task) { if (refcount_dec_and_test(&task->refcount)) @@ -489,10 +507,11 @@ void iscsi_put_task(struct iscsi_task *task) { struct iscsi_session *session = task->conn->session; - /* regular RX path uses back_lock */ - spin_lock_bh(&session->back_lock); - __iscsi_put_task(task); - spin_unlock_bh(&session->back_lock); + if (refcount_dec_and_test(&task->refcount)) { + spin_lock_bh(&session->back_lock); + iscsi_free_task(task); + spin_unlock_bh(&session->back_lock); + } } EXPORT_SYMBOL_GPL(iscsi_put_task); @@ -557,16 +576,19 @@ static bool cleanup_queued_task(struct iscsi_task *task) struct iscsi_conn *conn = task->conn; bool early_complete = false; - /* Bad target might have completed task while it was still running */ + /* + * We might have raced where we handled a R2T early and got a response + * but have not yet taken the task off the requeue list, then a TMF or + * recovery happened and so we can still see it here. + */ if (task->state == ISCSI_TASK_COMPLETED) early_complete = true; if (!list_empty(&task->running)) { list_del_init(&task->running); /* - * If it's on a list but still running, this could be from - * a bad target sending a rsp early, cleanup from a TMF, or - * session recovery. + * If it's on a list but still running this could be cleanup + * from a TMF or session recovery. */ if (task->state == ISCSI_TASK_RUNNING || task->state == ISCSI_TASK_COMPLETED) @@ -587,20 +609,17 @@ static bool cleanup_queued_task(struct iscsi_task *task) } /* - * session frwd lock must be held and if not called for a task that is still - * pending or from the xmit thread, then xmit thread must be suspended + * session back and frwd lock must be held and if not called for a task that + * is still pending or from the xmit thread, then xmit thread must be suspended */ -static void fail_scsi_task(struct iscsi_task *task, int err) +static void __fail_scsi_task(struct iscsi_task *task, int err) { struct iscsi_conn *conn = task->conn; struct scsi_cmnd *sc; int state; - spin_lock_bh(&conn->session->back_lock); - if (cleanup_queued_task(task)) { - spin_unlock_bh(&conn->session->back_lock); + if (cleanup_queued_task(task)) return; - } if (task->state == ISCSI_TASK_PENDING) { /* @@ -619,7 +638,15 @@ static void fail_scsi_task(struct iscsi_task *task, int err) sc->result = err << 16; scsi_set_resid(sc, scsi_bufflen(sc)); iscsi_complete_task(task, state); - spin_unlock_bh(&conn->session->back_lock); +} + +static void fail_scsi_task(struct iscsi_task *task, int err) +{ + struct iscsi_session *session = task->conn->session; + + spin_lock_bh(&session->back_lock); + __fail_scsi_task(task, err); + spin_unlock_bh(&session->back_lock); } static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, @@ -668,12 +695,18 @@ static int iscsi_prep_mgmt_task(struct iscsi_conn *conn, return 0; } +/** + * iscsi_alloc_mgmt_task - allocate and setup a mgmt task. + * @conn: iscsi conn that the task will be sent on. + * @hdr: iscsi pdu that will be sent. + * @data: buffer for data segment if needed. + * @data_size: length of data in bytes. + */ static struct iscsi_task * -__iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, +iscsi_alloc_mgmt_task(struct iscsi_conn *conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size) { struct iscsi_session *session = conn->session; - struct iscsi_host *ihost = shost_priv(session->host); uint8_t opcode = hdr->opcode & ISCSI_OPCODE_MASK; struct iscsi_task *task; itt_t itt; @@ -754,30 +787,59 @@ __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, task->conn->session->age); } - if (unlikely(READ_ONCE(conn->ping_task) == INVALID_SCSI_TASK)) - WRITE_ONCE(conn->ping_task, task); - - if (!ihost->workq) { - if (iscsi_prep_mgmt_task(conn, task)) - goto free_task; - - if (session->tt->xmit_task(task)) - goto free_task; - } else { - list_add_tail(&task->running, &conn->mgmtqueue); - iscsi_conn_queue_work(conn); - } - return task; free_task: - /* regular RX path uses back_lock */ - spin_lock(&session->back_lock); - __iscsi_put_task(task); - spin_unlock(&session->back_lock); + iscsi_put_task(task); return NULL; } +/** + * iscsi_send_mgmt_task - Send task created with iscsi_alloc_mgmt_task. + * @task: iscsi task to send. + * + * On failure this returns a non-zero error code, and the driver must free + * the task with iscsi_put_task; + */ +static int iscsi_send_mgmt_task(struct iscsi_task *task) +{ + struct iscsi_conn *conn = task->conn; + struct iscsi_session *session = conn->session; + struct iscsi_host *ihost = shost_priv(conn->session->host); + int rc = 0; + + if (!ihost->workq) { + rc = iscsi_prep_mgmt_task(conn, task); + if (rc) + return rc; + + rc = session->tt->xmit_task(task); + if (rc) + return rc; + } else { + list_add_tail(&task->running, &conn->mgmtqueue); + iscsi_conn_queue_xmit(conn); + } + + return 0; +} + +static int __iscsi_conn_send_pdu(struct iscsi_conn *conn, struct iscsi_hdr *hdr, + char *data, uint32_t data_size) +{ + struct iscsi_task *task; + int rc; + + task = iscsi_alloc_mgmt_task(conn, hdr, data, data_size); + if (!task) + return -ENOMEM; + + rc = iscsi_send_mgmt_task(task); + if (rc) + iscsi_put_task(task); + return rc; +} + int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, char *data, uint32_t data_size) { @@ -786,7 +848,7 @@ int iscsi_conn_send_pdu(struct iscsi_cls_conn *cls_conn, struct iscsi_hdr *hdr, int err = 0; spin_lock_bh(&session->frwd_lock); - if (!__iscsi_conn_send_pdu(conn, hdr, data, data_size)) + if (__iscsi_conn_send_pdu(conn, hdr, data, data_size)) err = -EPERM; spin_unlock_bh(&session->frwd_lock); return err; @@ -959,7 +1021,6 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) if (!rhdr) { if (READ_ONCE(conn->ping_task)) return -EINVAL; - WRITE_ONCE(conn->ping_task, INVALID_SCSI_TASK); } memset(&hdr, 0, sizeof(struct iscsi_nopout)); @@ -973,10 +1034,18 @@ static int iscsi_send_nopout(struct iscsi_conn *conn, struct iscsi_nopin *rhdr) } else hdr.ttt = RESERVED_ITT; - task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)&hdr, NULL, 0); - if (!task) { + task = iscsi_alloc_mgmt_task(conn, (struct iscsi_hdr *)&hdr, NULL, 0); + if (!task) + return -ENOMEM; + + if (!rhdr) + WRITE_ONCE(conn->ping_task, task); + + if (iscsi_send_mgmt_task(task)) { if (!rhdr) WRITE_ONCE(conn->ping_task, NULL); + iscsi_put_task(task); + iscsi_conn_printk(KERN_ERR, conn, "Could not send nopout\n"); return -EIO; } else if (!rhdr) { @@ -1434,11 +1503,17 @@ static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task, { int rc; - spin_lock_bh(&conn->session->back_lock); - if (!conn->task) { - /* Take a ref so we can access it after xmit_task() */ - __iscsi_get_task(task); + /* + * Take a ref so we can access it after xmit_task(). + * + * This should never fail because the failure paths will have + * stopped the xmit thread. + */ + if (!iscsi_get_task(task)) { + WARN_ON_ONCE(1); + return 0; + } } else { /* Already have a ref from when we failed to send it last call */ conn->task = NULL; @@ -1449,7 +1524,7 @@ static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task, * case a bad target sends a cmd rsp before we have handled the task. */ if (was_requeue) - __iscsi_put_task(task); + iscsi_put_task(task); /* * Do this after dropping the extra ref because if this was a requeue @@ -1461,10 +1536,8 @@ static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task, * task and get woken up again. */ conn->task = task; - spin_unlock_bh(&conn->session->back_lock); return -ENODATA; } - spin_unlock_bh(&conn->session->back_lock); spin_unlock_bh(&conn->session->frwd_lock); rc = conn->session->tt->xmit_task(task); @@ -1472,20 +1545,16 @@ static int iscsi_xmit_task(struct iscsi_conn *conn, struct iscsi_task *task, if (!rc) { /* done with this task */ task->last_xfer = jiffies; - } - /* regular RX path uses back_lock */ - spin_lock(&conn->session->back_lock); - if (rc && task->state == ISCSI_TASK_RUNNING) { + } else { /* * get an extra ref that is released next time we access it * as conn->task above. */ - __iscsi_get_task(task); + iscsi_get_task(task); conn->task = task; } - __iscsi_put_task(task); - spin_unlock(&conn->session->back_lock); + iscsi_put_task(task); return rc; } @@ -1513,7 +1582,7 @@ void iscsi_requeue_task(struct iscsi_task *task) */ iscsi_put_task(task); } - iscsi_conn_queue_work(conn); + iscsi_conn_queue_xmit(conn); spin_unlock_bh(&conn->session->frwd_lock); } EXPORT_SYMBOL_GPL(iscsi_requeue_task); @@ -1567,6 +1636,28 @@ check_mgmt: goto done; } +check_requeue: + while (!list_empty(&conn->requeue)) { + /* + * we always do fastlogout - conn stop code will clean up. + */ + if (conn->session->state == ISCSI_STATE_LOGGING_OUT) + break; + + task = list_entry(conn->requeue.next, struct iscsi_task, + running); + + if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) + break; + + list_del_init(&task->running); + rc = iscsi_xmit_task(conn, task, true); + if (rc) + goto done; + if (!list_empty(&conn->mgmtqueue)) + goto check_mgmt; + } + /* process pending command queue */ while (!list_empty(&conn->cmdqueue)) { task = list_entry(conn->cmdqueue.next, struct iscsi_task, @@ -1594,28 +1685,10 @@ check_mgmt: */ if (!list_empty(&conn->mgmtqueue)) goto check_mgmt; + if (!list_empty(&conn->requeue)) + goto check_requeue; } - while (!list_empty(&conn->requeue)) { - /* - * we always do fastlogout - conn stop code will clean up. - */ - if (conn->session->state == ISCSI_STATE_LOGGING_OUT) - break; - - task = list_entry(conn->requeue.next, struct iscsi_task, - running); - - if (iscsi_check_tmf_restrictions(task, ISCSI_OP_SCSI_DATA_OUT)) - break; - - list_del_init(&task->running); - rc = iscsi_xmit_task(conn, task, true); - if (rc) - goto done; - if (!list_empty(&conn->mgmtqueue)) - goto check_mgmt; - } spin_unlock_bh(&conn->session->frwd_lock); return -ENODATA; @@ -1782,7 +1855,7 @@ int iscsi_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *sc) } } else { list_add_tail(&task->running, &conn->cmdqueue); - iscsi_conn_queue_work(conn); + iscsi_conn_queue_xmit(conn); } session->queued_cmdsn++; @@ -1843,11 +1916,8 @@ static int iscsi_exec_task_mgmt_fn(struct iscsi_conn *conn, __must_hold(&session->frwd_lock) { struct iscsi_session *session = conn->session; - struct iscsi_task *task; - task = __iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, - NULL, 0); - if (!task) { + if (__iscsi_conn_send_pdu(conn, (struct iscsi_hdr *)hdr, NULL, 0)) { spin_unlock_bh(&session->frwd_lock); iscsi_conn_printk(KERN_ERR, conn, "Could not send TMF.\n"); iscsi_conn_failure(conn, ISCSI_ERR_CONN_FAILED); @@ -1895,6 +1965,7 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) struct iscsi_task *task; int i; +restart_cmd_loop: spin_lock_bh(&session->back_lock); for (i = 0; i < session->cmds_max; i++) { task = session->cmds[i]; @@ -1903,22 +1974,25 @@ static void fail_scsi_tasks(struct iscsi_conn *conn, u64 lun, int error) if (lun != -1 && lun != task->sc->device->lun) continue; - - __iscsi_get_task(task); - spin_unlock_bh(&session->back_lock); + /* + * The cmd is completing but if this is called from an eh + * callout path then when we return scsi-ml owns the cmd. Wait + * for the completion path to finish freeing the cmd. + */ + if (!iscsi_get_task(task)) { + spin_unlock_bh(&session->back_lock); + spin_unlock_bh(&session->frwd_lock); + udelay(ISCSI_CMD_COMPL_WAIT); + spin_lock_bh(&session->frwd_lock); + goto restart_cmd_loop; + } ISCSI_DBG_SESSION(session, "failing sc %p itt 0x%x state %d\n", task->sc, task->itt, task->state); - fail_scsi_task(task, error); - - spin_unlock_bh(&session->frwd_lock); - iscsi_put_task(task); - spin_lock_bh(&session->frwd_lock); - - spin_lock_bh(&session->back_lock); + __fail_scsi_task(task, error); + __iscsi_put_task(task); } - spin_unlock_bh(&session->back_lock); } @@ -1943,7 +2017,7 @@ EXPORT_SYMBOL_GPL(iscsi_suspend_queue); /** * iscsi_suspend_tx - suspend iscsi_data_xmit - * @conn: iscsi conn tp stop processing IO on. + * @conn: iscsi conn to stop processing IO on. * * This function sets the suspend bit to prevent iscsi_data_xmit * from sending new IO, and if work is queued on the xmit thread @@ -1956,16 +2030,31 @@ void iscsi_suspend_tx(struct iscsi_conn *conn) set_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); if (ihost->workq) - flush_workqueue(ihost->workq); + flush_work(&conn->xmitwork); } EXPORT_SYMBOL_GPL(iscsi_suspend_tx); static void iscsi_start_tx(struct iscsi_conn *conn) { clear_bit(ISCSI_CONN_FLAG_SUSPEND_TX, &conn->flags); - iscsi_conn_queue_work(conn); + iscsi_conn_queue_xmit(conn); } +/** + * iscsi_suspend_rx - Prevent recvwork from running again. + * @conn: iscsi conn to stop. + */ +void iscsi_suspend_rx(struct iscsi_conn *conn) +{ + struct Scsi_Host *shost = conn->session->host; + struct iscsi_host *ihost = shost_priv(shost); + + set_bit(ISCSI_CONN_FLAG_SUSPEND_RX, &conn->flags); + if (ihost->workq) + flush_work(&conn->recvwork); +} +EXPORT_SYMBOL_GPL(iscsi_suspend_rx); + /* * We want to make sure a ping is in flight. It has timed out. * And we are not busy processing a pdu that is making @@ -2008,7 +2097,16 @@ enum blk_eh_timer_return iscsi_eh_cmd_timed_out(struct scsi_cmnd *sc) spin_unlock(&session->back_lock); goto done; } - __iscsi_get_task(task); + if (!iscsi_get_task(task)) { + /* + * Racing with the completion path right now, so give it more + * time so that path can complete it like normal. + */ + rc = BLK_EH_RESET_TIMER; + task = NULL; + spin_unlock(&session->back_lock); + goto done; + } spin_unlock(&session->back_lock); if (session->state != ISCSI_STATE_LOGGED_IN) { @@ -2257,6 +2355,7 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) ISCSI_DBG_EH(session, "aborting sc %p\n", sc); +completion_check: mutex_lock(&session->eh_mutex); spin_lock_bh(&session->frwd_lock); /* @@ -2296,13 +2395,20 @@ int iscsi_eh_abort(struct scsi_cmnd *sc) return SUCCESS; } + if (!iscsi_get_task(task)) { + spin_unlock(&session->back_lock); + spin_unlock_bh(&session->frwd_lock); + mutex_unlock(&session->eh_mutex); + /* We are just about to call iscsi_free_task so wait for it. */ + udelay(ISCSI_CMD_COMPL_WAIT); + goto completion_check; + } + + ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt); conn = session->leadconn; iscsi_get_conn(conn->cls_conn); conn->eh_abort_cnt++; age = session->age; - - ISCSI_DBG_EH(session, "aborting [sc %p itt 0x%x]\n", sc, task->itt); - __iscsi_get_task(task); spin_unlock(&session->back_lock); if (task->state == ISCSI_TASK_PENDING) { @@ -2828,11 +2934,12 @@ static void iscsi_notify_host_removed(struct iscsi_cls_session *cls_session) /** * iscsi_host_remove - remove host and sessions * @shost: scsi host + * @is_shutdown: true if called from a driver shutdown callout * * If there are any sessions left, this will initiate the removal and wait * for the completion. */ -void iscsi_host_remove(struct Scsi_Host *shost) +void iscsi_host_remove(struct Scsi_Host *shost, bool is_shutdown) { struct iscsi_host *ihost = shost_priv(shost); unsigned long flags; @@ -2841,7 +2948,11 @@ void iscsi_host_remove(struct Scsi_Host *shost) ihost->state = ISCSI_HOST_REMOVED; spin_unlock_irqrestore(&ihost->lock, flags); - iscsi_host_for_each_session(shost, iscsi_notify_host_removed); + if (!is_shutdown) + iscsi_host_for_each_session(shost, iscsi_notify_host_removed); + else + iscsi_host_for_each_session(shost, iscsi_force_destroy_session); + wait_event_interruptible(ihost->session_removal_wq, ihost->num_sessions == 0); if (signal_pending(current)) diff --git a/drivers/scsi/libiscsi_tcp.c b/drivers/scsi/libiscsi_tcp.c index 883005757ddb..c182aa83f2c9 100644 --- a/drivers/scsi/libiscsi_tcp.c +++ b/drivers/scsi/libiscsi_tcp.c @@ -558,7 +558,11 @@ static int iscsi_tcp_r2t_rsp(struct iscsi_conn *conn, struct iscsi_hdr *hdr) return 0; } task->last_xfer = jiffies; - __iscsi_get_task(task); + if (!iscsi_get_task(task)) { + spin_unlock(&session->back_lock); + /* Let the path that got the early rsp complete it */ + return 0; + } tcp_conn = conn->dd_data; rhdr = (struct iscsi_r2t_rsp *)tcp_conn->in.hdr; diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 260e735d06fa..fa2209080cc2 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c @@ -175,13 +175,13 @@ static enum sas_device_type to_dev_type(struct discover_resp *dr) return dr->attached_dev_type; } -static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) +static void sas_set_ex_phy(struct domain_device *dev, int phy_id, + struct smp_disc_resp *disc_resp) { enum sas_device_type dev_type; enum sas_linkrate linkrate; u8 sas_addr[SAS_ADDR_SIZE]; - struct smp_resp *resp = rsp; - struct discover_resp *dr = &resp->disc; + struct discover_resp *dr = &disc_resp->disc; struct sas_ha_struct *ha = dev->port->ha; struct expander_device *ex = &dev->ex_dev; struct ex_phy *phy = &ex->ex_phy[phy_id]; @@ -198,7 +198,7 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, void *rsp) BUG_ON(!phy->phy); } - switch (resp->result) { + switch (disc_resp->result) { case SMP_RESP_PHY_VACANT: phy->phy_state = PHY_VACANT; break; @@ -347,12 +347,13 @@ struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id) } #define DISCOVER_REQ_SIZE 16 -#define DISCOVER_RESP_SIZE 56 +#define DISCOVER_RESP_SIZE sizeof(struct smp_disc_resp) static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, - u8 *disc_resp, int single) + struct smp_disc_resp *disc_resp, + int single) { - struct discover_resp *dr; + struct discover_resp *dr = &disc_resp->disc; int res; disc_req[9] = single; @@ -361,7 +362,6 @@ static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, disc_resp, DISCOVER_RESP_SIZE); if (res) return res; - dr = &((struct smp_resp *)disc_resp)->disc; if (memcmp(dev->sas_addr, dr->attached_sas_addr, SAS_ADDR_SIZE) == 0) { pr_notice("Found loopback topology, just ignore it!\n"); return 0; @@ -375,7 +375,7 @@ int sas_ex_phy_discover(struct domain_device *dev, int single) struct expander_device *ex = &dev->ex_dev; int res = 0; u8 *disc_req; - u8 *disc_resp; + struct smp_disc_resp *disc_resp; disc_req = alloc_smp_req(DISCOVER_REQ_SIZE); if (!disc_req) @@ -429,27 +429,14 @@ static int sas_expander_discover(struct domain_device *dev) #define MAX_EXPANDER_PHYS 128 -static void ex_assign_report_general(struct domain_device *dev, - struct smp_resp *resp) -{ - struct report_general_resp *rg = &resp->rg; - - dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); - dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); - dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); - dev->ex_dev.t2t_supp = rg->t2t_supp; - dev->ex_dev.conf_route_table = rg->conf_route_table; - dev->ex_dev.configuring = rg->configuring; - memcpy(dev->ex_dev.enclosure_logical_id, rg->enclosure_logical_id, 8); -} - #define RG_REQ_SIZE 8 -#define RG_RESP_SIZE 32 +#define RG_RESP_SIZE sizeof(struct smp_rg_resp) static int sas_ex_general(struct domain_device *dev) { u8 *rg_req; - struct smp_resp *rg_resp; + struct smp_rg_resp *rg_resp; + struct report_general_resp *rg; int res; int i; @@ -480,7 +467,15 @@ static int sas_ex_general(struct domain_device *dev) goto out; } - ex_assign_report_general(dev, rg_resp); + rg = &rg_resp->rg; + dev->ex_dev.ex_change_count = be16_to_cpu(rg->change_count); + dev->ex_dev.max_route_indexes = be16_to_cpu(rg->route_indexes); + dev->ex_dev.num_phys = min(rg->num_phys, (u8)MAX_EXPANDER_PHYS); + dev->ex_dev.t2t_supp = rg->t2t_supp; + dev->ex_dev.conf_route_table = rg->conf_route_table; + dev->ex_dev.configuring = rg->configuring; + memcpy(dev->ex_dev.enclosure_logical_id, + rg->enclosure_logical_id, 8); if (dev->ex_dev.configuring) { pr_debug("RG: ex %016llx self-configuring...\n", @@ -681,10 +676,10 @@ int sas_smp_get_phy_events(struct sas_phy *phy) #ifdef CONFIG_SCSI_SAS_ATA #define RPS_REQ_SIZE 16 -#define RPS_RESP_SIZE 60 +#define RPS_RESP_SIZE sizeof(struct smp_rps_resp) int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, - struct smp_resp *rps_resp) + struct smp_rps_resp *rps_resp) { int res; u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); @@ -1657,7 +1652,7 @@ out_err: /* ---------- Domain revalidation ---------- */ static int sas_get_phy_discover(struct domain_device *dev, - int phy_id, struct smp_resp *disc_resp) + int phy_id, struct smp_disc_resp *disc_resp) { int res; u8 *disc_req; @@ -1673,10 +1668,8 @@ static int sas_get_phy_discover(struct domain_device *dev, disc_resp, DISCOVER_RESP_SIZE); if (res) goto out; - else if (disc_resp->result != SMP_RESP_FUNC_ACC) { + if (disc_resp->result != SMP_RESP_FUNC_ACC) res = disc_resp->result; - goto out; - } out: kfree(disc_req); return res; @@ -1686,7 +1679,7 @@ static int sas_get_phy_change_count(struct domain_device *dev, int phy_id, int *pcc) { int res; - struct smp_resp *disc_resp; + struct smp_disc_resp *disc_resp; disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); if (!disc_resp) @@ -1704,19 +1697,17 @@ static int sas_get_phy_attached_dev(struct domain_device *dev, int phy_id, u8 *sas_addr, enum sas_device_type *type) { int res; - struct smp_resp *disc_resp; - struct discover_resp *dr; + struct smp_disc_resp *disc_resp; disc_resp = alloc_smp_resp(DISCOVER_RESP_SIZE); if (!disc_resp) return -ENOMEM; - dr = &disc_resp->disc; res = sas_get_phy_discover(dev, phy_id, disc_resp); if (res == 0) { memcpy(sas_addr, disc_resp->disc.attached_sas_addr, SAS_ADDR_SIZE); - *type = to_dev_type(dr); + *type = to_dev_type(&disc_resp->disc); if (*type == 0) memset(sas_addr, 0, SAS_ADDR_SIZE); } @@ -1760,7 +1751,7 @@ static int sas_get_ex_change_count(struct domain_device *dev, int *ecc) { int res; u8 *rg_req; - struct smp_resp *rg_resp; + struct smp_rg_resp *rg_resp; rg_req = alloc_smp_req(RG_REQ_SIZE); if (!rg_req) diff --git a/drivers/scsi/libsas/sas_init.c b/drivers/scsi/libsas/sas_init.c index dc35f0f8eae3..e4f77072a58d 100644 --- a/drivers/scsi/libsas/sas_init.c +++ b/drivers/scsi/libsas/sas_init.c @@ -531,6 +531,7 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) if (!d) return -ENOMEM; + pm_runtime_get_sync(ha->dev); /* libsas workqueue coordinates ata-eh reset with discovery */ mutex_lock(&d->event_lock); d->reset_result = 0; @@ -544,6 +545,7 @@ static int queue_phy_reset(struct sas_phy *phy, int hard_reset) if (rc == 0) rc = d->reset_result; mutex_unlock(&d->event_lock); + pm_runtime_put_sync(ha->dev); return rc; } @@ -558,6 +560,7 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) if (!d) return -ENOMEM; + pm_runtime_get_sync(ha->dev); /* libsas workqueue coordinates ata-eh reset with discovery */ mutex_lock(&d->event_lock); d->enable_result = 0; @@ -571,6 +574,7 @@ static int queue_phy_enable(struct sas_phy *phy, int enable) if (rc == 0) rc = d->enable_result; mutex_unlock(&d->event_lock); + pm_runtime_put_sync(ha->dev); return rc; } diff --git a/drivers/scsi/libsas/sas_internal.h b/drivers/scsi/libsas/sas_internal.h index 13d0ffaada93..8d0ad3abc7b5 100644 --- a/drivers/scsi/libsas/sas_internal.h +++ b/drivers/scsi/libsas/sas_internal.h @@ -83,7 +83,7 @@ struct domain_device *sas_find_dev_by_rphy(struct sas_rphy *rphy); struct domain_device *sas_ex_to_ata(struct domain_device *ex_dev, int phy_id); int sas_ex_phy_discover(struct domain_device *dev, int single); int sas_get_report_phy_sata(struct domain_device *dev, int phy_id, - struct smp_resp *rps_resp); + struct smp_rps_resp *rps_resp); int sas_try_ata_reset(struct asd_sas_phy *phy); void sas_hae_reset(struct work_struct *work); diff --git a/drivers/scsi/lpfc/lpfc.h b/drivers/scsi/lpfc/lpfc.h index da9070cdad91..e6a083d098a1 100644 --- a/drivers/scsi/lpfc/lpfc.h +++ b/drivers/scsi/lpfc/lpfc.h @@ -48,9 +48,6 @@ struct lpfc_sli2_slim; the NameServer before giving up. */ #define LPFC_CMD_PER_LUN 3 /* max outstanding cmds per lun */ #define LPFC_DEFAULT_SG_SEG_CNT 64 /* sg element count per scsi cmnd */ -#define LPFC_DEFAULT_MENLO_SG_SEG_CNT 128 /* sg element count per scsi - cmnd for menlo needs nearly twice as for firmware - downloads using bsg */ #define LPFC_DEFAULT_XPSGL_SIZE 256 #define LPFC_MAX_SG_TABLESIZE 0xffff @@ -604,7 +601,6 @@ struct lpfc_vport { #define FC_VFI_REGISTERED 0x800000 /* VFI is registered */ #define FC_FDISC_COMPLETED 0x1000000/* FDISC completed */ #define FC_DISC_DELAYED 0x2000000/* Delay NPort discovery */ -#define FC_RSCN_MEMENTO 0x4000000/* RSCN cmd processed */ uint32_t ct_flags; #define FC_CT_RFF_ID 0x1 /* RFF_ID accepted by switch */ @@ -987,7 +983,8 @@ struct lpfc_hba { u8 last_seq, u8 cr_cx_cmd); void (*__lpfc_sli_prep_abort_xri)(struct lpfc_iocbq *cmdiocbq, u16 ulp_context, u16 iotag, - u8 ulp_class, u16 cqid, bool ia); + u8 ulp_class, u16 cqid, bool ia, + bool wqec); /* expedite pool */ struct lpfc_epd_pool epd_pool; @@ -1439,8 +1436,6 @@ struct lpfc_hba { */ #define QUE_BUFTAG_BIT (1<<31) uint32_t buffer_tag_count; - int wait_4_mlo_maint_flg; - wait_queue_head_t wait_4_mlo_m_q; /* data structure used for latency data collection */ #define LPFC_NO_BUCKET 0 #define LPFC_LINEAR_BUCKET 1 @@ -1475,8 +1470,6 @@ struct lpfc_hba { /* RAS Support */ struct lpfc_ras_fwlog ras_fwlog; - uint8_t menlo_flag; /* menlo generic flags */ -#define HBA_MENLO_SUPPORT 0x1 /* HBA supports menlo commands */ uint32_t iocb_cnt; uint32_t iocb_max; atomic_t sdev_cnt; diff --git a/drivers/scsi/lpfc/lpfc_attr.c b/drivers/scsi/lpfc/lpfc_attr.c index 3caaa7c4af48..09cf2cd0ae60 100644 --- a/drivers/scsi/lpfc/lpfc_attr.c +++ b/drivers/scsi/lpfc/lpfc_attr.c @@ -921,25 +921,6 @@ lpfc_programtype_show(struct device *dev, struct device_attribute *attr, return scnprintf(buf, PAGE_SIZE, "%s\n", phba->ProgramType); } -/** - * lpfc_mlomgmt_show - Return the Menlo Maintenance sli flag - * @dev: class converted to a Scsi_host structure. - * @attr: device attribute, not used. - * @buf: on return contains the Menlo Maintenance sli flag. - * - * Returns: size of formatted string. - **/ -static ssize_t -lpfc_mlomgmt_show(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct Scsi_Host *shost = class_to_shost(dev); - struct lpfc_vport *vport = (struct lpfc_vport *)shost->hostdata; - struct lpfc_hba *phba = vport->phba; - - return scnprintf(buf, PAGE_SIZE, "%d\n", - (phba->sli.sli_flag & LPFC_MENLO_MAINT)); -} - /** * lpfc_vportnum_show - Return the port number in ascii of the hba * @dev: class converted to a Scsi_host structure. @@ -1109,10 +1090,7 @@ lpfc_link_state_show(struct device *dev, struct device_attribute *attr, "Unknown\n"); break; } - if (phba->sli.sli_flag & LPFC_MENLO_MAINT) - len += scnprintf(buf + len, PAGE_SIZE-len, - " Menlo Maint Mode\n"); - else if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) { + if (phba->fc_topology == LPFC_TOPOLOGY_LOOP) { if (vport->fc_flag & FC_PUBLIC_LOOP) len += scnprintf(buf + len, PAGE_SIZE-len, " Public Loop\n"); @@ -2827,7 +2805,6 @@ static DEVICE_ATTR(option_rom_version, S_IRUGO, lpfc_option_rom_version_show, NULL); static DEVICE_ATTR(num_discovered_ports, S_IRUGO, lpfc_num_discovered_ports_show, NULL); -static DEVICE_ATTR(menlo_mgmt_mode, S_IRUGO, lpfc_mlomgmt_show, NULL); static DEVICE_ATTR(nport_evt_cnt, S_IRUGO, lpfc_nport_evt_cnt_show, NULL); static DEVICE_ATTR_RO(lpfc_drvr_version); static DEVICE_ATTR_RO(lpfc_enable_fip); @@ -6220,7 +6197,6 @@ static struct attribute *lpfc_hba_attrs[] = { &dev_attr_option_rom_version.attr, &dev_attr_link_state.attr, &dev_attr_num_discovered_ports.attr, - &dev_attr_menlo_mgmt_mode.attr, &dev_attr_lpfc_drvr_version.attr, &dev_attr_lpfc_enable_fip.attr, &dev_attr_lpfc_temp_sensor.attr, @@ -7396,7 +7372,6 @@ lpfc_get_hba_function_mode(struct lpfc_hba *phba) case PCI_DEVICE_ID_LANCER_FCOE: case PCI_DEVICE_ID_LANCER_FCOE_VF: case PCI_DEVICE_ID_ZEPHYR_DCSP: - case PCI_DEVICE_ID_HORNET: case PCI_DEVICE_ID_TIGERSHARK: case PCI_DEVICE_ID_TOMCAT: phba->hba_flag |= HBA_FCOE_MODE; diff --git a/drivers/scsi/lpfc/lpfc_bsg.c b/drivers/scsi/lpfc/lpfc_bsg.c index 676e7d54b97a..9be3bb01a8ec 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.c +++ b/drivers/scsi/lpfc/lpfc_bsg.c @@ -88,17 +88,9 @@ struct lpfc_bsg_mbox { uint32_t outExtWLen; /* from app */ }; -#define MENLO_DID 0x0000FC0E - -struct lpfc_bsg_menlo { - struct lpfc_iocbq *cmdiocbq; - struct lpfc_dmabuf *rmp; -}; - #define TYPE_EVT 1 #define TYPE_IOCB 2 #define TYPE_MBOX 3 -#define TYPE_MENLO 4 struct bsg_job_data { uint32_t type; struct bsg_job *set_job; /* job waiting for this iocb to finish */ @@ -106,7 +98,6 @@ struct bsg_job_data { struct lpfc_bsg_event *evt; struct lpfc_bsg_iocb iocb; struct lpfc_bsg_mbox mbox; - struct lpfc_bsg_menlo menlo; } context_un; }; @@ -3502,15 +3493,6 @@ static int lpfc_bsg_check_cmd_access(struct lpfc_hba *phba, "1226 mbox: set_variable 0x%x, 0x%x\n", mb->un.varWords[0], mb->un.varWords[1]); - if ((mb->un.varWords[0] == SETVAR_MLOMNT) - && (mb->un.varWords[1] == 1)) { - phba->wait_4_mlo_maint_flg = 1; - } else if (mb->un.varWords[0] == SETVAR_MLORST) { - spin_lock_irq(&phba->hbalock); - phba->link_flag &= ~LS_LOOPBACK_MODE; - spin_unlock_irq(&phba->hbalock); - phba->fc_topology = LPFC_TOPOLOGY_PT_PT; - } break; case MBX_READ_SPARM64: case MBX_REG_LOGIN: @@ -4992,283 +4974,6 @@ lpfc_bsg_mbox_cmd(struct bsg_job *job) return rc; } -/** - * lpfc_bsg_menlo_cmd_cmp - lpfc_menlo_cmd completion handler - * @phba: Pointer to HBA context object. - * @cmdiocbq: Pointer to command iocb. - * @rspiocbq: Pointer to response iocb. - * - * This function is the completion handler for iocbs issued using - * lpfc_menlo_cmd function. This function is called by the - * ring event handler function without any lock held. This function - * can be called from both worker thread context and interrupt - * context. This function also can be called from another thread which - * cleans up the SLI layer objects. - * This function copies the contents of the response iocb to the - * response iocb memory object provided by the caller of - * lpfc_sli_issue_iocb_wait and then wakes up the thread which - * sleeps for the iocb completion. - **/ -static void -lpfc_bsg_menlo_cmd_cmp(struct lpfc_hba *phba, - struct lpfc_iocbq *cmdiocbq, - struct lpfc_iocbq *rspiocbq) -{ - struct bsg_job_data *dd_data; - struct bsg_job *job; - struct fc_bsg_reply *bsg_reply; - IOCB_t *rsp; - struct lpfc_dmabuf *bmp, *cmp, *rmp; - struct lpfc_bsg_menlo *menlo; - unsigned long flags; - struct menlo_response *menlo_resp; - unsigned int rsp_size; - int rc = 0; - - dd_data = cmdiocbq->context_un.dd_data; - cmp = cmdiocbq->cmd_dmabuf; - bmp = cmdiocbq->bpl_dmabuf; - menlo = &dd_data->context_un.menlo; - rmp = menlo->rmp; - rsp = &rspiocbq->iocb; - - /* Determine if job has been aborted */ - spin_lock_irqsave(&phba->ct_ev_lock, flags); - job = dd_data->set_job; - if (job) { - bsg_reply = job->reply; - /* Prevent timeout handling from trying to abort job */ - job->dd_data = NULL; - } - spin_unlock_irqrestore(&phba->ct_ev_lock, flags); - - /* Copy the job data or set the failing status for the job */ - - if (job) { - /* always return the xri, this would be used in the case - * of a menlo download to allow the data to be sent as a - * continuation of the exchange. - */ - - menlo_resp = (struct menlo_response *) - bsg_reply->reply_data.vendor_reply.vendor_rsp; - menlo_resp->xri = rsp->ulpContext; - if (rsp->ulpStatus) { - if (rsp->ulpStatus == IOSTAT_LOCAL_REJECT) { - switch (rsp->un.ulpWord[4] & IOERR_PARAM_MASK) { - case IOERR_SEQUENCE_TIMEOUT: - rc = -ETIMEDOUT; - break; - case IOERR_INVALID_RPI: - rc = -EFAULT; - break; - default: - rc = -EACCES; - break; - } - } else { - rc = -EACCES; - } - } else { - rsp_size = rsp->un.genreq64.bdl.bdeSize; - bsg_reply->reply_payload_rcv_len = - lpfc_bsg_copy_data(rmp, &job->reply_payload, - rsp_size, 0); - } - - } - - lpfc_sli_release_iocbq(phba, cmdiocbq); - lpfc_free_bsg_buffers(phba, cmp); - lpfc_free_bsg_buffers(phba, rmp); - lpfc_mbuf_free(phba, bmp->virt, bmp->phys); - kfree(bmp); - kfree(dd_data); - - /* Complete the job if active */ - - if (job) { - bsg_reply->result = rc; - bsg_job_done(job, bsg_reply->result, - bsg_reply->reply_payload_rcv_len); - } - - return; -} - -/** - * lpfc_menlo_cmd - send an ioctl for menlo hardware - * @job: fc_bsg_job to handle - * - * This function issues a gen request 64 CR ioctl for all menlo cmd requests, - * all the command completions will return the xri for the command. - * For menlo data requests a gen request 64 CX is used to continue the exchange - * supplied in the menlo request header xri field. - **/ -static int -lpfc_menlo_cmd(struct bsg_job *job) -{ - struct lpfc_vport *vport = shost_priv(fc_bsg_to_shost(job)); - struct fc_bsg_request *bsg_request = job->request; - struct fc_bsg_reply *bsg_reply = job->reply; - struct lpfc_hba *phba = vport->phba; - struct lpfc_iocbq *cmdiocbq; - IOCB_t *cmd; - int rc = 0; - struct menlo_command *menlo_cmd; - struct lpfc_dmabuf *bmp = NULL, *cmp = NULL, *rmp = NULL; - int request_nseg; - int reply_nseg; - struct bsg_job_data *dd_data; - struct ulp_bde64 *bpl = NULL; - - /* in case no data is returned return just the return code */ - bsg_reply->reply_payload_rcv_len = 0; - - if (job->request_len < - sizeof(struct fc_bsg_request) + - sizeof(struct menlo_command)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2784 Received MENLO_CMD request below " - "minimum size\n"); - rc = -ERANGE; - goto no_dd_data; - } - - if (job->reply_len < sizeof(*bsg_reply) + - sizeof(struct menlo_response)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2785 Received MENLO_CMD reply below " - "minimum size\n"); - rc = -ERANGE; - goto no_dd_data; - } - - if (!(phba->menlo_flag & HBA_MENLO_SUPPORT)) { - lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2786 Adapter does not support menlo " - "commands\n"); - rc = -EPERM; - goto no_dd_data; - } - - menlo_cmd = (struct menlo_command *) - bsg_request->rqst_data.h_vendor.vendor_cmd; - - /* allocate our bsg tracking structure */ - dd_data = kmalloc(sizeof(struct bsg_job_data), GFP_KERNEL); - if (!dd_data) { - lpfc_printf_log(phba, KERN_WARNING, LOG_LIBDFC, - "2787 Failed allocation of dd_data\n"); - rc = -ENOMEM; - goto no_dd_data; - } - - bmp = kmalloc(sizeof(struct lpfc_dmabuf), GFP_KERNEL); - if (!bmp) { - rc = -ENOMEM; - goto free_dd; - } - - bmp->virt = lpfc_mbuf_alloc(phba, 0, &bmp->phys); - if (!bmp->virt) { - rc = -ENOMEM; - goto free_bmp; - } - - INIT_LIST_HEAD(&bmp->list); - - bpl = (struct ulp_bde64 *)bmp->virt; - request_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64); - cmp = lpfc_alloc_bsg_buffers(phba, job->request_payload.payload_len, - 1, bpl, &request_nseg); - if (!cmp) { - rc = -ENOMEM; - goto free_bmp; - } - lpfc_bsg_copy_data(cmp, &job->request_payload, - job->request_payload.payload_len, 1); - - bpl += request_nseg; - reply_nseg = LPFC_BPL_SIZE/sizeof(struct ulp_bde64) - request_nseg; - rmp = lpfc_alloc_bsg_buffers(phba, job->reply_payload.payload_len, 0, - bpl, &reply_nseg); - if (!rmp) { - rc = -ENOMEM; - goto free_cmp; - } - - cmdiocbq = lpfc_sli_get_iocbq(phba); - if (!cmdiocbq) { - rc = -ENOMEM; - goto free_rmp; - } - - cmd = &cmdiocbq->iocb; - cmd->un.genreq64.bdl.ulpIoTag32 = 0; - cmd->un.genreq64.bdl.addrHigh = putPaddrHigh(bmp->phys); - cmd->un.genreq64.bdl.addrLow = putPaddrLow(bmp->phys); - cmd->un.genreq64.bdl.bdeFlags = BUFF_TYPE_BLP_64; - cmd->un.genreq64.bdl.bdeSize = - (request_nseg + reply_nseg) * sizeof(struct ulp_bde64); - cmd->un.genreq64.w5.hcsw.Fctl = (SI | LA); - cmd->un.genreq64.w5.hcsw.Dfctl = 0; - cmd->un.genreq64.w5.hcsw.Rctl = FC_RCTL_DD_UNSOL_CMD; - cmd->un.genreq64.w5.hcsw.Type = MENLO_TRANSPORT_TYPE; /* 0xfe */ - cmd->ulpBdeCount = 1; - cmd->ulpClass = CLASS3; - cmd->ulpOwner = OWN_CHIP; - cmd->ulpLe = 1; /* Limited Edition */ - cmdiocbq->cmd_flag |= LPFC_IO_LIBDFC; - cmdiocbq->vport = phba->pport; - /* We want the firmware to timeout before we do */ - cmd->ulpTimeout = MENLO_TIMEOUT - 5; - cmdiocbq->cmd_cmpl = lpfc_bsg_menlo_cmd_cmp; - cmdiocbq->context_un.dd_data = dd_data; - cmdiocbq->cmd_dmabuf = cmp; - cmdiocbq->bpl_dmabuf = bmp; - if (menlo_cmd->cmd == LPFC_BSG_VENDOR_MENLO_CMD) { - cmd->ulpCommand = CMD_GEN_REQUEST64_CR; - cmd->ulpPU = MENLO_PU; /* 3 */ - cmd->un.ulpWord[4] = MENLO_DID; /* 0x0000FC0E */ - cmd->ulpContext = MENLO_CONTEXT; /* 0 */ - } else { - cmd->ulpCommand = CMD_GEN_REQUEST64_CX; - cmd->ulpPU = 1; - cmd->un.ulpWord[4] = 0; - cmd->ulpContext = menlo_cmd->xri; - } - - dd_data->type = TYPE_MENLO; - dd_data->set_job = job; - dd_data->context_un.menlo.cmdiocbq = cmdiocbq; - dd_data->context_un.menlo.rmp = rmp; - job->dd_data = dd_data; - - rc = lpfc_sli_issue_iocb(phba, LPFC_ELS_RING, cmdiocbq, - MENLO_TIMEOUT - 5); - if (rc == IOCB_SUCCESS) - return 0; /* done for now */ - - lpfc_sli_release_iocbq(phba, cmdiocbq); - -free_rmp: - lpfc_free_bsg_buffers(phba, rmp); -free_cmp: - lpfc_free_bsg_buffers(phba, cmp); -free_bmp: - if (bmp->virt) - lpfc_mbuf_free(phba, bmp->virt, bmp->phys); - kfree(bmp); -free_dd: - kfree(dd_data); -no_dd_data: - /* make error code available to userspace */ - bsg_reply->result = rc; - job->dd_data = NULL; - return rc; -} - static int lpfc_forced_link_speed(struct bsg_job *job) { @@ -5823,10 +5528,6 @@ lpfc_bsg_hst_vendor(struct bsg_job *job) case LPFC_BSG_VENDOR_MBOX: rc = lpfc_bsg_mbox_cmd(job); break; - case LPFC_BSG_VENDOR_MENLO_CMD: - case LPFC_BSG_VENDOR_MENLO_DATA: - rc = lpfc_menlo_cmd(job); - break; case LPFC_BSG_VENDOR_FORCED_LINK_SPEED: rc = lpfc_forced_link_speed(job); break; @@ -5979,31 +5680,6 @@ lpfc_bsg_timeout(struct bsg_job *job) phba->mbox_ext_buf_ctx.state = LPFC_BSG_MBOX_ABTS; spin_unlock_irqrestore(&phba->ct_ev_lock, flags); break; - case TYPE_MENLO: - /* Check to see if IOCB was issued to the port or not. If not, - * remove it from the txq queue and call cancel iocbs. - * Otherwise, call abort iotag. - */ - cmdiocb = dd_data->context_un.menlo.cmdiocbq; - spin_unlock_irqrestore(&phba->ct_ev_lock, flags); - - spin_lock_irqsave(&phba->hbalock, flags); - list_for_each_entry_safe(check_iocb, next_iocb, &pring->txq, - list) { - if (check_iocb == cmdiocb) { - list_move_tail(&check_iocb->list, &completions); - break; - } - } - if (list_empty(&completions)) - lpfc_sli_issue_abort_iotag(phba, pring, cmdiocb, NULL); - spin_unlock_irqrestore(&phba->hbalock, flags); - if (!list_empty(&completions)) { - lpfc_sli_cancel_iocbs(phba, &completions, - IOSTAT_LOCAL_REJECT, - IOERR_SLI_ABORTED); - } - break; default: spin_unlock_irqrestore(&phba->ct_ev_lock, flags); break; diff --git a/drivers/scsi/lpfc/lpfc_bsg.h b/drivers/scsi/lpfc/lpfc_bsg.h index 749d6c43cfce..3c04ca2d7455 100644 --- a/drivers/scsi/lpfc/lpfc_bsg.h +++ b/drivers/scsi/lpfc/lpfc_bsg.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2021 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2010-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -33,8 +33,6 @@ #define LPFC_BSG_VENDOR_DIAG_RUN_LOOPBACK 5 #define LPFC_BSG_VENDOR_GET_MGMT_REV 6 #define LPFC_BSG_VENDOR_MBOX 7 -#define LPFC_BSG_VENDOR_MENLO_CMD 8 -#define LPFC_BSG_VENDOR_MENLO_DATA 9 #define LPFC_BSG_VENDOR_DIAG_MODE_END 10 #define LPFC_BSG_VENDOR_LINK_DIAG_TEST 11 #define LPFC_BSG_VENDOR_FORCED_LINK_SPEED 14 @@ -131,16 +129,6 @@ struct dfc_mbox_req { uint32_t extSeqNum; }; -/* Used for menlo command or menlo data. The xri is only used for menlo data */ -struct menlo_command { - uint32_t cmd; - uint32_t xri; -}; - -struct menlo_response { - uint32_t xri; /* return the xri of the iocb exchange */ -}; - /* * macros and data structures for handling sli-config mailbox command * pass-through support, this header file is shared between user and diff --git a/drivers/scsi/lpfc/lpfc_crtn.h b/drivers/scsi/lpfc/lpfc_crtn.h index f5d74958b664..bcad91204328 100644 --- a/drivers/scsi/lpfc/lpfc_crtn.h +++ b/drivers/scsi/lpfc/lpfc_crtn.h @@ -370,7 +370,7 @@ void lpfc_sli_prep_xmit_seq64(struct lpfc_hba *phba, u8 cr_cx_cmd); void lpfc_sli_prep_abort_xri(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, u16 ulp_context, u16 iotag, u8 ulp_class, u16 cqid, - bool ia); + bool ia, bool wqec); struct lpfc_sglq *__lpfc_clear_active_sglq(struct lpfc_hba *phba, uint16_t xri); struct lpfc_sglq *__lpfc_sli_get_nvmet_sglq(struct lpfc_hba *phba, struct lpfc_iocbq *piocbq); diff --git a/drivers/scsi/lpfc/lpfc_debugfs.c b/drivers/scsi/lpfc/lpfc_debugfs.c index 7b24c932e812..5037ea09a810 100644 --- a/drivers/scsi/lpfc/lpfc_debugfs.c +++ b/drivers/scsi/lpfc/lpfc_debugfs.c @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2021 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2007-2015 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -2607,8 +2607,8 @@ lpfc_debugfs_multixripools_write(struct file *file, const char __user *buf, struct lpfc_sli4_hdw_queue *qp; struct lpfc_multixri_pool *multixri_pool; - if (nbytes > 64) - nbytes = 64; + if (nbytes > sizeof(mybuf) - 1) + nbytes = sizeof(mybuf) - 1; memset(mybuf, 0, sizeof(mybuf)); @@ -2688,8 +2688,8 @@ lpfc_debugfs_nvmestat_write(struct file *file, const char __user *buf, if (!phba->targetport) return -ENXIO; - if (nbytes > 64) - nbytes = 64; + if (nbytes > sizeof(mybuf) - 1) + nbytes = sizeof(mybuf) - 1; memset(mybuf, 0, sizeof(mybuf)); @@ -2826,8 +2826,8 @@ lpfc_debugfs_ioktime_write(struct file *file, const char __user *buf, char mybuf[64]; char *pbuf; - if (nbytes > 64) - nbytes = 64; + if (nbytes > sizeof(mybuf) - 1) + nbytes = sizeof(mybuf) - 1; memset(mybuf, 0, sizeof(mybuf)); @@ -2954,8 +2954,8 @@ lpfc_debugfs_nvmeio_trc_write(struct file *file, const char __user *buf, char mybuf[64]; char *pbuf; - if (nbytes > 63) - nbytes = 63; + if (nbytes > sizeof(mybuf) - 1) + nbytes = sizeof(mybuf) - 1; memset(mybuf, 0, sizeof(mybuf)); @@ -3060,8 +3060,8 @@ lpfc_debugfs_hdwqstat_write(struct file *file, const char __user *buf, char *pbuf; int i; - if (nbytes > 64) - nbytes = 64; + if (nbytes > sizeof(mybuf) - 1) + nbytes = sizeof(mybuf) - 1; memset(mybuf, 0, sizeof(mybuf)); diff --git a/drivers/scsi/lpfc/lpfc_els.c b/drivers/scsi/lpfc/lpfc_els.c index 3fababb7c181..9e69de9eb992 100644 --- a/drivers/scsi/lpfc/lpfc_els.c +++ b/drivers/scsi/lpfc/lpfc_els.c @@ -1790,18 +1790,20 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, /* Move this back to NPR state */ if (memcmp(&ndlp->nlp_portname, name, sizeof(struct lpfc_name)) == 0) { - /* The new_ndlp is replacing ndlp totally, so we need - * to put ndlp on UNUSED list and try to free it. + /* The ndlp doesn't have a portname yet, but does have an + * NPort ID. The new_ndlp portname matches the Rport's + * portname. Reinstantiate the new_ndlp and reset the ndlp. */ lpfc_printf_vlog(vport, KERN_INFO, LOG_ELS, "3179 PLOGI confirm NEW: %x %x\n", new_ndlp->nlp_DID, keepDID); /* Two ndlps cannot have the same did on the nodelist. - * Note: for this case, ndlp has a NULL WWPN so setting - * the nlp_fc4_type isn't required. + * The KeepDID and keep_nlp_fc4_type need to be swapped + * because ndlp is inflight with no WWPN. */ ndlp->nlp_DID = keepDID; + ndlp->nlp_fc4_type = keep_nlp_fc4_type; lpfc_nlp_set_state(vport, ndlp, keep_nlp_state); if (phba->sli_rev == LPFC_SLI_REV4 && active_rrqs_xri_bitmap) @@ -1816,9 +1818,8 @@ lpfc_plogi_confirm_nport(struct lpfc_hba *phba, uint32_t *prsp, lpfc_unreg_rpi(vport, ndlp); - /* Two ndlps cannot have the same did and the fc4 - * type must be transferred because the ndlp is in - * flight. + /* The ndlp and new_ndlp both have WWPNs but are swapping + * NPort Ids and attributes. */ ndlp->nlp_DID = keepDID; ndlp->nlp_fc4_type = keep_nlp_fc4_type; @@ -1886,7 +1887,6 @@ lpfc_end_rscn(struct lpfc_vport *vport) else { spin_lock_irq(shost->host_lock); vport->fc_flag &= ~FC_RSCN_MODE; - vport->fc_flag |= FC_RSCN_MEMENTO; spin_unlock_irq(shost->host_lock); } } @@ -2434,14 +2434,13 @@ lpfc_issue_els_prli(struct lpfc_vport *vport, struct lpfc_nodelist *ndlp, u32 local_nlp_type, elscmd; /* - * If discovery was kicked off from RSCN mode, - * the FC4 types supported from a + * If we are in RSCN mode, the FC4 types supported from a * previous GFT_ID command may not be accurate. So, if we * are a NVME Initiator, always look for the possibility of * the remote NPort beng a NVME Target. */ if (phba->sli_rev == LPFC_SLI_REV4 && - vport->fc_flag & (FC_RSCN_MODE | FC_RSCN_MEMENTO) && + vport->fc_flag & FC_RSCN_MODE && vport->nvmei_support) ndlp->nlp_fc4_type |= NLP_FC4_NVME; local_nlp_type = ndlp->nlp_fc4_type; @@ -4571,15 +4570,6 @@ lpfc_els_retry(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocb, case IOSTAT_LOCAL_REJECT: switch ((ulp_word4 & IOERR_PARAM_MASK)) { case IOERR_LOOP_OPEN_FAILURE: - if (cmd == ELS_CMD_FLOGI) { - if (PCI_DEVICE_ID_HORNET == - phba->pcidev->device) { - phba->fc_topology = LPFC_TOPOLOGY_LOOP; - phba->pport->fc_myDID = 0; - phba->alpa_map[0] = 0; - phba->alpa_map[1] = 0; - } - } if (cmd == ELS_CMD_PLOGI && cmdiocb->retry == 0) delay = 1000; retry = 1; @@ -7915,7 +7905,6 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, if ((rscn_cnt < FC_MAX_HOLD_RSCN) && !(vport->fc_flag & FC_RSCN_DISCOVERY)) { vport->fc_flag |= FC_RSCN_MODE; - vport->fc_flag &= ~FC_RSCN_MEMENTO; spin_unlock_irq(shost->host_lock); if (rscn_cnt) { cmd = vport->fc_rscn_id_list[rscn_cnt-1]->virt; @@ -7965,7 +7954,6 @@ lpfc_els_rcv_rscn(struct lpfc_vport *vport, struct lpfc_iocbq *cmdiocb, spin_lock_irq(shost->host_lock); vport->fc_flag |= FC_RSCN_MODE; - vport->fc_flag &= ~FC_RSCN_MEMENTO; spin_unlock_irq(shost->host_lock); vport->fc_rscn_id_list[vport->fc_rscn_id_cnt++] = pcmd; /* Indicate we are done walking fc_rscn_id_list on this vport */ diff --git a/drivers/scsi/lpfc/lpfc_hbadisc.c b/drivers/scsi/lpfc/lpfc_hbadisc.c index fb36f26170e4..2645def612e6 100644 --- a/drivers/scsi/lpfc/lpfc_hbadisc.c +++ b/drivers/scsi/lpfc/lpfc_hbadisc.c @@ -1354,8 +1354,7 @@ lpfc_linkup_port(struct lpfc_vport *vport) spin_lock_irq(shost->host_lock); vport->fc_flag &= ~(FC_PT2PT | FC_PT2PT_PLOGI | FC_ABORT_DISCOVERY | - FC_RSCN_MEMENTO | FC_RSCN_MODE | - FC_NLP_MORE | FC_RSCN_DISCOVERY); + FC_RSCN_MODE | FC_NLP_MORE | FC_RSCN_DISCOVERY); vport->fc_flag |= FC_NDISC_ACTIVE; vport->fc_ns_retry = 0; spin_unlock_irq(shost->host_lock); @@ -3763,18 +3762,8 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) } phba->fc_eventTag = la->eventTag; - if (phba->sli_rev < LPFC_SLI_REV4) { - spin_lock_irqsave(&phba->hbalock, iflags); - if (bf_get(lpfc_mbx_read_top_mm, la)) - phba->sli.sli_flag |= LPFC_MENLO_MAINT; - else - phba->sli.sli_flag &= ~LPFC_MENLO_MAINT; - spin_unlock_irqrestore(&phba->hbalock, iflags); - } - phba->link_events++; - if ((attn_type == LPFC_ATT_LINK_UP) && - !(phba->sli.sli_flag & LPFC_MENLO_MAINT)) { + if (attn_type == LPFC_ATT_LINK_UP) { phba->fc_stat.LinkUp++; if (phba->link_flag & LS_LOOPBACK_MODE) { lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, @@ -3788,15 +3777,13 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) } else { lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1303 Link Up Event x%x received " - "Data: x%x x%x x%x x%x x%x x%x %d\n", + "Data: x%x x%x x%x x%x x%x\n", la->eventTag, phba->fc_eventTag, bf_get(lpfc_mbx_read_top_alpa_granted, la), bf_get(lpfc_mbx_read_top_link_spd, la), phba->alpa_map[0], - bf_get(lpfc_mbx_read_top_mm, la), - bf_get(lpfc_mbx_read_top_fa, la), - phba->wait_4_mlo_maint_flg); + bf_get(lpfc_mbx_read_top_fa, la)); } lpfc_mbx_process_link_up(phba, la); @@ -3816,58 +3803,25 @@ lpfc_mbx_cmpl_read_topology(struct lpfc_hba *phba, LPFC_MBOXQ_t *pmb) else if (attn_type == LPFC_ATT_UNEXP_WWPN) lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1313 Link Down Unexpected FA WWPN Event x%x " - "received Data: x%x x%x x%x x%x x%x\n", + "received Data: x%x x%x x%x x%x\n", la->eventTag, phba->fc_eventTag, phba->pport->port_state, vport->fc_flag, - bf_get(lpfc_mbx_read_top_mm, la), bf_get(lpfc_mbx_read_top_fa, la)); else lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, "1305 Link Down Event x%x received " - "Data: x%x x%x x%x x%x x%x\n", + "Data: x%x x%x x%x x%x\n", la->eventTag, phba->fc_eventTag, phba->pport->port_state, vport->fc_flag, - bf_get(lpfc_mbx_read_top_mm, la), bf_get(lpfc_mbx_read_top_fa, la)); lpfc_mbx_issue_link_down(phba); } - if (phba->sli.sli_flag & LPFC_MENLO_MAINT && - attn_type == LPFC_ATT_LINK_UP) { - if (phba->link_state != LPFC_LINK_DOWN) { - phba->fc_stat.LinkDown++; - lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, - "1312 Link Down Event x%x received " - "Data: x%x x%x x%x\n", - la->eventTag, phba->fc_eventTag, - phba->pport->port_state, vport->fc_flag); - lpfc_mbx_issue_link_down(phba); - } else - lpfc_enable_la(phba); - - lpfc_printf_log(phba, KERN_ERR, LOG_LINK_EVENT, - "1310 Menlo Maint Mode Link up Event x%x rcvd " - "Data: x%x x%x x%x\n", - la->eventTag, phba->fc_eventTag, - phba->pport->port_state, vport->fc_flag); - /* - * The cmnd that triggered this will be waiting for this - * signal. - */ - /* WAKEUP for MENLO_SET_MODE or MENLO_RESET command. */ - if (phba->wait_4_mlo_maint_flg) { - phba->wait_4_mlo_maint_flg = 0; - wake_up_interruptible(&phba->wait_4_mlo_m_q); - } - } if ((phba->sli_rev < LPFC_SLI_REV4) && - bf_get(lpfc_mbx_read_top_fa, la)) { - if (phba->sli.sli_flag & LPFC_MENLO_MAINT) - lpfc_issue_clear_la(phba, vport); + bf_get(lpfc_mbx_read_top_fa, la)) lpfc_printf_log(phba, KERN_INFO, LOG_LINK_EVENT, "1311 fa %d\n", bf_get(lpfc_mbx_read_top_fa, la)); - } lpfc_mbx_cmpl_read_topology_free_mbuf: lpfc_mbox_rsrc_cleanup(phba, pmb, MBOX_THD_UNLOCKED); diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 7b8cf678abb5..071983e2cdfe 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -1728,7 +1728,6 @@ struct lpfc_fdmi_reg_portattr { #define PCI_DEVICE_ID_HELIOS_SCSP 0xfd11 #define PCI_DEVICE_ID_HELIOS_DCSP 0xfd12 #define PCI_DEVICE_ID_ZEPHYR 0xfe00 -#define PCI_DEVICE_ID_HORNET 0xfe05 #define PCI_DEVICE_ID_ZEPHYR_SCSP 0xfe11 #define PCI_DEVICE_ID_ZEPHYR_DCSP 0xfe12 #define PCI_VENDOR_ID_SERVERENGINE 0x19a2 @@ -1773,7 +1772,6 @@ struct lpfc_fdmi_reg_portattr { #define ZEPHYR_JEDEC_ID 0x0577 #define VIPER_JEDEC_ID 0x4838 #define SATURN_JEDEC_ID 0x1004 -#define HORNET_JDEC_ID 0x2057706D #define JEDEC_ID_MASK 0x0FFFF000 #define JEDEC_ID_SHIFT 12 @@ -3074,7 +3072,6 @@ struct lpfc_mbx_read_top { #define lpfc_mbx_read_top_topology_WORD word3 #define LPFC_TOPOLOGY_PT_PT 0x01 /* Topology is pt-pt / pt-fabric */ #define LPFC_TOPOLOGY_LOOP 0x02 /* Topology is FC-AL */ -#define LPFC_TOPOLOGY_MM 0x05 /* maint mode zephtr to menlo */ /* store the LILP AL_PA position map into */ struct ulp_bde64 lilpBde64; #define LPFC_ALPA_MAP_SIZE 128 @@ -4423,11 +4420,4 @@ lpfc_error_lost_link(u32 ulp_status, u32 ulp_word4) ulp_word4 == IOERR_SLI_DOWN)); } -#define MENLO_TRANSPORT_TYPE 0xfe -#define MENLO_CONTEXT 0 -#define MENLO_PU 3 -#define MENLO_TIMEOUT 30 -#define SETVAR_MLOMNT 0x103107 -#define SETVAR_MLORST 0x103007 - #define BPL_ALIGN_SZ 8 /* 8 byte alignment for bpl and mbufs */ diff --git a/drivers/scsi/lpfc/lpfc_hw4.h b/drivers/scsi/lpfc/lpfc_hw4.h index f024415731ac..4527fef23ae7 100644 --- a/drivers/scsi/lpfc/lpfc_hw4.h +++ b/drivers/scsi/lpfc/lpfc_hw4.h @@ -4736,7 +4736,6 @@ struct create_xri_wqe { uint32_t rsvd_12_15[4]; /* word 12-15 */ }; -#define INHIBIT_ABORT 1 #define T_REQUEST_TAG 3 #define T_XRI_TAG 1 diff --git a/drivers/scsi/lpfc/lpfc_ids.h b/drivers/scsi/lpfc/lpfc_ids.h index a1b9be245560..0b1616e93cf4 100644 --- a/drivers/scsi/lpfc/lpfc_ids.h +++ b/drivers/scsi/lpfc/lpfc_ids.h @@ -1,7 +1,7 @@ /******************************************************************* * This file is part of the Emulex Linux Device Driver for * * Fibre Channel Host Bus Adapters. * - * Copyright (C) 2017-2021 Broadcom. All Rights Reserved. The term * + * Copyright (C) 2017-2022 Broadcom. All Rights Reserved. The term * * “Broadcom” refers to Broadcom Inc. and/or its subsidiaries. * * Copyright (C) 2004-2016 Emulex. All rights reserved. * * EMULEX and SLI are trademarks of Emulex. * @@ -60,8 +60,6 @@ const struct pci_device_id lpfc_id_table[] = { PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR, PCI_ANY_ID, PCI_ANY_ID, }, - {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_HORNET, - PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_SCSP, PCI_ANY_ID, PCI_ANY_ID, }, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_ZEPHYR_DCSP, diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 750dd1e9f2cc..4a0eadd1c22c 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -375,6 +375,9 @@ lpfc_update_vport_wwn(struct lpfc_vport *vport) if (phba->sli_rev == LPFC_SLI_REV4 && vport->port_type == LPFC_PHYSICAL_PORT && phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_FABRIC) { + if (!(phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG)) + phba->sli4_hba.fawwpn_flag &= + ~LPFC_FAWWPN_FABRIC; lpfc_printf_log(phba, KERN_INFO, LOG_SLI | LOG_DISCOVERY | LOG_ELS, "2701 FA-PWWN change WWPN from %llx to " @@ -2682,11 +2685,6 @@ lpfc_get_hba_model_desc(struct lpfc_hba *phba, uint8_t *mdp, uint8_t *descp) case PCI_DEVICE_ID_SAT_S: m = (typeof(m)){"LPe12000-S", "PCIe", "Fibre Channel Adapter"}; break; - case PCI_DEVICE_ID_HORNET: - m = (typeof(m)){"LP21000", "PCIe", - "Obsolete, Unsupported FCoE Adapter"}; - GE = 1; - break; case PCI_DEVICE_ID_PROTEUS_VF: m = (typeof(m)){"LPev12000", "PCIe IOV", "Obsolete, Unsupported Fibre Channel Adapter"}; @@ -7692,7 +7690,6 @@ lpfc_setup_driver_resource_phase1(struct lpfc_hba *phba) INIT_LIST_HEAD(&phba->port_list); INIT_LIST_HEAD(&phba->work_list); - init_waitqueue_head(&phba->wait_4_mlo_m_q); /* Initialize the wait queue head for the kernel thread */ init_waitqueue_head(&phba->work_waitq); @@ -7776,13 +7773,6 @@ lpfc_sli_driver_resource_setup(struct lpfc_hba *phba) if (rc) return -ENODEV; - if (phba->pcidev->device == PCI_DEVICE_ID_HORNET) { - phba->menlo_flag |= HBA_MENLO_SUPPORT; - /* check for menlo minimum sg count */ - if (phba->cfg_sg_seg_cnt < LPFC_DEFAULT_MENLO_SG_SEG_CNT) - phba->cfg_sg_seg_cnt = LPFC_DEFAULT_MENLO_SG_SEG_CNT; - } - if (!phba->sli.sli3_ring) phba->sli.sli3_ring = kcalloc(LPFC_SLI3_MAX_RING, sizeof(struct lpfc_sli_ring), @@ -9975,7 +9965,8 @@ lpfc_sli4_read_config(struct lpfc_hba *phba) "configured on\n"); phba->sli4_hba.fawwpn_flag |= LPFC_FAWWPN_CONFIG; } else { - phba->sli4_hba.fawwpn_flag = 0; + /* Clear FW configured flag, preserve driver flag */ + phba->sli4_hba.fawwpn_flag &= ~LPFC_FAWWPN_CONFIG; } phba->sli4_hba.conf_trunk = diff --git a/drivers/scsi/lpfc/lpfc_nvme.c b/drivers/scsi/lpfc/lpfc_nvme.c index cd10ee6482fc..152245f7cacc 100644 --- a/drivers/scsi/lpfc/lpfc_nvme.c +++ b/drivers/scsi/lpfc/lpfc_nvme.c @@ -2824,6 +2824,7 @@ lpfc_nvme_cancel_iocb(struct lpfc_hba *phba, struct lpfc_iocbq *pwqeIn, wcqep->word0 = 0; bf_set(lpfc_wcqe_c_status, wcqep, stat); wcqep->parameter = param; + wcqep->total_data_placed = 0; wcqep->word3 = 0; /* xb is 0 */ /* Call release with XB=1 to queue the IO into the abort list. */ diff --git a/drivers/scsi/lpfc/lpfc_nvmet.c b/drivers/scsi/lpfc/lpfc_nvmet.c index c0ee0b39075d..f7cfac0da9b6 100644 --- a/drivers/scsi/lpfc/lpfc_nvmet.c +++ b/drivers/scsi/lpfc/lpfc_nvmet.c @@ -722,7 +722,7 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, struct lpfc_nvmet_tgtport *tgtp; struct nvmefc_tgt_fcp_req *rsp; struct lpfc_async_xchg_ctx *ctxp; - uint32_t status, result, op, start_clean, logerr; + uint32_t status, result, op, logerr; struct lpfc_wcqe_complete *wcqe = &rspwqe->wcqe_cmpl; #ifdef CONFIG_SCSI_LPFC_DEBUG_FS int id; @@ -820,9 +820,7 @@ lpfc_nvmet_xmt_fcp_op_cmp(struct lpfc_hba *phba, struct lpfc_iocbq *cmdwqe, /* lpfc_nvmet_xmt_fcp_release() will recycle the context */ } else { ctxp->entry_cnt++; - start_clean = offsetof(struct lpfc_iocbq, cmd_flag); - memset(((char *)cmdwqe) + start_clean, 0, - (sizeof(struct lpfc_iocbq) - start_clean)); + memset_startat(cmdwqe, 0, cmd_flag); #ifdef CONFIG_SCSI_LPFC_DEBUG_FS if (ctxp->ts_cmd_nvme) { ctxp->ts_isr_data = cmdwqe->isr_timestamp; @@ -3337,46 +3335,6 @@ lpfc_nvmet_unsol_issue_abort(struct lpfc_hba *phba, return 1; } -/** - * lpfc_nvmet_prep_abort_wqe - set up 'abort' work queue entry. - * @pwqeq: Pointer to command iocb. - * @xritag: Tag that uniqely identifies the local exchange resource. - * @opt: Option bits - - * bit 0 = inhibit sending abts on the link - * - * This function is called with hbalock held. - **/ -static void -lpfc_nvmet_prep_abort_wqe(struct lpfc_iocbq *pwqeq, u16 xritag, u8 opt) -{ - union lpfc_wqe128 *wqe = &pwqeq->wqe; - - /* WQEs are reused. Clear stale data and set key fields to - * zero like ia, iaab, iaar, xri_tag, and ctxt_tag. - */ - memset(wqe, 0, sizeof(*wqe)); - - if (opt & INHIBIT_ABORT) - bf_set(abort_cmd_ia, &wqe->abort_cmd, 1); - /* Abort specified xri tag, with the mask deliberately zeroed */ - bf_set(abort_cmd_criteria, &wqe->abort_cmd, T_XRI_TAG); - - bf_set(wqe_cmnd, &wqe->abort_cmd.wqe_com, CMD_ABORT_XRI_CX); - - /* Abort the I/O associated with this outstanding exchange ID. */ - wqe->abort_cmd.wqe_com.abort_tag = xritag; - - /* iotag for the wqe completion. */ - bf_set(wqe_reqtag, &wqe->abort_cmd.wqe_com, pwqeq->iotag); - - bf_set(wqe_qosd, &wqe->abort_cmd.wqe_com, 1); - bf_set(wqe_lenloc, &wqe->abort_cmd.wqe_com, LPFC_WQE_LENLOC_NONE); - - bf_set(wqe_cmd_type, &wqe->abort_cmd.wqe_com, OTHER_COMMAND); - bf_set(wqe_wqec, &wqe->abort_cmd.wqe_com, 1); - bf_set(wqe_cqid, &wqe->abort_cmd.wqe_com, LPFC_WQE_CQ_ID_DEFAULT); -} - static int lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, struct lpfc_async_xchg_ctx *ctxp, @@ -3386,7 +3344,7 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, struct lpfc_iocbq *abts_wqeq; struct lpfc_nodelist *ndlp; unsigned long flags; - u8 opt; + bool ia; int rc; tgtp = (struct lpfc_nvmet_tgtport *)phba->targetport->private; @@ -3426,7 +3384,7 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, } abts_wqeq = ctxp->abort_wqeq; ctxp->state = LPFC_NVME_STE_ABORT; - opt = (ctxp->flag & LPFC_NVME_ABTS_RCV) ? INHIBIT_ABORT : 0; + ia = (ctxp->flag & LPFC_NVME_ABTS_RCV) ? true : false; spin_unlock_irqrestore(&ctxp->ctxlock, flags); /* Announce entry to new IO submit field. */ @@ -3472,7 +3430,9 @@ lpfc_nvmet_sol_fcp_issue_abort(struct lpfc_hba *phba, /* Ready - mark outstanding as aborted by driver. */ abts_wqeq->cmd_flag |= LPFC_DRIVER_ABORTED; - lpfc_nvmet_prep_abort_wqe(abts_wqeq, ctxp->wqeq->sli4_xritag, opt); + lpfc_sli_prep_abort_xri(phba, abts_wqeq, ctxp->wqeq->sli4_xritag, + abts_wqeq->iotag, CLASS3, + LPFC_WQE_CQ_ID_DEFAULT, ia, true); /* ABTS WQE must go to the same WQ as the WQE to be aborted */ abts_wqeq->hba_wqidx = ctxp->wqeq->hba_wqidx; diff --git a/drivers/scsi/lpfc/lpfc_scsi.c b/drivers/scsi/lpfc/lpfc_scsi.c index ba5e4016262e..084c0f9fdc3a 100644 --- a/drivers/scsi/lpfc/lpfc_scsi.c +++ b/drivers/scsi/lpfc/lpfc_scsi.c @@ -5456,7 +5456,6 @@ lpfc_queuecommand(struct Scsi_Host *shost, struct scsi_cmnd *cmnd) cur_iocbq->cmd_flag |= LPFC_IO_VMID; } } - atomic_inc(&ndlp->cmd_pending); #ifdef CONFIG_SCSI_LPFC_DEBUG_FS if (unlikely(phba->hdwqstat_on & LPFC_CHECK_SCSI_IO)) diff --git a/drivers/scsi/lpfc/lpfc_sli.c b/drivers/scsi/lpfc/lpfc_sli.c index 80ac3a051c19..608016725db9 100644 --- a/drivers/scsi/lpfc/lpfc_sli.c +++ b/drivers/scsi/lpfc/lpfc_sli.c @@ -2003,10 +2003,12 @@ initpath: sync_buf->cmd_flag |= LPFC_IO_CMF; ret_val = lpfc_sli4_issue_wqe(phba, &phba->sli4_hba.hdwq[0], sync_buf); - if (ret_val) + if (ret_val) { lpfc_printf_log(phba, KERN_INFO, LOG_CGN_MGMT, "6214 Cannot issue CMF_SYNC_WQE: x%x\n", ret_val); + __lpfc_sli_release_iocbq(phba, sync_buf); + } out_unlock: spin_unlock_irqrestore(&phba->hbalock, iflags); return ret_val; @@ -5263,7 +5265,8 @@ lpfc_sli_brdrestart_s4(struct lpfc_hba *phba) phba->pport->stopped = 0; phba->link_state = LPFC_INIT_START; phba->hba_flag = 0; - phba->sli4_hba.fawwpn_flag = 0; + /* Preserve FA-PWWN expectation */ + phba->sli4_hba.fawwpn_flag &= LPFC_FAWWPN_FABRIC; spin_unlock_irq(&phba->hbalock); memset(&psli->lnk_stat_offsets, 0, sizeof(psli->lnk_stat_offsets)); @@ -6052,6 +6055,10 @@ lpfc_sli4_retrieve_pport_name(struct lpfc_hba *phba) /* obtain link type and link number via READ_CONFIG */ phba->sli4_hba.lnk_info.lnk_dv = LPFC_LNK_DAT_INVAL; lpfc_sli4_read_config(phba); + + if (phba->sli4_hba.fawwpn_flag & LPFC_FAWWPN_CONFIG) + phba->sli4_hba.fawwpn_flag |= LPFC_FAWWPN_FABRIC; + if (phba->sli4_hba.lnk_info.lnk_dv == LPFC_LNK_DAT_VAL) goto retrieve_ppname; @@ -10216,16 +10223,6 @@ __lpfc_sli_issue_iocb_s3(struct lpfc_hba *phba, uint32_t ring_number, * can be issued if the link is not up. */ switch (piocb->iocb.ulpCommand) { - case CMD_GEN_REQUEST64_CR: - case CMD_GEN_REQUEST64_CX: - if (!(phba->sli.sli_flag & LPFC_MENLO_MAINT) || - (piocb->iocb.un.genreq64.w5.hcsw.Rctl != - FC_RCTL_DD_UNSOL_CMD) || - (piocb->iocb.un.genreq64.w5.hcsw.Type != - MENLO_TRANSPORT_TYPE)) - - goto iocb_busy; - break; case CMD_QUE_RING_BUF_CN: case CMD_QUE_RING_BUF64_CN: /* @@ -10549,6 +10546,7 @@ __lpfc_sli_prep_els_req_rsp_s3(struct lpfc_iocbq *cmdiocbq, cmd->un.elsreq64.bdl.bdeSize = sizeof(struct ulp_bde64); cmd->un.genreq64.xmit_els_remoteID = did; /* DID */ cmd->ulpCommand = CMD_XMIT_ELS_RSP64_CX; + cmd->ulpPU = PARM_NPIV_DID; } cmd->ulpBdeCount = 1; cmd->ulpLe = 1; @@ -10855,7 +10853,8 @@ lpfc_sli_prep_xmit_seq64(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, static void __lpfc_sli_prep_abort_xri_s3(struct lpfc_iocbq *cmdiocbq, u16 ulp_context, - u16 iotag, u8 ulp_class, u16 cqid, bool ia) + u16 iotag, u8 ulp_class, u16 cqid, bool ia, + bool wqec) { IOCB_t *icmd = NULL; @@ -10884,7 +10883,8 @@ __lpfc_sli_prep_abort_xri_s3(struct lpfc_iocbq *cmdiocbq, u16 ulp_context, static void __lpfc_sli_prep_abort_xri_s4(struct lpfc_iocbq *cmdiocbq, u16 ulp_context, - u16 iotag, u8 ulp_class, u16 cqid, bool ia) + u16 iotag, u8 ulp_class, u16 cqid, bool ia, + bool wqec) { union lpfc_wqe128 *wqe; @@ -10911,6 +10911,8 @@ __lpfc_sli_prep_abort_xri_s4(struct lpfc_iocbq *cmdiocbq, u16 ulp_context, bf_set(wqe_qosd, &wqe->abort_cmd.wqe_com, 1); /* Word 11 */ + if (wqec) + bf_set(wqe_wqec, &wqe->abort_cmd.wqe_com, 1); bf_set(wqe_cqid, &wqe->abort_cmd.wqe_com, cqid); bf_set(wqe_cmd_type, &wqe->abort_cmd.wqe_com, OTHER_COMMAND); } @@ -10918,10 +10920,10 @@ __lpfc_sli_prep_abort_xri_s4(struct lpfc_iocbq *cmdiocbq, u16 ulp_context, void lpfc_sli_prep_abort_xri(struct lpfc_hba *phba, struct lpfc_iocbq *cmdiocbq, u16 ulp_context, u16 iotag, u8 ulp_class, u16 cqid, - bool ia) + bool ia, bool wqec) { phba->__lpfc_sli_prep_abort_xri(cmdiocbq, ulp_context, iotag, ulp_class, - cqid, ia); + cqid, ia, wqec); } /** @@ -12199,7 +12201,7 @@ lpfc_sli_issue_abort_iotag(struct lpfc_hba *phba, struct lpfc_sli_ring *pring, lpfc_sli_prep_abort_xri(phba, abtsiocbp, ulp_context, iotag, cmdiocb->iocb.ulpClass, - LPFC_WQE_CQ_ID_DEFAULT, ia); + LPFC_WQE_CQ_ID_DEFAULT, ia, false); abtsiocbp->vport = vport; @@ -12659,7 +12661,7 @@ lpfc_sli_abort_taskmgmt(struct lpfc_vport *vport, struct lpfc_sli_ring *pring, lpfc_sli_prep_abort_xri(phba, abtsiocbq, ulp_context, iotag, iocbq->iocb.ulpClass, cqid, - ia); + ia, false); abtsiocbq->vport = vport; diff --git a/drivers/scsi/lpfc/lpfc_sli.h b/drivers/scsi/lpfc/lpfc_sli.h index 0af6860b8936..cd33dfec758c 100644 --- a/drivers/scsi/lpfc/lpfc_sli.h +++ b/drivers/scsi/lpfc/lpfc_sli.h @@ -355,7 +355,6 @@ struct lpfc_sli { #define LPFC_SLI_ACTIVE 0x200 /* SLI in firmware is active */ #define LPFC_PROCESS_LA 0x400 /* Able to process link attention */ #define LPFC_BLOCK_MGMT_IO 0x800 /* Don't allow mgmt mbx or iocb cmds */ -#define LPFC_MENLO_MAINT 0x1000 /* need for menl fw download */ #define LPFC_SLI_ASYNC_MBX_BLK 0x2000 /* Async mailbox is blocked */ #define LPFC_SLI_SUPPRESS_RSP 0x4000 /* Suppress RSP feature is supported */ #define LPFC_SLI_USE_EQDR 0x8000 /* EQ Delay Register is supported */ diff --git a/drivers/scsi/lpfc/lpfc_version.h b/drivers/scsi/lpfc/lpfc_version.h index 2ab6f7db64d8..63eba9928e4b 100644 --- a/drivers/scsi/lpfc/lpfc_version.h +++ b/drivers/scsi/lpfc/lpfc_version.h @@ -20,7 +20,7 @@ * included with this package. * *******************************************************************/ -#define LPFC_DRIVER_VERSION "14.2.0.4" +#define LPFC_DRIVER_VERSION "14.2.0.5" #define LPFC_DRIVER_NAME "lpfc" /* Used for SLI 2/3 */ diff --git a/drivers/scsi/megaraid/megaraid_mbox.c b/drivers/scsi/megaraid/megaraid_mbox.c index 2a339d4a7e9d..157c3bdb50be 100644 --- a/drivers/scsi/megaraid/megaraid_mbox.c +++ b/drivers/scsi/megaraid/megaraid_mbox.c @@ -181,7 +181,7 @@ MODULE_PARM_DESC(cmd_per_lun, * This would result in non-disk devices being skipped during driver load * time. These can be later added though, using /proc/scsi/scsi */ -static unsigned int megaraid_fast_load = 0; +static unsigned int megaraid_fast_load; module_param_named(fast_load, megaraid_fast_load, int, 0); MODULE_PARM_DESC(fast_load, "Faster loading of the driver, skips physical devices! (default=0)"); diff --git a/drivers/scsi/megaraid/megaraid_sas_base.c b/drivers/scsi/megaraid/megaraid_sas_base.c index 0917b05059b4..a3e117a4b8e7 100644 --- a/drivers/scsi/megaraid/megaraid_sas_base.c +++ b/drivers/scsi/megaraid/megaraid_sas_base.c @@ -3950,9 +3950,9 @@ process_fw_state_change_wq(struct work_struct *work) u32 wait; unsigned long flags; - if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) { + if (atomic_read(&instance->adprecovery) != MEGASAS_ADPRESET_SM_INFAULT) { dev_notice(&instance->pdev->dev, "error, recovery st %x\n", - atomic_read(&instance->adprecovery)); + atomic_read(&instance->adprecovery)); return ; } diff --git a/drivers/scsi/mpi3mr/mpi3mr.h b/drivers/scsi/mpi3mr/mpi3mr.h index 0e1cb4aa4ca2..0935b2e80662 100644 --- a/drivers/scsi/mpi3mr/mpi3mr.h +++ b/drivers/scsi/mpi3mr/mpi3mr.h @@ -66,12 +66,14 @@ extern atomic64_t event_counter; #define MPI3MR_NAME_LENGTH 32 #define IOCNAME "%s: " +#define MPI3MR_MAX_SECTORS 2048 + /* Definitions for internal SGL and Chain SGL buffers */ #define MPI3MR_PAGE_SIZE_4K 4096 #define MPI3MR_SG_DEPTH (MPI3MR_PAGE_SIZE_4K / sizeof(struct mpi3_sge_common)) /* Definitions for MAX values for shost */ -#define MPI3MR_MAX_CMDS_LUN 7 +#define MPI3MR_MAX_CMDS_LUN 128 #define MPI3MR_MAX_CDB_LENGTH 32 /* Admin queue management definitions */ @@ -333,6 +335,12 @@ struct mpi3mr_ioc_facts { u8 sge_mod_mask; u8 sge_mod_value; u8 sge_mod_shift; + u8 max_dev_per_tg; + u16 max_io_throttle_group; + u16 io_throttle_data_length; + u16 io_throttle_low; + u16 io_throttle_high; + }; /** @@ -424,6 +432,31 @@ struct mpi3mr_intr_info { char name[MPI3MR_NAME_LENGTH]; }; +/** + * struct mpi3mr_throttle_group_info - Throttle group info + * + * @io_divert: Flag indicates io divert is on or off for the TG + * @need_qd_reduction: Flag to indicate QD reduction is needed + * @qd_reduction: Queue Depth reduction in units of 10% + * @fw_qd: QueueDepth value reported by the firmware + * @modified_qd: Modified QueueDepth value due to throttling + * @id: Throttle Group ID. + * @high: High limit to turn on throttling in 512 byte blocks + * @low: Low limit to turn off throttling in 512 byte blocks + * @pend_large_data_sz: Counter to track pending large data + */ +struct mpi3mr_throttle_group_info { + u8 io_divert; + u8 need_qd_reduction; + u8 qd_reduction; + u16 fw_qd; + u16 modified_qd; + u16 id; + u32 high; + u32 low; + atomic_t pend_large_data_sz; +}; + /** * struct tgt_dev_sas_sata - SAS/SATA device specific * information cached from firmware given data @@ -457,22 +490,33 @@ struct tgt_dev_pcie { }; /** - * struct tgt_dev_volume - virtual device specific information + * struct tgt_dev_vd - virtual device specific information * cached from firmware given data * * @state: State of the VD + * @tg_qd_reduction: Queue Depth reduction in units of 10% + * @tg_id: VDs throttle group ID + * @high: High limit to turn on throttling in 512 byte blocks + * @low: Low limit to turn off throttling in 512 byte blocks + * @tg: Pointer to throttle group info */ -struct tgt_dev_volume { +struct tgt_dev_vd { u8 state; + u8 tg_qd_reduction; + u16 tg_id; + u32 tg_high; + u32 tg_low; + struct mpi3mr_throttle_group_info *tg; }; + /** * union _form_spec_inf - union of device specific information */ union _form_spec_inf { struct tgt_dev_sas_sata sas_sata_inf; struct tgt_dev_pcie pcie_inf; - struct tgt_dev_volume vol_inf; + struct tgt_dev_vd vd_inf; }; @@ -490,6 +534,7 @@ union _form_spec_inf { * @dev_type: SAS/SATA/PCIE device type * @is_hidden: Should be exposed to upper layers or not * @host_exposed: Already exposed to host or not + * @io_throttle_enabled: I/O throttling needed or not * @q_depth: Device specific Queue Depth * @wwid: World wide ID * @dev_spec: Device type specific information @@ -506,6 +551,7 @@ struct mpi3mr_tgt_dev { u8 dev_type; u8 is_hidden; u8 host_exposed; + u8 io_throttle_enabled; u16 q_depth; u64 wwid; union _form_spec_inf dev_spec; @@ -557,6 +603,9 @@ static inline void mpi3mr_tgtdev_put(struct mpi3mr_tgt_dev *s) * @dev_removed: Device removed in the Firmware * @dev_removedelay: Device is waiting to be removed in FW * @dev_type: Device type + * @io_throttle_enabled: I/O throttling needed or not + * @io_divert: Flag indicates io divert is on or off for the dev + * @throttle_group: Pointer to throttle group info * @tgt_dev: Internal target device pointer * @pend_count: Counter to track pending I/Os during error * handling @@ -570,6 +619,9 @@ struct mpi3mr_stgt_priv_data { u8 dev_removed; u8 dev_removedelay; u8 dev_type; + u8 io_throttle_enabled; + u8 io_divert; + struct mpi3mr_throttle_group_info *throttle_group; struct mpi3mr_tgt_dev *tgt_dev; u32 pend_count; }; @@ -796,6 +848,12 @@ struct scmd_priv { * @logdata_buf: Circular buffer to store log data entries * @logdata_buf_idx: Index of entry in buffer to store * @logdata_entry_sz: log data entry size + * @pend_large_data_sz: Counter to track pending large data + * @io_throttle_data_length: I/O size to track in 512b blocks + * @io_throttle_high: I/O size to start throttle in 512b blocks + * @io_throttle_low: I/O size to stop throttle in 512b blocks + * @num_io_throttle_group: Maximum number of throttle groups + * @throttle_groups: Pointer to throttle group info structures */ struct mpi3mr_ioc { struct list_head list; @@ -960,6 +1018,13 @@ struct mpi3mr_ioc { u8 *logdata_buf; u16 logdata_buf_idx; u16 logdata_entry_sz; + + atomic_t pend_large_data_sz; + u32 io_throttle_data_length; + u32 io_throttle_high; + u32 io_throttle_low; + u16 num_io_throttle_group; + struct mpi3mr_throttle_group_info *throttle_groups; }; /** diff --git a/drivers/scsi/mpi3mr/mpi3mr_fw.c b/drivers/scsi/mpi3mr/mpi3mr_fw.c index f1d4ea8ba989..0866dfd43318 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_fw.c +++ b/drivers/scsi/mpi3mr/mpi3mr_fw.c @@ -2785,6 +2785,27 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, mrioc->facts.shutdown_timeout = le16_to_cpu(facts_data->shutdown_timeout); + mrioc->facts.max_dev_per_tg = + facts_data->max_devices_per_throttle_group; + mrioc->facts.io_throttle_data_length = + le16_to_cpu(facts_data->io_throttle_data_length); + mrioc->facts.max_io_throttle_group = + le16_to_cpu(facts_data->max_io_throttle_group); + mrioc->facts.io_throttle_low = le16_to_cpu(facts_data->io_throttle_low); + mrioc->facts.io_throttle_high = + le16_to_cpu(facts_data->io_throttle_high); + + /* Store in 512b block count */ + if (mrioc->facts.io_throttle_data_length) + mrioc->io_throttle_data_length = + (mrioc->facts.io_throttle_data_length * 2 * 4); + else + /* set the length to 1MB + 1K to disable throttle */ + mrioc->io_throttle_data_length = MPI3MR_MAX_SECTORS + 2; + + mrioc->io_throttle_high = (mrioc->facts.io_throttle_high * 2 * 1024); + mrioc->io_throttle_low = (mrioc->facts.io_throttle_low * 2 * 1024); + ioc_info(mrioc, "ioc_num(%d), maxopQ(%d), maxopRepQ(%d), maxdh(%d),", mrioc->facts.ioc_num, mrioc->facts.max_op_req_q, mrioc->facts.max_op_reply_q, mrioc->facts.max_devhandle); @@ -2798,6 +2819,13 @@ static void mpi3mr_process_factsdata(struct mpi3mr_ioc *mrioc, ioc_info(mrioc, "DMA mask %d InitialPE status 0x%x\n", mrioc->facts.dma_mask, (facts_flags & MPI3_IOCFACTS_FLAGS_INITIAL_PORT_ENABLE_MASK)); + ioc_info(mrioc, + "max_dev_per_throttle_group(%d), max_throttle_groups(%d)\n", + mrioc->facts.max_dev_per_tg, mrioc->facts.max_io_throttle_group); + ioc_info(mrioc, + "io_throttle_data_len(%dKiB), io_throttle_high(%dMiB), io_throttle_low(%dMiB)\n", + mrioc->facts.io_throttle_data_length * 4, + mrioc->facts.io_throttle_high, mrioc->facts.io_throttle_low); } /** @@ -3666,6 +3694,7 @@ int mpi3mr_init_ioc(struct mpi3mr_ioc *mrioc) int retval = 0; u8 retry = 0; struct mpi3_ioc_facts_data facts_data; + u32 sz; retry_init: retval = mpi3mr_bring_ioc_ready(mrioc); @@ -3691,6 +3720,9 @@ retry_init: mrioc->max_host_ios = mrioc->facts.max_reqs - MPI3MR_INTERNAL_CMDS_RESVD; + mrioc->num_io_throttle_group = mrioc->facts.max_io_throttle_group; + atomic_set(&mrioc->pend_large_data_sz, 0); + if (reset_devices) mrioc->max_host_ios = min_t(int, mrioc->max_host_ios, MPI3MR_HOST_IOS_KDUMP); @@ -3760,6 +3792,15 @@ retry_init: } } + if (!mrioc->throttle_groups && mrioc->num_io_throttle_group) { + dprint_init(mrioc, "allocating memory for throttle groups\n"); + sz = sizeof(struct mpi3mr_throttle_group_info); + mrioc->throttle_groups = (struct mpi3mr_throttle_group_info *) + kcalloc(mrioc->num_io_throttle_group, sz, GFP_KERNEL); + if (!mrioc->throttle_groups) + goto out_failed_noretry; + } + retval = mpi3mr_enable_events(mrioc); if (retval) { ioc_err(mrioc, "failed to enable events %d\n", @@ -3981,6 +4022,7 @@ static void mpi3mr_memset_op_req_q_buffers(struct mpi3mr_ioc *mrioc, u16 qidx) void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) { u16 i; + struct mpi3mr_throttle_group_info *tg; mrioc->change_count = 0; mrioc->active_poll_qcount = 0; @@ -4029,6 +4071,22 @@ void mpi3mr_memset_buffers(struct mpi3mr_ioc *mrioc) spin_lock_init(&mrioc->req_qinfo[i].q_lock); mpi3mr_memset_op_req_q_buffers(mrioc, i); } + + atomic_set(&mrioc->pend_large_data_sz, 0); + if (mrioc->throttle_groups) { + tg = mrioc->throttle_groups; + for (i = 0; i < mrioc->num_io_throttle_group; i++, tg++) { + tg->id = 0; + tg->fw_qd = 0; + tg->modified_qd = 0; + tg->io_divert = 0; + tg->need_qd_reduction = 0; + tg->high = 0; + tg->low = 0; + tg->qd_reduction = 0; + atomic_set(&tg->pend_large_data_sz, 0); + } + } } /** @@ -4663,6 +4721,15 @@ int mpi3mr_soft_reset_handler(struct mpi3mr_ioc *mrioc, ioc_err(mrioc, "Failed to issue soft reset to the ioc\n"); goto out; } + if (mrioc->num_io_throttle_group != + mrioc->facts.max_io_throttle_group) { + ioc_err(mrioc, + "max io throttle group doesn't match old(%d), new(%d)\n", + mrioc->num_io_throttle_group, + mrioc->facts.max_io_throttle_group); + retval = -EPERM; + goto out; + } mpi3mr_flush_delayed_cmd_lists(mrioc); mpi3mr_flush_drv_cmds(mrioc); diff --git a/drivers/scsi/mpi3mr/mpi3mr_os.c b/drivers/scsi/mpi3mr/mpi3mr_os.c index 59a18769a4fe..bfa1165e23b6 100644 --- a/drivers/scsi/mpi3mr/mpi3mr_os.c +++ b/drivers/scsi/mpi3mr/mpi3mr_os.c @@ -38,6 +38,8 @@ MODULE_PARM_DESC(logging_level, static void mpi3mr_send_event_ack(struct mpi3mr_ioc *mrioc, u8 event, struct mpi3mr_drv_cmd *cmdparam, u32 event_ctx); +#define MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION (0xFFFF) + /** * mpi3mr_host_tag_for_scmd - Get host tag for a scmd * @mrioc: Adapter instance reference @@ -354,6 +356,50 @@ void mpi3mr_cleanup_fwevt_list(struct mpi3mr_ioc *mrioc) } } +/** + * mpi3mr_queue_qd_reduction_event - Queue TG QD reduction event + * @mrioc: Adapter instance reference + * @tg: Throttle group information pointer + * + * Accessor to queue on synthetically generated driver event to + * the event worker thread, the driver event will be used to + * reduce the QD of all VDs in the TG from the worker thread. + * + * Return: None. + */ +static void mpi3mr_queue_qd_reduction_event(struct mpi3mr_ioc *mrioc, + struct mpi3mr_throttle_group_info *tg) +{ + struct mpi3mr_fwevt *fwevt; + u16 sz = sizeof(struct mpi3mr_throttle_group_info *); + + /* + * If the QD reduction event is already queued due to throttle and if + * the QD is not restored through device info change event + * then dont queue further reduction events + */ + if (tg->fw_qd != tg->modified_qd) + return; + + fwevt = mpi3mr_alloc_fwevt(sz); + if (!fwevt) { + ioc_warn(mrioc, "failed to queue TG QD reduction event\n"); + return; + } + *(struct mpi3mr_throttle_group_info **)fwevt->event_data = tg; + fwevt->mrioc = mrioc; + fwevt->event_id = MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION; + fwevt->send_ack = 0; + fwevt->process_evt = 1; + fwevt->evt_ctx = 0; + fwevt->event_data_size = sz; + tg->modified_qd = max_t(u16, (tg->fw_qd * tg->qd_reduction) / 10, 8); + + dprint_event_bh(mrioc, "qd reduction event queued for tg_id(%d)\n", + tg->id); + mpi3mr_fwevt_add_to_list(mrioc, fwevt); +} + /** * mpi3mr_invalidate_devhandles -Invalidate device handles * @mrioc: Adapter instance reference @@ -373,6 +419,9 @@ void mpi3mr_invalidate_devhandles(struct mpi3mr_ioc *mrioc) if (tgtdev->starget && tgtdev->starget->hostdata) { tgt_priv = tgtdev->starget->hostdata; tgt_priv->dev_handle = MPI3MR_INVALID_DEV_HANDLE; + tgt_priv->io_throttle_enabled = 0; + tgt_priv->io_divert = 0; + tgt_priv->throttle_group = NULL; } } } @@ -709,6 +758,35 @@ static struct mpi3mr_tgt_dev *__mpi3mr_get_tgtdev_from_tgtpriv( return tgtdev; } +/** + * mpi3mr_set_io_divert_for_all_vd_in_tg -set divert for TG VDs + * @mrioc: Adapter instance reference + * @tg: Throttle group information pointer + * @divert_value: 1 or 0 + * + * Accessor to set io_divert flag for each device associated + * with the given throttle group with the given value. + * + * Return: None. + */ +static void mpi3mr_set_io_divert_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc, + struct mpi3mr_throttle_group_info *tg, u8 divert_value) +{ + unsigned long flags; + struct mpi3mr_tgt_dev *tgtdev; + struct mpi3mr_stgt_priv_data *tgt_priv; + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { + if (tgtdev->starget && tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + if (tgt_priv->throttle_group == tg) + tgt_priv->io_divert = divert_value; + } + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); +} + /** * mpi3mr_print_device_event_notice - print notice related to post processing of * device event after controller reset. @@ -840,6 +918,7 @@ static int mpi3mr_change_queue_depth(struct scsi_device *sdev, else if (!q_depth) q_depth = MPI3MR_DEFAULT_SDEV_QD; retval = scsi_change_queue_depth(sdev, q_depth); + sdev->max_queue_depth = sdev->queue_depth; return retval; } @@ -926,6 +1005,7 @@ void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc) * @mrioc: Adapter instance reference * @tgtdev: Target device internal structure * @dev_pg0: New device page0 + * @is_added: Flag to indicate the device is just added * * Update the information from the device page0 into the driver * cached target device structure. @@ -933,10 +1013,11 @@ void mpi3mr_rfresh_tgtdevs(struct mpi3mr_ioc *mrioc) * Return: Nothing. */ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, - struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0) + struct mpi3mr_tgt_dev *tgtdev, struct mpi3_device_page0 *dev_pg0, + bool is_added) { u16 flags = 0; - struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data; + struct mpi3mr_stgt_priv_data *scsi_tgt_priv_data = NULL; u8 prot_mask = 0; tgtdev->perst_id = le16_to_cpu(dev_pg0->persistent_id); @@ -951,12 +1032,19 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, flags = le16_to_cpu(dev_pg0->flags); tgtdev->is_hidden = (flags & MPI3_DEVICE0_FLAGS_HIDDEN); + if (is_added == true) + tgtdev->io_throttle_enabled = + (flags & MPI3_DEVICE0_FLAGS_IO_THROTTLING_REQUIRED) ? 1 : 0; + + if (tgtdev->starget && tgtdev->starget->hostdata) { scsi_tgt_priv_data = (struct mpi3mr_stgt_priv_data *) tgtdev->starget->hostdata; scsi_tgt_priv_data->perst_id = tgtdev->perst_id; scsi_tgt_priv_data->dev_handle = tgtdev->dev_handle; scsi_tgt_priv_data->dev_type = tgtdev->dev_type; + scsi_tgt_priv_data->io_throttle_enabled = + tgtdev->io_throttle_enabled; } switch (dev_pg0->access_status) { @@ -1034,10 +1122,32 @@ static void mpi3mr_update_tgtdev(struct mpi3mr_ioc *mrioc, { struct mpi3_device0_vd_format *vdinf = &dev_pg0->device_specific.vd_format; + struct mpi3mr_throttle_group_info *tg = NULL; + u16 vdinf_io_throttle_group = + le16_to_cpu(vdinf->io_throttle_group); - tgtdev->dev_spec.vol_inf.state = vdinf->vd_state; + tgtdev->dev_spec.vd_inf.state = vdinf->vd_state; if (vdinf->vd_state == MPI3_DEVICE0_VD_STATE_OFFLINE) tgtdev->is_hidden = 1; + tgtdev->dev_spec.vd_inf.tg_id = vdinf_io_throttle_group; + tgtdev->dev_spec.vd_inf.tg_high = + le16_to_cpu(vdinf->io_throttle_group_high) * 2048; + tgtdev->dev_spec.vd_inf.tg_low = + le16_to_cpu(vdinf->io_throttle_group_low) * 2048; + if (vdinf_io_throttle_group < mrioc->num_io_throttle_group) { + tg = mrioc->throttle_groups + vdinf_io_throttle_group; + tg->id = vdinf_io_throttle_group; + tg->high = tgtdev->dev_spec.vd_inf.tg_high; + tg->low = tgtdev->dev_spec.vd_inf.tg_low; + tg->qd_reduction = + tgtdev->dev_spec.vd_inf.tg_qd_reduction; + if (is_added == true) + tg->fw_qd = tgtdev->q_depth; + tg->modified_qd = tgtdev->q_depth; + } + tgtdev->dev_spec.vd_inf.tg = tg; + if (scsi_tgt_priv_data) + scsi_tgt_priv_data->throttle_group = tg; break; } default: @@ -1134,7 +1244,7 @@ static void mpi3mr_devinfochg_evt_bh(struct mpi3mr_ioc *mrioc, tgtdev = mpi3mr_get_tgtdev_by_handle(mrioc, dev_handle); if (!tgtdev) goto out; - mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, false); if (!tgtdev->is_hidden && !tgtdev->host_exposed) mpi3mr_report_tgtdev_to_host(mrioc, perst_id); if (tgtdev->is_hidden && tgtdev->host_exposed) @@ -1427,6 +1537,60 @@ static void mpi3mr_logdata_evt_bh(struct mpi3mr_ioc *mrioc, fwevt->event_data_size); } +/** + * mpi3mr_update_sdev_qd - Update SCSI device queue depath + * @sdev: SCSI device reference + * @data: Queue depth reference + * + * This is an iterator function called for each SCSI device in a + * target to update the QD of each SCSI device. + * + * Return: Nothing. + */ +static void mpi3mr_update_sdev_qd(struct scsi_device *sdev, void *data) +{ + u16 *q_depth = (u16 *)data; + + scsi_change_queue_depth(sdev, (int)*q_depth); + sdev->max_queue_depth = sdev->queue_depth; +} + +/** + * mpi3mr_set_qd_for_all_vd_in_tg -set QD for TG VDs + * @mrioc: Adapter instance reference + * @tg: Throttle group information pointer + * + * Accessor to reduce QD for each device associated with the + * given throttle group. + * + * Return: None. + */ +static void mpi3mr_set_qd_for_all_vd_in_tg(struct mpi3mr_ioc *mrioc, + struct mpi3mr_throttle_group_info *tg) +{ + unsigned long flags; + struct mpi3mr_tgt_dev *tgtdev; + struct mpi3mr_stgt_priv_data *tgt_priv; + + + spin_lock_irqsave(&mrioc->tgtdev_lock, flags); + list_for_each_entry(tgtdev, &mrioc->tgtdev_list, list) { + if (tgtdev->starget && tgtdev->starget->hostdata) { + tgt_priv = tgtdev->starget->hostdata; + if (tgt_priv->throttle_group == tg) { + dprint_event_bh(mrioc, + "updating qd due to throttling for persist_id(%d) original_qd(%d), reduced_qd (%d)\n", + tgt_priv->perst_id, tgtdev->q_depth, + tg->modified_qd); + starget_for_each_device(tgtdev->starget, + (void *)&tg->modified_qd, + mpi3mr_update_sdev_qd); + } + } + } + spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); +} + /** * mpi3mr_fwevt_bh - Firmware event bottomhalf handler * @mrioc: Adapter instance reference @@ -1484,6 +1648,20 @@ static void mpi3mr_fwevt_bh(struct mpi3mr_ioc *mrioc, mpi3mr_logdata_evt_bh(mrioc, fwevt); break; } + case MPI3MR_DRIVER_EVENT_TG_QD_REDUCTION: + { + struct mpi3mr_throttle_group_info *tg; + + tg = *(struct mpi3mr_throttle_group_info **)fwevt->event_data; + dprint_event_bh(mrioc, + "qd reduction event processed for tg_id(%d) reduction_needed(%d)\n", + tg->id, tg->need_qd_reduction); + if (tg->need_qd_reduction) { + mpi3mr_set_qd_for_all_vd_in_tg(mrioc, tg); + tg->need_qd_reduction = 0; + } + break; + } default: break; } @@ -1540,13 +1718,13 @@ static int mpi3mr_create_tgtdev(struct mpi3mr_ioc *mrioc, perst_id = le16_to_cpu(dev_pg0->persistent_id); tgtdev = mpi3mr_get_tgtdev_by_perst_id(mrioc, perst_id); if (tgtdev) { - mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true); mpi3mr_tgtdev_put(tgtdev); } else { tgtdev = mpi3mr_alloc_tgtdev(); if (!tgtdev) return -ENOMEM; - mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0); + mpi3mr_update_tgtdev(mrioc, tgtdev, dev_pg0, true); mpi3mr_tgtdev_add_to_list(mrioc, tgtdev); } @@ -2558,6 +2736,11 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, u32 xfer_count = 0, sense_count = 0, resp_data = 0; u16 dev_handle = 0xFFFF; struct scsi_sense_hdr sshdr; + struct mpi3mr_stgt_priv_data *stgt_priv_data = NULL; + struct mpi3mr_sdev_priv_data *sdev_priv_data = NULL; + u32 ioc_pend_data_len = 0, tg_pend_data_len = 0, data_len_blks = 0; + struct mpi3mr_throttle_group_info *tg = NULL; + u8 throttle_enabled_dev = 0; *reply_dma = 0; reply_desc_type = le16_to_cpu(reply_desc->reply_flags) & @@ -2614,6 +2797,51 @@ void mpi3mr_process_op_reply_desc(struct mpi3mr_ioc *mrioc, goto out; } priv = scsi_cmd_priv(scmd); + + data_len_blks = scsi_bufflen(scmd) >> 9; + sdev_priv_data = scmd->device->hostdata; + if (sdev_priv_data) { + stgt_priv_data = sdev_priv_data->tgt_priv_data; + if (stgt_priv_data) { + tg = stgt_priv_data->throttle_group; + throttle_enabled_dev = + stgt_priv_data->io_throttle_enabled; + } + } + if (unlikely((data_len_blks >= mrioc->io_throttle_data_length) && + throttle_enabled_dev)) { + ioc_pend_data_len = atomic_sub_return(data_len_blks, + &mrioc->pend_large_data_sz); + if (tg) { + tg_pend_data_len = atomic_sub_return(data_len_blks, + &tg->pend_large_data_sz); + if (tg->io_divert && ((ioc_pend_data_len <= + mrioc->io_throttle_low) && + (tg_pend_data_len <= tg->low))) { + tg->io_divert = 0; + mpi3mr_set_io_divert_for_all_vd_in_tg( + mrioc, tg, 0); + } + } else { + if (ioc_pend_data_len <= mrioc->io_throttle_low) + stgt_priv_data->io_divert = 0; + } + } else if (unlikely((stgt_priv_data && stgt_priv_data->io_divert))) { + ioc_pend_data_len = atomic_read(&mrioc->pend_large_data_sz); + if (!tg) { + if (ioc_pend_data_len <= mrioc->io_throttle_low) + stgt_priv_data->io_divert = 0; + + } else if (ioc_pend_data_len <= mrioc->io_throttle_low) { + tg_pend_data_len = atomic_read(&tg->pend_large_data_sz); + if (tg->io_divert && (tg_pend_data_len <= tg->low)) { + tg->io_divert = 0; + mpi3mr_set_io_divert_for_all_vd_in_tg( + mrioc, tg, 0); + } + } + } + if (success_desc) { scmd->result = DID_OK << 16; goto out_success; @@ -3834,6 +4062,11 @@ static int mpi3mr_target_alloc(struct scsi_target *starget) tgt_dev->starget = starget; atomic_set(&scsi_tgt_priv_data->block_io, 0); retval = 0; + scsi_tgt_priv_data->io_throttle_enabled = + tgt_dev->io_throttle_enabled; + if (tgt_dev->dev_type == MPI3_DEVICE_DEVFORM_VD) + scsi_tgt_priv_data->throttle_group = + tgt_dev->dev_spec.vd_inf.tg; } else retval = -ENXIO; spin_unlock_irqrestore(&mrioc->tgtdev_lock, flags); @@ -3989,10 +4222,13 @@ static int mpi3mr_qcmd(struct Scsi_Host *shost, int retval = 0; u16 dev_handle; u16 host_tag; - u32 scsiio_flags = 0; + u32 scsiio_flags = 0, data_len_blks = 0; struct request *rq = scsi_cmd_to_rq(scmd); int iprio_class; u8 is_pcie_dev = 0; + u32 tracked_io_sz = 0; + u32 ioc_pend_data_len = 0, tg_pend_data_len = 0; + struct mpi3mr_throttle_group_info *tg = NULL; if (mrioc->unrecoverable) { scmd->result = DID_ERROR << 16; @@ -4096,11 +4332,50 @@ static int mpi3mr_qcmd(struct Scsi_Host *shost, goto out; } op_req_q = &mrioc->req_qinfo[scmd_priv_data->req_q_idx]; + data_len_blks = scsi_bufflen(scmd) >> 9; + if ((data_len_blks >= mrioc->io_throttle_data_length) && + stgt_priv_data->io_throttle_enabled) { + tracked_io_sz = data_len_blks; + tg = stgt_priv_data->throttle_group; + if (tg) { + ioc_pend_data_len = atomic_add_return(data_len_blks, + &mrioc->pend_large_data_sz); + tg_pend_data_len = atomic_add_return(data_len_blks, + &tg->pend_large_data_sz); + if (!tg->io_divert && ((ioc_pend_data_len >= + mrioc->io_throttle_high) || + (tg_pend_data_len >= tg->high))) { + tg->io_divert = 1; + tg->need_qd_reduction = 1; + mpi3mr_set_io_divert_for_all_vd_in_tg(mrioc, + tg, 1); + mpi3mr_queue_qd_reduction_event(mrioc, tg); + } + } else { + ioc_pend_data_len = atomic_add_return(data_len_blks, + &mrioc->pend_large_data_sz); + if (ioc_pend_data_len >= mrioc->io_throttle_high) + stgt_priv_data->io_divert = 1; + } + } + + if (stgt_priv_data->io_divert) { + scsiio_req->msg_flags |= + MPI3_SCSIIO_MSGFLAGS_DIVERT_TO_FIRMWARE; + scsiio_flags |= MPI3_SCSIIO_FLAGS_DIVERT_REASON_IO_THROTTLING; + } + scsiio_req->flags = cpu_to_le32(scsiio_flags); if (mpi3mr_op_request_post(mrioc, op_req_q, scmd_priv_data->mpi3mr_scsiio_req)) { mpi3mr_clear_scmd_priv(mrioc, scmd); retval = SCSI_MLQUEUE_HOST_BUSY; + if (tracked_io_sz) { + atomic_sub(tracked_io_sz, &mrioc->pend_large_data_sz); + if (tg) + atomic_sub(tracked_io_sz, + &tg->pend_large_data_sz); + } goto out; } @@ -4313,6 +4588,8 @@ mpi3mr_probe(struct pci_dev *pdev, const struct pci_device_id *id) shost->max_channel = 0; shost->max_id = 0xFFFFFFFF; + shost->host_tagset = 1; + if (prot_mask >= 0) scsi_host_set_prot(shost, prot_mask); else { diff --git a/drivers/scsi/mpt3sas/mpt3sas_base.c b/drivers/scsi/mpt3sas/mpt3sas_base.c index 9a1ae52bb621..565339a0811d 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_base.c +++ b/drivers/scsi/mpt3sas/mpt3sas_base.c @@ -873,7 +873,7 @@ mpt3sas_base_stop_watchdog(struct MPT3SAS_ADAPTER *ioc) * @fault_code: fault code */ void -mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc , u16 fault_code) +mpt3sas_base_fault_info(struct MPT3SAS_ADAPTER *ioc, u16 fault_code) { ioc_err(ioc, "fault_state(0x%04x)!\n", fault_code); } @@ -1057,7 +1057,7 @@ _base_sas_ioc_info(struct MPT3SAS_ADAPTER *ioc, MPI2DefaultReply_t *mpi_reply, desc = "config no defaults"; break; case MPI2_IOCSTATUS_CONFIG_CANT_COMMIT: - desc = "config cant commit"; + desc = "config can't commit"; break; /**************************************************************************** @@ -1321,7 +1321,7 @@ _base_display_event_data(struct MPT3SAS_ADAPTER *ioc, * @log_info: log info */ static void -_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc , u32 log_info) +_base_sas_log_info(struct MPT3SAS_ADAPTER *ioc, u32 log_info) { union loginfo_type { u32 loginfo; @@ -1393,7 +1393,7 @@ _base_display_reply_info(struct MPT3SAS_ADAPTER *ioc, u16 smid, u8 msix_index, if ((ioc_status & MPI2_IOCSTATUS_MASK) && (ioc->logging_level & MPT_DEBUG_REPLY)) { - _base_sas_ioc_info(ioc , mpi_reply, + _base_sas_ioc_info(ioc, mpi_reply, mpt3sas_base_get_msg_frame(ioc, smid)); } diff --git a/drivers/scsi/mpt3sas/mpt3sas_scsih.c b/drivers/scsi/mpt3sas/mpt3sas_scsih.c index 5e8887fa02c8..def37a7e5980 100644 --- a/drivers/scsi/mpt3sas/mpt3sas_scsih.c +++ b/drivers/scsi/mpt3sas/mpt3sas_scsih.c @@ -5294,7 +5294,7 @@ _scsih_normalize_sense(char *sense_buffer, struct sense_info *data) } /** - * _scsih_scsi_ioc_info - translated non-succesfull SCSI_IO request + * _scsih_scsi_ioc_info - translated non-successful SCSI_IO request * @ioc: per adapter object * @scmd: pointer to scsi command object * @mpi_reply: reply mf payload returned from firmware @@ -12410,7 +12410,6 @@ scsih_suspend(struct device *dev) return rc; mpt3sas_base_stop_watchdog(ioc); - flush_scheduled_work(); scsi_block_requests(shost); _scsih_nvme_shutdown(ioc); ioc_info(ioc, "pdev=0x%p, slot=%s, entering operating state\n", diff --git a/drivers/scsi/pm8001/pm8001_hwi.c b/drivers/scsi/pm8001/pm8001_hwi.c index 991eb01bb1e0..4acaff479916 100644 --- a/drivers/scsi/pm8001/pm8001_hwi.c +++ b/drivers/scsi/pm8001/pm8001_hwi.c @@ -699,6 +699,10 @@ static int pm8001_chip_init(struct pm8001_hba_info *pm8001_ha) return 0; } +static void pm8001_chip_post_init(struct pm8001_hba_info *pm8001_ha) +{ +} + static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha) { u32 max_wait_count; @@ -4934,6 +4938,7 @@ pm8001_chip_sas_re_initialization(struct pm8001_hba_info *pm8001_ha) const struct pm8001_dispatch pm8001_8001_dispatch = { .name = "pmc8001", .chip_init = pm8001_chip_init, + .chip_post_init = pm8001_chip_post_init, .chip_soft_rst = pm8001_chip_soft_rst, .chip_rst = pm8001_hw_chip_rst, .chip_iounmap = pm8001_chip_iounmap, diff --git a/drivers/scsi/pm8001/pm8001_init.c b/drivers/scsi/pm8001/pm8001_init.c index 01f2f41928eb..a0028e130a7e 100644 --- a/drivers/scsi/pm8001/pm8001_init.c +++ b/drivers/scsi/pm8001/pm8001_init.c @@ -56,7 +56,7 @@ MODULE_PARM_DESC(link_rate, "Enable link rate.\n" " 8: Link rate 12.0G\n"); static struct scsi_transport_template *pm8001_stt; -static int pm8001_init_ccb_tag(struct pm8001_hba_info *, struct Scsi_Host *, struct pci_dev *); +static int pm8001_init_ccb_tag(struct pm8001_hba_info *); /* * chip info structure to identify chip key functionality as @@ -81,6 +81,18 @@ LIST_HEAD(hba_list); struct workqueue_struct *pm8001_wq; +static int pm8001_map_queues(struct Scsi_Host *shost) +{ + struct sas_ha_struct *sha = SHOST_TO_SAS_HA(shost); + struct pm8001_hba_info *pm8001_ha = sha->lldd_ha; + struct blk_mq_queue_map *qmap = &shost->tag_set.map[HCTX_TYPE_DEFAULT]; + + if (pm8001_ha->number_of_intr > 1) + blk_mq_pci_map_queues(qmap, pm8001_ha->pdev, 1); + + return blk_mq_map_queues(qmap); +} + /* * The main structure which LLDD must register for scsi core. */ @@ -109,6 +121,8 @@ static struct scsi_host_template pm8001_sht = { #endif .shost_groups = pm8001_host_groups, .track_queue_depth = 1, + .cmd_per_lun = 32, + .map_queues = pm8001_map_queues, }; /* @@ -607,12 +621,8 @@ static int pm8001_prep_sas_ha_init(struct Scsi_Host *shost, shost->transportt = pm8001_stt; shost->max_id = PM8001_MAX_DEVICES; - shost->max_lun = 8; - shost->max_channel = 0; shost->unique_id = pm8001_id; shost->max_cmd_len = 16; - shost->can_queue = PM8001_CAN_QUEUE; - shost->cmd_per_lun = 32; return 0; exit_free1: kfree(arr_port); @@ -933,31 +943,35 @@ static int pm8001_configure_phy_settings(struct pm8001_hba_info *pm8001_ha) */ static u32 pm8001_setup_msix(struct pm8001_hba_info *pm8001_ha) { - u32 number_of_intr; - int rc, cpu_online_count; unsigned int allocated_irq_vectors; + int rc; /* SPCv controllers supports 64 msi-x */ if (pm8001_ha->chip_id == chip_8001) { - number_of_intr = 1; + rc = pci_alloc_irq_vectors(pm8001_ha->pdev, 1, 1, + PCI_IRQ_MSIX); } else { - number_of_intr = PM8001_MAX_MSIX_VEC; + /* + * Queue index #0 is used always for housekeeping, so don't + * include in the affinity spreading. + */ + struct irq_affinity desc = { + .pre_vectors = 1, + }; + rc = pci_alloc_irq_vectors_affinity( + pm8001_ha->pdev, 2, PM8001_MAX_MSIX_VEC, + PCI_IRQ_MSIX | PCI_IRQ_AFFINITY, &desc); } - cpu_online_count = num_online_cpus(); - number_of_intr = min_t(int, cpu_online_count, number_of_intr); - rc = pci_alloc_irq_vectors(pm8001_ha->pdev, number_of_intr, - number_of_intr, PCI_IRQ_MSIX); allocated_irq_vectors = rc; if (rc < 0) return rc; /* Assigns the number of interrupts */ - number_of_intr = min_t(int, allocated_irq_vectors, number_of_intr); - pm8001_ha->number_of_intr = number_of_intr; + pm8001_ha->number_of_intr = allocated_irq_vectors; /* Maximum queue number updating in HBA structure */ - pm8001_ha->max_q_num = number_of_intr; + pm8001_ha->max_q_num = allocated_irq_vectors; pm8001_dbg(pm8001_ha, INIT, "pci_alloc_irq_vectors request ret:%d no of intr %d\n", @@ -1124,10 +1138,23 @@ static int pm8001_pci_probe(struct pci_dev *pdev, goto err_out_ha_free; } - rc = pm8001_init_ccb_tag(pm8001_ha, shost, pdev); + rc = pm8001_init_ccb_tag(pm8001_ha); if (rc) goto err_out_enable; + + PM8001_CHIP_DISP->chip_post_init(pm8001_ha); + + if (pm8001_ha->number_of_intr > 1) { + shost->nr_hw_queues = pm8001_ha->number_of_intr - 1; + /* + * For now, ensure we're not sent too many commands by setting + * host_tagset. This is also required if we start using request + * tag. + */ + shost->host_tagset = 1; + } + rc = scsi_add_host(shost, &pdev->dev); if (rc) goto err_out_ha_free; @@ -1177,16 +1204,14 @@ err_out_enable: /** * pm8001_init_ccb_tag - allocate memory to CCB and tag. * @pm8001_ha: our hba card information. - * @shost: scsi host which has been allocated outside. - * @pdev: pci device. */ -static int -pm8001_init_ccb_tag(struct pm8001_hba_info *pm8001_ha, struct Scsi_Host *shost, - struct pci_dev *pdev) +static int pm8001_init_ccb_tag(struct pm8001_hba_info *pm8001_ha) { - int i = 0; + struct Scsi_Host *shost = pm8001_ha->shost; + struct device *dev = pm8001_ha->dev; u32 max_out_io, ccb_count; u32 can_queue; + int i; max_out_io = pm8001_ha->main_cfg_tbl.pm80xx_tbl.max_out_io; ccb_count = min_t(int, PM8001_MAX_CCB, max_out_io); @@ -1209,7 +1234,7 @@ pm8001_init_ccb_tag(struct pm8001_hba_info *pm8001_ha, struct Scsi_Host *shost, goto err_out_noccb; } for (i = 0; i < ccb_count; i++) { - pm8001_ha->ccb_info[i].buf_prd = dma_alloc_coherent(&pdev->dev, + pm8001_ha->ccb_info[i].buf_prd = dma_alloc_coherent(dev, sizeof(struct pm8001_prd) * PM8001_MAX_DMA_SG, &pm8001_ha->ccb_info[i].ccb_dma_handle, GFP_KERNEL); diff --git a/drivers/scsi/pm8001/pm8001_sas.c b/drivers/scsi/pm8001/pm8001_sas.c index 3a863d776724..8e3f2f9ddaac 100644 --- a/drivers/scsi/pm8001/pm8001_sas.c +++ b/drivers/scsi/pm8001/pm8001_sas.c @@ -66,7 +66,11 @@ static int pm8001_find_tag(struct sas_task *task, u32 *tag) void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag) { void *bitmap = pm8001_ha->tags; - clear_bit(tag, bitmap); + unsigned long flags; + + spin_lock_irqsave(&pm8001_ha->bitmap_lock, flags); + __clear_bit(tag, bitmap); + spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags); } /** @@ -76,9 +80,9 @@ void pm8001_tag_free(struct pm8001_hba_info *pm8001_ha, u32 tag) */ int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out) { - unsigned int tag; void *bitmap = pm8001_ha->tags; unsigned long flags; + unsigned int tag; spin_lock_irqsave(&pm8001_ha->bitmap_lock, flags); tag = find_first_zero_bit(bitmap, pm8001_ha->tags_num); @@ -86,7 +90,7 @@ int pm8001_tag_alloc(struct pm8001_hba_info *pm8001_ha, u32 *tag_out) spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags); return -SAS_QUEUE_FULL; } - set_bit(tag, bitmap); + __set_bit(tag, bitmap); spin_unlock_irqrestore(&pm8001_ha->bitmap_lock, flags); *tag_out = tag; return 0; diff --git a/drivers/scsi/pm8001/pm8001_sas.h b/drivers/scsi/pm8001/pm8001_sas.h index 060ab680a7ed..c5e3f380a01c 100644 --- a/drivers/scsi/pm8001/pm8001_sas.h +++ b/drivers/scsi/pm8001/pm8001_sas.h @@ -55,6 +55,8 @@ #include #include #include +#include +#include #include "pm8001_defs.h" #define DRV_NAME "pm80xx" @@ -172,6 +174,7 @@ struct forensic_data { struct pm8001_dispatch { char *name; int (*chip_init)(struct pm8001_hba_info *pm8001_ha); + void (*chip_post_init)(struct pm8001_hba_info *pm8001_ha); int (*chip_soft_rst)(struct pm8001_hba_info *pm8001_ha); void (*chip_rst)(struct pm8001_hba_info *pm8001_ha); int (*chip_ioremap)(struct pm8001_hba_info *pm8001_ha); diff --git a/drivers/scsi/pm8001/pm80xx_hwi.c b/drivers/scsi/pm8001/pm80xx_hwi.c index 303cd05fec50..f8b8624458f7 100644 --- a/drivers/scsi/pm8001/pm80xx_hwi.c +++ b/drivers/scsi/pm8001/pm80xx_hwi.c @@ -1469,11 +1469,18 @@ static int pm80xx_chip_init(struct pm8001_hba_info *pm8001_ha) } else return -EBUSY; + return 0; +} + +static void pm80xx_chip_post_init(struct pm8001_hba_info *pm8001_ha) +{ /* send SAS protocol timer configuration page to FW */ - ret = pm80xx_set_sas_protocol_timer_config(pm8001_ha); + pm80xx_set_sas_protocol_timer_config(pm8001_ha); /* Check for encryption */ if (pm8001_ha->chip->encrypt) { + int ret; + pm8001_dbg(pm8001_ha, INIT, "Checking for encryption\n"); ret = pm80xx_get_encrypt_info(pm8001_ha); if (ret == -1) { @@ -1485,7 +1492,6 @@ static int pm80xx_chip_init(struct pm8001_hba_info *pm8001_ha) } } } - return 0; } static int mpi_uninit_check(struct pm8001_hba_info *pm8001_ha) @@ -4349,6 +4355,29 @@ static int check_enc_sat_cmd(struct sas_task *task) return ret; } +static u32 pm80xx_chip_get_q_index(struct sas_task *task) +{ + struct scsi_cmnd *scmd = NULL; + u32 blk_tag; + + if (task->uldd_task) { + struct ata_queued_cmd *qc; + + if (dev_is_sata(task->dev)) { + qc = task->uldd_task; + scmd = qc->scsicmd; + } else { + scmd = task->uldd_task; + } + } + + if (!scmd) + return 0; + + blk_tag = blk_mq_unique_tag(scsi_cmd_to_rq(scmd)); + return blk_mq_unique_tag_to_hwq(blk_tag); +} + /** * pm80xx_chip_ssp_io_req - send an SSP task to FW * @pm8001_ha: our hba card information. @@ -4364,7 +4393,7 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, u32 tag = ccb->ccb_tag; u64 phys_addr, end_addr; u32 end_addr_high, end_addr_low; - u32 q_index, cpu_id; + u32 q_index; u32 opc = OPC_INB_SSPINIIOSTART; memset(&ssp_cmd, 0, sizeof(ssp_cmd)); @@ -4385,8 +4414,7 @@ static int pm80xx_chip_ssp_io_req(struct pm8001_hba_info *pm8001_ha, ssp_cmd.ssp_iu.efb_prio_attr |= (task->ssp_task.task_attr & 7); memcpy(ssp_cmd.ssp_iu.cdb, task->ssp_task.cmd->cmnd, task->ssp_task.cmd->cmd_len); - cpu_id = smp_processor_id(); - q_index = (u32) (cpu_id) % (pm8001_ha->max_q_num); + q_index = pm80xx_chip_get_q_index(task); /* Check if encryption is set */ if (pm8001_ha->chip->encrypt && @@ -4515,8 +4543,7 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, struct domain_device *dev = task->dev; struct pm8001_device *pm8001_ha_dev = dev->lldd_dev; struct ata_queued_cmd *qc = task->uldd_task; - u32 tag = ccb->ccb_tag; - u32 q_index, cpu_id; + u32 tag = ccb->ccb_tag, q_index; struct sata_start_req sata_cmd; u32 hdr_tag, ncg_tag = 0; u64 phys_addr, end_addr; @@ -4526,8 +4553,8 @@ static int pm80xx_chip_sata_req(struct pm8001_hba_info *pm8001_ha, unsigned long flags; u32 opc = OPC_INB_SATA_HOST_OPSTART; memset(&sata_cmd, 0, sizeof(sata_cmd)); - cpu_id = smp_processor_id(); - q_index = (u32) (cpu_id) % (pm8001_ha->max_q_num); + + q_index = pm80xx_chip_get_q_index(task); if (task->data_dir == DMA_NONE && !task->ata_task.use_ncq) { ATAP = 0x04; /* no data*/ @@ -5011,6 +5038,7 @@ void pm8001_set_phy_profile_single(struct pm8001_hba_info *pm8001_ha, const struct pm8001_dispatch pm8001_80xx_dispatch = { .name = "pmc80xx", .chip_init = pm80xx_chip_init, + .chip_post_init = pm80xx_chip_post_init, .chip_soft_rst = pm80xx_chip_soft_rst, .chip_rst = pm80xx_hw_chip_rst, .chip_iounmap = pm8001_chip_iounmap, diff --git a/drivers/scsi/qedi/qedi_main.c b/drivers/scsi/qedi/qedi_main.c index 83ffba7f51da..cecfb2cb4c7b 100644 --- a/drivers/scsi/qedi/qedi_main.c +++ b/drivers/scsi/qedi/qedi_main.c @@ -2414,9 +2414,12 @@ static void __qedi_remove(struct pci_dev *pdev, int mode) int rval; u16 retry = 10; - if (mode == QEDI_MODE_NORMAL || mode == QEDI_MODE_SHUTDOWN) { - iscsi_host_remove(qedi->shost); + if (mode == QEDI_MODE_NORMAL) + iscsi_host_remove(qedi->shost, false); + else if (mode == QEDI_MODE_SHUTDOWN) + iscsi_host_remove(qedi->shost, true); + if (mode == QEDI_MODE_NORMAL || mode == QEDI_MODE_SHUTDOWN) { if (qedi->tmf_thread) { destroy_workqueue(qedi->tmf_thread); qedi->tmf_thread = NULL; @@ -2491,7 +2494,7 @@ static void qedi_board_disable_work(struct work_struct *work) if (test_and_set_bit(QEDI_IN_SHUTDOWN, &qedi->flags)) return; - __qedi_remove(qedi->pdev, QEDI_MODE_SHUTDOWN); + __qedi_remove(qedi->pdev, QEDI_MODE_NORMAL); } static void qedi_shutdown(struct pci_dev *pdev) @@ -2791,7 +2794,7 @@ remove_host: #ifdef CONFIG_DEBUG_FS qedi_dbg_host_exit(&qedi->dbg_ctx); #endif - iscsi_host_remove(qedi->shost); + iscsi_host_remove(qedi->shost, false); stop_iscsi_func: qedi_ops->stop(qedi->cdev); stop_slowpath: diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 3b3e4234f37a..fa1fcbfb946f 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -2476,7 +2476,6 @@ static DEVICE_ATTR(port_speed, 0644, qla2x00_port_speed_show, qla2x00_port_speed_store); static DEVICE_ATTR(port_no, 0444, qla2x00_port_no_show, NULL); static DEVICE_ATTR(fw_attr, 0444, qla2x00_fw_attr_show, NULL); -static DEVICE_ATTR_RO(edif_doorbell); static struct attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version.attr, @@ -2521,7 +2520,6 @@ static struct attribute *qla2x00_host_attrs[] = { &dev_attr_port_no.attr, &dev_attr_fw_attr.attr, &dev_attr_dport_diagnostics.attr, - &dev_attr_edif_doorbell.attr, &dev_attr_mpi_pause.attr, &dev_attr_qlini_mode.attr, &dev_attr_ql2xiniexchg.attr, @@ -2716,17 +2714,27 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) if (!fcport) return; - /* Now that the rport has been deleted, set the fcport state to - FCS_DEVICE_DEAD */ - qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD); + ql_dbg(ql_dbg_async, fcport->vha, 0x5101, + DBG_FCPORT_PRFMT(fcport, "dev_loss_tmo expiry, rport_state=%d", + rport->port_state)); + + /* + * Now that the rport has been deleted, set the fcport state to + * FCS_DEVICE_DEAD, if the fcport is still lost. + */ + if (fcport->scan_state != QLA_FCPORT_FOUND) + qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD); /* * Transport has effectively 'deleted' the rport, clear * all local references. */ spin_lock_irqsave(host->host_lock, flags); - fcport->rport = fcport->drport = NULL; - *((fc_port_t **)rport->dd_data) = NULL; + /* Confirm port has not reappeared before clearing pointers. */ + if (rport->port_state != FC_PORTSTATE_ONLINE) { + fcport->rport = fcport->drport = NULL; + *((fc_port_t **)rport->dd_data) = NULL; + } spin_unlock_irqrestore(host->host_lock, flags); if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags)) @@ -2759,9 +2767,12 @@ qla2x00_terminate_rport_io(struct fc_rport *rport) /* * At this point all fcport's software-states are cleared. Perform any * final cleanup of firmware resources (PCBs and XCBs). + * + * Attempt to cleanup only lost devices. */ if (fcport->loop_id != FC_NO_LOOP_ID) { - if (IS_FWI2_CAPABLE(fcport->vha->hw)) { + if (IS_FWI2_CAPABLE(fcport->vha->hw) && + fcport->scan_state != QLA_FCPORT_FOUND) { if (fcport->loop_id != FC_NO_LOOP_ID) fcport->logout_on_delete = 1; @@ -2771,7 +2782,7 @@ qla2x00_terminate_rport_io(struct fc_rport *rport) __LINE__); qlt_schedule_sess_for_deletion(fcport); } - } else { + } else if (!IS_FWI2_CAPABLE(fcport->vha->hw)) { qla2x00_port_logout(fcport->vha, fcport); } } diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index c2f00f076f79..5db9bf69dcff 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -2424,6 +2424,89 @@ qla2x00_do_dport_diagnostics(struct bsg_job *bsg_job) return 0; } +static int +qla2x00_do_dport_diagnostics_v2(struct bsg_job *bsg_job) +{ + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + struct Scsi_Host *host = fc_bsg_to_shost(bsg_job); + scsi_qla_host_t *vha = shost_priv(host); + int rval; + struct qla_dport_diag_v2 *dd; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + uint16_t options; + + if (!IS_DPORT_CAPABLE(vha->hw)) + return -EPERM; + + dd = kzalloc(sizeof(*dd), GFP_KERNEL); + if (!dd) + return -ENOMEM; + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, dd, sizeof(*dd)); + + options = dd->options; + + /* Check dport Test in progress */ + if (options == QLA_GET_DPORT_RESULT_V2 && + vha->dport_status & DPORT_DIAG_IN_PROGRESS) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_DPORT_DIAG_IN_PROCESS; + goto dportcomplete; + } + + /* Check chip reset in progress and start/restart requests arrive */ + if (vha->dport_status & DPORT_DIAG_CHIP_RESET_IN_PROGRESS && + (options == QLA_START_DPORT_TEST_V2 || + options == QLA_RESTART_DPORT_TEST_V2)) { + vha->dport_status &= ~DPORT_DIAG_CHIP_RESET_IN_PROGRESS; + } + + /* Check chip reset in progress and get result request arrive */ + if (vha->dport_status & DPORT_DIAG_CHIP_RESET_IN_PROGRESS && + options == QLA_GET_DPORT_RESULT_V2) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_DPORT_DIAG_NOT_RUNNING; + goto dportcomplete; + } + + rval = qla26xx_dport_diagnostics_v2(vha, dd, mcp); + + if (rval == QLA_SUCCESS) { + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_OK; + if (options == QLA_START_DPORT_TEST_V2 || + options == QLA_RESTART_DPORT_TEST_V2) { + dd->mbx1 = mcp->mb[0]; + dd->mbx2 = mcp->mb[1]; + vha->dport_status |= DPORT_DIAG_IN_PROGRESS; + } else if (options == QLA_GET_DPORT_RESULT_V2) { + dd->mbx1 = le16_to_cpu(vha->dport_data[1]); + dd->mbx2 = le16_to_cpu(vha->dport_data[2]); + } + } else { + dd->mbx1 = mcp->mb[0]; + dd->mbx2 = mcp->mb[1]; + bsg_reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_DPORT_DIAG_ERR; + } + +dportcomplete: + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, dd, sizeof(*dd)); + + bsg_reply->reply_payload_rcv_len = sizeof(*dd); + bsg_job->reply_len = sizeof(*bsg_reply); + bsg_reply->result = DID_OK << 16; + bsg_job_done(bsg_job, bsg_reply->result, + bsg_reply->reply_payload_rcv_len); + + kfree(dd); + + return 0; +} + static int qla2x00_get_flash_image_status(struct bsg_job *bsg_job) { @@ -2860,6 +2943,9 @@ qla2x00_process_vendor_specific(struct scsi_qla_host *vha, struct bsg_job *bsg_j case QL_VND_DPORT_DIAGNOSTICS: return qla2x00_do_dport_diagnostics(bsg_job); + case QL_VND_DPORT_DIAGNOSTICS_V2: + return qla2x00_do_dport_diagnostics_v2(bsg_job); + case QL_VND_EDIF_MGMT: return qla_edif_app_mgmt(bsg_job); @@ -2975,6 +3061,13 @@ qla24xx_bsg_timeout(struct bsg_job *bsg_job) ql_log(ql_log_info, vha, 0x708b, "%s CMD timeout. bsg ptr %p.\n", __func__, bsg_job); + + if (qla2x00_isp_reg_stat(ha)) { + ql_log(ql_log_info, vha, 0x9007, + "PCI/Register disconnect.\n"); + qla_pci_set_eeh_busy(vha); + } + /* find the bsg job from the active list of commands */ spin_lock_irqsave(&ha->hardware_lock, flags); for (que = 0; que < ha->max_req_queues; que++) { @@ -2992,7 +3085,8 @@ qla24xx_bsg_timeout(struct bsg_job *bsg_job) sp->u.bsg_job == bsg_job) { req->outstanding_cmds[cnt] = NULL; spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (ha->isp_ops->abort_command(sp)) { + + if (!ha->flags.eeh_busy && ha->isp_ops->abort_command(sp)) { ql_log(ql_log_warn, vha, 0x7089, "mbx abort_command failed.\n"); bsg_reply->result = -EIO; diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h index 6d2b0a7436c1..bb64b9c5a74b 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.h +++ b/drivers/scsi/qla2xxx/qla_bsg.h @@ -37,6 +37,7 @@ #define QL_VND_GET_TGT_STATS 0x25 #define QL_VND_MANAGE_HOST_PORT 0x26 #define QL_VND_MBX_PASSTHRU 0x2B +#define QL_VND_DPORT_DIAGNOSTICS_V2 0x2C /* BSG Vendor specific subcode returns */ #define EXT_STATUS_OK 0 @@ -60,6 +61,9 @@ #define EXT_STATUS_TIMEOUT 30 #define EXT_STATUS_THREAD_FAILED 31 #define EXT_STATUS_DATA_CMP_FAILED 32 +#define EXT_STATUS_DPORT_DIAG_ERR 40 +#define EXT_STATUS_DPORT_DIAG_IN_PROCESS 41 +#define EXT_STATUS_DPORT_DIAG_NOT_RUNNING 42 /* BSG definations for interpreting CommandSent field */ #define INT_DEF_LB_LOOPBACK_CMD 0 @@ -288,6 +292,17 @@ struct qla_dport_diag { uint8_t unused[62]; } __packed; +#define QLA_GET_DPORT_RESULT_V2 0 /* Get Result */ +#define QLA_RESTART_DPORT_TEST_V2 1 /* Restart test */ +#define QLA_START_DPORT_TEST_V2 2 /* Start test */ +struct qla_dport_diag_v2 { + uint16_t options; + uint16_t mbx1; + uint16_t mbx2; + uint8_t unused[58]; + uint8_t buf[1024]; /* Test Result */ +} __packed; + /* D_Port options */ #define QLA_DPORT_RESULT 0x0 #define QLA_DPORT_START 0x2 diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index f1f6c740bdcd..feeb1666227f 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -383,5 +383,5 @@ ql_mask_match(uint level) if (ql2xextended_error_logging == 1) ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK; - return (level & ql2xextended_error_logging) == level; + return level && ((level & ql2xextended_error_logging) == level); } diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index e8f69c486be1..3ec6a200942e 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -78,7 +78,7 @@ typedef union { #include "qla_nvme.h" #define QLA2XXX_DRIVER_NAME "qla2xxx" #define QLA2XXX_APIDEV "ql2xapidev" -#define QLA2XXX_MANUFACTURER "QLogic Corporation" +#define QLA2XXX_MANUFACTURER "Marvell Semiconductor, Inc." /* * We have MAILBOX_REGISTER_COUNT sized arrays in a few places, @@ -1173,6 +1173,12 @@ static inline bool qla2xxx_is_valid_mbs(unsigned int mbs) /* ISP mailbox loopback echo diagnostic error code */ #define MBS_LB_RESET 0x17 + +/* AEN mailbox Port Diagnostics test */ +#define AEN_START_DIAG_TEST 0x0 /* start the diagnostics */ +#define AEN_DONE_DIAG_TEST_WITH_NOERR 0x1 /* Done with no errors */ +#define AEN_DONE_DIAG_TEST_WITH_ERR 0x2 /* Done with error.*/ + /* * Firmware options 1, 2, 3. */ @@ -2158,6 +2164,11 @@ typedef struct { #define CS_IOCB_ERROR 0x31 /* Generic error for IOCB request failure */ #define CS_REJECT_RECEIVED 0x4E /* Reject received */ +#define CS_EDIF_AUTH_ERROR 0x63 /* decrypt error */ +#define CS_EDIF_PAD_LEN_ERROR 0x65 /* pad > frame size, not 4byte align */ +#define CS_EDIF_INV_REQ 0x66 /* invalid request */ +#define CS_EDIF_SPI_ERROR 0x67 /* rx frame unable to locate sa */ +#define CS_EDIF_HDR_ERROR 0x69 /* data frame != expected len */ #define CS_BAD_PAYLOAD 0x80 /* Driver defined */ #define CS_UNKNOWN 0x81 /* Driver defined */ #define CS_RETRY 0x82 /* Driver defined */ @@ -2626,7 +2637,6 @@ typedef struct fc_port { struct { uint32_t enable:1; /* device is edif enabled/req'd */ uint32_t app_stop:2; - uint32_t app_started:1; uint32_t aes_gmac:1; uint32_t app_sess_online:1; uint32_t tx_sa_set:1; @@ -2637,6 +2647,7 @@ typedef struct fc_port { uint32_t rx_rekey_cnt; uint64_t tx_bytes; uint64_t rx_bytes; + uint8_t sess_down_acked; uint8_t auth_state; uint16_t authok:1; uint16_t rekey_cnt; @@ -3204,6 +3215,8 @@ struct ct_sns_rsp { #define GFF_NVME_OFFSET 23 /* type = 28h */ struct { uint8_t fc4_features[128]; +#define FC4_FF_TARGET BIT_0 +#define FC4_FF_INITIATOR BIT_1 } gff_id; struct { uint8_t reserved; @@ -3975,6 +3988,7 @@ struct qla_hw_data { /* SRB cache. */ #define SRB_MIN_REQ 128 mempool_t *srb_mempool; + u8 port_name[WWN_SIZE]; volatile struct { uint32_t mbox_int :1; @@ -4040,6 +4054,9 @@ struct qla_hw_data { uint32_t n2n_fw_acc_sec:1; uint32_t plogi_template_valid:1; uint32_t port_isolated:1; + uint32_t eeh_flush:2; +#define EEH_FLUSH_RDY 1 +#define EEH_FLUSH_DONE 2 } flags; uint16_t max_exchg; @@ -4074,6 +4091,7 @@ struct qla_hw_data { uint32_t rsp_que_len; uint32_t req_que_off; uint32_t rsp_que_off; + unsigned long eeh_jif; /* Multi queue data structs */ device_reg_t *mqiobase; @@ -4256,8 +4274,8 @@ struct qla_hw_data { #define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001) #define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS) #define IS_CT6_SUPPORTED(ha) ((ha)->device_type & DT_CT6_SUPPORTED) -#define IS_MQUE_CAPABLE(ha) ((ha)->mqenable || IS_QLA83XX(ha) || \ - IS_QLA27XX(ha) || IS_QLA28XX(ha)) +#define IS_MQUE_CAPABLE(ha) (IS_QLA83XX(ha) || IS_QLA27XX(ha) || \ + IS_QLA28XX(ha)) #define IS_BIDI_CAPABLE(ha) \ (IS_QLA25XX(ha) || IS_QLA2031(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) /* Bit 21 of fw_attributes decides the MCTP capabilities */ @@ -5012,6 +5030,10 @@ typedef struct scsi_qla_host { u64 short_link_down_cnt; struct edif_dbell e_dbell; struct pur_core pur_cinfo; + +#define DPORT_DIAG_IN_PROGRESS BIT_0 +#define DPORT_DIAG_CHIP_RESET_IN_PROGRESS BIT_1 + uint16_t dport_status; } scsi_qla_host_t; struct qla27xx_image_status { @@ -5443,4 +5465,10 @@ struct ql_vnd_tgt_stats_resp { #define IS_SESSION_DELETED(_fcport) (_fcport->disc_state == DSC_DELETE_PEND || \ _fcport->disc_state == DSC_DELETED) +#define DBG_FCPORT_PRFMT(_fp, _fmt, _args...) \ + "%s: %8phC: " _fmt " (state=%d disc_state=%d scan_state=%d loopid=0x%x deleted=%d flags=0x%x)\n", \ + __func__, _fp->port_name, ##_args, atomic_read(&_fp->state), \ + _fp->disc_state, _fp->scan_state, _fp->loop_id, _fp->deleted, \ + _fp->flags + #endif diff --git a/drivers/scsi/qla2xxx/qla_edif.c b/drivers/scsi/qla2xxx/qla_edif.c index cb8145a9ac09..400a8b6f3982 100644 --- a/drivers/scsi/qla2xxx/qla_edif.c +++ b/drivers/scsi/qla2xxx/qla_edif.c @@ -52,6 +52,31 @@ const char *sc_to_str(uint16_t cmd) return "unknown"; } +static struct edb_node *qla_edb_getnext(scsi_qla_host_t *vha) +{ + unsigned long flags; + struct edb_node *edbnode = NULL; + + spin_lock_irqsave(&vha->e_dbell.db_lock, flags); + + /* db nodes are fifo - no qualifications done */ + if (!list_empty(&vha->e_dbell.head)) { + edbnode = list_first_entry(&vha->e_dbell.head, + struct edb_node, list); + list_del_init(&edbnode->list); + } + + spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); + + return edbnode; +} + +static void qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node) +{ + list_del_init(&node->list); + kfree(node); +} + static struct edif_list_entry *qla_edif_list_find_sa_index(fc_port_t *fcport, uint16_t handle) { @@ -257,14 +282,8 @@ qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id) f = NULL; list_for_each_entry_safe(f, tf, &vha->vp_fcports, list) { - if ((f->flags & FCF_FCSP_DEVICE)) { - ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x2058, - "Found secure fcport - nn %8phN pn %8phN portid=0x%x, 0x%x.\n", - f->node_name, f->port_name, - f->d_id.b24, id->b24); - if (f->d_id.b24 == id->b24) - return f; - } + if (f->d_id.b24 == id->b24) + return f; } return NULL; } @@ -280,14 +299,19 @@ qla_edif_app_check(scsi_qla_host_t *vha, struct app_id appid) { /* check that the app is allow/known to the driver */ - if (appid.app_vid == EDIF_APP_ID) { - ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x911d, "%s app id ok\n", __func__); - return true; + if (appid.app_vid != EDIF_APP_ID) { + ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app id not ok (%x)", + __func__, appid.app_vid); + return false; } - ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app id not ok (%x)", - __func__, appid.app_vid); - return false; + if (appid.version != EDIF_VERSION1) { + ql_dbg(ql_dbg_edif, vha, 0x911d, "%s app version is not ok (%x)", + __func__, appid.version); + return false; + } + + return true; } static void @@ -486,16 +510,35 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) /* mark doorbell as active since an app is now present */ vha->e_dbell.db_flags |= EDB_ACTIVE; } else { - ql_dbg(ql_dbg_edif, vha, 0x911e, "%s doorbell already active\n", - __func__); + goto out; } if (N2N_TOPO(vha->hw)) { - if (vha->hw->flags.n2n_fw_acc_sec) - set_bit(N2N_LINK_RESET, &vha->dpc_flags); - else + list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) + fcport->n2n_link_reset_cnt = 0; + + if (vha->hw->flags.n2n_fw_acc_sec) { + list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) + qla_edif_sa_ctl_init(vha, fcport); + + /* + * While authentication app was not running, remote device + * could still try to login with this local port. Let's + * clear the state and try again. + */ + qla2x00_wait_for_sess_deletion(vha); + + /* bounce the link to get the other guy to relogin */ + if (!vha->hw->flags.n2n_bigger) { + set_bit(N2N_LINK_RESET, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + } else { + qla2x00_wait_for_hba_online(vha); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_hba_online(vha); + } } else { list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { ql_dbg(ql_dbg_edif, vha, 0x2058, @@ -517,19 +560,31 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) if (atomic_read(&vha->loop_state) == LOOP_DOWN) break; - fcport->edif.app_started = 1; fcport->login_retry = vha->hw->login_retry_count; - /* no activity */ fcport->edif.app_stop = 0; + fcport->edif.app_sess_online = 0; + + if (fcport->scan_state != QLA_FCPORT_FOUND) + continue; + + if (fcport->port_type == FCT_UNKNOWN && + !fcport->fc4_features) + rval = qla24xx_async_gffid(vha, fcport, true); + + if (!rval && !(fcport->fc4_features & FC4_FF_TARGET || + fcport->port_type & (FCT_TARGET|FCT_NVME_TARGET))) + continue; + + rval = 0; ql_dbg(ql_dbg_edif, vha, 0x911e, "%s wwpn %8phC calling qla_edif_reset_auth_wait\n", __func__, fcport->port_name); - fcport->edif.app_sess_online = 0; qlt_schedule_sess_for_deletion(fcport); qla_edif_sa_ctl_init(vha, fcport); } + set_bit(RELOGIN_NEEDED, &vha->dpc_flags); } if (vha->pur_cinfo.enode_flags != ENODE_ACTIVE) { @@ -540,9 +595,11 @@ qla_edif_app_start(scsi_qla_host_t *vha, struct bsg_job *bsg_job) __func__); } +out: appreply.host_support_edif = vha->hw->flags.edif_enabled; appreply.edif_enode_active = vha->pur_cinfo.enode_flags; appreply.edif_edb_active = vha->e_dbell.db_flags; + appreply.version = EDIF_VERSION1; bsg_job->reply_len = sizeof(struct fc_bsg_reply); @@ -610,9 +667,6 @@ qla_edif_app_stop(scsi_qla_host_t *vha, struct bsg_job *bsg_job) fcport->send_els_logo = 1; qlt_schedule_sess_for_deletion(fcport); - - /* qla_edif_flush_sa_ctl_lists(fcport); */ - fcport->edif.app_started = 0; } } @@ -672,6 +726,7 @@ qla_edif_app_authok(scsi_qla_host_t *vha, struct bsg_job *bsg_job) portid.b.area = appplogiok.u.d_id.b.area; portid.b.al_pa = appplogiok.u.d_id.b.al_pa; + appplogireply.version = EDIF_VERSION1; switch (appplogiok.type) { case PL_TYPE_WWPN: fcport = qla2x00_find_fcport_by_wwpn(vha, @@ -864,6 +919,8 @@ qla_edif_app_getfcinfo(scsi_qla_host_t *vha, struct bsg_job *bsg_job) } else { struct fc_port *fcport = NULL, *tf; + app_reply->version = EDIF_VERSION1; + list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { if (!(fcport->flags & FCF_FCSP_DEVICE)) continue; @@ -880,9 +937,25 @@ qla_edif_app_getfcinfo(scsi_qla_host_t *vha, struct bsg_job *bsg_job) if (tdid.b24 != 0 && tdid.b24 != fcport->d_id.b24) continue; - app_reply->ports[pcnt].rekey_count = - fcport->edif.rekey_cnt; + if (!N2N_TOPO(vha->hw)) { + if (fcport->scan_state != QLA_FCPORT_FOUND) + continue; + if (fcport->port_type == FCT_UNKNOWN && + !fcport->fc4_features) + rval = qla24xx_async_gffid(vha, fcport, + true); + + if (!rval && + !(fcport->fc4_features & FC4_FF_TARGET || + fcport->port_type & + (FCT_TARGET | FCT_NVME_TARGET))) + continue; + } + + rval = 0; + + app_reply->ports[pcnt].version = EDIF_VERSION1; app_reply->ports[pcnt].remote_type = VND_CMD_RTYPE_UNKNOWN; if (fcport->port_type & (FCT_NVME_TARGET | FCT_TARGET)) @@ -979,6 +1052,8 @@ qla_edif_app_getstats(scsi_qla_host_t *vha, struct bsg_job *bsg_job) } else { struct fc_port *fcport = NULL, *tf; + app_reply->version = EDIF_VERSION1; + list_for_each_entry_safe(fcport, tf, &vha->vp_fcports, list) { if (fcport->edif.enable) { if (pcnt > app_req.num_ports) @@ -1012,6 +1087,164 @@ qla_edif_app_getstats(scsi_qla_host_t *vha, struct bsg_job *bsg_job) return rval; } +static int32_t +qla_edif_ack(scsi_qla_host_t *vha, struct bsg_job *bsg_job) +{ + struct fc_port *fcport; + struct aen_complete_cmd ack; + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, &ack, sizeof(ack)); + + ql_dbg(ql_dbg_edif, vha, 0x70cf, + "%s: %06x event_code %x\n", + __func__, ack.port_id.b24, ack.event_code); + + fcport = qla2x00_find_fcport_by_pid(vha, &ack.port_id); + SET_DID_STATUS(bsg_reply->result, DID_OK); + + if (!fcport) { + ql_dbg(ql_dbg_edif, vha, 0x70cf, + "%s: unable to find fcport %06x \n", + __func__, ack.port_id.b24); + return 0; + } + + switch (ack.event_code) { + case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: + fcport->edif.sess_down_acked = 1; + break; + default: + break; + } + return 0; +} + +static int qla_edif_consume_dbell(scsi_qla_host_t *vha, struct bsg_job *bsg_job) +{ + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + u32 sg_skip, reply_payload_len; + bool keep; + struct edb_node *dbnode = NULL; + struct edif_app_dbell ap; + int dat_size = 0; + + sg_skip = 0; + reply_payload_len = bsg_job->reply_payload.payload_len; + + while ((reply_payload_len - sg_skip) >= sizeof(struct edb_node)) { + dbnode = qla_edb_getnext(vha); + if (dbnode) { + keep = true; + dat_size = 0; + ap.event_code = dbnode->ntype; + switch (dbnode->ntype) { + case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: + case VND_CMD_AUTH_STATE_NEEDED: + ap.port_id = dbnode->u.plogi_did; + dat_size += sizeof(ap.port_id); + break; + case VND_CMD_AUTH_STATE_ELS_RCVD: + ap.port_id = dbnode->u.els_sid; + dat_size += sizeof(ap.port_id); + break; + case VND_CMD_AUTH_STATE_SAUPDATE_COMPL: + ap.port_id = dbnode->u.sa_aen.port_id; + memcpy(&ap.event_data, &dbnode->u, + sizeof(struct edif_sa_update_aen)); + dat_size += sizeof(struct edif_sa_update_aen); + break; + default: + keep = false; + ql_log(ql_log_warn, vha, 0x09102, + "%s unknown DB type=%d %p\n", + __func__, dbnode->ntype, dbnode); + break; + } + ap.event_data_size = dat_size; + /* 8 = sizeof(ap.event_code + ap.event_data_size) */ + dat_size += 8; + if (keep) + sg_skip += sg_copy_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, + &ap, dat_size, sg_skip, false); + + ql_dbg(ql_dbg_edif, vha, 0x09102, + "%s Doorbell consumed : type=%d %p\n", + __func__, dbnode->ntype, dbnode); + + kfree(dbnode); + } else { + break; + } + } + + SET_DID_STATUS(bsg_reply->result, DID_OK); + bsg_reply->reply_payload_rcv_len = sg_skip; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + + return 0; +} + +static void __qla_edif_dbell_bsg_done(scsi_qla_host_t *vha, struct bsg_job *bsg_job, + u32 delay) +{ + struct fc_bsg_reply *bsg_reply = bsg_job->reply; + + /* small sleep for doorbell events to accumulate */ + if (delay) + msleep(delay); + + qla_edif_consume_dbell(vha, bsg_job); + + bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len); +} + +static void qla_edif_dbell_bsg_done(scsi_qla_host_t *vha) +{ + unsigned long flags; + struct bsg_job *prev_bsg_job = NULL; + + spin_lock_irqsave(&vha->e_dbell.db_lock, flags); + if (vha->e_dbell.dbell_bsg_job) { + prev_bsg_job = vha->e_dbell.dbell_bsg_job; + vha->e_dbell.dbell_bsg_job = NULL; + } + spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); + + if (prev_bsg_job) + __qla_edif_dbell_bsg_done(vha, prev_bsg_job, 0); +} + +static int +qla_edif_dbell_bsg(scsi_qla_host_t *vha, struct bsg_job *bsg_job) +{ + unsigned long flags; + bool return_bsg = false; + + /* flush previous dbell bsg */ + qla_edif_dbell_bsg_done(vha); + + spin_lock_irqsave(&vha->e_dbell.db_lock, flags); + if (list_empty(&vha->e_dbell.head) && DBELL_ACTIVE(vha)) { + /* + * when the next db event happens, bsg_job will return. + * Otherwise, timer will return it. + */ + vha->e_dbell.dbell_bsg_job = bsg_job; + vha->e_dbell.bsg_expire = jiffies + 10 * HZ; + } else { + return_bsg = true; + } + spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); + + if (return_bsg) + __qla_edif_dbell_bsg_done(vha, bsg_job, 1); + + return 0; +} + int32_t qla_edif_app_mgmt(struct bsg_job *bsg_job) { @@ -1023,8 +1256,13 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) bool done = true; int32_t rval = 0; uint32_t vnd_sc = bsg_request->rqst_data.h_vendor.vendor_cmd[1]; + u32 level = ql_dbg_edif; - ql_dbg(ql_dbg_edif, vha, 0x911d, "%s vnd subcmd=%x\n", + /* doorbell is high traffic */ + if (vnd_sc == QL_VND_SC_READ_DBELL) + level = 0; + + ql_dbg(level, vha, 0x911d, "%s vnd subcmd=%x\n", __func__, vnd_sc); sg_copy_to_buffer(bsg_job->request_payload.sg_list, @@ -1033,7 +1271,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) if (!vha->hw->flags.edif_enabled || test_bit(VPORT_DELETE, &vha->dpc_flags)) { - ql_dbg(ql_dbg_edif, vha, 0x911d, + ql_dbg(level, vha, 0x911d, "%s edif not enabled or vp delete. bsg ptr done %p. dpc_flags %lx\n", __func__, bsg_job, vha->dpc_flags); @@ -1042,7 +1280,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) } if (!qla_edif_app_check(vha, appcheck)) { - ql_dbg(ql_dbg_edif, vha, 0x911d, + ql_dbg(level, vha, 0x911d, "%s app checked failed.\n", __func__); @@ -1074,6 +1312,13 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) case QL_VND_SC_GET_STATS: rval = qla_edif_app_getstats(vha, bsg_job); break; + case QL_VND_SC_AEN_COMPLETE: + rval = qla_edif_ack(vha, bsg_job); + break; + case QL_VND_SC_READ_DBELL: + rval = qla_edif_dbell_bsg(vha, bsg_job); + done = false; + break; default: ql_dbg(ql_dbg_edif, vha, 0x911d, "%s unknown cmd=%x\n", __func__, @@ -1085,7 +1330,7 @@ qla_edif_app_mgmt(struct bsg_job *bsg_job) done: if (done) { - ql_dbg(ql_dbg_user, vha, 0x7009, + ql_dbg(level, vha, 0x7009, "%s: %d bsg ptr done %p\n", __func__, __LINE__, bsg_job); bsg_job_done(bsg_job, bsg_reply->result, bsg_reply->reply_payload_rcv_len); @@ -1247,6 +1492,8 @@ qla24xx_check_sadb_avail_slot(struct bsg_job *bsg_job, fc_port_t *fcport, #define QLA_SA_UPDATE_FLAGS_RX_KEY 0x0 #define QLA_SA_UPDATE_FLAGS_TX_KEY 0x2 +#define EDIF_MSLEEP_INTERVAL 100 +#define EDIF_RETRY_COUNT 50 int qla24xx_sadb_update(struct bsg_job *bsg_job) @@ -1259,7 +1506,7 @@ qla24xx_sadb_update(struct bsg_job *bsg_job) struct edif_list_entry *edif_entry = NULL; int found = 0; int rval = 0; - int result = 0; + int result = 0, cnt; struct qla_sa_update_frame sa_frame; struct srb_iocb *iocb_cmd; port_id_t portid; @@ -1500,11 +1747,23 @@ force_rx_delete: sp->done = qla2x00_bsg_job_done; iocb_cmd = &sp->u.iocb_cmd; iocb_cmd->u.sa_update.sa_frame = sa_frame; - + cnt = 0; +retry: rval = qla2x00_start_sp(sp); - if (rval != QLA_SUCCESS) { + switch (rval) { + case QLA_SUCCESS: + break; + case EAGAIN: + msleep(EDIF_MSLEEP_INTERVAL); + cnt++; + if (cnt < EDIF_RETRY_COUNT) + goto retry; + + fallthrough; + default: ql_log(ql_dbg_edif, vha, 0x70e3, - "qla2x00_start_sp failed=%d.\n", rval); + "%s qla2x00_start_sp failed=%d.\n", + __func__, rval); qla2x00_rel_sp(sp); rval = -EIO; @@ -1797,30 +2056,6 @@ qla_edb_init(scsi_qla_host_t *vha) /* initialize lock which protects doorbell & init list */ spin_lock_init(&vha->e_dbell.db_lock); INIT_LIST_HEAD(&vha->e_dbell.head); - - /* create and initialize doorbell */ - init_completion(&vha->e_dbell.dbell); -} - -static void -qla_edb_node_free(scsi_qla_host_t *vha, struct edb_node *node) -{ - /* - * releases the space held by this edb node entry - * this function does _not_ free the edb node itself - * NB: the edb node entry passed should not be on any list - * - * currently for doorbell there's no additional cleanup - * needed, but here as a placeholder for furture use. - */ - - if (!node) { - ql_dbg(ql_dbg_edif, vha, 0x09122, - "%s error - no valid node passed\n", __func__); - return; - } - - node->ntype = N_UNDEF; } static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid) @@ -1867,11 +2102,8 @@ static void qla_edb_clear(scsi_qla_host_t *vha, port_id_t portid) } spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); - list_for_each_entry_safe(e, tmp, &edb_list, list) { + list_for_each_entry_safe(e, tmp, &edb_list, list) qla_edb_node_free(vha, e); - list_del_init(&e->list); - kfree(e); - } } /* function called when app is stopping */ @@ -1899,14 +2131,10 @@ qla_edb_stop(scsi_qla_host_t *vha) "%s freeing edb_node type=%x\n", __func__, node->ntype); qla_edb_node_free(vha, node); - list_del(&node->list); - - kfree(node); } spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); - /* wake up doorbell waiters - they'll be dismissed with error code */ - complete_all(&vha->e_dbell.dbell); + qla_edif_dbell_bsg_done(vha); } static struct edb_node * @@ -1944,9 +2172,6 @@ qla_edb_node_add(scsi_qla_host_t *vha, struct edb_node *ptr) list_add_tail(&ptr->list, &vha->e_dbell.head); spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); - /* ring doorbell for waiters */ - complete(&vha->e_dbell.dbell); - return true; } @@ -2010,47 +2235,29 @@ qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype, edbnode->u.sa_aen.port_id = fcport->d_id; edbnode->u.sa_aen.status = data; edbnode->u.sa_aen.key_type = data2; + edbnode->u.sa_aen.version = EDIF_VERSION1; break; default: ql_dbg(ql_dbg_edif, vha, 0x09102, "%s unknown type: %x\n", __func__, dbtype); - qla_edb_node_free(vha, edbnode); kfree(edbnode); edbnode = NULL; break; } - if (edbnode && (!qla_edb_node_add(vha, edbnode))) { + if (edbnode) { + if (!qla_edb_node_add(vha, edbnode)) { + ql_dbg(ql_dbg_edif, vha, 0x09102, + "%s unable to add dbnode\n", __func__); + kfree(edbnode); + return; + } ql_dbg(ql_dbg_edif, vha, 0x09102, - "%s unable to add dbnode\n", __func__); - qla_edb_node_free(vha, edbnode); - kfree(edbnode); - return; + "%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode); + qla_edif_dbell_bsg_done(vha); + if (fcport) + fcport->edif.auth_state = dbtype; } - if (edbnode && fcport) - fcport->edif.auth_state = dbtype; - ql_dbg(ql_dbg_edif, vha, 0x09102, - "%s Doorbell produced : type=%d %p\n", __func__, dbtype, edbnode); -} - -static struct edb_node * -qla_edb_getnext(scsi_qla_host_t *vha) -{ - unsigned long flags; - struct edb_node *edbnode = NULL; - - spin_lock_irqsave(&vha->e_dbell.db_lock, flags); - - /* db nodes are fifo - no qualifications done */ - if (!list_empty(&vha->e_dbell.head)) { - edbnode = list_first_entry(&vha->e_dbell.head, - struct edb_node, list); - list_del(&edbnode->list); - } - - spin_unlock_irqrestore(&vha->e_dbell.db_lock, flags); - - return edbnode; } void @@ -2078,89 +2285,14 @@ qla_edif_timer(scsi_qla_host_t *vha) ha->edif_post_stop_cnt_down = 60; } } -} -/* - * app uses separate thread to read this. It'll wait until the doorbell - * is rung by the driver or the max wait time has expired - */ -ssize_t -edif_doorbell_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - struct edb_node *dbnode = NULL; - struct edif_app_dbell *ap = (struct edif_app_dbell *)buf; - uint32_t dat_siz, buf_size, sz; - - /* TODO: app currently hardcoded to 256. Will transition to bsg */ - sz = 256; - - /* stop new threads from waiting if we're not init'd */ - if (DBELL_INACTIVE(vha)) { - ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x09122, - "%s error - edif db not enabled\n", __func__); - return 0; - } - - if (!vha->hw->flags.edif_enabled) { - /* edif not enabled */ - ql_dbg(ql_dbg_edif + ql_dbg_verbose, vha, 0x09122, - "%s error - edif not enabled\n", __func__); - return -1; - } - - buf_size = 0; - while ((sz - buf_size) >= sizeof(struct edb_node)) { - /* remove the next item from the doorbell list */ - dat_siz = 0; - dbnode = qla_edb_getnext(vha); - if (dbnode) { - ap->event_code = dbnode->ntype; - switch (dbnode->ntype) { - case VND_CMD_AUTH_STATE_SESSION_SHUTDOWN: - case VND_CMD_AUTH_STATE_NEEDED: - ap->port_id = dbnode->u.plogi_did; - dat_siz += sizeof(ap->port_id); - break; - case VND_CMD_AUTH_STATE_ELS_RCVD: - ap->port_id = dbnode->u.els_sid; - dat_siz += sizeof(ap->port_id); - break; - case VND_CMD_AUTH_STATE_SAUPDATE_COMPL: - ap->port_id = dbnode->u.sa_aen.port_id; - memcpy(ap->event_data, &dbnode->u, - sizeof(struct edif_sa_update_aen)); - dat_siz += sizeof(struct edif_sa_update_aen); - break; - default: - /* unknown node type, rtn unknown ntype */ - ap->event_code = VND_CMD_AUTH_STATE_UNDEF; - memcpy(ap->event_data, &dbnode->ntype, 4); - dat_siz += 4; - break; - } - - ql_dbg(ql_dbg_edif, vha, 0x09102, - "%s Doorbell consumed : type=%d %p\n", - __func__, dbnode->ntype, dbnode); - /* we're done with the db node, so free it up */ - qla_edb_node_free(vha, dbnode); - kfree(dbnode); - } else { - break; - } - - ap->event_data_size = dat_siz; - /* 8bytes = ap->event_code + ap->event_data_size */ - buf_size += dat_siz + 8; - ap = (struct edif_app_dbell *)(buf + buf_size); - } - return buf_size; + if (vha->e_dbell.dbell_bsg_job && time_after_eq(jiffies, vha->e_dbell.bsg_expire)) + qla_edif_dbell_bsg_done(vha); } static void qla_noop_sp_done(srb_t *sp, int res) { + sp->fcport->flags &= ~(FCF_ASYNC_SENT | FCF_ASYNC_ACTIVE); /* ref: INIT */ kref_put(&sp->cmd_kref, qla2x00_sp_release); } @@ -2185,7 +2317,8 @@ qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha, struct qla_work_evt *e) if (!sa_ctl) { ql_dbg(ql_dbg_edif, vha, 0x70e6, "sa_ctl allocation failed\n"); - return -ENOMEM; + rval = -ENOMEM; + goto done; } fcport = sa_ctl->fcport; @@ -2195,7 +2328,8 @@ qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha, struct qla_work_evt *e) if (!sp) { ql_dbg(ql_dbg_edif, vha, 0x70e6, "SRB allocation failed\n"); - return -ENOMEM; + rval = -ENOMEM; + goto done; } fcport->flags |= FCF_ASYNC_SENT; @@ -2224,9 +2358,16 @@ qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha, struct qla_work_evt *e) rval = qla2x00_start_sp(sp); - if (rval != QLA_SUCCESS) - rval = QLA_FUNCTION_FAILED; + if (rval != QLA_SUCCESS) { + goto done_free_sp; + } + return rval; +done_free_sp: + kref_put(&sp->cmd_kref, qla2x00_sp_release); + fcport->flags &= ~FCF_ASYNC_SENT; +done: + fcport->flags &= ~FCF_ASYNC_ACTIVE; return rval; } @@ -2446,8 +2587,7 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) fcport = qla2x00_find_fcport_by_pid(host, &purex->pur_info.pur_sid); - if (DBELL_INACTIVE(vha) || - (fcport && EDIF_SESSION_DOWN(fcport))) { + if (DBELL_INACTIVE(vha)) { ql_dbg(ql_dbg_edif, host, 0x0910c, "%s e_dbell.db_flags =%x %06x\n", __func__, host->e_dbell.db_flags, fcport ? fcport->d_id.b24 : 0); @@ -2457,6 +2597,22 @@ void qla24xx_auth_els(scsi_qla_host_t *vha, void **pkt, struct rsp_que **rsp) return; } + if (fcport && EDIF_SESSION_DOWN(fcport)) { + ql_dbg(ql_dbg_edif, host, 0x13b6, + "%s terminate exchange. Send logo to 0x%x\n", + __func__, a.did.b24); + + a.tx_byte_count = a.tx_len = 0; + a.tx_addr = 0; + a.control_flags = EPD_RX_XCHG; /* EPD_RX_XCHG = terminate cmd */ + qla_els_reject_iocb(host, (*rsp)->qpair, &a); + qla_enode_free(host, ptr); + /* send logo to let remote port knows to tear down session */ + fcport->send_els_logo = 1; + qlt_schedule_sess_for_deletion(fcport); + return; + } + /* add the local enode to the list */ qla_enode_add(host, ptr); @@ -2832,6 +2988,12 @@ qla28xx_start_scsi_edif(srb_t *sp) tot_dsds = nseg; req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + + sp->iores.res_type = RESOURCE_INI; + sp->iores.iocb_cnt = req_cnt; + if (qla_get_iocbs(sp->qpair, &sp->iores)) + goto queuing_error; + if (req->cnt < (req_cnt + 2)) { cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : rd_reg_dword(req->req_q_out); @@ -3023,6 +3185,7 @@ queuing_error: mempool_free(sp->u.scmd.ct6_ctx, ha->ctx_mempool); sp->u.scmd.ct6_ctx = NULL; } + qla_put_iocbs(sp->qpair, &sp->iores); spin_unlock_irqrestore(lock, flags); return QLA_FUNCTION_FAILED; @@ -3349,10 +3512,14 @@ int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsg_job) fc_port_t *fcport = NULL; struct qla_hw_data *ha = vha->hw; srb_t *sp; - int rval = (DID_ERROR << 16); + int rval = (DID_ERROR << 16), cnt; port_id_t d_id; struct qla_bsg_auth_els_request *p = (struct qla_bsg_auth_els_request *)bsg_job->request; + struct qla_bsg_auth_els_reply *rpl = + (struct qla_bsg_auth_els_reply *)bsg_job->reply; + + rpl->version = EDIF_VERSION1; d_id.b.al_pa = bsg_request->rqst_data.h_els.port_id[2]; d_id.b.area = bsg_request->rqst_data.h_els.port_id[1]; @@ -3371,7 +3538,7 @@ int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsg_job) if (qla_bsg_check(vha, bsg_job, fcport)) return 0; - if (fcport->loop_id == FC_NO_LOOP_ID) { + if (EDIF_SESS_DELETE(fcport)) { ql_dbg(ql_dbg_edif, vha, 0x910d, "%s ELS code %x, no loop id.\n", __func__, bsg_request->rqst_data.r_els.els_code); @@ -3440,17 +3607,26 @@ int qla_edif_process_els(scsi_qla_host_t *vha, struct bsg_job *bsg_job) sp->free = qla2x00_bsg_sp_free; sp->done = qla2x00_bsg_job_done; + cnt = 0; +retry: rval = qla2x00_start_sp(sp); - - ql_dbg(ql_dbg_edif, vha, 0x700a, - "%s %s %8phN xchg %x ctlflag %x hdl %x reqlen %xh bsg ptr %p\n", - __func__, sc_to_str(p->e.sub_cmd), fcport->port_name, - p->e.extra_rx_xchg_address, p->e.extra_control_flags, - sp->handle, sp->remap.req.len, bsg_job); - - if (rval != QLA_SUCCESS) { + switch (rval) { + case QLA_SUCCESS: + ql_dbg(ql_dbg_edif, vha, 0x700a, + "%s %s %8phN xchg %x ctlflag %x hdl %x reqlen %xh bsg ptr %p\n", + __func__, sc_to_str(p->e.sub_cmd), fcport->port_name, + p->e.extra_rx_xchg_address, p->e.extra_control_flags, + sp->handle, sp->remap.req.len, bsg_job); + break; + case EAGAIN: + msleep(EDIF_MSLEEP_INTERVAL); + cnt++; + if (cnt < EDIF_RETRY_COUNT) + goto retry; + fallthrough; + default: ql_log(ql_log_warn, vha, 0x700e, - "qla2x00_start_sp failed = %d\n", rval); + "%s qla2x00_start_sp failed = %d\n", __func__, rval); SET_DID_STATUS(bsg_reply->result, DID_IMM_RETRY); rval = -EIO; goto done_free_remap_rsp; @@ -3472,14 +3648,29 @@ done: void qla_edif_sess_down(struct scsi_qla_host *vha, struct fc_port *sess) { + u16 cnt = 0; + if (sess->edif.app_sess_online && DBELL_ACTIVE(vha)) { ql_dbg(ql_dbg_disc, vha, 0xf09c, "%s: sess %8phN send port_offline event\n", __func__, sess->port_name); sess->edif.app_sess_online = 0; + sess->edif.sess_down_acked = 0; qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_SESSION_SHUTDOWN, sess->d_id.b24, 0, sess); qla2x00_post_aen_work(vha, FCH_EVT_PORT_OFFLINE, sess->d_id.b24); + + while (!READ_ONCE(sess->edif.sess_down_acked) && + !test_bit(VPORT_DELETE, &vha->dpc_flags)) { + msleep(100); + cnt++; + if (cnt > 100) + break; + } + sess->edif.sess_down_acked = 0; + ql_dbg(ql_dbg_disc, vha, 0xf09c, + "%s: sess %8phN port_offline event completed\n", + __func__, sess->port_name); } } diff --git a/drivers/scsi/qla2xxx/qla_edif.h b/drivers/scsi/qla2xxx/qla_edif.h index a965ca8e47ce..7cdb89ccdc6e 100644 --- a/drivers/scsi/qla2xxx/qla_edif.h +++ b/drivers/scsi/qla2xxx/qla_edif.h @@ -51,7 +51,8 @@ struct edif_dbell { enum db_flags_t db_flags; spinlock_t db_lock; struct list_head head; - struct completion dbell; + struct bsg_job *dbell_bsg_job; + unsigned long bsg_expire; }; #define SA_UPDATE_IOCB_TYPE 0x71 /* Security Association Update IOCB entry */ @@ -140,4 +141,8 @@ struct enode { (DBELL_ACTIVE(_fcport->vha) && \ (_fcport->disc_state == DSC_LOGIN_AUTH_PEND)) +#define EDIF_SESS_DELETE(_s) \ + (qla_ini_mode_enabled(_s->vha) && (_s->disc_state == DSC_DELETE_PEND || \ + _s->disc_state == DSC_DELETED)) + #endif /* __QLA_EDIF_H */ diff --git a/drivers/scsi/qla2xxx/qla_edif_bsg.h b/drivers/scsi/qla2xxx/qla_edif_bsg.h index 5a26c77157da..0931f4e4e127 100644 --- a/drivers/scsi/qla2xxx/qla_edif_bsg.h +++ b/drivers/scsi/qla2xxx/qla_edif_bsg.h @@ -7,13 +7,15 @@ #ifndef __QLA_EDIF_BSG_H #define __QLA_EDIF_BSG_H +#define EDIF_VERSION1 1 + /* BSG Vendor specific commands */ #define ELS_MAX_PAYLOAD 2112 #ifndef WWN_SIZE #define WWN_SIZE 8 #endif -#define VND_CMD_APP_RESERVED_SIZE 32 - +#define VND_CMD_APP_RESERVED_SIZE 28 +#define VND_CMD_PAD_SIZE 3 enum auth_els_sub_cmd { SEND_ELS = 0, SEND_ELS_REPLY, @@ -28,7 +30,9 @@ struct extra_auth_els { #define BSG_CTL_FLAG_LS_ACC 1 #define BSG_CTL_FLAG_LS_RJT 2 #define BSG_CTL_FLAG_TRM 3 - uint8_t extra_rsvd[3]; + uint8_t version; + uint8_t pad[2]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; struct qla_bsg_auth_els_request { @@ -39,51 +43,46 @@ struct qla_bsg_auth_els_request { struct qla_bsg_auth_els_reply { struct fc_bsg_reply r; uint32_t rx_xchg_address; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; }; struct app_id { int app_vid; - uint8_t app_key[32]; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; struct app_start_reply { uint32_t host_support_edif; uint32_t edif_enode_active; uint32_t edif_edb_active; - uint32_t reserved[VND_CMD_APP_RESERVED_SIZE]; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; struct app_start { struct app_id app_info; - uint32_t prli_to; - uint32_t key_shred; uint8_t app_start_flags; - uint8_t reserved[VND_CMD_APP_RESERVED_SIZE - 1]; + uint8_t version; + uint8_t pad[2]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; struct app_stop { struct app_id app_info; - char buf[16]; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; struct app_plogi_reply { uint32_t prli_status; - uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; -} __packed; - -#define RECFG_TIME 1 -#define RECFG_BYTES 2 - -struct app_rekey_cfg { - struct app_id app_info; - uint8_t rekey_mode; - port_id_t d_id; - uint8_t force; - union { - int64_t bytes; - int64_t time; - } rky_units; - + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; @@ -91,7 +90,9 @@ struct app_pinfo_req { struct app_id app_info; uint8_t num_ports; port_id_t remote_pid; - uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; struct app_pinfo { @@ -103,11 +104,8 @@ struct app_pinfo { #define VND_CMD_RTYPE_INITIATOR 2 uint8_t remote_state; uint8_t auth_state; - uint8_t rekey_mode; - int64_t rekey_count; - int64_t rekey_config_value; - int64_t rekey_consumed_value; - + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; @@ -120,6 +118,8 @@ struct app_pinfo { struct app_pinfo_reply { uint8_t port_count; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; struct app_pinfo ports[]; } __packed; @@ -127,6 +127,8 @@ struct app_pinfo_reply { struct app_sinfo_req { struct app_id app_info; uint8_t num_ports; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; @@ -140,6 +142,9 @@ struct app_sinfo { struct app_stats_reply { uint8_t elem_count; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; struct app_sinfo elem[]; } __packed; @@ -163,9 +168,11 @@ struct qla_sa_update_frame { uint8_t node_name[WWN_SIZE]; uint8_t port_name[WWN_SIZE]; port_id_t port_id; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved2[VND_CMD_APP_RESERVED_SIZE]; } __packed; -// used for edif mgmt bsg interface #define QL_VND_SC_UNDEF 0 #define QL_VND_SC_SA_UPDATE 1 #define QL_VND_SC_APP_START 2 @@ -175,6 +182,22 @@ struct qla_sa_update_frame { #define QL_VND_SC_REKEY_CONFIG 6 #define QL_VND_SC_GET_FCINFO 7 #define QL_VND_SC_GET_STATS 8 +#define QL_VND_SC_AEN_COMPLETE 9 +#define QL_VND_SC_READ_DBELL 10 + +/* + * bsg caller to provide empty buffer for doorbell events. + * + * sg_io_v4.din_xferp = empty buffer for door bell events + * sg_io_v4.dout_xferp = struct edif_read_dbell *buf + */ +struct edif_read_dbell { + struct app_id app_info; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; +}; + /* Application interface data structure for rtn data */ #define EXT_DEF_EVENT_DATA_SIZE 64 @@ -191,7 +214,9 @@ struct edif_sa_update_aen { port_id_t port_id; uint32_t key_type; /* Tx (1) or RX (2) */ uint32_t status; /* 0 succes, 1 failed, 2 timeout , 3 error */ - uint8_t reserved[16]; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; #define QL_VND_SA_STAT_SUCCESS 0 @@ -212,9 +237,22 @@ struct auth_complete_cmd { uint8_t wwpn[WWN_SIZE]; port_id_t d_id; } u; - uint32_t reserved[VND_CMD_APP_RESERVED_SIZE]; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; +} __packed; + +struct aen_complete_cmd { + struct app_id app_info; + port_id_t port_id; + uint32_t event_code; + uint8_t version; + uint8_t pad[VND_CMD_PAD_SIZE]; + uint8_t reserved[VND_CMD_APP_RESERVED_SIZE]; } __packed; #define RX_DELAY_DELETE_TIMEOUT 20 +#define FCH_EVT_VENDOR_UNIQUE_VPORT_DOWN 1 + #endif /* QLA_EDIF_BSG_H */ diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 0bb1d562f0bf..361015b5763e 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -807,7 +807,7 @@ struct els_entry_24xx { #define EPD_ELS_COMMAND (0 << 13) #define EPD_ELS_ACC (1 << 13) #define EPD_ELS_RJT (2 << 13) -#define EPD_RX_XCHG (3 << 13) +#define EPD_RX_XCHG (3 << 13) /* terminate exchange */ #define ECF_CLR_PASSTHRU_PEND BIT_12 #define ECF_INCL_FRAME_HDR BIT_11 #define ECF_SEC_LOGIN BIT_3 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index dac27b5ff0ac..5dd2932382ee 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -193,6 +193,8 @@ extern int ql2xsecenable; extern int ql2xenforce_iocb_limit; extern int ql2xabts_wait_nvme; extern u32 ql2xnvme_queues; +extern int ql2xrspq_follow_inptr; +extern int ql2xrspq_follow_inptr_legacy; extern int qla2x00_loop_reset(scsi_qla_host_t *); extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int); @@ -335,6 +337,7 @@ extern int qla24xx_configure_prot_mode(srb_t *, uint16_t *); extern int qla24xx_issue_sa_replace_iocb(scsi_qla_host_t *vha, struct qla_work_evt *e); void qla2x00_sp_release(struct kref *kref); +void qla2x00_els_dcmd2_iocb_timeout(void *data); /* * Global Function Prototypes in qla_mbx.c source file. @@ -433,7 +436,8 @@ extern int qla2x00_get_resource_cnts(scsi_qla_host_t *); extern int -qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map); +qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map, + u8 *num_entries); extern int qla2x00_get_link_status(scsi_qla_host_t *, uint16_t, struct link_statistics *, @@ -554,6 +558,10 @@ qla2x00_dump_mctp_data(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); extern int qla26xx_dport_diagnostics(scsi_qla_host_t *, void *, uint, uint); +extern int +qla26xx_dport_diagnostics_v2(scsi_qla_host_t *, + struct qla_dport_diag_v2 *, mbx_cmd_t *); + int qla24xx_send_mb_cmd(struct scsi_qla_host *, mbx_cmd_t *); int qla24xx_gpdb_wait(struct scsi_qla_host *, fc_port_t *, u8); int qla24xx_gidlist_wait(struct scsi_qla_host *, void *, dma_addr_t, @@ -727,7 +735,7 @@ int qla24xx_async_gpsc(scsi_qla_host_t *, fc_port_t *); void qla24xx_handle_gpsc_event(scsi_qla_host_t *, struct event_arg *); int qla2x00_mgmt_svr_login(scsi_qla_host_t *); void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea); -int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport); +int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport, bool); int qla24xx_async_gpnft(scsi_qla_host_t *, u8, srb_t *); void qla24xx_async_gpnft_done(scsi_qla_host_t *, srb_t *); void qla24xx_async_gnnft_done(scsi_qla_host_t *, srb_t *); @@ -989,7 +997,6 @@ fc_port_t *qla2x00_find_fcport_by_pid(scsi_qla_host_t *vha, port_id_t *id); void qla_edb_eventcreate(scsi_qla_host_t *vha, uint32_t dbtype, uint32_t data, uint32_t data2, fc_port_t *fcport); void qla_edb_stop(scsi_qla_host_t *vha); -ssize_t edif_doorbell_show(struct device *dev, struct device_attribute *attr, char *buf); int32_t qla_edif_app_mgmt(struct bsg_job *bsg_job); void qla_enode_init(scsi_qla_host_t *vha); void qla_enode_stop(scsi_qla_host_t *vha); diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index e811de2f6a25..64ab070b8716 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1596,7 +1596,6 @@ qla2x00_hba_attributes(scsi_qla_host_t *vha, void *entries, unsigned int callopt) { struct qla_hw_data *ha = vha->hw; - struct init_cb_24xx *icb24 = (void *)ha->init_cb; struct new_utsname *p_sysid = utsname(); struct ct_fdmi_hba_attr *eiter; uint16_t alen; @@ -1617,7 +1616,7 @@ qla2x00_hba_attributes(scsi_qla_host_t *vha, void *entries, eiter->type = cpu_to_be16(FDMI_HBA_MANUFACTURER); alen = scnprintf( eiter->a.manufacturer, sizeof(eiter->a.manufacturer), - "%s", "QLogic Corporation"); + "%s", QLA2XXX_MANUFACTURER); alen += FDMI_ATTR_ALIGNMENT(alen); alen += FDMI_ATTR_TYPELEN(eiter); eiter->len = cpu_to_be16(alen); @@ -1758,8 +1757,8 @@ qla2x00_hba_attributes(scsi_qla_host_t *vha, void *entries, /* MAX CT Payload Length */ eiter = entries + size; eiter->type = cpu_to_be16(FDMI_HBA_MAXIMUM_CT_PAYLOAD_LENGTH); - eiter->a.max_ct_len = cpu_to_be32(le16_to_cpu(IS_FWI2_CAPABLE(ha) ? - icb24->frame_payload_size : ha->init_cb->frame_payload_size)); + eiter->a.max_ct_len = cpu_to_be32(ha->frame_payload_size >> 2); + alen = sizeof(eiter->a.max_ct_len); alen += FDMI_ATTR_TYPELEN(eiter); eiter->len = cpu_to_be16(alen); @@ -1851,7 +1850,6 @@ qla2x00_port_attributes(scsi_qla_host_t *vha, void *entries, unsigned int callopt) { struct qla_hw_data *ha = vha->hw; - struct init_cb_24xx *icb24 = (void *)ha->init_cb; struct new_utsname *p_sysid = utsname(); char *hostname = p_sysid ? p_sysid->nodename : fc_host_system_hostname(vha->host); @@ -1903,8 +1901,7 @@ qla2x00_port_attributes(scsi_qla_host_t *vha, void *entries, /* Max frame size. */ eiter = entries + size; eiter->type = cpu_to_be16(FDMI_PORT_MAX_FRAME_SIZE); - eiter->a.max_frame_size = cpu_to_be32(le16_to_cpu(IS_FWI2_CAPABLE(ha) ? - icb24->frame_payload_size : ha->init_cb->frame_payload_size)); + eiter->a.max_frame_size = cpu_to_be32(ha->frame_payload_size); alen = sizeof(eiter->a.max_frame_size); alen += FDMI_ATTR_TYPELEN(eiter); eiter->len = cpu_to_be16(alen); @@ -3280,19 +3277,12 @@ done: return rval; } -void qla24xx_handle_gffid_event(scsi_qla_host_t *vha, struct event_arg *ea) -{ - fc_port_t *fcport = ea->fcport; - - qla24xx_post_gnl_work(vha, fcport); -} void qla24xx_async_gffid_sp_done(srb_t *sp, int res) { struct scsi_qla_host *vha = sp->vha; fc_port_t *fcport = sp->fcport; struct ct_sns_rsp *ct_rsp; - struct event_arg ea; uint8_t fc4_scsi_feat; uint8_t fc4_nvme_feat; @@ -3300,10 +3290,10 @@ void qla24xx_async_gffid_sp_done(srb_t *sp, int res) "Async done-%s res %x ID %x. %8phC\n", sp->name, res, fcport->d_id.b24, fcport->port_name); - fcport->flags &= ~FCF_ASYNC_SENT; - ct_rsp = &fcport->ct_desc.ct_sns->p.rsp; + ct_rsp = sp->u.iocb_cmd.u.ctarg.rsp; fc4_scsi_feat = ct_rsp->rsp.gff_id.fc4_features[GFF_FCP_SCSI_OFFSET]; fc4_nvme_feat = ct_rsp->rsp.gff_id.fc4_features[GFF_NVME_OFFSET]; + sp->rc = res; /* * FC-GS-7, 5.2.3.12 FC-4 Features - format @@ -3324,24 +3314,42 @@ void qla24xx_async_gffid_sp_done(srb_t *sp, int res) } } - memset(&ea, 0, sizeof(ea)); - ea.sp = sp; - ea.fcport = sp->fcport; - ea.rc = res; + if (sp->flags & SRB_WAKEUP_ON_COMP) { + complete(sp->comp); + } else { + if (sp->u.iocb_cmd.u.ctarg.req) { + dma_free_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.req_allocated_size, + sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.req_dma); + sp->u.iocb_cmd.u.ctarg.req = NULL; + } - qla24xx_handle_gffid_event(vha, &ea); - /* ref: INIT */ - kref_put(&sp->cmd_kref, qla2x00_sp_release); + if (sp->u.iocb_cmd.u.ctarg.rsp) { + dma_free_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.rsp_allocated_size, + sp->u.iocb_cmd.u.ctarg.rsp, + sp->u.iocb_cmd.u.ctarg.rsp_dma); + sp->u.iocb_cmd.u.ctarg.rsp = NULL; + } + + /* ref: INIT */ + kref_put(&sp->cmd_kref, qla2x00_sp_release); + /* we should not be here */ + dump_stack(); + } } /* Get FC4 Feature with Nport ID. */ -int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport) +int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport, bool wait) { int rval = QLA_FUNCTION_FAILED; struct ct_sns_req *ct_req; srb_t *sp; + DECLARE_COMPLETION_ONSTACK(comp); - if (!vha->flags.online || (fcport->flags & FCF_ASYNC_SENT)) + /* this routine does not have handling for no wait */ + if (!vha->flags.online || !wait) return rval; /* ref: INIT */ @@ -3349,43 +3357,86 @@ int qla24xx_async_gffid(scsi_qla_host_t *vha, fc_port_t *fcport) if (!sp) return rval; - fcport->flags |= FCF_ASYNC_SENT; sp->type = SRB_CT_PTHRU_CMD; sp->name = "gffid"; sp->gen1 = fcport->rscn_gen; sp->gen2 = fcport->login_gen; qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha) + 2, qla24xx_async_gffid_sp_done); + sp->comp = ∁ + sp->u.iocb_cmd.timeout = qla2x00_els_dcmd2_iocb_timeout; + + if (wait) + sp->flags = SRB_WAKEUP_ON_COMP; + + sp->u.iocb_cmd.u.ctarg.req_allocated_size = sizeof(struct ct_sns_pkt); + sp->u.iocb_cmd.u.ctarg.req = dma_alloc_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.req_allocated_size, + &sp->u.iocb_cmd.u.ctarg.req_dma, + GFP_KERNEL); + if (!sp->u.iocb_cmd.u.ctarg.req) { + ql_log(ql_log_warn, vha, 0xd041, + "%s: Failed to allocate ct_sns request.\n", + __func__); + goto done_free_sp; + } + + sp->u.iocb_cmd.u.ctarg.rsp_allocated_size = sizeof(struct ct_sns_pkt); + sp->u.iocb_cmd.u.ctarg.rsp = dma_alloc_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.rsp_allocated_size, + &sp->u.iocb_cmd.u.ctarg.rsp_dma, + GFP_KERNEL); + if (!sp->u.iocb_cmd.u.ctarg.rsp) { + ql_log(ql_log_warn, vha, 0xd041, + "%s: Failed to allocate ct_sns response.\n", + __func__); + goto done_free_sp; + } /* CT_IU preamble */ - ct_req = qla2x00_prep_ct_req(fcport->ct_desc.ct_sns, GFF_ID_CMD, - GFF_ID_RSP_SIZE); + ct_req = qla2x00_prep_ct_req(sp->u.iocb_cmd.u.ctarg.req, GFF_ID_CMD, GFF_ID_RSP_SIZE); ct_req->req.gff_id.port_id[0] = fcport->d_id.b.domain; ct_req->req.gff_id.port_id[1] = fcport->d_id.b.area; ct_req->req.gff_id.port_id[2] = fcport->d_id.b.al_pa; - sp->u.iocb_cmd.u.ctarg.req = fcport->ct_desc.ct_sns; - sp->u.iocb_cmd.u.ctarg.req_dma = fcport->ct_desc.ct_sns_dma; - sp->u.iocb_cmd.u.ctarg.rsp = fcport->ct_desc.ct_sns; - sp->u.iocb_cmd.u.ctarg.rsp_dma = fcport->ct_desc.ct_sns_dma; sp->u.iocb_cmd.u.ctarg.req_size = GFF_ID_REQ_SIZE; sp->u.iocb_cmd.u.ctarg.rsp_size = GFF_ID_RSP_SIZE; sp->u.iocb_cmd.u.ctarg.nport_handle = NPH_SNS; - ql_dbg(ql_dbg_disc, vha, 0x2132, - "Async-%s hdl=%x %8phC.\n", sp->name, - sp->handle, fcport->port_name); - rval = qla2x00_start_sp(sp); - if (rval != QLA_SUCCESS) - goto done_free_sp; - return rval; + if (rval != QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + goto done_free_sp; + } else { + ql_dbg(ql_dbg_disc, vha, 0x3074, + "Async-%s hdl=%x portid %06x\n", + sp->name, sp->handle, fcport->d_id.b24); + } + + wait_for_completion(sp->comp); + rval = sp->rc; + done_free_sp: + if (sp->u.iocb_cmd.u.ctarg.req) { + dma_free_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.req_allocated_size, + sp->u.iocb_cmd.u.ctarg.req, + sp->u.iocb_cmd.u.ctarg.req_dma); + sp->u.iocb_cmd.u.ctarg.req = NULL; + } + + if (sp->u.iocb_cmd.u.ctarg.rsp) { + dma_free_coherent(&vha->hw->pdev->dev, + sp->u.iocb_cmd.u.ctarg.rsp_allocated_size, + sp->u.iocb_cmd.u.ctarg.rsp, + sp->u.iocb_cmd.u.ctarg.rsp_dma); + sp->u.iocb_cmd.u.ctarg.rsp = NULL; + } + /* ref: INIT */ kref_put(&sp->cmd_kref, qla2x00_sp_release); - fcport->flags &= ~FCF_ASYNC_SENT; return rval; } @@ -3578,7 +3629,7 @@ login_logout: do_delete) { if (fcport->loop_id != FC_NO_LOOP_ID) { if (fcport->flags & FCF_FCP2_DEVICE) - fcport->logout_on_delete = 0; + continue; ql_log(ql_log_warn, vha, 0x20f0, "%s %d %8phC post del sess\n", diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 3f3417a3e891..e7fe0e52c11d 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -47,6 +47,7 @@ qla2x00_sp_timeout(struct timer_list *t) { srb_t *sp = from_timer(sp, t, u.iocb_cmd.timer); struct srb_iocb *iocb; + scsi_qla_host_t *vha = sp->vha; WARN_ON(irqs_disabled()); iocb = &sp->u.iocb_cmd; @@ -54,6 +55,12 @@ qla2x00_sp_timeout(struct timer_list *t) /* ref: TMR */ kref_put(&sp->cmd_kref, qla2x00_sp_release); + + if (vha && qla2x00_isp_reg_stat(vha->hw)) { + ql_log(ql_log_info, vha, 0x9008, + "PCI/Register disconnect.\n"); + qla_pci_set_eeh_busy(vha); + } } void qla2x00_sp_free(srb_t *sp) @@ -161,6 +168,7 @@ int qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait) struct srb_iocb *abt_iocb; srb_t *sp; int rval = QLA_FUNCTION_FAILED; + uint8_t bail; /* ref: INIT for ABTS command */ sp = qla2xxx_get_qpair_sp(cmd_sp->vha, cmd_sp->qpair, cmd_sp->fcport, @@ -168,6 +176,7 @@ int qla24xx_async_abort_cmd(srb_t *cmd_sp, bool wait) if (!sp) return QLA_MEMORY_ALLOC_FAILED; + QLA_VHA_MARK_BUSY(vha, bail); abt_iocb = &sp->u.iocb_cmd; sp->type = SRB_ABT_CMD; sp->name = "abort"; @@ -1480,7 +1489,6 @@ static int qla_chk_secure_login(scsi_qla_host_t *vha, fc_port_t *fcport, ql_dbg(ql_dbg_disc, vha, 0x20ef, "%s %d %8phC EDIF: post DB_AUTH: AUTH needed\n", __func__, __LINE__, fcport->port_name); - fcport->edif.app_started = 1; fcport->edif.app_sess_online = 1; qla_edb_eventcreate(vha, VND_CMD_AUTH_STATE_NEEDED, @@ -1763,8 +1771,16 @@ int qla24xx_fcport_handle_login(struct scsi_qla_host *vha, fc_port_t *fcport) break; case DSC_LOGIN_PEND: - if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) + if (vha->hw->flags.edif_enabled) + break; + + if (fcport->fw_login_state == DSC_LS_PLOGI_COMP) { + ql_dbg(ql_dbg_disc, vha, 0x2118, + "%s %d %8phC post %s PRLI\n", + __func__, __LINE__, fcport->port_name, + NVME_TARGET(vha->hw, fcport) ? "NVME" : "FC"); qla24xx_post_prli_work(vha, fcport); + } break; case DSC_UPD_FCPORT: @@ -1818,7 +1834,8 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) case RSCN_PORT_ADDR: fcport = qla2x00_find_fcport_by_nportid(vha, &ea->id, 1); if (fcport) { - if (fcport->flags & FCF_FCP2_DEVICE) { + if (fcport->flags & FCF_FCP2_DEVICE && + atomic_read(&fcport->state) == FCS_ONLINE) { ql_dbg(ql_dbg_disc, vha, 0x2115, "Delaying session delete for FCP2 portid=%06x %8phC ", fcport->d_id.b24, fcport->port_name); @@ -1850,7 +1867,8 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) break; case RSCN_AREA_ADDR: list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (fcport->flags & FCF_FCP2_DEVICE) + if (fcport->flags & FCF_FCP2_DEVICE && + atomic_read(&fcport->state) == FCS_ONLINE) continue; if ((ea->id.b24 & 0xffff00) == (fcport->d_id.b24 & 0xffff00)) { @@ -1861,7 +1879,8 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) break; case RSCN_DOM_ADDR: list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (fcport->flags & FCF_FCP2_DEVICE) + if (fcport->flags & FCF_FCP2_DEVICE && + atomic_read(&fcport->state) == FCS_ONLINE) continue; if ((ea->id.b24 & 0xff0000) == (fcport->d_id.b24 & 0xff0000)) { @@ -1873,7 +1892,8 @@ void qla2x00_handle_rscn(scsi_qla_host_t *vha, struct event_arg *ea) case RSCN_FAB_ADDR: default: list_for_each_entry(fcport, &vha->vp_fcports, list) { - if (fcport->flags & FCF_FCP2_DEVICE) + if (fcport->flags & FCF_FCP2_DEVICE && + atomic_read(&fcport->state) == FCS_ONLINE) continue; fcport->scan_needed = 1; @@ -2000,12 +2020,14 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun, struct srb_iocb *tm_iocb; srb_t *sp; int rval = QLA_FUNCTION_FAILED; + uint8_t bail; /* ref: INIT */ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; + QLA_VHA_MARK_BUSY(vha, bail); sp->type = SRB_TM_CMD; sp->name = "tmf"; qla2x00_init_async_sp(sp, qla2x00_get_async_timeout(vha), @@ -2124,6 +2146,13 @@ qla24xx_handle_prli_done_event(struct scsi_qla_host *vha, struct event_arg *ea) } if (N2N_TOPO(vha->hw)) { + if (ea->fcport->n2n_link_reset_cnt == + vha->hw->login_retry_count && + ea->fcport->flags & FCF_FCSP_DEVICE) { + /* remote authentication app just started */ + ea->fcport->n2n_link_reset_cnt = 0; + } + if (ea->fcport->n2n_link_reset_cnt < vha->hw->login_retry_count) { ea->fcport->n2n_link_reset_cnt++; @@ -4509,6 +4538,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha) BIT_6) != 0; ql_dbg(ql_dbg_init, vha, 0x00bc, "FA-WWPN Support: %s.\n", (ha->flags.fawwpn_enabled) ? "enabled" : "disabled"); + /* Init_cb will be reused for other command(s). Save a backup copy of port_name */ + memcpy(ha->port_name, ha->init_cb->port_name, WWN_SIZE); } /* ELS pass through payload is limit by frame size. */ @@ -5273,9 +5304,6 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) INIT_LIST_HEAD(&fcport->edif.tx_sa_list); INIT_LIST_HEAD(&fcport->edif.rx_sa_list); - if (vha->e_dbell.db_flags == EDB_ACTIVE) - fcport->edif.app_started = 1; - spin_lock_init(&fcport->edif.indx_list_lock); INIT_LIST_HEAD(&fcport->edif.edif_indx_list); @@ -5488,6 +5516,22 @@ static int qla2x00_configure_n2n_loop(scsi_qla_host_t *vha) return QLA_FUNCTION_FAILED; } +static void +qla_reinitialize_link(scsi_qla_host_t *vha) +{ + int rval; + + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); + rval = qla2x00_full_login_lip(vha); + if (rval == QLA_SUCCESS) { + ql_dbg(ql_dbg_disc, vha, 0xd050, "Link reinitialized\n"); + } else { + ql_dbg(ql_dbg_disc, vha, 0xd051, + "Link reinitialization failed (%d)\n", rval); + } +} + /* * qla2x00_configure_local_loop * Updates Fibre Channel Device Database with local loop devices. @@ -5539,6 +5583,19 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) spin_unlock_irqrestore(&vha->work_lock, flags); if (vha->scan.scan_retry < MAX_SCAN_RETRIES) { + u8 loop_map_entries = 0; + int rc; + + rc = qla2x00_get_fcal_position_map(vha, NULL, + &loop_map_entries); + if (rc == QLA_SUCCESS && loop_map_entries > 1) { + /* + * There are devices that are still not logged + * in. Reinitialize to give them a chance. + */ + qla_reinitialize_link(vha); + return QLA_FUNCTION_FAILED; + } set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); } @@ -5767,8 +5824,6 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) if (atomic_read(&fcport->state) == FCS_ONLINE) return; - qla2x00_set_fcport_state(fcport, FCS_ONLINE); - rport_ids.node_name = wwn_to_u64(fcport->node_name); rport_ids.port_name = wwn_to_u64(fcport->port_name); rport_ids.port_id = fcport->d_id.b.domain << 16 | @@ -5869,7 +5924,6 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) qla2x00_reg_remote_port(vha, fcport); break; case MODE_TARGET: - qla2x00_set_fcport_state(fcport, FCS_ONLINE); if (!vha->vha_tgt.qla_tgt->tgt_stop && !vha->vha_tgt.qla_tgt->tgt_stopped) qlt_fc_port_added(vha, fcport); @@ -5887,6 +5941,8 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) if (NVME_TARGET(vha->hw, fcport)) qla_nvme_register_remote(vha, fcport); + qla2x00_set_fcport_state(fcport, FCS_ONLINE); + if (IS_IIDMA_CAPABLE(vha->hw) && vha->hw->flags.gpsc_supported) { if (fcport->id_changed) { fcport->id_changed = 0; @@ -7197,6 +7253,9 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) if (vha->flags.online) { qla2x00_abort_isp_cleanup(vha); + vha->dport_status |= DPORT_DIAG_CHIP_RESET_IN_PROGRESS; + vha->dport_status &= ~DPORT_DIAG_IN_PROGRESS; + if (vha->hw->flags.port_isolated) return status; @@ -9657,6 +9716,12 @@ int qla2xxx_disable_port(struct Scsi_Host *host) vha->hw->flags.port_isolated = 1; + if (qla2x00_isp_reg_stat(vha->hw)) { + ql_log(ql_log_info, vha, 0x9006, + "PCI/Register disconnect, exiting.\n"); + qla_pci_set_eeh_busy(vha); + return FAILED; + } if (qla2x00_chip_is_down(vha)) return 0; @@ -9672,6 +9737,13 @@ int qla2xxx_enable_port(struct Scsi_Host *host) { scsi_qla_host_t *vha = shost_priv(host); + if (qla2x00_isp_reg_stat(vha->hw)) { + ql_log(ql_log_info, vha, 0x9001, + "PCI/Register disconnect, exiting.\n"); + qla_pci_set_eeh_busy(vha); + return FAILED; + } + vha->hw->flags.port_isolated = 0; /* Set the flag to 1, so that isp_abort can proceed */ vha->flags.online = 1; diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index e0fe9ddb4bd2..42ce4e1fe744 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -2819,7 +2819,7 @@ qla24xx_els_logo_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) sp->vha->qla_stats.control_requests++; } -static void +void qla2x00_els_dcmd2_iocb_timeout(void *data) { srb_t *sp = data; @@ -2882,6 +2882,9 @@ static void qla2x00_els_dcmd2_sp_done(srb_t *sp, int res) sp->name, res, sp->handle, fcport->d_id.b24, fcport->port_name); fcport->flags &= ~(FCF_ASYNC_SENT|FCF_ASYNC_ACTIVE); + /* For edif, set logout on delete to ensure any residual key from FW is flushed.*/ + fcport->logout_on_delete = 1; + fcport->chip_reset = vha->hw->base_qpair->chip_reset; if (sp->flags & SRB_WAKEUP_ON_COMP) complete(&lio->u.els_plogi.comp); diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 21b31d6359c8..76e79f350a22 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1354,9 +1354,7 @@ skip_rio: if (!vha->vp_idx) { if (ha->flags.fawwpn_enabled && (ha->current_topology == ISP_CFG_F)) { - void *wwpn = ha->init_cb->port_name; - - memcpy(vha->port_name, wwpn, WWN_SIZE); + memcpy(vha->port_name, ha->port_name, WWN_SIZE); fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name); ql_dbg(ql_dbg_init + ql_dbg_verbose, @@ -1761,6 +1759,9 @@ global_port_update: break; case MBA_DPORT_DIAGNOSTICS: + if ((mb[1] & 0xF) == AEN_DONE_DIAG_TEST_WITH_NOERR || + (mb[1] & 0xF) == AEN_DONE_DIAG_TEST_WITH_ERR) + vha->dport_status &= ~DPORT_DIAG_IN_PROGRESS; ql_dbg(ql_dbg_async, vha, 0x5052, "D-Port Diagnostics: %04x %04x %04x %04x\n", mb[0], mb[1], mb[2], mb[3]); @@ -2245,9 +2246,9 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, res = DID_ERROR << 16; } - if (logit) { - if (sp->remap.remapped && - ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_RJT) { + if (sp->remap.remapped && + ((u8 *)sp->remap.rsp.buf)[0] == ELS_LS_RJT) { + if (logit) { ql_dbg(ql_dbg_user, vha, 0x503f, "%s IOCB Done LS_RJT hdl=%x comp_status=0x%x\n", type, sp->handle, comp_status); @@ -2259,18 +2260,24 @@ qla24xx_els_ct_entry(scsi_qla_host_t *v, struct req_que *req, pkt)->total_byte_count), e->s_id[0], e->s_id[2], e->s_id[1], e->d_id[2], e->d_id[1], e->d_id[0]); - } else { - ql_log(ql_log_info, vha, 0x503f, - "%s IOCB Done hdl=%x comp_status=0x%x\n", - type, sp->handle, comp_status); - ql_log(ql_log_info, vha, 0x503f, - "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n", - fw_status[1], fw_status[2], - le32_to_cpu(((struct els_sts_entry_24xx *) - pkt)->total_byte_count), - e->s_id[0], e->s_id[2], e->s_id[1], - e->d_id[2], e->d_id[1], e->d_id[0]); } + if (sp->fcport && sp->fcport->flags & FCF_FCSP_DEVICE && + sp->type == SRB_ELS_CMD_HST_NOLOGIN) { + ql_dbg(ql_dbg_edif, vha, 0x911e, + "%s rcv reject. Sched delete\n", __func__); + qlt_schedule_sess_for_deletion(sp->fcport); + } + } else if (logit) { + ql_log(ql_log_info, vha, 0x503f, + "%s IOCB Done hdl=%x comp_status=0x%x\n", + type, sp->handle, comp_status); + ql_log(ql_log_info, vha, 0x503f, + "subcode 1=0x%x subcode 2=0x%x bytes=0x%x %02x%02x%02x -> %02x%02x%02x\n", + fw_status[1], fw_status[2], + le32_to_cpu(((struct els_sts_entry_24xx *) + pkt)->total_byte_count), + e->s_id[0], e->s_id[2], e->s_id[1], + e->d_id[2], e->d_id[1], e->d_id[0]); } } goto els_ct_done; @@ -2639,7 +2646,7 @@ static void qla24xx_nvme_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, } if (unlikely(logit)) - ql_log(ql_dbg_io, fcport->vha, 0x5060, + ql_dbg(ql_dbg_io, fcport->vha, 0x5060, "NVME-%s ERR Handling - hdl=%x status(%x) tr_len:%x resid=%x ox_id=%x\n", sp->name, sp->handle, comp_status, fd->transferred_length, le32_to_cpu(sts->residual_len), @@ -3426,6 +3433,7 @@ check_scsi_status: case CS_PORT_UNAVAILABLE: case CS_TIMEOUT: case CS_RESET: + case CS_EDIF_INV_REQ: /* * We are going to have the fc class block the rport @@ -3496,7 +3504,7 @@ check_scsi_status: out: if (logit) - ql_log(ql_dbg_io, fcport->vha, 0x3022, + ql_dbg(ql_dbg_io, fcport->vha, 0x3022, "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%llu portid=%02x%02x%02x oxid=0x%x cdb=%10phN len=0x%x rsp_info=0x%x resid=0x%x fw_resid=0x%x sp=%p cp=%p.\n", comp_status, scsi_status, res, vha->host_no, cp->device->id, cp->device->lun, fcport->d_id.b.domain, @@ -3712,12 +3720,11 @@ void qla24xx_nvme_ls4_iocb(struct scsi_qla_host *vha, * Return: 0 all iocbs has arrived, xx- all iocbs have not arrived. */ static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha, - struct rsp_que *rsp, response_t *pkt) + struct rsp_que *rsp, response_t *pkt, u32 rsp_q_in) { - int start_pkt_ring_index, end_pkt_ring_index, n_ring_index; - response_t *end_pkt; + int start_pkt_ring_index; + u32 iocb_cnt = 0; int rc = 0; - u32 rsp_q_in; if (pkt->entry_count == 1) return rc; @@ -3728,34 +3735,18 @@ static int qla_chk_cont_iocb_avail(struct scsi_qla_host *vha, else start_pkt_ring_index = rsp->ring_index - 1; - if ((start_pkt_ring_index + pkt->entry_count) >= rsp->length) - end_pkt_ring_index = start_pkt_ring_index + pkt->entry_count - - rsp->length - 1; + if (rsp_q_in < start_pkt_ring_index) + /* q in ptr is wrapped */ + iocb_cnt = rsp->length - start_pkt_ring_index + rsp_q_in; else - end_pkt_ring_index = start_pkt_ring_index + pkt->entry_count - 1; + iocb_cnt = rsp_q_in - start_pkt_ring_index; - end_pkt = rsp->ring + end_pkt_ring_index; - - /* next pkt = end_pkt + 1 */ - n_ring_index = end_pkt_ring_index + 1; - if (n_ring_index >= rsp->length) - n_ring_index = 0; - - rsp_q_in = rsp->qpair->use_shadow_reg ? *rsp->in_ptr : - rd_reg_dword(rsp->rsp_q_in); - - /* rsp_q_in is either wrapped or pointing beyond endpkt */ - if ((rsp_q_in < start_pkt_ring_index && rsp_q_in < n_ring_index) || - rsp_q_in >= n_ring_index) - /* all IOCBs arrived. */ - rc = 0; - else + if (iocb_cnt < pkt->entry_count) rc = -EIO; - ql_dbg(ql_dbg_init + ql_dbg_verbose, vha, 0x5091, - "%s - ring %p pkt %p end pkt %p entry count %#x rsp_q_in %d rc %d\n", - __func__, rsp->ring, pkt, end_pkt, pkt->entry_count, - rsp_q_in, rc); + ql_dbg(ql_dbg_init, vha, 0x5091, + "%s - ring %p pkt %p entry count %d iocb_cnt %d rsp_q_in %d rc %d\n", + __func__, rsp->ring, pkt, pkt->entry_count, iocb_cnt, rsp_q_in, rc); return rc; } @@ -3772,6 +3763,8 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, struct qla_hw_data *ha = vha->hw; struct purex_entry_24xx *purex_entry; struct purex_item *pure_item; + u16 rsp_in = 0, cur_ring_index; + int follow_inptr, is_shadow_hba; if (!ha->flags.fw_started) return; @@ -3781,8 +3774,27 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, qla_cpu_update(rsp->qpair, smp_processor_id()); } - while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) { +#define __update_rsp_in(_update, _is_shadow_hba, _rsp, _rsp_in) \ + do { \ + if (_update) { \ + _rsp_in = _is_shadow_hba ? *(_rsp)->in_ptr : \ + rd_reg_dword_relaxed((_rsp)->rsp_q_in); \ + } \ + } while (0) + + is_shadow_hba = IS_SHADOW_REG_CAPABLE(ha); + follow_inptr = is_shadow_hba ? ql2xrspq_follow_inptr : + ql2xrspq_follow_inptr_legacy; + + __update_rsp_in(follow_inptr, is_shadow_hba, rsp, rsp_in); + + while ((likely(follow_inptr && + rsp->ring_index != rsp_in && + rsp->ring_ptr->signature != RESPONSE_PROCESSED)) || + (!follow_inptr && + rsp->ring_ptr->signature != RESPONSE_PROCESSED)) { pkt = (struct sts_entry_24xx *)rsp->ring_ptr; + cur_ring_index = rsp->ring_index; rsp->ring_index++; if (rsp->ring_index == rsp->length) { @@ -3894,6 +3906,8 @@ process_err: } pure_item = qla27xx_copy_fpin_pkt(vha, (void **)&pkt, &rsp); + __update_rsp_in(follow_inptr, is_shadow_hba, + rsp, rsp_in); if (!pure_item) break; qla24xx_queue_purex_item(vha, pure_item, @@ -3901,7 +3915,17 @@ process_err: break; case ELS_AUTH_ELS: - if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt)) { + if (qla_chk_cont_iocb_avail(vha, rsp, (response_t *)pkt, rsp_in)) { + /* + * ring_ptr and ring_index were + * pre-incremented above. Reset them + * back to current. Wait for next + * interrupt with all IOCBs to arrive + * and re-process. + */ + rsp->ring_ptr = (response_t *)pkt; + rsp->ring_index = cur_ring_index; + ql_dbg(ql_dbg_init, vha, 0x5091, "Defer processing ELS opcode %#x...\n", purex_entry->els_frame_payload[3]); @@ -4420,16 +4444,12 @@ msix_register_fail: } /* Enable MSI-X vector for response queue update for queue 0 */ - if (IS_QLA83XX(ha) || IS_QLA27XX(ha) || IS_QLA28XX(ha)) { - if (ha->msixbase && ha->mqiobase && - (ha->max_rsp_queues > 1 || ha->max_req_queues > 1 || - ql2xmqsupport)) - ha->mqenable = 1; - } else - if (ha->mqiobase && - (ha->max_rsp_queues > 1 || ha->max_req_queues > 1 || - ql2xmqsupport)) - ha->mqenable = 1; + if (IS_MQUE_CAPABLE(ha) && + (ha->msixbase && ha->mqiobase && ha->max_qpairs)) + ha->mqenable = 1; + else + ha->mqenable = 0; + ql_dbg(ql_dbg_multiq, vha, 0xc005, "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n", ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 892caf2475df..359595a64664 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -238,6 +238,8 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) ql_dbg(ql_dbg_mbx, vha, 0x1112, "mbox[%d]<-0x%04x\n", cnt, *iptr); wrt_reg_word(optr, *iptr); + } else { + wrt_reg_word(optr, 0); } mboxes >>= 1; @@ -274,6 +276,12 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) atomic_inc(&ha->num_pend_mbx_stage3); if (!wait_for_completion_timeout(&ha->mbx_intr_comp, mcp->tov * HZ)) { + ql_dbg(ql_dbg_mbx, vha, 0x117a, + "cmd=%x Timeout.\n", command); + spin_lock_irqsave(&ha->hardware_lock, flags); + clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (chip_reset != ha->chip_reset) { eeh_delay = ha->flags.eeh_busy ? 1 : 0; @@ -286,12 +294,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) rval = QLA_ABORTED; goto premature_exit; } - ql_dbg(ql_dbg_mbx, vha, 0x117a, - "cmd=%x Timeout.\n", command); - spin_lock_irqsave(&ha->hardware_lock, flags); - clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - } else if (ha->flags.purge_mbox || chip_reset != ha->chip_reset) { eeh_delay = ha->flags.eeh_busy ? 1 : 0; @@ -3066,7 +3068,8 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha) * Kernel context. */ int -qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map) +qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map, + u8 *num_entries) { int rval; mbx_cmd_t mc; @@ -3106,6 +3109,8 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map) if (pos_map) memcpy(pos_map, pmap, FCAL_MAP_SIZE); + if (num_entries) + *num_entries = pmap[0]; } dma_pool_free(ha->s_dma_pool, pmap, pmap_dma); @@ -6471,6 +6476,54 @@ qla26xx_dport_diagnostics(scsi_qla_host_t *vha, return rval; } +int +qla26xx_dport_diagnostics_v2(scsi_qla_host_t *vha, + struct qla_dport_diag_v2 *dd, mbx_cmd_t *mcp) +{ + int rval; + dma_addr_t dd_dma; + uint size = sizeof(dd->buf); + uint16_t options = dd->options; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x119f, + "Entered %s.\n", __func__); + + dd_dma = dma_map_single(&vha->hw->pdev->dev, + dd->buf, size, DMA_FROM_DEVICE); + if (dma_mapping_error(&vha->hw->pdev->dev, dd_dma)) { + ql_log(ql_log_warn, vha, 0x1194, + "Failed to map dma buffer.\n"); + return QLA_MEMORY_ALLOC_FAILED; + } + + memset(dd->buf, 0, size); + + mcp->mb[0] = MBC_DPORT_DIAGNOSTICS; + mcp->mb[1] = options; + mcp->mb[2] = MSW(LSD(dd_dma)); + mcp->mb[3] = LSW(LSD(dd_dma)); + mcp->mb[6] = MSW(MSD(dd_dma)); + mcp->mb[7] = LSW(MSD(dd_dma)); + mcp->mb[8] = size; + mcp->out_mb = MBX_8 | MBX_7 | MBX_6 | MBX_3 | MBX_2 | MBX_1 | MBX_0; + mcp->in_mb = MBX_3 | MBX_2 | MBX_1 | MBX_0; + mcp->buf_size = size; + mcp->flags = MBX_DMA_IN; + mcp->tov = MBX_TOV_SECONDS * 4; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1195, "Failed=%x.\n", rval); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1196, + "Done %s.\n", __func__); + } + + dma_unmap_single(&vha->hw->pdev->dev, dd_dma, size, DMA_FROM_DEVICE); + + return rval; +} + static void qla2x00_async_mb_sp_done(srb_t *sp, int res) { sp->u.iocb_cmd.u.mbx.rc = res; diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 346d47b61c07..16a9f22bb860 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -166,9 +166,13 @@ qla24xx_disable_vp(scsi_qla_host_t *vha) int ret = QLA_SUCCESS; fc_port_t *fcport; - if (vha->hw->flags.edif_enabled) + if (vha->hw->flags.edif_enabled) { + if (DBELL_ACTIVE(vha)) + qla2x00_post_aen_work(vha, FCH_EVT_VENDOR_UNIQUE, + FCH_EVT_VENDOR_UNIQUE_VPORT_DOWN); /* delete sessions and flush sa_indexes */ qla2x00_wait_for_sess_deletion(vha); + } if (vha->hw->flags.fw_started) ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); diff --git a/drivers/scsi/qla2xxx/qla_nvme.c b/drivers/scsi/qla2xxx/qla_nvme.c index 87c9404aa401..7450c3458be7 100644 --- a/drivers/scsi/qla2xxx/qla_nvme.c +++ b/drivers/scsi/qla2xxx/qla_nvme.c @@ -37,11 +37,6 @@ int qla_nvme_register_remote(struct scsi_qla_host *vha, struct fc_port *fcport) (fcport->nvme_flag & NVME_FLAG_REGISTERED)) return 0; - if (atomic_read(&fcport->state) == FCS_ONLINE) - return 0; - - qla2x00_set_fcport_state(fcport, FCS_ONLINE); - fcport->nvme_flag &= ~NVME_FLAG_RESETTING; memset(&req, 0, sizeof(struct nvme_fc_port_info)); diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 73073fb08369..0bd0fd1042df 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -333,6 +333,21 @@ MODULE_PARM_DESC(ql2xabts_wait_nvme, "To wait for ABTS response on I/O timeouts for NVMe. (default: 1)"); +u32 ql2xdelay_before_pci_error_handling = 5; +module_param(ql2xdelay_before_pci_error_handling, uint, 0644); +MODULE_PARM_DESC(ql2xdelay_before_pci_error_handling, + "Number of seconds delayed before qla begin PCI error self-handling (default: 5).\n"); + +int ql2xrspq_follow_inptr = 1; +module_param(ql2xrspq_follow_inptr, int, 0644); +MODULE_PARM_DESC(ql2xrspq_follow_inptr, + "Follow RSP IN pointer for RSP updates for HBAs 27xx and newer (default: 1)."); + +int ql2xrspq_follow_inptr_legacy = 1; +module_param(ql2xrspq_follow_inptr_legacy, int, 0644); +MODULE_PARM_DESC(ql2xrspq_follow_inptr_legacy, + "Follow RSP IN pointer for RSP updates for HBAs older than 27XX. (default: 1)."); + static void qla2x00_clear_drv_active(struct qla_hw_data *); static void qla2x00_free_device(scsi_qla_host_t *); static int qla2xxx_map_queues(struct Scsi_Host *shost); @@ -1337,21 +1352,20 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) /* * Returns: QLA_SUCCESS or QLA_FUNCTION_FAILED. */ -int -qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, - uint64_t l, enum nexus_wait_type type) +static int +__qla2x00_eh_wait_for_pending_commands(struct qla_qpair *qpair, unsigned int t, + uint64_t l, enum nexus_wait_type type) { int cnt, match, status; unsigned long flags; - struct qla_hw_data *ha = vha->hw; - struct req_que *req; + scsi_qla_host_t *vha = qpair->vha; + struct req_que *req = qpair->req; srb_t *sp; struct scsi_cmnd *cmd; status = QLA_SUCCESS; - spin_lock_irqsave(&ha->hardware_lock, flags); - req = vha->req; + spin_lock_irqsave(qpair->qp_lock_ptr, flags); for (cnt = 1; status == QLA_SUCCESS && cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; @@ -1378,15 +1392,35 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, if (!match) continue; - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(qpair->qp_lock_ptr, flags); status = qla2x00_eh_wait_on_command(cmd); - spin_lock_irqsave(&ha->hardware_lock, flags); + spin_lock_irqsave(qpair->qp_lock_ptr, flags); } - spin_unlock_irqrestore(&ha->hardware_lock, flags); + spin_unlock_irqrestore(qpair->qp_lock_ptr, flags); return status; } +int +qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, + uint64_t l, enum nexus_wait_type type) +{ + struct qla_qpair *qpair; + struct qla_hw_data *ha = vha->hw; + int i, status = QLA_SUCCESS; + + status = __qla2x00_eh_wait_for_pending_commands(ha->base_qpair, t, l, + type); + for (i = 0; status == QLA_SUCCESS && i < ha->max_qpairs; i++) { + qpair = ha->queue_pair_map[i]; + if (!qpair) + continue; + status = __qla2x00_eh_wait_for_pending_commands(qpair, t, l, + type); + } + return status; +} + static char *reset_errors[] = { "HBA not online", "HBA not ready", @@ -1420,7 +1454,7 @@ qla2xxx_eh_device_reset(struct scsi_cmnd *cmd) return err; if (fcport->deleted) - return SUCCESS; + return FAILED; ql_log(ql_log_info, vha, 0x8009, "DEVICE RESET ISSUED nexus=%ld:%d:%llu cmd=%p.\n", vha->host_no, @@ -1488,7 +1522,7 @@ qla2xxx_eh_target_reset(struct scsi_cmnd *cmd) return err; if (fcport->deleted) - return SUCCESS; + return FAILED; ql_log(ql_log_info, vha, 0x8009, "TARGET RESET ISSUED nexus=%ld:%d cmd=%p.\n", vha->host_no, @@ -5472,7 +5506,7 @@ qla2x00_do_work(struct scsi_qla_host *vha) e->u.fcport.fcport, false); break; case QLA_EVT_SA_REPLACE: - qla24xx_issue_sa_replace_iocb(vha, e); + rc = qla24xx_issue_sa_replace_iocb(vha, e); break; } @@ -7238,6 +7272,44 @@ static void qla_heart_beat(struct scsi_qla_host *vha, u16 dpc_started) } } +static void qla_wind_down_chip(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (!ha->flags.eeh_busy) + return; + if (ha->pci_error_state) + /* system is trying to recover */ + return; + + /* + * Current system is not handling PCIE error. At this point, this is + * best effort to wind down the adapter. + */ + if (time_after_eq(jiffies, ha->eeh_jif + ql2xdelay_before_pci_error_handling * HZ) && + !ha->flags.eeh_flush) { + ql_log(ql_log_info, vha, 0x9009, + "PCI Error detected, attempting to reset hardware.\n"); + + ha->isp_ops->reset_chip(vha); + ha->isp_ops->disable_intrs(ha); + + ha->flags.eeh_flush = EEH_FLUSH_RDY; + ha->eeh_jif = jiffies; + + } else if (ha->flags.eeh_flush == EEH_FLUSH_RDY && + time_after_eq(jiffies, ha->eeh_jif + 5 * HZ)) { + pci_clear_master(ha->pdev); + + /* flush all command */ + qla2x00_abort_isp_cleanup(vha); + ha->flags.eeh_flush = EEH_FLUSH_DONE; + + ql_log(ql_log_info, vha, 0x900a, + "PCI Error handling complete, all IOs aborted.\n"); + } +} + /************************************************************************** * qla2x00_timer * @@ -7261,6 +7333,8 @@ qla2x00_timer(struct timer_list *t) fc_port_t *fcport = NULL; if (ha->flags.eeh_busy) { + qla_wind_down_chip(vha); + ql_dbg(ql_dbg_timer, vha, 0x6000, "EEH = %d, restarting timer.\n", ha->flags.eeh_busy); @@ -7841,6 +7915,9 @@ void qla_pci_set_eeh_busy(struct scsi_qla_host *vha) spin_lock_irqsave(&base_vha->work_lock, flags); if (!ha->flags.eeh_busy) { + ha->eeh_jif = jiffies; + ha->flags.eeh_flush = 0; + ha->flags.eeh_busy = 1; do_cleanup = true; } diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index cb97f625970d..2b2f68288375 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -981,22 +981,6 @@ void qlt_free_session_done(struct work_struct *work) sess->send_els_logo); if (!IS_SW_RESV_ADDR(sess->d_id)) { - if (ha->flags.edif_enabled && - (!own || own->iocb.u.isp24.status_subcode == ELS_PLOGI)) { - sess->edif.authok = 0; - if (!ha->flags.host_shutting_down) { - ql_dbg(ql_dbg_edif, vha, 0x911e, - "%s wwpn %8phC calling qla2x00_release_all_sadb\n", - __func__, sess->port_name); - qla2x00_release_all_sadb(vha, sess); - } else { - ql_dbg(ql_dbg_edif, vha, 0x911e, - "%s bypassing release_all_sadb\n", - __func__); - } - qla_edif_clear_appdata(vha, sess); - qla_edif_sess_down(vha, sess); - } qla2x00_mark_device_lost(vha, sess, 0); if (sess->send_els_logo) { @@ -1042,6 +1026,25 @@ void qlt_free_session_done(struct work_struct *work) sess->nvme_flag |= NVME_FLAG_DELETING; qla_nvme_unregister_remote_port(sess); } + + if (ha->flags.edif_enabled && + (!own || (own && + own->iocb.u.isp24.status_subcode == ELS_PLOGI))) { + sess->edif.authok = 0; + if (!ha->flags.host_shutting_down) { + ql_dbg(ql_dbg_edif, vha, 0x911e, + "%s wwpn %8phC calling qla2x00_release_all_sadb\n", + __func__, sess->port_name); + qla2x00_release_all_sadb(vha, sess); + } else { + ql_dbg(ql_dbg_edif, vha, 0x911e, + "%s bypassing release_all_sadb\n", + __func__); + } + + qla_edif_clear_appdata(vha, sess); + qla_edif_sess_down(vha, sess); + } } /* diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index b09d7d2080c0..f3257d46b6d2 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -6,9 +6,9 @@ /* * Driver version */ -#define QLA2XXX_VERSION "10.02.07.400-k" +#define QLA2XXX_VERSION "10.02.07.800-k" #define QLA_DRIVER_MAJOR_VER 10 #define QLA_DRIVER_MINOR_VER 2 #define QLA_DRIVER_PATCH_VER 7 -#define QLA_DRIVER_BETA_VER 400 +#define QLA_DRIVER_BETA_VER 800 diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index b776cefc7cda..448748e3fba5 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -463,14 +463,12 @@ static void scsi_report_sense(struct scsi_device *sdev, evt_type = SDEV_EVT_LUN_CHANGE_REPORTED; scsi_report_lun_change(sdev); sdev_printk(KERN_WARNING, sdev, - "Warning! Received an indication that the " "LUN assignments on this target have " "changed. The Linux SCSI layer does not " "automatically remap LUN assignments.\n"); } else if (sshdr->asc == 0x3f) sdev_printk(KERN_WARNING, sdev, - "Warning! Received an indication that the " - "operating parameters on this target have " + "Operating parameters on this target have " "changed. The Linux SCSI layer does not " "automatically adjust these parameters.\n"); diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 17a617db9ae0..b59a71aedcd7 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -75,13 +75,6 @@ int scsi_init_sense_cache(struct Scsi_Host *shost) return ret; } -/* - * When to reinvoke queueing after a resource shortage. It's 3 msecs to - * not change behaviour from the previous unplug mechanism, experimentation - * may prove this needs changing. - */ -#define SCSI_QUEUE_DELAY 3 - static void scsi_set_blocked(struct scsi_cmnd *cmd, int reason) { @@ -1648,6 +1641,13 @@ static void scsi_mq_put_budget(struct request_queue *q, int budget_token) sbitmap_put(&sdev->budget_map, budget_token); } +/* + * When to reinvoke queueing after a resource shortage. It's 3 msecs to + * not change behaviour from the previous unplug mechanism, experimentation + * may prove this needs changing. + */ +#define SCSI_QUEUE_DELAY 3 + static int scsi_mq_get_budget(struct request_queue *q) { struct scsi_device *sdev = q->queuedata; diff --git a/drivers/scsi/scsi_transport_iscsi.c b/drivers/scsi/scsi_transport_iscsi.c index 5d21f07456c6..cd3db9684e52 100644 --- a/drivers/scsi/scsi_transport_iscsi.c +++ b/drivers/scsi/scsi_transport_iscsi.c @@ -1980,7 +1980,7 @@ static void __iscsi_unbind_session(struct work_struct *work) scsi_remove_target(&session->dev); if (session->ida_used) - ida_simple_remove(&iscsi_sess_ida, target_id); + ida_free(&iscsi_sess_ida, target_id); unbind_session_exit: iscsi_session_event(session, ISCSI_KEVENT_UNBIND_SESSION); @@ -2049,7 +2049,7 @@ int iscsi_add_session(struct iscsi_cls_session *session, unsigned int target_id) return -ENOMEM; if (target_id == ISCSI_MAX_TARGET) { - id = ida_simple_get(&iscsi_sess_ida, 0, 0, GFP_KERNEL); + id = ida_alloc(&iscsi_sess_ida, GFP_KERNEL); if (id < 0) { iscsi_cls_session_printk(KERN_ERR, session, @@ -2088,7 +2088,7 @@ release_dev: device_del(&session->dev); release_ida: if (session->ida_used) - ida_simple_remove(&iscsi_sess_ida, session->target_id); + ida_free(&iscsi_sess_ida, session->target_id); destroy_wq: destroy_workqueue(session->workq); return err; @@ -2143,8 +2143,6 @@ static int iscsi_iter_destroy_conn_fn(struct device *dev, void *data) return 0; iscsi_remove_conn(iscsi_dev_to_conn(dev)); - iscsi_put_conn(iscsi_dev_to_conn(dev)); - return 0; } @@ -2264,17 +2262,19 @@ static void iscsi_if_disconnect_bound_ep(struct iscsi_cls_conn *conn, } } -static int iscsi_if_stop_conn(struct iscsi_transport *transport, - struct iscsi_uevent *ev) +static int iscsi_if_stop_conn(struct iscsi_cls_conn *conn, int flag) { - int flag = ev->u.stop_conn.flag; - struct iscsi_cls_conn *conn; - - conn = iscsi_conn_lookup(ev->u.stop_conn.sid, ev->u.stop_conn.cid); - if (!conn) - return -EINVAL; - ISCSI_DBG_TRANS_CONN(conn, "iscsi if conn stop.\n"); + /* + * For offload, iscsid may not know about the ep like when iscsid is + * restarted or for kernel based session shutdown iscsid is not even + * up. For these cases, we do the disconnect now. + */ + mutex_lock(&conn->ep_mutex); + if (conn->ep) + iscsi_if_disconnect_bound_ep(conn, conn->ep, true); + mutex_unlock(&conn->ep_mutex); + /* * If this is a termination we have to call stop_conn with that flag * so the correct states get set. If we haven't run the work yet try to @@ -2284,16 +2284,6 @@ static int iscsi_if_stop_conn(struct iscsi_transport *transport, cancel_work_sync(&conn->cleanup_work); iscsi_stop_conn(conn, flag); } else { - /* - * For offload, when iscsid is restarted it won't know about - * existing endpoints so it can't do a ep_disconnect. We clean - * it up here for userspace. - */ - mutex_lock(&conn->ep_mutex); - if (conn->ep) - iscsi_if_disconnect_bound_ep(conn, conn->ep, true); - mutex_unlock(&conn->ep_mutex); - /* * Figure out if it was the kernel or userspace initiating this. */ @@ -2349,6 +2339,55 @@ static void iscsi_cleanup_conn_work_fn(struct work_struct *work) ISCSI_DBG_TRANS_CONN(conn, "cleanup done.\n"); } +static int iscsi_iter_force_destroy_conn_fn(struct device *dev, void *data) +{ + struct iscsi_transport *transport; + struct iscsi_cls_conn *conn; + + if (!iscsi_is_conn_dev(dev)) + return 0; + + conn = iscsi_dev_to_conn(dev); + transport = conn->transport; + + if (READ_ONCE(conn->state) != ISCSI_CONN_DOWN) + iscsi_if_stop_conn(conn, STOP_CONN_TERM); + + transport->destroy_conn(conn); + return 0; +} + +/** + * iscsi_force_destroy_session - destroy a session from the kernel + * @session: session to destroy + * + * Force the destruction of a session from the kernel. This should only be + * used when userspace is no longer running during system shutdown. + */ +void iscsi_force_destroy_session(struct iscsi_cls_session *session) +{ + struct iscsi_transport *transport = session->transport; + unsigned long flags; + + WARN_ON_ONCE(system_state == SYSTEM_RUNNING); + + spin_lock_irqsave(&sesslock, flags); + if (list_empty(&session->sess_list)) { + spin_unlock_irqrestore(&sesslock, flags); + /* + * Conn/ep is already freed. Session is being torn down via + * async path. For shutdown we don't care about it so return. + */ + return; + } + spin_unlock_irqrestore(&sesslock, flags); + + device_for_each_child(&session->dev, NULL, + iscsi_iter_force_destroy_conn_fn); + transport->destroy_session(session); +} +EXPORT_SYMBOL_GPL(iscsi_force_destroy_session); + void iscsi_free_session(struct iscsi_cls_session *session) { ISCSI_DBG_TRANS_SESSION(session, "Freeing session\n"); @@ -3720,7 +3759,12 @@ static int iscsi_if_transport_conn(struct iscsi_transport *transport, case ISCSI_UEVENT_DESTROY_CONN: return iscsi_if_destroy_conn(transport, ev); case ISCSI_UEVENT_STOP_CONN: - return iscsi_if_stop_conn(transport, ev); + conn = iscsi_conn_lookup(ev->u.stop_conn.sid, + ev->u.stop_conn.cid); + if (!conn) + return -EINVAL; + + return iscsi_if_stop_conn(conn, ev->u.stop_conn.flag); } /* @@ -4812,7 +4856,7 @@ free_priv: } EXPORT_SYMBOL_GPL(iscsi_register_transport); -int iscsi_unregister_transport(struct iscsi_transport *tt) +void iscsi_unregister_transport(struct iscsi_transport *tt) { struct iscsi_internal *priv; unsigned long flags; @@ -4835,8 +4879,6 @@ int iscsi_unregister_transport(struct iscsi_transport *tt) sysfs_remove_group(&priv->dev.kobj, &iscsi_transport_group); device_unregister(&priv->dev); mutex_unlock(&rx_queue_mutex); - - return 0; } EXPORT_SYMBOL_GPL(iscsi_unregister_transport); diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index eb02d939dd44..6f7a39b96750 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -103,6 +103,7 @@ static void sd_config_discard(struct scsi_disk *, unsigned int); static void sd_config_write_same(struct scsi_disk *); static int sd_revalidate_disk(struct gendisk *); static void sd_unlock_native_capacity(struct gendisk *disk); +static void sd_start_done_work(struct work_struct *work); static int sd_probe(struct device *); static int sd_remove(struct device *); static void sd_shutdown(struct device *); @@ -3463,6 +3464,7 @@ static int sd_probe(struct device *dev) sdkp->max_retries = SD_MAX_RETRIES; atomic_set(&sdkp->openers, 0); atomic_set(&sdkp->device->ioerr_cnt, 0); + INIT_WORK(&sdkp->start_done_work, sd_start_done_work); if (!sdp->request_queue->rq_timeout) { if (sdp->type != TYPE_MOD) @@ -3585,12 +3587,69 @@ static void scsi_disk_release(struct device *dev) kfree(sdkp); } +/* Process sense data after a START command finished. */ +static void sd_start_done_work(struct work_struct *work) +{ + struct scsi_disk *sdkp = container_of(work, typeof(*sdkp), + start_done_work); + struct scsi_sense_hdr sshdr; + int res = sdkp->start_result; + + if (res == 0) + return; + + sd_print_result(sdkp, "Start/Stop Unit failed", res); + + if (res < 0) + return; + + if (scsi_normalize_sense(sdkp->start_sense_buffer, + sdkp->start_sense_len, &sshdr)) + sd_print_sense_hdr(sdkp, &sshdr); +} + +/* A START command finished. May be called from interrupt context. */ +static void sd_start_done(struct request *req, blk_status_t status) +{ + const struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req); + struct scsi_disk *sdkp = scsi_disk(req->q->disk); + + sdkp->start_result = scmd->result; + WARN_ON_ONCE(scmd->sense_len > SCSI_SENSE_BUFFERSIZE); + sdkp->start_sense_len = scmd->sense_len; + memcpy(sdkp->start_sense_buffer, scmd->sense_buffer, + ARRAY_SIZE(sdkp->start_sense_buffer)); + WARN_ON_ONCE(!schedule_work(&sdkp->start_done_work)); +} + +/* Submit a START command asynchronously. */ +static int sd_submit_start(struct scsi_disk *sdkp, u8 cmd[], u8 cmd_len) +{ + struct scsi_device *sdev = sdkp->device; + struct request_queue *q = sdev->request_queue; + struct request *req; + struct scsi_cmnd *scmd; + + req = scsi_alloc_request(q, REQ_OP_DRV_IN, BLK_MQ_REQ_PM); + if (IS_ERR(req)) + return PTR_ERR(req); + + scmd = blk_mq_rq_to_pdu(req); + scmd->cmd_len = cmd_len; + memcpy(scmd->cmnd, cmd, cmd_len); + scmd->allowed = sdkp->max_retries; + req->timeout = SD_TIMEOUT; + req->rq_flags |= RQF_PM | RQF_QUIET; + req->end_io = sd_start_done; + blk_execute_rq_nowait(req, /*at_head=*/true); + + return 0; +} + static int sd_start_stop_device(struct scsi_disk *sdkp, int start) { unsigned char cmd[6] = { START_STOP }; /* START_VALID */ - struct scsi_sense_hdr sshdr; struct scsi_device *sdp = sdkp->device; - int res; if (start) cmd[4] |= 1; /* START */ @@ -3601,23 +3660,10 @@ static int sd_start_stop_device(struct scsi_disk *sdkp, int start) if (!scsi_device_online(sdp)) return -ENODEV; - res = scsi_execute(sdp, cmd, DMA_NONE, NULL, 0, NULL, &sshdr, - SD_TIMEOUT, sdkp->max_retries, 0, RQF_PM, NULL); - if (res) { - sd_print_result(sdkp, "Start/Stop Unit failed", res); - if (res > 0 && scsi_sense_valid(&sshdr)) { - sd_print_sense_hdr(sdkp, &sshdr); - /* 0x3a is medium not present */ - if (sshdr.asc == 0x3a) - res = 0; - } - } + /* Wait until processing of sense data has finished. */ + flush_work(&sdkp->start_done_work); - /* SCSI error codes must not go to the generic layer */ - if (res) - return -EIO; - - return 0; + return sd_submit_start(sdkp, cmd, sizeof(cmd)); } /* @@ -3644,6 +3690,8 @@ static void sd_shutdown(struct device *dev) sd_printk(KERN_NOTICE, sdkp, "Stopping disk\n"); sd_start_stop_device(sdkp, 0); } + + flush_work(&sdkp->start_done_work); } static int sd_suspend_common(struct device *dev, bool ignore_stop_errors) diff --git a/drivers/scsi/sd.h b/drivers/scsi/sd.h index 5eea762f84d1..b89187761d61 100644 --- a/drivers/scsi/sd.h +++ b/drivers/scsi/sd.h @@ -150,6 +150,11 @@ struct scsi_disk { unsigned urswrz : 1; unsigned security : 1; unsigned ignore_medium_access_errors : 1; + + int start_result; + u32 start_sense_len; + u8 start_sense_buffer[SCSI_SENSE_BUFFERSIZE]; + struct work_struct start_done_work; }; #define to_scsi_disk(obj) container_of(obj, struct scsi_disk, disk_dev) diff --git a/drivers/scsi/sg.c b/drivers/scsi/sg.c index 118c7b4a8af2..340b050ad28d 100644 --- a/drivers/scsi/sg.c +++ b/drivers/scsi/sg.c @@ -195,7 +195,7 @@ static void sg_link_reserve(Sg_fd * sfp, Sg_request * srp, int size); static void sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp); static Sg_fd *sg_add_sfp(Sg_device * sdp); static void sg_remove_sfp(struct kref *); -static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id); +static Sg_request *sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy); static Sg_request *sg_add_request(Sg_fd * sfp); static int sg_remove_request(Sg_fd * sfp, Sg_request * srp); static Sg_device *sg_get_dev(int dev); @@ -444,6 +444,7 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) Sg_fd *sfp; Sg_request *srp; int req_pack_id = -1; + bool busy; sg_io_hdr_t *hp; struct sg_header *old_hdr; int retval; @@ -466,20 +467,16 @@ sg_read(struct file *filp, char __user *buf, size_t count, loff_t * ppos) if (retval) return retval; - srp = sg_get_rq_mark(sfp, req_pack_id); + srp = sg_get_rq_mark(sfp, req_pack_id, &busy); if (!srp) { /* now wait on packet to arrive */ - if (atomic_read(&sdp->detaching)) - return -ENODEV; if (filp->f_flags & O_NONBLOCK) return -EAGAIN; retval = wait_event_interruptible(sfp->read_wait, - (atomic_read(&sdp->detaching) || - (srp = sg_get_rq_mark(sfp, req_pack_id)))); - if (atomic_read(&sdp->detaching)) - return -ENODEV; - if (retval) - /* -ERESTARTSYS as signal hit process */ - return retval; + ((srp = sg_get_rq_mark(sfp, req_pack_id, &busy)) || + (!busy && atomic_read(&sdp->detaching)))); + if (!srp) + /* signal or detaching */ + return retval ? retval : -ENODEV; } if (srp->header.interface_id != '\0') return sg_new_read(sfp, buf, count, srp); @@ -940,9 +937,7 @@ sg_ioctl_common(struct file *filp, Sg_device *sdp, Sg_fd *sfp, if (result < 0) return result; result = wait_event_interruptible(sfp->read_wait, - (srp_done(sfp, srp) || atomic_read(&sdp->detaching))); - if (atomic_read(&sdp->detaching)) - return -ENODEV; + srp_done(sfp, srp)); write_lock_irq(&sfp->rq_list_lock); if (srp->done) { srp->done = 2; @@ -2079,19 +2074,28 @@ sg_unlink_reserve(Sg_fd * sfp, Sg_request * srp) } static Sg_request * -sg_get_rq_mark(Sg_fd * sfp, int pack_id) +sg_get_rq_mark(Sg_fd * sfp, int pack_id, bool *busy) { Sg_request *resp; unsigned long iflags; + *busy = false; write_lock_irqsave(&sfp->rq_list_lock, iflags); list_for_each_entry(resp, &sfp->rq_list, entry) { - /* look for requests that are ready + not SG_IO owned */ - if ((1 == resp->done) && (!resp->sg_io_owned) && + /* look for requests that are not SG_IO owned */ + if ((!resp->sg_io_owned) && ((-1 == pack_id) || (resp->header.pack_id == pack_id))) { - resp->done = 2; /* guard against other readers */ - write_unlock_irqrestore(&sfp->rq_list_lock, iflags); - return resp; + switch (resp->done) { + case 0: /* request active */ + *busy = true; + break; + case 1: /* request done; response ready to return */ + resp->done = 2; /* guard against other readers */ + write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + return resp; + case 2: /* response already being returned */ + break; + } } } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); @@ -2145,6 +2149,15 @@ sg_remove_request(Sg_fd * sfp, Sg_request * srp) res = 1; } write_unlock_irqrestore(&sfp->rq_list_lock, iflags); + + /* + * If the device is detaching, wakeup any readers in case we just + * removed the last response, which would leave nothing for them to + * return other than -ENODEV. + */ + if (unlikely(atomic_read(&sfp->parentdp->detaching))) + wake_up_interruptible_all(&sfp->read_wait); + return res; } diff --git a/drivers/scsi/smartpqi/Kconfig b/drivers/scsi/smartpqi/Kconfig index 6f83e2df4d64..973d240649ab 100644 --- a/drivers/scsi/smartpqi/Kconfig +++ b/drivers/scsi/smartpqi/Kconfig @@ -1,7 +1,7 @@ # # Kernel configuration file for the SMARTPQI # -# Copyright (c) 2019-2021 Microchip Technology Inc. and its subsidiaries +# Copyright (c) 2019-2022 Microchip Technology Inc. and its subsidiaries # Copyright (c) 2017-2018 Microsemi Corporation # Copyright (c) 2016 Microsemi Corporation # Copyright (c) 2016 PMC-Sierra, Inc. diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index 2e40320129c0..e550b12e525a 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * driver for Microchip PQI-based storage controllers - * Copyright (c) 2019-2021 Microchip Technology Inc. and its subsidiaries + * Copyright (c) 2019-2022 Microchip Technology Inc. and its subsidiaries * Copyright (c) 2016-2018 Microsemi Corporation * Copyright (c) 2016 PMC-Sierra, Inc. * @@ -293,7 +293,8 @@ struct pqi_raid_path_request { u8 additional_cdb_bytes_usage : 3; u8 reserved5 : 3; u8 cdb[16]; - u8 reserved6[12]; + u8 reserved6[11]; + u8 ml_device_lun_number; __le32 timeout; struct pqi_sg_descriptor sg_descriptors[PQI_MAX_EMBEDDED_SG_DESCRIPTORS]; }; @@ -467,7 +468,8 @@ struct pqi_task_management_request { struct pqi_iu_header header; __le16 request_id; __le16 nexus_id; - u8 reserved[2]; + u8 reserved; + u8 ml_device_lun_number; __le16 timeout; u8 lun_number[8]; __le16 protocol_specific; @@ -708,6 +710,7 @@ typedef u32 pqi_index_t; #define SOP_TMF_COMPLETE 0x0 #define SOP_TMF_REJECTED 0x4 #define SOP_TMF_FUNCTION_SUCCEEDED 0x8 +#define SOP_RC_INCORRECT_LOGICAL_UNIT 0x9 /* additional CDB bytes usage field codes */ #define SOP_ADDITIONAL_CDB_BYTES_0 0 /* 16-byte CDB */ @@ -863,7 +866,8 @@ struct pqi_config_table_firmware_features { #define PQI_FIRMWARE_FEATURE_UNIQUE_WWID_IN_REPORT_PHYS_LUN 16 #define PQI_FIRMWARE_FEATURE_FW_TRIAGE 17 #define PQI_FIRMWARE_FEATURE_RPL_EXTENDED_FORMAT_4_5 18 -#define PQI_FIRMWARE_FEATURE_MAXIMUM 18 +#define PQI_FIRMWARE_FEATURE_MULTI_LUN_DEVICE_SUPPORT 21 +#define PQI_FIRMWARE_FEATURE_MAXIMUM 21 struct pqi_config_table_debug { struct pqi_config_table_section_header header; @@ -1081,6 +1085,8 @@ struct pqi_stream_data { u32 last_accessed; }; +#define PQI_MAX_LUNS_PER_DEVICE 256 + struct pqi_scsi_dev { int devtype; /* as reported by INQUIRY command */ u8 device_type; /* as reported by */ @@ -1124,6 +1130,7 @@ struct pqi_scsi_dev { u8 phy_id; u8 ncq_prio_enable; u8 ncq_prio_support; + u8 multi_lun_device_lun_count; bool raid_bypass_configured; /* RAID bypass configured */ bool raid_bypass_enabled; /* RAID bypass enabled */ u32 next_bypass_group[RAID_MAP_MAX_DATA_DISKS_PER_ROW]; @@ -1139,7 +1146,7 @@ struct pqi_scsi_dev { struct list_head delete_list_entry; struct pqi_stream_data stream_data[NUM_STREAMS_PER_LUN]; - atomic_t scsi_cmds_outstanding; + atomic_t scsi_cmds_outstanding[PQI_MAX_LUNS_PER_DEVICE]; atomic_t raid_bypass_cnt; }; @@ -1262,6 +1269,12 @@ struct pqi_event { #define PQI_CTRL_PRODUCT_REVISION_A 0 #define PQI_CTRL_PRODUCT_REVISION_B 1 +enum pqi_ctrl_removal_state { + PQI_CTRL_PRESENT = 0, + PQI_CTRL_GRACEFUL_REMOVAL, + PQI_CTRL_SURPRISE_REMOVAL +}; + struct pqi_ctrl_info { unsigned int ctrl_id; struct pci_dev *pci_dev; @@ -1332,12 +1345,13 @@ struct pqi_ctrl_info { u8 tmf_iu_timeout_supported : 1; u8 firmware_triage_supported : 1; u8 rpl_extended_format_4_5_supported : 1; + u8 multi_lun_device_supported : 1; u8 enable_r1_writes : 1; u8 enable_r5_writes : 1; u8 enable_r6_writes : 1; u8 lv_drive_type_mix_valid : 1; u8 enable_stream_detection : 1; - + u8 disable_managed_interrupts : 1; u8 ciss_report_log_flags; u32 max_transfer_encrypted_sas_sata; u32 max_transfer_encrypted_nvme; @@ -1381,6 +1395,7 @@ struct pqi_ctrl_info { struct work_struct ofa_quiesce_work; u32 ofa_bytes_requested; u16 ofa_cancel_reason; + enum pqi_ctrl_removal_state ctrl_removal_state; }; enum pqi_ctrl_mode { diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 7c0d069a3158..7a8c2c75acba 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * driver for Microchip PQI-based storage controllers - * Copyright (c) 2019-2021 Microchip Technology Inc. and its subsidiaries + * Copyright (c) 2019-2022 Microchip Technology Inc. and its subsidiaries * Copyright (c) 2016-2018 Microsemi Corporation * Copyright (c) 2016 PMC-Sierra, Inc. * @@ -33,11 +33,11 @@ #define BUILD_TIMESTAMP #endif -#define DRIVER_VERSION "2.1.14-035" +#define DRIVER_VERSION "2.1.18-045" #define DRIVER_MAJOR 2 #define DRIVER_MINOR 1 -#define DRIVER_RELEASE 14 -#define DRIVER_REVISION 35 +#define DRIVER_RELEASE 18 +#define DRIVER_REVISION 45 #define DRIVER_NAME "Microchip SmartPQI Driver (v" \ DRIVER_VERSION BUILD_TIMESTAMP ")" @@ -94,7 +94,8 @@ static void pqi_ofa_setup_host_buffer(struct pqi_ctrl_info *ctrl_info); static void pqi_ofa_free_host_buffer(struct pqi_ctrl_info *ctrl_info); static int pqi_ofa_host_memory_update(struct pqi_ctrl_info *ctrl_info); static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info, - struct pqi_scsi_dev *device, unsigned long timeout_msecs); + struct pqi_scsi_dev *device, u8 lun, unsigned long timeout_msecs); +static void pqi_fail_all_outstanding_requests(struct pqi_ctrl_info *ctrl_info); /* for flags argument to pqi_submit_raid_request_synchronous() */ #define PQI_SYNC_FLAGS_INTERRUPTABLE 0x1 @@ -174,6 +175,18 @@ module_param_named(hide_vsep, pqi_hide_vsep, int, 0644); MODULE_PARM_DESC(hide_vsep, "Hide the virtual SEP for direct attached drives."); +static int pqi_disable_managed_interrupts; +module_param_named(disable_managed_interrupts, + pqi_disable_managed_interrupts, int, 0644); +MODULE_PARM_DESC(disable_managed_interrupts, + "Disable the kernel automatically assigning SMP affinity to IRQs."); + +static unsigned int pqi_ctrl_ready_timeout_secs; +module_param_named(ctrl_ready_timeout, + pqi_ctrl_ready_timeout_secs, uint, 0644); +MODULE_PARM_DESC(ctrl_ready_timeout, + "Timeout in seconds for driver to wait for controller ready."); + static char *raid_levels[] = { "RAID-0", "RAID-4", @@ -1597,7 +1610,9 @@ static int pqi_get_physical_device_info(struct pqi_ctrl_info *ctrl_info, &id_phys->alternate_paths_phys_connector, sizeof(device->phys_connector)); device->bay = id_phys->phys_bay_in_box; - + device->multi_lun_device_lun_count = id_phys->multi_lun_device_lun_count; + if (!device->multi_lun_device_lun_count) + device->multi_lun_device_lun_count = 1; if ((id_phys->even_more_flags & PQI_DEVICE_PHY_MAP_SUPPORTED) && id_phys->phy_count) device->phy_id = @@ -1880,15 +1895,18 @@ static int pqi_add_device(struct pqi_ctrl_info *ctrl_info, static inline void pqi_remove_device(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device) { int rc; + int lun; - rc = pqi_device_wait_for_pending_io(ctrl_info, device, - PQI_REMOVE_DEVICE_PENDING_IO_TIMEOUT_MSECS); - if (rc) - dev_err(&ctrl_info->pci_dev->dev, - "scsi %d:%d:%d:%d removing device with %d outstanding command(s)\n", - ctrl_info->scsi_host->host_no, device->bus, - device->target, device->lun, - atomic_read(&device->scsi_cmds_outstanding)); + for (lun = 0; lun < device->multi_lun_device_lun_count; lun++) { + rc = pqi_device_wait_for_pending_io(ctrl_info, device, lun, + PQI_REMOVE_DEVICE_PENDING_IO_TIMEOUT_MSECS); + if (rc) + dev_err(&ctrl_info->pci_dev->dev, + "scsi %d:%d:%d:%d removing device with %d outstanding command(s)\n", + ctrl_info->scsi_host->host_no, device->bus, + device->target, lun, + atomic_read(&device->scsi_cmds_outstanding[lun])); + } if (pqi_is_logical_device(device)) scsi_remove_device(device->sdev); @@ -2020,6 +2038,23 @@ static void pqi_dev_info(struct pqi_ctrl_info *ctrl_info, dev_info(&ctrl_info->pci_dev->dev, "%s %s\n", action, buffer); } +static bool pqi_raid_maps_equal(struct raid_map *raid_map1, struct raid_map *raid_map2) +{ + u32 raid_map1_size; + u32 raid_map2_size; + + if (raid_map1 == NULL || raid_map2 == NULL) + return raid_map1 == raid_map2; + + raid_map1_size = get_unaligned_le32(&raid_map1->structure_size); + raid_map2_size = get_unaligned_le32(&raid_map2->structure_size); + + if (raid_map1_size != raid_map2_size) + return false; + + return memcmp(raid_map1, raid_map2, raid_map1_size) == 0; +} + /* Assumes the SCSI device list lock is held. */ static void pqi_scsi_update_device(struct pqi_ctrl_info *ctrl_info, @@ -2033,49 +2068,51 @@ static void pqi_scsi_update_device(struct pqi_ctrl_info *ctrl_info, existing_device->target_lun_valid = true; } - if (pqi_is_logical_device(existing_device) && - ctrl_info->logical_volume_rescan_needed) - existing_device->rescan = true; - /* By definition, the scsi3addr and wwid fields are already the same. */ existing_device->is_physical_device = new_device->is_physical_device; - existing_device->is_external_raid_device = - new_device->is_external_raid_device; - existing_device->is_expander_smp_device = - new_device->is_expander_smp_device; - existing_device->aio_enabled = new_device->aio_enabled; - memcpy(existing_device->vendor, new_device->vendor, - sizeof(existing_device->vendor)); - memcpy(existing_device->model, new_device->model, - sizeof(existing_device->model)); + memcpy(existing_device->vendor, new_device->vendor, sizeof(existing_device->vendor)); + memcpy(existing_device->model, new_device->model, sizeof(existing_device->model)); existing_device->sas_address = new_device->sas_address; - existing_device->raid_level = new_device->raid_level; existing_device->queue_depth = new_device->queue_depth; - existing_device->aio_handle = new_device->aio_handle; - existing_device->volume_status = new_device->volume_status; - existing_device->active_path_index = new_device->active_path_index; - existing_device->phy_id = new_device->phy_id; - existing_device->path_map = new_device->path_map; - existing_device->bay = new_device->bay; - existing_device->box_index = new_device->box_index; - existing_device->phys_box_on_bus = new_device->phys_box_on_bus; - existing_device->phy_connected_dev_type = new_device->phy_connected_dev_type; - memcpy(existing_device->box, new_device->box, - sizeof(existing_device->box)); - memcpy(existing_device->phys_connector, new_device->phys_connector, - sizeof(existing_device->phys_connector)); - memset(existing_device->next_bypass_group, 0, sizeof(existing_device->next_bypass_group)); - kfree(existing_device->raid_map); - existing_device->raid_map = new_device->raid_map; - existing_device->raid_bypass_configured = - new_device->raid_bypass_configured; - existing_device->raid_bypass_enabled = - new_device->raid_bypass_enabled; existing_device->device_offline = false; - /* To prevent this from being freed later. */ - new_device->raid_map = NULL; + if (pqi_is_logical_device(existing_device)) { + existing_device->is_external_raid_device = new_device->is_external_raid_device; + + if (existing_device->devtype == TYPE_DISK) { + existing_device->raid_level = new_device->raid_level; + existing_device->volume_status = new_device->volume_status; + if (ctrl_info->logical_volume_rescan_needed) + existing_device->rescan = true; + memset(existing_device->next_bypass_group, 0, sizeof(existing_device->next_bypass_group)); + if (!pqi_raid_maps_equal(existing_device->raid_map, new_device->raid_map)) { + kfree(existing_device->raid_map); + existing_device->raid_map = new_device->raid_map; + /* To prevent this from being freed later. */ + new_device->raid_map = NULL; + } + existing_device->raid_bypass_configured = new_device->raid_bypass_configured; + existing_device->raid_bypass_enabled = new_device->raid_bypass_enabled; + } + } else { + existing_device->aio_enabled = new_device->aio_enabled; + existing_device->aio_handle = new_device->aio_handle; + existing_device->is_expander_smp_device = new_device->is_expander_smp_device; + existing_device->active_path_index = new_device->active_path_index; + existing_device->phy_id = new_device->phy_id; + existing_device->path_map = new_device->path_map; + existing_device->bay = new_device->bay; + existing_device->box_index = new_device->box_index; + existing_device->phys_box_on_bus = new_device->phys_box_on_bus; + existing_device->phy_connected_dev_type = new_device->phy_connected_dev_type; + memcpy(existing_device->box, new_device->box, sizeof(existing_device->box)); + memcpy(existing_device->phys_connector, new_device->phys_connector, sizeof(existing_device->phys_connector)); + + existing_device->multi_lun_device_lun_count = new_device->multi_lun_device_lun_count; + if (existing_device->multi_lun_device_lun_count == 0) + existing_device->multi_lun_device_lun_count = 1; + } } static inline void pqi_free_device(struct pqi_scsi_dev *device) @@ -2505,23 +2542,6 @@ out: return rc; } -static void pqi_remove_all_scsi_devices(struct pqi_ctrl_info *ctrl_info) -{ - unsigned long flags; - struct pqi_scsi_dev *device; - struct pqi_scsi_dev *next; - - list_for_each_entry_safe(device, next, &ctrl_info->scsi_device_list, - scsi_device_list_entry) { - if (pqi_is_device_added(device)) - pqi_remove_device(ctrl_info, device); - spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); - list_del(&device->scsi_device_list_entry); - pqi_free_device(device); - spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); - } -} - static int pqi_scan_scsi_devices(struct pqi_ctrl_info *ctrl_info) { int rc; @@ -3322,6 +3342,9 @@ static int pqi_interpret_task_management_response(struct pqi_ctrl_info *ctrl_inf case SOP_TMF_REJECTED: rc = -EAGAIN; break; + case SOP_RC_INCORRECT_LOGICAL_UNIT: + rc = -ENODEV; + break; default: rc = -EIO; break; @@ -3663,6 +3686,20 @@ static bool pqi_ofa_process_event(struct pqi_ctrl_info *ctrl_info, return ack_event; } +static void pqi_disable_raid_bypass(struct pqi_ctrl_info *ctrl_info) +{ + unsigned long flags; + struct pqi_scsi_dev *device; + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + + list_for_each_entry(device, &ctrl_info->scsi_device_list, scsi_device_list_entry) + if (device->raid_bypass_enabled) + device->raid_bypass_enabled = false; + + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); +} + static void pqi_event_worker(struct work_struct *work) { unsigned int i; @@ -3690,6 +3727,8 @@ static void pqi_event_worker(struct work_struct *work) rescan_needed = true; if (event->event_type == PQI_EVENT_TYPE_LOGICAL_DEVICE) ctrl_info->logical_volume_rescan_needed = true; + else if (event->event_type == PQI_EVENT_TYPE_AIO_STATE_CHANGE) + pqi_disable_raid_bypass(ctrl_info); } if (ack_event) pqi_acknowledge_event(ctrl_info, event); @@ -3697,8 +3736,11 @@ static void pqi_event_worker(struct work_struct *work) event++; } +#define PQI_RESCAN_WORK_FOR_EVENT_DELAY (5 * HZ) + if (rescan_needed) - pqi_schedule_rescan_worker_delayed(ctrl_info); + pqi_schedule_rescan_worker_with_delay(ctrl_info, + PQI_RESCAN_WORK_FOR_EVENT_DELAY); out: pqi_ctrl_unbusy(ctrl_info); @@ -3992,10 +4034,14 @@ static void pqi_free_irqs(struct pqi_ctrl_info *ctrl_info) static int pqi_enable_msix_interrupts(struct pqi_ctrl_info *ctrl_info) { int num_vectors_enabled; + unsigned int flags = PCI_IRQ_MSIX; + + if (!pqi_disable_managed_interrupts) + flags |= PCI_IRQ_AFFINITY; num_vectors_enabled = pci_alloc_irq_vectors(ctrl_info->pci_dev, PQI_MIN_MSIX_VECTORS, ctrl_info->num_queue_groups, - PCI_IRQ_MSIX | PCI_IRQ_AFFINITY); + flags); if (num_vectors_enabled < 0) { dev_err(&ctrl_info->pci_dev->dev, "MSI-X init failed with error %d\n", @@ -5457,6 +5503,7 @@ static int pqi_raid_submit_scsi_cmd_with_io_request( put_unaligned_le16(io_request->index, &request->request_id); request->error_index = request->request_id; memcpy(request->lun_number, device->scsi3addr, sizeof(request->lun_number)); + request->ml_device_lun_number = (u8)scmd->device->lun; cdb_length = min_t(size_t, scmd->cmd_len, sizeof(request->cdb)); memcpy(request->cdb, scmd->cmnd, cdb_length); @@ -5484,10 +5531,10 @@ static int pqi_raid_submit_scsi_cmd_with_io_request( } switch (scmd->sc_data_direction) { - case DMA_TO_DEVICE: + case DMA_FROM_DEVICE: request->data_direction = SOP_READ_FLAG; break; - case DMA_FROM_DEVICE: + case DMA_TO_DEVICE: request->data_direction = SOP_WRITE_FLAG; break; case DMA_NONE: @@ -5621,7 +5668,9 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, int rc; struct pqi_io_request *io_request; struct pqi_aio_path_request *request; + struct pqi_scsi_dev *device; + device = scmd->device->hostdata; io_request = pqi_alloc_io_request(ctrl_info); io_request->io_complete_callback = pqi_aio_io_complete; io_request->scmd = scmd; @@ -5637,6 +5686,8 @@ static int pqi_aio_submit_io(struct pqi_ctrl_info *ctrl_info, request->command_priority = io_high_prio; put_unaligned_le16(io_request->index, &request->request_id); request->error_index = request->request_id; + if (!pqi_is_logical_device(device) && ctrl_info->multi_lun_device_supported) + put_unaligned_le64(((scmd->device->lun) << 8), &request->lun_number); if (cdb_length > sizeof(request->cdb)) cdb_length = sizeof(request->cdb); request->cdb_length = cdb_length; @@ -5846,7 +5897,7 @@ void pqi_prep_for_scsi_done(struct scsi_cmnd *scmd) return; } - atomic_dec(&device->scsi_cmds_outstanding); + atomic_dec(&device->scsi_cmds_outstanding[scmd->device->lun]); } static bool pqi_is_parity_write_stream(struct pqi_ctrl_info *ctrl_info, @@ -5941,7 +5992,7 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scm return 0; } - atomic_inc(&device->scsi_cmds_outstanding); + atomic_inc(&device->scsi_cmds_outstanding[scmd->device->lun]); ctrl_info = shost_to_hba(shost); @@ -5987,7 +6038,7 @@ static int pqi_scsi_queue_command(struct Scsi_Host *shost, struct scsi_cmnd *scm out: if (rc) - atomic_dec(&device->scsi_cmds_outstanding); + atomic_dec(&device->scsi_cmds_outstanding[scmd->device->lun]); return rc; } @@ -6127,7 +6178,7 @@ static void pqi_fail_io_queued_for_device(struct pqi_ctrl_info *ctrl_info, #define PQI_PENDING_IO_WARNING_TIMEOUT_SECS 10 static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info, - struct pqi_scsi_dev *device, unsigned long timeout_msecs) + struct pqi_scsi_dev *device, u8 lun, unsigned long timeout_msecs) { int cmds_outstanding; unsigned long start_jiffies; @@ -6137,23 +6188,25 @@ static int pqi_device_wait_for_pending_io(struct pqi_ctrl_info *ctrl_info, start_jiffies = jiffies; warning_timeout = (PQI_PENDING_IO_WARNING_TIMEOUT_SECS * HZ) + start_jiffies; - while ((cmds_outstanding = atomic_read(&device->scsi_cmds_outstanding)) > 0) { - pqi_check_ctrl_health(ctrl_info); - if (pqi_ctrl_offline(ctrl_info)) - return -ENXIO; + while ((cmds_outstanding = atomic_read(&device->scsi_cmds_outstanding[lun])) > 0) { + if (ctrl_info->ctrl_removal_state != PQI_CTRL_GRACEFUL_REMOVAL) { + pqi_check_ctrl_health(ctrl_info); + if (pqi_ctrl_offline(ctrl_info)) + return -ENXIO; + } msecs_waiting = jiffies_to_msecs(jiffies - start_jiffies); if (msecs_waiting >= timeout_msecs) { dev_err(&ctrl_info->pci_dev->dev, "scsi %d:%d:%d:%d: timed out after %lu seconds waiting for %d outstanding command(s)\n", ctrl_info->scsi_host->host_no, device->bus, device->target, - device->lun, msecs_waiting / 1000, cmds_outstanding); + lun, msecs_waiting / 1000, cmds_outstanding); return -ETIMEDOUT; } if (time_after(jiffies, warning_timeout)) { dev_warn(&ctrl_info->pci_dev->dev, "scsi %d:%d:%d:%d: waiting %lu seconds for %d outstanding command(s)\n", ctrl_info->scsi_host->host_no, device->bus, device->target, - device->lun, msecs_waiting / 1000, cmds_outstanding); + lun, msecs_waiting / 1000, cmds_outstanding); warning_timeout = (PQI_PENDING_IO_WARNING_TIMEOUT_SECS * HZ) + jiffies; } usleep_range(1000, 2000); @@ -6173,7 +6226,7 @@ static void pqi_lun_reset_complete(struct pqi_io_request *io_request, #define PQI_LUN_RESET_POLL_COMPLETION_SECS 10 static int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info, - struct pqi_scsi_dev *device, struct completion *wait) + struct pqi_scsi_dev *device, u8 lun, struct completion *wait) { int rc; unsigned int wait_secs; @@ -6195,10 +6248,10 @@ static int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info, } wait_secs += PQI_LUN_RESET_POLL_COMPLETION_SECS; - cmds_outstanding = atomic_read(&device->scsi_cmds_outstanding); + cmds_outstanding = atomic_read(&device->scsi_cmds_outstanding[lun]); dev_warn(&ctrl_info->pci_dev->dev, "scsi %d:%d:%d:%d: waiting %u seconds for LUN reset to complete (%d command(s) outstanding)\n", - ctrl_info->scsi_host->host_no, device->bus, device->target, device->lun, wait_secs, cmds_outstanding); + ctrl_info->scsi_host->host_no, device->bus, device->target, lun, wait_secs, cmds_outstanding); } return rc; @@ -6206,13 +6259,15 @@ static int pqi_wait_for_lun_reset_completion(struct pqi_ctrl_info *ctrl_info, #define PQI_LUN_RESET_FIRMWARE_TIMEOUT_SECS 30 -static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device) +static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd) { int rc; struct pqi_io_request *io_request; DECLARE_COMPLETION_ONSTACK(wait); struct pqi_task_management_request *request; + struct pqi_scsi_dev *device; + device = scmd->device->hostdata; io_request = pqi_alloc_io_request(ctrl_info); io_request->io_complete_callback = pqi_lun_reset_complete; io_request->context = &wait; @@ -6226,6 +6281,8 @@ static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *d put_unaligned_le16(io_request->index, &request->request_id); memcpy(request->lun_number, device->scsi3addr, sizeof(request->lun_number)); + if (!pqi_is_logical_device(device) && ctrl_info->multi_lun_device_supported) + request->ml_device_lun_number = (u8)scmd->device->lun; request->task_management_function = SOP_TASK_MANAGEMENT_LUN_RESET; if (ctrl_info->tmf_iu_timeout_supported) put_unaligned_le16(PQI_LUN_RESET_FIRMWARE_TIMEOUT_SECS, &request->timeout); @@ -6233,7 +6290,7 @@ static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *d pqi_start_io(ctrl_info, &ctrl_info->queue_groups[PQI_DEFAULT_QUEUE_GROUP], RAID_PATH, io_request); - rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, &wait); + rc = pqi_wait_for_lun_reset_completion(ctrl_info, device, (u8)scmd->device->lun, &wait); if (rc == 0) rc = io_request->status; @@ -6247,16 +6304,18 @@ static int pqi_lun_reset(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *d #define PQI_LUN_RESET_PENDING_IO_TIMEOUT_MSECS (10 * 60 * 1000) #define PQI_LUN_RESET_FAILED_PENDING_IO_TIMEOUT_MSECS (2 * 60 * 1000) -static int pqi_lun_reset_with_retries(struct pqi_ctrl_info *ctrl_info, struct pqi_scsi_dev *device) +static int pqi_lun_reset_with_retries(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd) { int reset_rc; int wait_rc; unsigned int retries; unsigned long timeout_msecs; + struct pqi_scsi_dev *device; + device = scmd->device->hostdata; for (retries = 0;;) { - reset_rc = pqi_lun_reset(ctrl_info, device); - if (reset_rc == 0 || ++retries > PQI_LUN_RESET_RETRIES) + reset_rc = pqi_lun_reset(ctrl_info, scmd); + if (reset_rc == 0 || reset_rc == -ENODEV || ++retries > PQI_LUN_RESET_RETRIES) break; msleep(PQI_LUN_RESET_RETRY_INTERVAL_MSECS); } @@ -6264,18 +6323,19 @@ static int pqi_lun_reset_with_retries(struct pqi_ctrl_info *ctrl_info, struct pq timeout_msecs = reset_rc ? PQI_LUN_RESET_FAILED_PENDING_IO_TIMEOUT_MSECS : PQI_LUN_RESET_PENDING_IO_TIMEOUT_MSECS; - wait_rc = pqi_device_wait_for_pending_io(ctrl_info, device, timeout_msecs); + wait_rc = pqi_device_wait_for_pending_io(ctrl_info, device, scmd->device->lun, timeout_msecs); if (wait_rc && reset_rc == 0) reset_rc = wait_rc; return reset_rc == 0 ? SUCCESS : FAILED; } -static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, - struct pqi_scsi_dev *device) +static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, struct scsi_cmnd *scmd) { int rc; + struct pqi_scsi_dev *device; + device = scmd->device->hostdata; pqi_ctrl_block_requests(ctrl_info); pqi_ctrl_wait_until_quiesced(ctrl_info); pqi_fail_io_queued_for_device(ctrl_info, device); @@ -6283,7 +6343,7 @@ static int pqi_device_reset(struct pqi_ctrl_info *ctrl_info, if (rc) rc = FAILED; else - rc = pqi_lun_reset_with_retries(ctrl_info, device); + rc = pqi_lun_reset_with_retries(ctrl_info, scmd); pqi_ctrl_unblock_requests(ctrl_info); return rc; @@ -6305,18 +6365,18 @@ static int pqi_eh_device_reset_handler(struct scsi_cmnd *scmd) dev_err(&ctrl_info->pci_dev->dev, "resetting scsi %d:%d:%d:%d due to cmd 0x%02x\n", shost->host_no, - device->bus, device->target, device->lun, + device->bus, device->target, (u32)scmd->device->lun, scmd->cmd_len > 0 ? scmd->cmnd[0] : 0xff); pqi_check_ctrl_health(ctrl_info); if (pqi_ctrl_offline(ctrl_info)) rc = FAILED; else - rc = pqi_device_reset(ctrl_info, device); + rc = pqi_device_reset(ctrl_info, scmd); dev_err(&ctrl_info->pci_dev->dev, "reset of scsi %d:%d:%d:%d: %s\n", - shost->host_no, device->bus, device->target, device->lun, + shost->host_no, device->bus, device->target, (u32)scmd->device->lun, rc == SUCCESS ? "SUCCESS" : "FAILED"); mutex_unlock(&ctrl_info->lun_reset_mutex); @@ -6405,6 +6465,35 @@ static int pqi_slave_configure(struct scsi_device *sdev) return rc; } +static void pqi_slave_destroy(struct scsi_device *sdev) +{ + struct pqi_ctrl_info *ctrl_info; + struct pqi_scsi_dev *device; + int mutex_acquired; + unsigned long flags; + + ctrl_info = shost_to_hba(sdev->host); + + mutex_acquired = mutex_trylock(&ctrl_info->scan_mutex); + if (!mutex_acquired) + return; + + device = sdev->hostdata; + if (!device) { + mutex_unlock(&ctrl_info->scan_mutex); + return; + } + + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); + list_del(&device->scsi_device_list_entry); + spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); + + mutex_unlock(&ctrl_info->scan_mutex); + + pqi_dev_info(ctrl_info, "removed", device); + pqi_free_device(device); +} + static int pqi_getpciinfo_ioctl(struct pqi_ctrl_info *ctrl_info, void __user *arg) { struct pci_dev *pci_dev; @@ -6919,6 +7008,9 @@ static ssize_t pqi_unique_id_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -6955,6 +7047,9 @@ static ssize_t pqi_lunid_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -6990,6 +7085,9 @@ static ssize_t pqi_path_info_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -7067,6 +7165,9 @@ static ssize_t pqi_sas_address_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -7093,6 +7194,9 @@ static ssize_t pqi_ssd_smart_path_enabled_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -7122,6 +7226,9 @@ static ssize_t pqi_raid_level_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -7152,6 +7259,9 @@ static ssize_t pqi_raid_bypass_cnt_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -7179,6 +7289,9 @@ static ssize_t pqi_sas_ncq_prio_enable_show(struct device *dev, sdev = to_scsi_device(dev); ctrl_info = shost_to_hba(sdev->host); + if (pqi_ctrl_offline(ctrl_info)) + return -ENODEV; + spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); device = sdev->hostdata; @@ -7268,6 +7381,7 @@ static struct scsi_host_template pqi_driver_template = { .ioctl = pqi_ioctl, .slave_alloc = pqi_slave_alloc, .slave_configure = pqi_slave_configure, + .slave_destroy = pqi_slave_destroy, .map_queues = pqi_map_queues, .sdev_groups = pqi_sdev_groups, .shost_groups = pqi_shost_groups, @@ -7290,6 +7404,7 @@ static int pqi_register_scsi(struct pqi_ctrl_info *ctrl_info) shost->this_id = -1; shost->max_channel = PQI_MAX_BUS; shost->max_cmd_len = MAX_COMMAND_SIZE; + shost->max_lun = PQI_MAX_LUNS_PER_DEVICE; shost->max_lun = ~0; shost->max_id = ~0; shost->max_sectors = ctrl_info->max_sectors; @@ -7358,8 +7473,7 @@ static int pqi_wait_for_pqi_reset_completion(struct pqi_ctrl_info *ctrl_info) reset_reg.all_bits = readl(&pqi_registers->device_reset); if (reset_reg.bits.reset_action == PQI_RESET_ACTION_COMPLETED) break; - pqi_check_ctrl_health(ctrl_info); - if (pqi_ctrl_offline(ctrl_info)) { + if (!sis_is_firmware_running(ctrl_info)) { rc = -ENXIO; break; } @@ -7463,6 +7577,9 @@ static int pqi_get_ctrl_product_details(struct pqi_ctrl_info *ctrl_info) sizeof(identify->vendor_id)); ctrl_info->vendor[sizeof(identify->vendor_id)] = '\0'; + dev_info(&ctrl_info->pci_dev->dev, + "Firmware version: %s\n", ctrl_info->firmware_version); + out: kfree(identify); @@ -7634,6 +7751,9 @@ static void pqi_ctrl_update_feature_flags(struct pqi_ctrl_info *ctrl_info, case PQI_FIRMWARE_FEATURE_RPL_EXTENDED_FORMAT_4_5: ctrl_info->rpl_extended_format_4_5_supported = firmware_feature->enabled; break; + case PQI_FIRMWARE_FEATURE_MULTI_LUN_DEVICE_SUPPORT: + ctrl_info->multi_lun_device_supported = firmware_feature->enabled; + break; } pqi_firmware_feature_status(ctrl_info, firmware_feature); @@ -7734,6 +7854,11 @@ static struct pqi_firmware_feature pqi_firmware_features[] = { .feature_bit = PQI_FIRMWARE_FEATURE_RPL_EXTENDED_FORMAT_4_5, .feature_status = pqi_ctrl_update_feature_flags, }, + { + .feature_name = "Multi-LUN Target", + .feature_bit = PQI_FIRMWARE_FEATURE_MULTI_LUN_DEVICE_SUPPORT, + .feature_status = pqi_ctrl_update_feature_flags, + }, }; static void pqi_process_firmware_features( @@ -7835,6 +7960,7 @@ static void pqi_ctrl_reset_config(struct pqi_ctrl_info *ctrl_info) ctrl_info->tmf_iu_timeout_supported = false; ctrl_info->firmware_triage_supported = false; ctrl_info->rpl_extended_format_4_5_supported = false; + ctrl_info->multi_lun_device_supported = false; } static int pqi_process_config_table(struct pqi_ctrl_info *ctrl_info) @@ -8491,6 +8617,7 @@ static struct pqi_ctrl_info *pqi_alloc_ctrl_info(int numa_node) ctrl_info->max_write_raid_5_6 = PQI_DEFAULT_MAX_WRITE_RAID_5_6; ctrl_info->max_write_raid_1_10_2drive = ~0; ctrl_info->max_write_raid_1_10_3drive = ~0; + ctrl_info->disable_managed_interrupts = pqi_disable_managed_interrupts; return ctrl_info; } @@ -8508,7 +8635,6 @@ static void pqi_free_interrupts(struct pqi_ctrl_info *ctrl_info) static void pqi_free_ctrl_resources(struct pqi_ctrl_info *ctrl_info) { - pqi_stop_heartbeat_timer(ctrl_info); pqi_free_interrupts(ctrl_info); if (ctrl_info->queue_memory_base) dma_free_coherent(&ctrl_info->pci_dev->dev, @@ -8533,9 +8659,15 @@ static void pqi_free_ctrl_resources(struct pqi_ctrl_info *ctrl_info) static void pqi_remove_ctrl(struct pqi_ctrl_info *ctrl_info) { + ctrl_info->controller_online = false; + pqi_stop_heartbeat_timer(ctrl_info); + pqi_ctrl_block_requests(ctrl_info); pqi_cancel_rescan_worker(ctrl_info); pqi_cancel_update_time_worker(ctrl_info); - pqi_remove_all_scsi_devices(ctrl_info); + if (ctrl_info->ctrl_removal_state == PQI_CTRL_SURPRISE_REMOVAL) { + pqi_fail_all_outstanding_requests(ctrl_info); + ctrl_info->pqi_mode_enabled = false; + } pqi_unregister_scsi(ctrl_info); if (ctrl_info->pqi_mode_enabled) pqi_revert_to_sis_mode(ctrl_info); @@ -8875,11 +9007,18 @@ error: static void pqi_pci_remove(struct pci_dev *pci_dev) { struct pqi_ctrl_info *ctrl_info; + u16 vendor_id; ctrl_info = pci_get_drvdata(pci_dev); if (!ctrl_info) return; + pci_read_config_word(ctrl_info->pci_dev, PCI_SUBSYSTEM_VENDOR_ID, &vendor_id); + if (vendor_id == 0xffff) + ctrl_info->ctrl_removal_state = PQI_CTRL_SURPRISE_REMOVAL; + else + ctrl_info->ctrl_removal_state = PQI_CTRL_GRACEFUL_REMOVAL; + pqi_remove_ctrl(ctrl_info); } @@ -8956,9 +9095,31 @@ static void pqi_process_lockup_action_param(void) DRIVER_NAME_SHORT, pqi_lockup_action_param); } +#define PQI_CTRL_READY_TIMEOUT_PARAM_MIN_SECS 30 +#define PQI_CTRL_READY_TIMEOUT_PARAM_MAX_SECS (30 * 60) + +static void pqi_process_ctrl_ready_timeout_param(void) +{ + if (pqi_ctrl_ready_timeout_secs == 0) + return; + + if (pqi_ctrl_ready_timeout_secs < PQI_CTRL_READY_TIMEOUT_PARAM_MIN_SECS) { + pr_warn("%s: ctrl_ready_timeout parm of %u second(s) is less than minimum timeout of %d seconds - setting timeout to %d seconds\n", + DRIVER_NAME_SHORT, pqi_ctrl_ready_timeout_secs, PQI_CTRL_READY_TIMEOUT_PARAM_MIN_SECS, PQI_CTRL_READY_TIMEOUT_PARAM_MIN_SECS); + pqi_ctrl_ready_timeout_secs = PQI_CTRL_READY_TIMEOUT_PARAM_MIN_SECS; + } else if (pqi_ctrl_ready_timeout_secs > PQI_CTRL_READY_TIMEOUT_PARAM_MAX_SECS) { + pr_warn("%s: ctrl_ready_timeout parm of %u seconds is greater than maximum timeout of %d seconds - setting timeout to %d seconds\n", + DRIVER_NAME_SHORT, pqi_ctrl_ready_timeout_secs, PQI_CTRL_READY_TIMEOUT_PARAM_MAX_SECS, PQI_CTRL_READY_TIMEOUT_PARAM_MAX_SECS); + pqi_ctrl_ready_timeout_secs = PQI_CTRL_READY_TIMEOUT_PARAM_MAX_SECS; + } + + sis_ctrl_ready_timeout_secs = pqi_ctrl_ready_timeout_secs; +} + static void pqi_process_module_params(void) { pqi_process_lockup_action_param(); + pqi_process_ctrl_ready_timeout_param(); } #if defined(CONFIG_PM) @@ -9273,6 +9434,10 @@ static const struct pci_device_id pqi_pci_id_table[] = { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, PCI_VENDOR_ID_ADAPTEC2, 0x0608) }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_ADAPTEC2, 0x0659) + }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, PCI_VENDOR_ID_ADAPTEC2, 0x0800) @@ -9737,6 +9902,46 @@ static const struct pci_device_id pqi_pci_id_table[] = { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, 0x1cf2, 0x0b45) }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1cc4, 0x0101) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + 0x1cc4, 0x0201) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0220) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0221) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0520) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0522) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0620) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0621) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0622) + }, + { + PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, + PCI_VENDOR_ID_LENOVO, 0x0623) + }, { PCI_DEVICE_SUB(PCI_VENDOR_ID_ADAPTEC2, 0x028f, PCI_ANY_ID, PCI_ANY_ID) diff --git a/drivers/scsi/smartpqi/smartpqi_sas_transport.c b/drivers/scsi/smartpqi/smartpqi_sas_transport.c index dea4ebaf1677..13e8c539010e 100644 --- a/drivers/scsi/smartpqi/smartpqi_sas_transport.c +++ b/drivers/scsi/smartpqi/smartpqi_sas_transport.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * driver for Microchip PQI-based storage controllers - * Copyright (c) 2019-2021 Microchip Technology Inc. and its subsidiaries + * Copyright (c) 2019-2022 Microchip Technology Inc. and its subsidiaries * Copyright (c) 2016-2018 Microsemi Corporation * Copyright (c) 2016 PMC-Sierra, Inc. * diff --git a/drivers/scsi/smartpqi/smartpqi_sis.c b/drivers/scsi/smartpqi/smartpqi_sis.c index afc27adf68e9..5811fb3c22a9 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.c +++ b/drivers/scsi/smartpqi/smartpqi_sis.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * driver for Microchip PQI-based storage controllers - * Copyright (c) 2019-2021 Microchip Technology Inc. and its subsidiaries + * Copyright (c) 2019-2022 Microchip Technology Inc. and its subsidiaries * Copyright (c) 2016-2018 Microsemi Corporation * Copyright (c) 2016 PMC-Sierra, Inc. * @@ -86,6 +86,8 @@ struct sis_base_struct { #pragma pack() +unsigned int sis_ctrl_ready_timeout_secs = SIS_CTRL_READY_TIMEOUT_SECS; + static int sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info *ctrl_info, unsigned int timeout_secs) { @@ -122,7 +124,7 @@ static int sis_wait_for_ctrl_ready_with_timeout(struct pqi_ctrl_info *ctrl_info, int sis_wait_for_ctrl_ready(struct pqi_ctrl_info *ctrl_info) { return sis_wait_for_ctrl_ready_with_timeout(ctrl_info, - SIS_CTRL_READY_TIMEOUT_SECS); + sis_ctrl_ready_timeout_secs); } int sis_wait_for_ctrl_ready_resume(struct pqi_ctrl_info *ctrl_info) @@ -138,7 +140,7 @@ bool sis_is_firmware_running(struct pqi_ctrl_info *ctrl_info) status = readl(&ctrl_info->registers->sis_firmware_status); - if (status & SIS_CTRL_KERNEL_PANIC) + if (status != ~0 && (status & SIS_CTRL_KERNEL_PANIC)) running = false; else running = true; @@ -194,6 +196,7 @@ static int sis_send_sync_cmd(struct pqi_ctrl_info *ctrl_info, /* Disable doorbell interrupts by masking all interrupts. */ writel(~0, ®isters->sis_interrupt_mask); + usleep_range(1000, 2000); /* * Force the completion of the interrupt mask register write before @@ -383,6 +386,7 @@ static int sis_wait_for_doorbell_bit_to_clear( static inline int sis_set_doorbell_bit(struct pqi_ctrl_info *ctrl_info, u32 bit) { writel(bit, &ctrl_info->registers->sis_host_to_ctrl_doorbell); + usleep_range(1000, 2000); return sis_wait_for_doorbell_bit_to_clear(ctrl_info, bit); } @@ -423,6 +427,7 @@ int sis_reenable_sis_mode(struct pqi_ctrl_info *ctrl_info) void sis_write_driver_scratch(struct pqi_ctrl_info *ctrl_info, u32 value) { writel(value, &ctrl_info->registers->sis_driver_scratch); + usleep_range(1000, 2000); } u32 sis_read_driver_scratch(struct pqi_ctrl_info *ctrl_info) diff --git a/drivers/scsi/smartpqi/smartpqi_sis.h b/drivers/scsi/smartpqi/smartpqi_sis.h index 5f3575261a8e..9dcbae96a5c6 100644 --- a/drivers/scsi/smartpqi/smartpqi_sis.h +++ b/drivers/scsi/smartpqi/smartpqi_sis.h @@ -1,7 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* * driver for Microchip PQI-based storage controllers - * Copyright (c) 2019-2021 Microchip Technology Inc. and its subsidiaries + * Copyright (c) 2019-2022 Microchip Technology Inc. and its subsidiaries * Copyright (c) 2016-2018 Microsemi Corporation * Copyright (c) 2016 PMC-Sierra, Inc. * @@ -32,4 +32,6 @@ void sis_soft_reset(struct pqi_ctrl_info *ctrl_info); u32 sis_get_product_id(struct pqi_ctrl_info *ctrl_info); int sis_wait_for_fw_triage_completion(struct pqi_ctrl_info *ctrl_info); +extern unsigned int sis_ctrl_ready_timeout_secs; + #endif /* _SMARTPQI_SIS_H */ diff --git a/drivers/scsi/snic/cq_desc.h b/drivers/scsi/snic/cq_desc.h index a5290562c1fa..52a916fd0824 100644 --- a/drivers/scsi/snic/cq_desc.h +++ b/drivers/scsi/snic/cq_desc.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _CQ_DESC_H_ #define _CQ_DESC_H_ diff --git a/drivers/scsi/snic/cq_enet_desc.h b/drivers/scsi/snic/cq_enet_desc.h index 0a1be2ed0288..bd7381e52521 100644 --- a/drivers/scsi/snic/cq_enet_desc.h +++ b/drivers/scsi/snic/cq_enet_desc.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _CQ_ENET_DESC_H_ #define _CQ_ENET_DESC_H_ diff --git a/drivers/scsi/snic/snic.h b/drivers/scsi/snic/snic.h index 4ec7e30678e1..32f5a34b6987 100644 --- a/drivers/scsi/snic/snic.h +++ b/drivers/scsi/snic/snic.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _SNIC_H_ #define _SNIC_H_ diff --git a/drivers/scsi/snic/snic_attrs.c b/drivers/scsi/snic/snic_attrs.c index dc03ce1ec909..3ddbdbc3ded1 100644 --- a/drivers/scsi/snic/snic_attrs.c +++ b/drivers/scsi/snic/snic_attrs.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_ctl.c b/drivers/scsi/snic/snic_ctl.c index 703f229862fc..5f4fca96b192 100644 --- a/drivers/scsi/snic/snic_ctl.c +++ b/drivers/scsi/snic/snic_ctl.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_debugfs.c b/drivers/scsi/snic/snic_debugfs.c index 5e0faeba516e..57bdc3ba49d9 100644 --- a/drivers/scsi/snic/snic_debugfs.c +++ b/drivers/scsi/snic/snic_debugfs.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_disc.c b/drivers/scsi/snic/snic_disc.c index 27e98df83b31..9b2b5f8c23b9 100644 --- a/drivers/scsi/snic/snic_disc.c +++ b/drivers/scsi/snic/snic_disc.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_disc.h b/drivers/scsi/snic/snic_disc.h index 97fa3f5c5bb4..9ad7f84a3484 100644 --- a/drivers/scsi/snic/snic_disc.h +++ b/drivers/scsi/snic/snic_disc.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef __SNIC_DISC_H #define __SNIC_DISC_H diff --git a/drivers/scsi/snic/snic_fwint.h b/drivers/scsi/snic/snic_fwint.h index 2a045a57e365..2550ba964b03 100644 --- a/drivers/scsi/snic/snic_fwint.h +++ b/drivers/scsi/snic/snic_fwint.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef __SNIC_FWINT_H #define __SNIC_FWINT_H @@ -159,7 +145,7 @@ struct snic_exch_ver_req { * HBA Capabilities * Bit 1: Reserved. * Bit 2: Dynamic Discovery of LUNs. - * Bit 3: Async event notifications on on tgt online/offline events. + * Bit 3: Async event notifications on tgt online/offline events. * Bit 4: IO timeout support in FW. * Bit 5-31: Reserved. */ diff --git a/drivers/scsi/snic/snic_io.c b/drivers/scsi/snic/snic_io.c index 159ee94d2a55..32a77bee41d5 100644 --- a/drivers/scsi/snic/snic_io.c +++ b/drivers/scsi/snic/snic_io.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_io.h b/drivers/scsi/snic/snic_io.h index 093d6524cd42..de6694a24c5f 100644 --- a/drivers/scsi/snic/snic_io.h +++ b/drivers/scsi/snic/snic_io.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _SNIC_IO_H #define _SNIC_IO_H diff --git a/drivers/scsi/snic/snic_isr.c b/drivers/scsi/snic/snic_isr.c index c4da3673f2ae..471a37422da9 100644 --- a/drivers/scsi/snic/snic_isr.c +++ b/drivers/scsi/snic/snic_isr.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_main.c b/drivers/scsi/snic/snic_main.c index 29d56396058c..174f7811fe50 100644 --- a/drivers/scsi/snic/snic_main.c +++ b/drivers/scsi/snic/snic_main.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_res.c b/drivers/scsi/snic/snic_res.c index b54912c8ca0c..43f1a2823514 100644 --- a/drivers/scsi/snic/snic_res.c +++ b/drivers/scsi/snic/snic_res.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_res.h b/drivers/scsi/snic/snic_res.h index 273f72f2a023..53cf6b19ab28 100644 --- a/drivers/scsi/snic/snic_res.h +++ b/drivers/scsi/snic/snic_res.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef __SNIC_RES_H #define __SNIC_RES_H diff --git a/drivers/scsi/snic/snic_scsi.c b/drivers/scsi/snic/snic_scsi.c index 5f17666f3e1d..961af6fc21bc 100644 --- a/drivers/scsi/snic/snic_scsi.c +++ b/drivers/scsi/snic/snic_scsi.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_stats.h b/drivers/scsi/snic/snic_stats.h index faf0cb601954..f0285c5a35f8 100644 --- a/drivers/scsi/snic/snic_stats.h +++ b/drivers/scsi/snic/snic_stats.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef __SNIC_STATS_H #define __SNIC_STATS_H diff --git a/drivers/scsi/snic/snic_trc.c b/drivers/scsi/snic/snic_trc.c index f23fe2f88438..c2e5ab7e976c 100644 --- a/drivers/scsi/snic/snic_trc.c +++ b/drivers/scsi/snic/snic_trc.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/snic_trc.h b/drivers/scsi/snic/snic_trc.h index ce305b4b8fa2..c38e0dadc958 100644 --- a/drivers/scsi/snic/snic_trc.h +++ b/drivers/scsi/snic/snic_trc.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef __SNIC_TRC_H #define __SNIC_TRC_H diff --git a/drivers/scsi/snic/vnic_cq.c b/drivers/scsi/snic/vnic_cq.c index 3455dd7e73f4..0d5d3bd4be1c 100644 --- a/drivers/scsi/snic/vnic_cq.c +++ b/drivers/scsi/snic/vnic_cq.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/vnic_cq.h b/drivers/scsi/snic/vnic_cq.h index 6e651c3e16f7..6cee911eec5f 100644 --- a/drivers/scsi/snic/vnic_cq.h +++ b/drivers/scsi/snic/vnic_cq.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_CQ_H_ #define _VNIC_CQ_H_ diff --git a/drivers/scsi/snic/vnic_cq_fw.h b/drivers/scsi/snic/vnic_cq_fw.h index c2d1bbd44bd1..d74954bc70e3 100644 --- a/drivers/scsi/snic/vnic_cq_fw.h +++ b/drivers/scsi/snic/vnic_cq_fw.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_CQ_FW_H_ #define _VNIC_CQ_FW_H_ diff --git a/drivers/scsi/snic/vnic_dev.c b/drivers/scsi/snic/vnic_dev.c index 05e374f80946..760f3f22095c 100644 --- a/drivers/scsi/snic/vnic_dev.c +++ b/drivers/scsi/snic/vnic_dev.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/vnic_dev.h b/drivers/scsi/snic/vnic_dev.h index e65726da6504..d2f9b6f7b313 100644 --- a/drivers/scsi/snic/vnic_dev.h +++ b/drivers/scsi/snic/vnic_dev.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_DEV_H_ #define _VNIC_DEV_H_ diff --git a/drivers/scsi/snic/vnic_devcmd.h b/drivers/scsi/snic/vnic_devcmd.h index 0e0fa38f8d90..9d82fcb7414b 100644 --- a/drivers/scsi/snic/vnic_devcmd.h +++ b/drivers/scsi/snic/vnic_devcmd.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_DEVCMD_H_ #define _VNIC_DEVCMD_H_ diff --git a/drivers/scsi/snic/vnic_intr.c b/drivers/scsi/snic/vnic_intr.c index a7d54806787d..23627f9591f2 100644 --- a/drivers/scsi/snic/vnic_intr.c +++ b/drivers/scsi/snic/vnic_intr.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/vnic_intr.h b/drivers/scsi/snic/vnic_intr.h index 4547f603fe5e..7bff60fafb07 100644 --- a/drivers/scsi/snic/vnic_intr.h +++ b/drivers/scsi/snic/vnic_intr.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_INTR_H_ #define _VNIC_INTR_H_ diff --git a/drivers/scsi/snic/vnic_resource.h b/drivers/scsi/snic/vnic_resource.h index 9713d6835db3..372596b0915f 100644 --- a/drivers/scsi/snic/vnic_resource.h +++ b/drivers/scsi/snic/vnic_resource.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_RESOURCE_H_ #define _VNIC_RESOURCE_H_ diff --git a/drivers/scsi/snic/vnic_snic.h b/drivers/scsi/snic/vnic_snic.h index 514d39f5cf00..ffc8a0fee577 100644 --- a/drivers/scsi/snic/vnic_snic.h +++ b/drivers/scsi/snic/vnic_snic.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_SNIC_H_ #define _VNIC_SNIC_H_ diff --git a/drivers/scsi/snic/vnic_stats.h b/drivers/scsi/snic/vnic_stats.h index 370a37c97748..38155aae7a52 100644 --- a/drivers/scsi/snic/vnic_stats.h +++ b/drivers/scsi/snic/vnic_stats.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_STATS_H_ #define _VNIC_STATS_H_ diff --git a/drivers/scsi/snic/vnic_wq.c b/drivers/scsi/snic/vnic_wq.c index 1e91d432089e..48be9a3f4c3d 100644 --- a/drivers/scsi/snic/vnic_wq.c +++ b/drivers/scsi/snic/vnic_wq.c @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright 2014 Cisco Systems, Inc. All rights reserved. #include #include diff --git a/drivers/scsi/snic/vnic_wq.h b/drivers/scsi/snic/vnic_wq.h index 7cc031c7ceba..1415da4b68dc 100644 --- a/drivers/scsi/snic/vnic_wq.h +++ b/drivers/scsi/snic/vnic_wq.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _VNIC_WQ_H_ #define _VNIC_WQ_H_ diff --git a/drivers/scsi/snic/wq_enet_desc.h b/drivers/scsi/snic/wq_enet_desc.h index 68f62b6d105b..e8025331b503 100644 --- a/drivers/scsi/snic/wq_enet_desc.h +++ b/drivers/scsi/snic/wq_enet_desc.h @@ -1,19 +1,5 @@ -/* - * Copyright 2014 Cisco Systems, Inc. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright 2014 Cisco Systems, Inc. All rights reserved. */ #ifndef _WQ_ENET_DESC_H_ #define _WQ_ENET_DESC_H_ diff --git a/drivers/scsi/sym53c8xx_2/sym_hipd.c b/drivers/scsi/sym53c8xx_2/sym_hipd.c index 255a2d48d421..f0db17e34ea0 100644 --- a/drivers/scsi/sym53c8xx_2/sym_hipd.c +++ b/drivers/scsi/sym53c8xx_2/sym_hipd.c @@ -3598,7 +3598,7 @@ static void sym_sir_task_recovery(struct sym_hcb *np, int num) } /* - * Gerard's alchemy:) that deals with with the data + * Gerard's alchemy:) that deals with the data * pointer for both MDP and the residual calculation. * * I didn't want to bloat the code by more than 200 diff --git a/drivers/soc/ti/knav_dma.c b/drivers/soc/ti/knav_dma.c index d756591de973..84afebd355be 100644 --- a/drivers/soc/ti/knav_dma.c +++ b/drivers/soc/ti/knav_dma.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright (C) 2014 Texas Instruments Incorporated * Authors: Santosh Shilimkar * Sandeep Nair * Cyril Chemparathy - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c index 1c14d682ffed..8f97a3eacdea 100644 --- a/drivers/spi/spi.c +++ b/drivers/spi/spi.c @@ -2687,11 +2687,6 @@ int spi_slave_abort(struct spi_device *spi) } EXPORT_SYMBOL_GPL(spi_slave_abort); -static int match_true(struct device *dev, void *data) -{ - return 1; -} - static ssize_t slave_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -2699,7 +2694,7 @@ static ssize_t slave_show(struct device *dev, struct device_attribute *attr, dev); struct device *child; - child = device_find_child(&ctlr->dev, NULL, match_true); + child = device_find_any_child(&ctlr->dev); return sprintf(buf, "%s\n", child ? to_spi_device(child)->modalias : NULL); } @@ -2718,7 +2713,7 @@ static ssize_t slave_store(struct device *dev, struct device_attribute *attr, if (rc != 1 || !name[0]) return -EINVAL; - child = device_find_child(&ctlr->dev, NULL, match_true); + child = device_find_any_child(&ctlr->dev); if (child) { /* Remove registered slave */ device_unregister(child); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 829112972036..f907a890ffbb 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -42,8 +42,6 @@ source "drivers/staging/rts5208/Kconfig" source "drivers/staging/octeon/Kconfig" -source "drivers/staging/octeon-usb/Kconfig" - source "drivers/staging/vt6655/Kconfig" source "drivers/staging/vt6656/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 6ae8f46fc151..459d0865a3d8 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -11,7 +11,6 @@ obj-$(CONFIG_R8712U) += rtl8712/ obj-$(CONFIG_R8188EU) += r8188eu/ obj-$(CONFIG_RTS5208) += rts5208/ obj-$(CONFIG_OCTEON_ETHERNET) += octeon/ -obj-$(CONFIG_OCTEON_USB) += octeon-usb/ obj-$(CONFIG_VT6655) += vt6655/ obj-$(CONFIG_VT6656) += vt6656/ obj-$(CONFIG_VME_BUS) += vme_user/ diff --git a/drivers/staging/fbtft/fb_ssd1351.c b/drivers/staging/fbtft/fb_ssd1351.c index 6fd549a424d5..b8d55aa8c5c7 100644 --- a/drivers/staging/fbtft/fb_ssd1351.c +++ b/drivers/staging/fbtft/fb_ssd1351.c @@ -196,8 +196,7 @@ static int update_onboard_backlight(struct backlight_device *bd) "%s: power=%d, fb_blank=%d\n", __func__, bd->props.power, bd->props.fb_blank); - on = (bd->props.power == FB_BLANK_UNBLANK) && - (bd->props.fb_blank == FB_BLANK_UNBLANK); + on = !backlight_is_blank(bd); /* Onboard backlight connected to GPIO0 on SSD1351, GPIO1 unused */ write_reg(par, 0xB5, on ? 0x03 : 0x02); diff --git a/drivers/staging/fbtft/fbtft-core.c b/drivers/staging/fbtft/fbtft-core.c index 60b2278d8b16..afaba94d1d1c 100644 --- a/drivers/staging/fbtft/fbtft-core.c +++ b/drivers/staging/fbtft/fbtft-core.c @@ -137,8 +137,7 @@ static int fbtft_backlight_update_status(struct backlight_device *bd) "%s: polarity=%d, power=%d, fb_blank=%d\n", __func__, polarity, bd->props.power, bd->props.fb_blank); - if ((bd->props.power == FB_BLANK_UNBLANK) && - (bd->props.fb_blank == FB_BLANK_UNBLANK)) + if (!backlight_is_blank(bd)) gpiod_set_value(par->gpio.led[0], polarity); else gpiod_set_value(par->gpio.led[0], !polarity); @@ -655,7 +654,6 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, fbdefio->delay = HZ / fps; fbdefio->sort_pagereflist = true; fbdefio->deferred_io = fbtft_deferred_io; - fb_deferred_io_init(info); snprintf(info->fix.id, sizeof(info->fix.id), "%s", dev->driver->name); info->fix.type = FB_TYPE_PACKED_PIXELS; @@ -666,6 +664,7 @@ struct fb_info *fbtft_framebuffer_alloc(struct fbtft_display *display, info->fix.line_length = width * bpp / 8; info->fix.accel = FB_ACCEL_NONE; info->fix.smem_len = vmem_size; + fb_deferred_io_init(info); info->var.rotate = pdata->rotate; info->var.xres = width; diff --git a/drivers/staging/gdm724x/gdm_tty.c b/drivers/staging/gdm724x/gdm_tty.c index 04df6f9f5403..cc6d80554c98 100644 --- a/drivers/staging/gdm724x/gdm_tty.c +++ b/drivers/staging/gdm724x/gdm_tty.c @@ -17,12 +17,6 @@ #define GDM_TTY_MAJOR 0 #define GDM_TTY_MINOR 32 -#define ACM_CTRL_DTR 0x01 -#define ACM_CTRL_RTS 0x02 -#define ACM_CTRL_DSR 0x02 -#define ACM_CTRL_RI 0x08 -#define ACM_CTRL_DCD 0x01 - #define WRITE_SIZE 2048 #define MUX_TX_MAX_SIZE 2048 diff --git a/drivers/staging/greybus/audio_helper.c b/drivers/staging/greybus/audio_helper.c index 843760675876..05e91e6bc2a0 100644 --- a/drivers/staging/greybus/audio_helper.c +++ b/drivers/staging/greybus/audio_helper.c @@ -115,7 +115,7 @@ int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm, int num) { int i; - struct snd_soc_dapm_widget *w, *next_w; + struct snd_soc_dapm_widget *w, *tmp_w; #ifdef CONFIG_DEBUG_FS struct dentry *parent = dapm->debugfs_dapm; struct dentry *debugfs_w = NULL; @@ -124,13 +124,13 @@ int gbaudio_dapm_free_controls(struct snd_soc_dapm_context *dapm, mutex_lock(&dapm->card->dapm_mutex); for (i = 0; i < num; i++) { /* below logic can be optimized to identify widget pointer */ - list_for_each_entry_safe(w, next_w, &dapm->card->widgets, - list) { - if (w->dapm != dapm) - continue; - if (!strcmp(w->name, widget->name)) + w = NULL; + list_for_each_entry(tmp_w, &dapm->card->widgets, list) { + if (tmp_w->dapm == dapm && + !strcmp(tmp_w->name, widget->name)) { + w = tmp_w; break; - w = NULL; + } } if (!w) { dev_err(dapm->dev, "%s: widget not found\n", diff --git a/drivers/staging/greybus/fw-management.c b/drivers/staging/greybus/fw-management.c index 687c6405c65b..3342b84597da 100644 --- a/drivers/staging/greybus/fw-management.c +++ b/drivers/staging/greybus/fw-management.c @@ -102,7 +102,7 @@ unlock: } static int fw_mgmt_interface_fw_version_operation(struct fw_mgmt *fw_mgmt, - struct fw_mgmt_ioc_get_intf_version *fw_info) + struct fw_mgmt_ioc_get_intf_version *fw_info) { struct gb_connection *connection = fw_mgmt->connection; struct gb_fw_mgmt_interface_fw_version_response response; @@ -240,7 +240,7 @@ static int fw_mgmt_interface_fw_loaded_operation(struct gb_operation *op) } static int fw_mgmt_backend_fw_version_operation(struct fw_mgmt *fw_mgmt, - struct fw_mgmt_ioc_get_backend_version *fw_info) + struct fw_mgmt_ioc_get_backend_version *fw_info) { struct gb_connection *connection = fw_mgmt->connection; struct gb_fw_mgmt_backend_fw_version_request request; @@ -473,7 +473,7 @@ static int fw_mgmt_ioctl(struct fw_mgmt *fw_mgmt, unsigned int cmd, return -EFAULT; ret = fw_mgmt_backend_fw_update_operation(fw_mgmt, - backend_update.firmware_tag); + backend_update.firmware_tag); if (ret) return ret; diff --git a/drivers/staging/greybus/loopback.c b/drivers/staging/greybus/loopback.c index 2471448ba42a..1a61fce98056 100644 --- a/drivers/staging/greybus/loopback.c +++ b/drivers/staging/greybus/loopback.c @@ -870,7 +870,7 @@ static int gb_loopback_fn(void *data) if (gb->send_count == gb->iteration_max) { mutex_unlock(&gb->mutex); - /* Wait for synchronous and asynchronus completion */ + /* Wait for synchronous and asynchronous completion */ gb_loopback_async_wait_all(gb); /* Mark complete unless user-space has poked us */ diff --git a/drivers/staging/octeon-usb/Kconfig b/drivers/staging/octeon-usb/Kconfig deleted file mode 100644 index 6a5d842ee0f2..000000000000 --- a/drivers/staging/octeon-usb/Kconfig +++ /dev/null @@ -1,11 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -config OCTEON_USB - tristate "Cavium Networks Octeon USB support" - depends on CAVIUM_OCTEON_SOC && USB - help - This driver supports USB host controller on some Cavium - Networks' products in the Octeon family. - - To compile this driver as a module, choose M here. The module - will be called octeon-hcd. - diff --git a/drivers/staging/octeon-usb/Makefile b/drivers/staging/octeon-usb/Makefile deleted file mode 100644 index 9873a0130ad5..000000000000 --- a/drivers/staging/octeon-usb/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 -obj-${CONFIG_OCTEON_USB} := octeon-hcd.o diff --git a/drivers/staging/octeon-usb/TODO b/drivers/staging/octeon-usb/TODO deleted file mode 100644 index 2b29acca5caa..000000000000 --- a/drivers/staging/octeon-usb/TODO +++ /dev/null @@ -1,8 +0,0 @@ -This driver is functional and has been tested on EdgeRouter Lite, -D-Link DSR-1000N and EBH5600 evaluation board with USB mass storage. - -TODO: - - kernel coding style - - checkpatch warnings - -Contact: Aaro Koskinen diff --git a/drivers/staging/octeon/ethernet-rx.c b/drivers/staging/octeon/ethernet-rx.c index 9ebd665e5d42..965330eec80a 100644 --- a/drivers/staging/octeon/ethernet-rx.c +++ b/drivers/staging/octeon/ethernet-rx.c @@ -469,8 +469,8 @@ void cvm_oct_rx_initialize(void) if (!(pow_receive_groups & BIT(i))) continue; - netif_napi_add(dev_for_napi, &oct_rx_group[i].napi, - cvm_oct_napi_poll, rx_napi_weight); + netif_napi_add_weight(dev_for_napi, &oct_rx_group[i].napi, + cvm_oct_napi_poll, rx_napi_weight); napi_enable(&oct_rx_group[i].napi); oct_rx_group[i].irq = OCTEON_IRQ_WORKQ0 + i; diff --git a/drivers/staging/olpc_dcon/olpc_dcon.c b/drivers/staging/olpc_dcon/olpc_dcon.c index 7284cb4ac395..9363c5cfe50f 100644 --- a/drivers/staging/olpc_dcon/olpc_dcon.c +++ b/drivers/staging/olpc_dcon/olpc_dcon.c @@ -383,7 +383,7 @@ static void dcon_set_source(struct dcon_priv *dcon, int arg) static void dcon_set_source_sync(struct dcon_priv *dcon, int arg) { dcon_set_source(dcon, arg); - flush_scheduled_work(); + flush_work(&dcon->switch_source); } static ssize_t dcon_mode_show(struct device *dev, @@ -517,10 +517,7 @@ static struct device_attribute dcon_device_files[] = { static int dcon_bl_update(struct backlight_device *dev) { struct dcon_priv *dcon = bl_get_data(dev); - u8 level = dev->props.brightness & 0x0F; - - if (dev->props.power != FB_BLANK_UNBLANK) - level = 0; + u8 level = backlight_get_brightness(dev) & 0x0F; if (level != dcon->bl_val) dcon_set_backlight(dcon, level); diff --git a/drivers/staging/pi433/pi433_if.c b/drivers/staging/pi433/pi433_if.c index 941aaa7eab2e..df02335fdbab 100644 --- a/drivers/staging/pi433/pi433_if.c +++ b/drivers/staging/pi433/pi433_if.c @@ -1406,7 +1406,7 @@ static int __init pi433_init(void) /* * Claim device numbers. Then register a class - * that will key udev/mdev to add/remove /dev nodes. Last, register + * that will key udev/mdev to add/remove /dev nodes. * Last, register the driver which manages those device numbers. */ status = alloc_chrdev_region(&pi433_dev, 0, N_PI433_MINORS, "pi433"); diff --git a/drivers/staging/qlge/qlge_main.c b/drivers/staging/qlge/qlge_main.c index 6cd7fc9589c3..ca6b966f5dd3 100644 --- a/drivers/staging/qlge/qlge_main.c +++ b/drivers/staging/qlge/qlge_main.c @@ -1976,7 +1976,7 @@ static unsigned long qlge_process_mac_rx_intr(struct qlge_adapter *qdev, vlan_id); } else { /* Non-TCP/UDP large frames that span multiple buffers - * can be processed corrrectly by the split frame logic. + * can be processed correctly by the split frame logic. */ qlge_process_mac_split_rx_intr(qdev, rx_ring, ib_mac_rsp, vlan_id); @@ -2955,7 +2955,7 @@ static int qlge_start_rx_ring(struct qlge_adapter *qdev, struct rx_ring *rx_ring void __iomem *doorbell_area = qdev->doorbell_area + (DB_PAGE_SIZE * (128 + rx_ring->cq_id)); int err = 0; - u64 tmp; + u64 dma; __le64 *base_indirect_ptr; int page_entries; @@ -3004,15 +3004,15 @@ static int qlge_start_rx_ring(struct qlge_adapter *qdev, struct rx_ring *rx_ring FLAGS_LI; /* Load irq delay values */ if (rx_ring->cq_id < qdev->rss_ring_count) { cqicb->flags |= FLAGS_LL; /* Load lbq values */ - tmp = (u64)rx_ring->lbq.base_dma; + dma = (u64)rx_ring->lbq.base_dma; base_indirect_ptr = rx_ring->lbq.base_indirect; - page_entries = 0; - do { - *base_indirect_ptr = cpu_to_le64(tmp); - tmp += DB_PAGE_SIZE; - base_indirect_ptr++; - page_entries++; - } while (page_entries < MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN)); + + for (page_entries = 0; + page_entries < MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN); + page_entries++) { + base_indirect_ptr[page_entries] = cpu_to_le64(dma); + dma += DB_PAGE_SIZE; + } cqicb->lbq_addr = cpu_to_le64(rx_ring->lbq.base_indirect_dma); cqicb->lbq_buf_size = cpu_to_le16(QLGE_FIT16(qdev->lbq_buf_size)); @@ -3021,15 +3021,15 @@ static int qlge_start_rx_ring(struct qlge_adapter *qdev, struct rx_ring *rx_ring rx_ring->lbq.next_to_clean = 0; cqicb->flags |= FLAGS_LS; /* Load sbq values */ - tmp = (u64)rx_ring->sbq.base_dma; + dma = (u64)rx_ring->sbq.base_dma; base_indirect_ptr = rx_ring->sbq.base_indirect; - page_entries = 0; - do { - *base_indirect_ptr = cpu_to_le64(tmp); - tmp += DB_PAGE_SIZE; - base_indirect_ptr++; - page_entries++; - } while (page_entries < MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN)); + + for (page_entries = 0; + page_entries < MAX_DB_PAGES_PER_BQ(QLGE_BQ_LEN); + page_entries++) { + base_indirect_ptr[page_entries] = cpu_to_le64(dma); + dma += DB_PAGE_SIZE; + } cqicb->sbq_addr = cpu_to_le64(rx_ring->sbq.base_indirect_dma); cqicb->sbq_buf_size = cpu_to_le16(SMALL_BUFFER_SIZE); @@ -3041,8 +3041,8 @@ static int qlge_start_rx_ring(struct qlge_adapter *qdev, struct rx_ring *rx_ring /* Inbound completion handling rx_rings run in * separate NAPI contexts. */ - netif_napi_add(qdev->ndev, &rx_ring->napi, qlge_napi_poll_msix, - 64); + netif_napi_add_weight(qdev->ndev, &rx_ring->napi, + qlge_napi_poll_msix, 64); cqicb->irq_delay = cpu_to_le16(qdev->rx_coalesce_usecs); cqicb->pkt_delay = cpu_to_le16(qdev->rx_max_coalesced_frames); } else { diff --git a/drivers/staging/r8188eu/Makefile b/drivers/staging/r8188eu/Makefile index 1d7982b618ba..eea16eb7caa0 100644 --- a/drivers/staging/r8188eu/Makefile +++ b/drivers/staging/r8188eu/Makefile @@ -5,7 +5,6 @@ r8188eu-y = \ hal/HalHWImg8188E_RF.o \ hal/HalPhyRf_8188e.o \ hal/HalPwrSeqCmd.o \ - hal/Hal8188EPwrSeq.o \ hal/Hal8188ERateAdaptive.o \ hal/hal_intf.o \ hal/hal_com.o \ diff --git a/drivers/staging/r8188eu/core/rtw_ap.c b/drivers/staging/r8188eu/core/rtw_ap.c index ac6effbecf6d..5bd9dfa57cc5 100644 --- a/drivers/staging/r8188eu/core/rtw_ap.c +++ b/drivers/staging/r8188eu/core/rtw_ap.c @@ -654,18 +654,17 @@ void update_beacon(struct adapter *padapter, u8 ie_id, u8 *oui, u8 tx) set_tx_beacon_cmd(padapter); } -/* -op_mode -Set to 0 (HT pure) under the following conditions - - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or - - all STAs in the BSS are 20 MHz HT in 20 MHz BSS -Set to 1 (HT non-member protection) if there may be non-HT STAs - in both the primary and the secondary channel -Set to 2 if only HT STAs are associated in BSS, - however and at least one 20 MHz HT STA is associated -Set to 3 (HT mixed mode) when one or more non-HT STAs are associated - (currently non-GF HT station is considered as non-HT STA also) -*/ +/* op_mode + * Set to 0 (HT pure) under the following conditions + * - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or + * - all STAs in the BSS are 20 MHz HT in 20 MHz BSS + * Set to 1 (HT non-member protection) if there may be non-HT STAs + * in both the primary and the secondary channel + * Set to 2 if only HT STAs are associated in BSS, + * however and at least one 20 MHz HT STA is associated + * Set to 3 (HT mixed mode) when one or more non-HT STAs are associated + * (currently non-GF HT station is considered as non-HT STA also) + */ static int rtw_ht_operation_update(struct adapter *padapter) { u16 cur_op_mode, new_op_mode; diff --git a/drivers/staging/r8188eu/core/rtw_cmd.c b/drivers/staging/r8188eu/core/rtw_cmd.c index 06523d91939a..5b6a891b5d67 100644 --- a/drivers/staging/r8188eu/core/rtw_cmd.c +++ b/drivers/staging/r8188eu/core/rtw_cmd.c @@ -898,8 +898,12 @@ static void traffic_status_watchdog(struct adapter *padapter) static void rtl8188e_sreset_xmit_status_check(struct adapter *padapter) { u32 txdma_status; + int res; + + res = rtw_read32(padapter, REG_TXDMA_STATUS, &txdma_status); + if (res) + return; - txdma_status = rtw_read32(padapter, REG_TXDMA_STATUS); if (txdma_status != 0x00) rtw_write32(padapter, REG_TXDMA_STATUS, txdma_status); /* total xmit irp = 4 */ @@ -1177,7 +1181,14 @@ exit: static bool rtw_is_hi_queue_empty(struct adapter *adapter) { - return (rtw_read32(adapter, REG_HGQ_INFORMATION) & 0x0000ff00) == 0; + int res; + u32 reg; + + res = rtw_read32(adapter, REG_HGQ_INFORMATION, ®); + if (res) + return false; + + return (reg & 0x0000ff00) == 0; } static void rtw_chk_hi_queue_hdl(struct adapter *padapter) diff --git a/drivers/staging/r8188eu/core/rtw_efuse.c b/drivers/staging/r8188eu/core/rtw_efuse.c index 0e0e60638880..df9534dd25cb 100644 --- a/drivers/staging/r8188eu/core/rtw_efuse.c +++ b/drivers/staging/r8188eu/core/rtw_efuse.c @@ -28,22 +28,35 @@ ReadEFuseByte( u32 value32; u8 readbyte; u16 retry; + int res; /* Write Address */ rtw_write8(Adapter, EFUSE_CTRL + 1, (_offset & 0xff)); - readbyte = rtw_read8(Adapter, EFUSE_CTRL + 2); + res = rtw_read8(Adapter, EFUSE_CTRL + 2, &readbyte); + if (res) + return; + rtw_write8(Adapter, EFUSE_CTRL + 2, ((_offset >> 8) & 0x03) | (readbyte & 0xfc)); /* Write bit 32 0 */ - readbyte = rtw_read8(Adapter, EFUSE_CTRL + 3); + res = rtw_read8(Adapter, EFUSE_CTRL + 3, &readbyte); + if (res) + return; + rtw_write8(Adapter, EFUSE_CTRL + 3, (readbyte & 0x7f)); /* Check bit 32 read-ready */ - retry = 0; - value32 = rtw_read32(Adapter, EFUSE_CTRL); - while (!(((value32 >> 24) & 0xff) & 0x80) && (retry < 10000)) { - value32 = rtw_read32(Adapter, EFUSE_CTRL); - retry++; + res = rtw_read32(Adapter, EFUSE_CTRL, &value32); + if (res) + return; + + for (retry = 0; retry < 10000; retry++) { + res = rtw_read32(Adapter, EFUSE_CTRL, &value32); + if (res) + continue; + + if (((value32 >> 24) & 0xff) & 0x80) + break; } /* 20100205 Joseph: Add delay suggested by SD1 Victor. */ @@ -51,37 +64,11 @@ ReadEFuseByte( /* Designer says that there shall be some delay after ready bit is set, or the */ /* result will always stay on last data we read. */ udelay(50); - value32 = rtw_read32(Adapter, EFUSE_CTRL); + res = rtw_read32(Adapter, EFUSE_CTRL, &value32); + if (res) + return; *pbuf = (u8)(value32 & 0xff); -} - -/*----------------------------------------------------------------------------- - * Function: EFUSE_ShadowMapUpdate - * - * Overview: Transfer current EFUSE content to shadow init and modify map. - * - * Input: NONE - * - * Output: NONE - * - * Return: NONE - * - * Revised History: - * When Who Remark - * 11/13/2008 MHC Create Version 0. - * - *---------------------------------------------------------------------------*/ -void EFUSE_ShadowMapUpdate(struct adapter *pAdapter) -{ - struct eeprom_priv *pEEPROM = &pAdapter->eeprompriv; - - if (pEEPROM->bautoload_fail_flag) { - memset(pEEPROM->efuse_eeprom_data, 0xFF, EFUSE_MAP_LEN_88E); - return; - } - - rtl8188e_EfusePowerSwitch(pAdapter, true); - rtl8188e_ReadEFuse(pAdapter, 0, EFUSE_MAP_LEN_88E, pEEPROM->efuse_eeprom_data); - rtl8188e_EfusePowerSwitch(pAdapter, false); + + /* FIXME: return an error to caller */ } diff --git a/drivers/staging/r8188eu/core/rtw_fw.c b/drivers/staging/r8188eu/core/rtw_fw.c index 0451e5177644..95534f9c7a0f 100644 --- a/drivers/staging/r8188eu/core/rtw_fw.c +++ b/drivers/staging/r8188eu/core/rtw_fw.c @@ -44,18 +44,28 @@ static_assert(sizeof(struct rt_firmware_hdr) == 32); static void fw_download_enable(struct adapter *padapter, bool enable) { u8 tmp; + int res; if (enable) { /* MCU firmware download enable. */ - tmp = rtw_read8(padapter, REG_MCUFWDL); + res = rtw_read8(padapter, REG_MCUFWDL, &tmp); + if (res) + return; + rtw_write8(padapter, REG_MCUFWDL, tmp | 0x01); /* 8051 reset */ - tmp = rtw_read8(padapter, REG_MCUFWDL + 2); + res = rtw_read8(padapter, REG_MCUFWDL + 2, &tmp); + if (res) + return; + rtw_write8(padapter, REG_MCUFWDL + 2, tmp & 0xf7); } else { /* MCU firmware download disable. */ - tmp = rtw_read8(padapter, REG_MCUFWDL); + res = rtw_read8(padapter, REG_MCUFWDL, &tmp); + if (res) + return; + rtw_write8(padapter, REG_MCUFWDL, tmp & 0xfe); /* Reserved for fw extension. */ @@ -125,8 +135,13 @@ static int page_write(struct adapter *padapter, u32 page, u8 *buffer, u32 size) { u8 value8; u8 u8Page = (u8)(page & 0x07); + int res; - value8 = (rtw_read8(padapter, REG_MCUFWDL + 2) & 0xF8) | u8Page; + res = rtw_read8(padapter, REG_MCUFWDL + 2, &value8); + if (res) + return _FAIL; + + value8 = (value8 & 0xF8) | u8Page; rtw_write8(padapter, REG_MCUFWDL + 2, value8); return block_write(padapter, buffer, size); @@ -165,8 +180,12 @@ exit: void rtw_reset_8051(struct adapter *padapter) { u8 val8; + int res; + + res = rtw_read8(padapter, REG_SYS_FUNC_EN + 1, &val8); + if (res) + return; - val8 = rtw_read8(padapter, REG_SYS_FUNC_EN + 1); rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 & (~BIT(2))); rtw_write8(padapter, REG_SYS_FUNC_EN + 1, val8 | (BIT(2))); } @@ -175,10 +194,14 @@ static int fw_free_to_go(struct adapter *padapter) { u32 counter = 0; u32 value32; + int res; /* polling CheckSum report */ do { - value32 = rtw_read32(padapter, REG_MCUFWDL); + res = rtw_read32(padapter, REG_MCUFWDL, &value32); + if (res) + continue; + if (value32 & FWDL_CHKSUM_RPT) break; } while (counter++ < POLLING_READY_TIMEOUT_COUNT); @@ -186,7 +209,10 @@ static int fw_free_to_go(struct adapter *padapter) if (counter >= POLLING_READY_TIMEOUT_COUNT) return _FAIL; - value32 = rtw_read32(padapter, REG_MCUFWDL); + res = rtw_read32(padapter, REG_MCUFWDL, &value32); + if (res) + return _FAIL; + value32 |= MCUFWDL_RDY; value32 &= ~WINTINI_RDY; rtw_write32(padapter, REG_MCUFWDL, value32); @@ -196,9 +222,10 @@ static int fw_free_to_go(struct adapter *padapter) /* polling for FW ready */ counter = 0; do { - value32 = rtw_read32(padapter, REG_MCUFWDL); - if (value32 & WINTINI_RDY) + res = rtw_read32(padapter, REG_MCUFWDL, &value32); + if (!res && value32 & WINTINI_RDY) return _SUCCESS; + udelay(5); } while (counter++ < POLLING_READY_TIMEOUT_COUNT); @@ -239,7 +266,7 @@ exit: int rtl8188e_firmware_download(struct adapter *padapter) { int ret = _SUCCESS; - u8 write_fw_retry = 0; + u8 reg; unsigned long fwdl_timeout; struct dvobj_priv *dvobj = adapter_to_dvobj(padapter); struct device *device = dvobj_to_dev(dvobj); @@ -259,9 +286,9 @@ int rtl8188e_firmware_download(struct adapter *padapter) fwhdr = (struct rt_firmware_hdr *)dvobj->firmware.data; if (IS_FW_HEADER_EXIST(fwhdr)) { - pr_info_once("R8188EU: Firmware Version %d, SubVersion %d, Signature 0x%x\n", - le16_to_cpu(fwhdr->version), fwhdr->subversion, - le16_to_cpu(fwhdr->signature)); + dev_info_once(device, "Firmware Version %d, SubVersion %d, Signature 0x%x\n", + le16_to_cpu(fwhdr->version), fwhdr->subversion, + le16_to_cpu(fwhdr->signature)); fw_data = fw_data + sizeof(struct rt_firmware_hdr); fw_size = fw_size - sizeof(struct rt_firmware_hdr); @@ -269,23 +296,34 @@ int rtl8188e_firmware_download(struct adapter *padapter) /* Suggested by Filen. If 8051 is running in RAM code, driver should inform Fw to reset by itself, */ /* or it will cause download Fw fail. 2010.02.01. by tynli. */ - if (rtw_read8(padapter, REG_MCUFWDL) & RAM_DL_SEL) { /* 8051 RAM code */ + ret = rtw_read8(padapter, REG_MCUFWDL, ®); + if (ret) { + ret = _FAIL; + goto exit; + } + + if (reg & RAM_DL_SEL) { /* 8051 RAM code */ rtw_write8(padapter, REG_MCUFWDL, 0x00); rtw_reset_8051(padapter); } fw_download_enable(padapter, true); fwdl_timeout = jiffies + msecs_to_jiffies(500); - while (1) { + do { /* reset the FWDL chksum */ - rtw_write8(padapter, REG_MCUFWDL, rtw_read8(padapter, REG_MCUFWDL) | FWDL_CHKSUM_RPT); + ret = rtw_read8(padapter, REG_MCUFWDL, ®); + if (ret) { + ret = _FAIL; + continue; + } + + rtw_write8(padapter, REG_MCUFWDL, reg | FWDL_CHKSUM_RPT); ret = write_fw(padapter, fw_data, fw_size); - - if (ret == _SUCCESS || - (time_after(jiffies, fwdl_timeout) && write_fw_retry++ >= 3)) + if (ret == _SUCCESS) break; - } + } while (!time_after(jiffies, fwdl_timeout)); + fw_download_enable(padapter, false); if (ret != _SUCCESS) goto exit; diff --git a/drivers/staging/r8188eu/core/rtw_ieee80211.c b/drivers/staging/r8188eu/core/rtw_ieee80211.c index 385a9ed8eff7..bc8543ea2e66 100644 --- a/drivers/staging/r8188eu/core/rtw_ieee80211.c +++ b/drivers/staging/r8188eu/core/rtw_ieee80211.c @@ -1048,6 +1048,7 @@ static int rtw_get_cipher_info(struct wlan_network *pnetwork) unsigned char *pbuf; int group_cipher = 0, pairwise_cipher = 0, is8021x = 0; int ret = _FAIL; + pbuf = rtw_get_wpa_ie(&pnetwork->network.IEs[12], &wpa_ielen, pnetwork->network.IELength - 12); if (pbuf && (wpa_ielen > 0)) { diff --git a/drivers/staging/r8188eu/core/rtw_ioctl_set.c b/drivers/staging/r8188eu/core/rtw_ioctl_set.c index 7ba75f73e47e..17f6bcbeebf4 100644 --- a/drivers/staging/r8188eu/core/rtw_ioctl_set.c +++ b/drivers/staging/r8188eu/core/rtw_ioctl_set.c @@ -71,7 +71,6 @@ u8 rtw_do_join(struct adapter *padapter) pibss = padapter->registrypriv.dev_network.MacAddress; - memset(&pdev_network->Ssid, 0, sizeof(struct ndis_802_11_ssid)); memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid)); rtw_update_registrypriv_dev_network(padapter); diff --git a/drivers/staging/r8188eu/core/rtw_iol.c b/drivers/staging/r8188eu/core/rtw_iol.c index af8e84a41b85..31e196ccd899 100644 --- a/drivers/staging/r8188eu/core/rtw_iol.c +++ b/drivers/staging/r8188eu/core/rtw_iol.c @@ -67,7 +67,7 @@ bool rtw_IOL_applied(struct adapter *adapter) return false; } -int _rtw_IOL_append_WB_cmd(struct xmit_frame *xmit_frame, u16 addr, u8 value, u8 mask) +int rtw_IOL_append_WB_cmd(struct xmit_frame *xmit_frame, u16 addr, u8 value, u8 mask) { struct ioreg_cfg cmd = {8, IOREG_CMD_WB_REG, 0x0, 0x0, 0x0}; @@ -81,7 +81,7 @@ int _rtw_IOL_append_WB_cmd(struct xmit_frame *xmit_frame, u16 addr, u8 value, u8 return rtw_IOL_append_cmds(xmit_frame, (u8 *)&cmd, cmd.length); } -int _rtw_IOL_append_WW_cmd(struct xmit_frame *xmit_frame, u16 addr, u16 value, u16 mask) +int rtw_IOL_append_WW_cmd(struct xmit_frame *xmit_frame, u16 addr, u16 value, u16 mask) { struct ioreg_cfg cmd = {8, IOREG_CMD_WW_REG, 0x0, 0x0, 0x0}; @@ -95,7 +95,7 @@ int _rtw_IOL_append_WW_cmd(struct xmit_frame *xmit_frame, u16 addr, u16 value, u return rtw_IOL_append_cmds(xmit_frame, (u8 *)&cmd, cmd.length); } -int _rtw_IOL_append_WD_cmd(struct xmit_frame *xmit_frame, u16 addr, u32 value, u32 mask) +int rtw_IOL_append_WD_cmd(struct xmit_frame *xmit_frame, u16 addr, u32 value, u32 mask) { struct ioreg_cfg cmd = {8, IOREG_CMD_WD_REG, 0x0, 0x0, 0x0}; @@ -109,7 +109,7 @@ int _rtw_IOL_append_WD_cmd(struct xmit_frame *xmit_frame, u16 addr, u32 value, u return rtw_IOL_append_cmds(xmit_frame, (u8 *)&cmd, cmd.length); } -int _rtw_IOL_append_WRF_cmd(struct xmit_frame *xmit_frame, u8 rf_path, u16 addr, u32 value, u32 mask) +int rtw_IOL_append_WRF_cmd(struct xmit_frame *xmit_frame, u8 rf_path, u16 addr, u32 value, u32 mask) { struct ioreg_cfg cmd = {8, IOREG_CMD_W_RF, 0x0, 0x0, 0x0}; diff --git a/drivers/staging/r8188eu/core/rtw_led.c b/drivers/staging/r8188eu/core/rtw_led.c index 2f3000428af7..d5c6c5e29621 100644 --- a/drivers/staging/r8188eu/core/rtw_led.c +++ b/drivers/staging/r8188eu/core/rtw_led.c @@ -16,7 +16,7 @@ (l)->CurrLedState == LED_BLINK_WPS_STOP || \ (l)->bLedWPSBlinkInProgress) -static void ResetLedStatus(struct LED_871x *pLed) +static void ResetLedStatus(struct led_priv *pLed) { pLed->CurrLedState = RTW_LED_OFF; /* Current LED state. */ pLed->bLedOn = false; /* true if LED is ON, false if LED is OFF. */ @@ -32,30 +32,40 @@ static void ResetLedStatus(struct LED_871x *pLed) pLed->bLedScanBlinkInProgress = false; } -static void SwLedOn(struct adapter *padapter, struct LED_871x *pLed) +static void SwLedOn(struct adapter *padapter, struct led_priv *pLed) { u8 LedCfg; + int res; if (padapter->bSurpriseRemoved || padapter->bDriverStopped) return; - LedCfg = rtw_read8(padapter, REG_LEDCFG2); + res = rtw_read8(padapter, REG_LEDCFG2, &LedCfg); + if (res) + return; + rtw_write8(padapter, REG_LEDCFG2, (LedCfg & 0xf0) | BIT(5) | BIT(6)); /* SW control led0 on. */ pLed->bLedOn = true; } -static void SwLedOff(struct adapter *padapter, struct LED_871x *pLed) +static void SwLedOff(struct adapter *padapter, struct led_priv *pLed) { u8 LedCfg; + int res; if (padapter->bSurpriseRemoved || padapter->bDriverStopped) goto exit; - LedCfg = rtw_read8(padapter, REG_LEDCFG2);/* 0x4E */ + res = rtw_read8(padapter, REG_LEDCFG2, &LedCfg);/* 0x4E */ + if (res) + goto exit; LedCfg &= 0x90; /* Set to software control. */ rtw_write8(padapter, REG_LEDCFG2, (LedCfg | BIT(3))); - LedCfg = rtw_read8(padapter, REG_MAC_PINMUX_CFG); + res = rtw_read8(padapter, REG_MAC_PINMUX_CFG, &LedCfg); + if (res) + goto exit; + LedCfg &= 0xFE; rtw_write8(padapter, REG_MAC_PINMUX_CFG, LedCfg); exit: @@ -65,7 +75,7 @@ exit: static void blink_work(struct work_struct *work) { struct delayed_work *dwork = to_delayed_work(work); - struct LED_871x *pLed = container_of(dwork, struct LED_871x, blink_work); + struct led_priv *pLed = container_of(dwork, struct led_priv, blink_work); struct adapter *padapter = pLed->padapter; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; @@ -172,35 +182,32 @@ static void blink_work(struct work_struct *work) void rtl8188eu_InitSwLeds(struct adapter *padapter) { struct led_priv *pledpriv = &padapter->ledpriv; - struct LED_871x *pLed = &pledpriv->SwLed0; - pLed->padapter = padapter; - ResetLedStatus(pLed); - INIT_DELAYED_WORK(&pLed->blink_work, blink_work); + pledpriv->padapter = padapter; + ResetLedStatus(pledpriv); + INIT_DELAYED_WORK(&pledpriv->blink_work, blink_work); } void rtl8188eu_DeInitSwLeds(struct adapter *padapter) { struct led_priv *ledpriv = &padapter->ledpriv; - struct LED_871x *pLed = &ledpriv->SwLed0; - cancel_delayed_work_sync(&pLed->blink_work); - ResetLedStatus(pLed); - SwLedOff(padapter, pLed); + cancel_delayed_work_sync(&ledpriv->blink_work); + ResetLedStatus(ledpriv); + SwLedOff(padapter, ledpriv); } void rtw_led_control(struct adapter *padapter, enum LED_CTL_MODE LedAction) { - struct led_priv *ledpriv = &padapter->ledpriv; + struct led_priv *pLed = &padapter->ledpriv; struct registry_priv *registry_par; - struct LED_871x *pLed = &ledpriv->SwLed0; struct mlme_priv *pmlmepriv = &padapter->mlmepriv; if ((padapter->bSurpriseRemoved) || (padapter->bDriverStopped) || (!padapter->hw_init_completed)) return; - if (!ledpriv->bRegUseLed) + if (!pLed->bRegUseLed) return; registry_par = &padapter->registrypriv; diff --git a/drivers/staging/r8188eu/core/rtw_mlme.c b/drivers/staging/r8188eu/core/rtw_mlme.c index 5a815642c3f6..2705c9d87b14 100644 --- a/drivers/staging/r8188eu/core/rtw_mlme.c +++ b/drivers/staging/r8188eu/core/rtw_mlme.c @@ -676,7 +676,6 @@ void rtw_surveydone_event_callback(struct adapter *adapter, u8 *pbuf) _clr_fwstate_(pmlmepriv, _FW_UNDER_SURVEY); - memset(&pdev_network->Ssid, 0, sizeof(struct ndis_802_11_ssid)); memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid)); rtw_update_registrypriv_dev_network(adapter); @@ -1118,7 +1117,7 @@ void rtw_sta_media_status_rpt(struct adapter *adapter, struct sta_info *psta, /* MACID|OPMODE:1 connect */ media_status_rpt = (u16)((psta->mac_id << 8) | mstatus); - SetHwReg8188EU(adapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status_rpt); + rtl8188e_set_FwMediaStatus_cmd(adapter, media_status_rpt); } void rtw_stassoc_event_callback(struct adapter *adapter, u8 *pbuf) @@ -1196,7 +1195,7 @@ void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf) u16 media_status; media_status = (mac_id << 8) | 0; /* MACID|OPMODE:0 means disconnect */ /* for STA, AP, ADHOC mode, report disconnect stauts to FW */ - SetHwReg8188EU(adapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status); + rtl8188e_set_FwMediaStatus_cmd(adapter, media_status); } if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) @@ -1253,7 +1252,6 @@ void rtw_stadel_event_callback(struct adapter *adapter, u8 *pbuf) memcpy(pdev_network, &tgt_network->network, get_wlan_bssid_ex_sz(&tgt_network->network)); - memset(&pdev_network->Ssid, 0, sizeof(struct ndis_802_11_ssid)); memcpy(&pdev_network->Ssid, &pmlmepriv->assoc_ssid, sizeof(struct ndis_802_11_ssid)); rtw_update_registrypriv_dev_network(adapter); diff --git a/drivers/staging/r8188eu/core/rtw_mlme_ext.c b/drivers/staging/r8188eu/core/rtw_mlme_ext.c index faf23fc950c5..32d0e101d0c2 100644 --- a/drivers/staging/r8188eu/core/rtw_mlme_ext.c +++ b/drivers/staging/r8188eu/core/rtw_mlme_ext.c @@ -428,6 +428,58 @@ static u32 p2p_listen_state_process(struct adapter *padapter, unsigned char *da) return _SUCCESS; } +static void update_TSF(struct mlme_ext_priv *pmlmeext, u8 *pframe) +{ + u8 *pIE; + __le32 *pbuf; + + pIE = pframe + sizeof(struct ieee80211_hdr_3addr); + pbuf = (__le32 *)pIE; + + pmlmeext->TSFValue = le32_to_cpu(*(pbuf + 1)); + + pmlmeext->TSFValue = pmlmeext->TSFValue << 32; + + pmlmeext->TSFValue |= le32_to_cpu(*pbuf); +} + +static void correct_TSF(struct adapter *padapter) +{ + u8 reg; + int res; + u64 tsf; + struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; + struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; + + tsf = pmlmeext->TSFValue - do_div(pmlmeext->TSFValue, + pmlmeinfo->bcn_interval * 1024) - 1024; /* us */ + + if (((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) || + ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE)) + rtw_stop_tx_beacon(padapter); + + /* disable related TSF function */ + res = rtw_read8(padapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(padapter, REG_BCN_CTRL, reg & (~BIT(3))); + + rtw_write32(padapter, REG_TSFTR, tsf); + rtw_write32(padapter, REG_TSFTR + 4, tsf >> 32); + + /* enable related TSF function */ + res = rtw_read8(padapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(padapter, REG_BCN_CTRL, reg | BIT(3)); + + if (((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) || + ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE)) + rtw_resume_tx_beacon(padapter); +} + /**************************************************************************** Following are the callback functions for each subtype of the management frames @@ -582,7 +634,7 @@ unsigned int OnBeacon(struct adapter *padapter, struct recv_frame *precv_frame) pmlmeinfo->assoc_AP_vendor = check_assoc_AP(pframe + sizeof(struct ieee80211_hdr_3addr), len - sizeof(struct ieee80211_hdr_3addr)); /* update TSF Value */ - update_TSF(pmlmeext, pframe, len); + update_TSF(pmlmeext, pframe); /* start auth */ start_clnt_auth(padapter); @@ -625,7 +677,7 @@ unsigned int OnBeacon(struct adapter *padapter, struct recv_frame *precv_frame) } /* update TSF Value */ - update_TSF(pmlmeext, pframe, len); + update_TSF(pmlmeext, pframe); /* report sta add event */ report_add_sta_event(padapter, GetAddr2Ptr(pframe), cam_idx); @@ -5363,26 +5415,20 @@ exit: return ret; } -void issue_action_BA(struct adapter *padapter, unsigned char *raddr, unsigned char action, unsigned short status) +void issue_action_BA(struct adapter *padapter, unsigned char *raddr, u8 action, u16 status) { - u8 category = WLAN_CATEGORY_BACK; u16 start_seq; - u16 BA_para_set; - u16 reason_code; - u16 BA_timeout_value; - __le16 le_tmp; u16 BA_starting_seqctrl = 0; struct xmit_frame *pmgntframe; struct pkt_attrib *pattrib; - u8 *pframe; - struct ieee80211_hdr *pwlanhdr; - __le16 *fctrl; struct xmit_priv *pxmitpriv = &padapter->xmitpriv; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; struct sta_info *psta; struct sta_priv *pstapriv = &padapter->stapriv; struct registry_priv *pregpriv = &padapter->registrypriv; + struct ieee80211_mgmt *mgmt; + u16 capab, params; pmgntframe = alloc_mgtxmitframe(pxmitpriv); if (!pmgntframe) @@ -5394,81 +5440,70 @@ void issue_action_BA(struct adapter *padapter, unsigned char *raddr, unsigned ch memset(pmgntframe->buf_addr, 0, WLANHDR_OFFSET + TXDESC_OFFSET); - pframe = (u8 *)(pmgntframe->buf_addr) + TXDESC_OFFSET; - pwlanhdr = (struct ieee80211_hdr *)pframe; + mgmt = (struct ieee80211_mgmt *)(pmgntframe->buf_addr + TXDESC_OFFSET); - fctrl = &pwlanhdr->frame_control; - *(fctrl) = 0; + mgmt->frame_control = cpu_to_le16(IEEE80211_STYPE_ACTION | IEEE80211_FTYPE_MGMT); - /* memcpy(pwlanhdr->addr1, get_my_bssid(&(pmlmeinfo->network)), ETH_ALEN); */ - memcpy(pwlanhdr->addr1, raddr, ETH_ALEN); - memcpy(pwlanhdr->addr2, myid(&padapter->eeprompriv), ETH_ALEN); - memcpy(pwlanhdr->addr3, get_my_bssid(&pmlmeinfo->network), ETH_ALEN); + memcpy(mgmt->da, raddr, ETH_ALEN); + memcpy(mgmt->sa, myid(&padapter->eeprompriv), ETH_ALEN); + memcpy(mgmt->bssid, get_my_bssid(&pmlmeinfo->network), ETH_ALEN); - SetSeqNum(pwlanhdr, pmlmeext->mgnt_seq); + mgmt->seq_ctrl = cpu_to_le16(pmlmeext->mgnt_seq); pmlmeext->mgnt_seq++; - SetFrameSubType(pframe, WIFI_ACTION); - pframe += sizeof(struct ieee80211_hdr_3addr); - pattrib->pktlen = sizeof(struct ieee80211_hdr_3addr); + mgmt->u.action.category = WLAN_CATEGORY_BACK; - pframe = rtw_set_fixed_ie(pframe, 1, &(category), &pattrib->pktlen); - pframe = rtw_set_fixed_ie(pframe, 1, &(action), &pattrib->pktlen); + switch (action) { + case WLAN_ACTION_ADDBA_REQ: + mgmt->u.action.u.addba_req.action_code = WLAN_ACTION_ADDBA_REQ; + do { + pmlmeinfo->dialogToken++; + } while (pmlmeinfo->dialogToken == 0); + mgmt->u.action.u.addba_req.dialog_token = pmlmeinfo->dialogToken; - if (category == 3) { - switch (action) { - case 0: /* ADDBA req */ - do { - pmlmeinfo->dialogToken++; - } while (pmlmeinfo->dialogToken == 0); - pframe = rtw_set_fixed_ie(pframe, 1, &pmlmeinfo->dialogToken, &pattrib->pktlen); + /* immediate ack & 64 buffer size */ + capab = u16_encode_bits(64, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK); + capab |= u16_encode_bits(1, IEEE80211_ADDBA_PARAM_POLICY_MASK); + capab |= u16_encode_bits(status, IEEE80211_ADDBA_PARAM_TID_MASK); + mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab); - BA_para_set = (0x1002 | ((status & 0xf) << 2)); /* immediate ack & 64 buffer size */ - le_tmp = cpu_to_le16(BA_para_set); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&le_tmp, &pattrib->pktlen); + mgmt->u.action.u.addba_req.timeout = cpu_to_le16(5000); /* 5 ms */ - BA_timeout_value = 5000;/* 5ms */ - le_tmp = cpu_to_le16(BA_timeout_value); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&le_tmp, &pattrib->pktlen); + psta = rtw_get_stainfo(pstapriv, raddr); + if (psta) { + start_seq = (psta->sta_xmitpriv.txseq_tid[status & 0x07] & 0xfff) + 1; - psta = rtw_get_stainfo(pstapriv, raddr); - if (psta) { - start_seq = (psta->sta_xmitpriv.txseq_tid[status & 0x07] & 0xfff) + 1; + psta->BA_starting_seqctrl[status & 0x07] = start_seq; - psta->BA_starting_seqctrl[status & 0x07] = start_seq; - - BA_starting_seqctrl = start_seq << 4; - } - le_tmp = cpu_to_le16(BA_starting_seqctrl); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&le_tmp, &pattrib->pktlen); - break; - case 1: /* ADDBA rsp */ - pframe = rtw_set_fixed_ie(pframe, 1, &pmlmeinfo->ADDBA_req.dialog_token, &pattrib->pktlen); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&status, &pattrib->pktlen); - BA_para_set = le16_to_cpu(pmlmeinfo->ADDBA_req.BA_para_set) & 0x3f; - BA_para_set |= 0x1000; /* 64 buffer size */ - - if (pregpriv->ampdu_amsdu == 0)/* disabled */ - BA_para_set = BA_para_set & ~BIT(0); - else if (pregpriv->ampdu_amsdu == 1)/* enabled */ - BA_para_set = BA_para_set | BIT(0); - le_tmp = cpu_to_le16(BA_para_set); - - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&le_tmp, &pattrib->pktlen); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&pmlmeinfo->ADDBA_req.BA_timeout_value, &pattrib->pktlen); - break; - case 2:/* DELBA */ - BA_para_set = (status & 0x1F) << 3; - le_tmp = cpu_to_le16(BA_para_set); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&le_tmp, &pattrib->pktlen); - - reason_code = 37;/* Requested from peer STA as it does not want to use the mechanism */ - le_tmp = cpu_to_le16(reason_code); - pframe = rtw_set_fixed_ie(pframe, 2, (unsigned char *)&le_tmp, &pattrib->pktlen); - break; - default: - break; + BA_starting_seqctrl = start_seq << 4; } + mgmt->u.action.u.addba_req.start_seq_num = cpu_to_le16(BA_starting_seqctrl); + + pattrib->pktlen = offsetofend(struct ieee80211_mgmt, + u.action.u.addba_req.start_seq_num); + break; + case WLAN_ACTION_ADDBA_RESP: + mgmt->u.action.u.addba_resp.action_code = WLAN_ACTION_ADDBA_RESP; + mgmt->u.action.u.addba_resp.dialog_token = pmlmeinfo->ADDBA_req.dialog_token; + mgmt->u.action.u.addba_resp.status = cpu_to_le16(status); + capab = le16_to_cpu(pmlmeinfo->ADDBA_req.BA_para_set) & 0x3f; + capab |= u16_encode_bits(64, IEEE80211_ADDBA_PARAM_BUF_SIZE_MASK); + capab |= u16_encode_bits(pregpriv->ampdu_amsdu, IEEE80211_ADDBA_PARAM_AMSDU_MASK); + mgmt->u.action.u.addba_req.capab = cpu_to_le16(capab); + mgmt->u.action.u.addba_resp.timeout = pmlmeinfo->ADDBA_req.BA_timeout_value; + pattrib->pktlen = offsetofend(struct ieee80211_mgmt, u.action.u.addba_resp.timeout); + break; + case WLAN_ACTION_DELBA: + mgmt->u.action.u.delba.action_code = WLAN_ACTION_DELBA; + mgmt->u.action.u.delba.params = cpu_to_le16((status & 0x1F) << 3); + params = u16_encode_bits((status & 0x1), IEEE80211_DELBA_PARAM_INITIATOR_MASK); + params |= u16_encode_bits((status >> 1) & 0xF, IEEE80211_DELBA_PARAM_TID_MASK); + mgmt->u.action.u.delba.params = cpu_to_le16(params); + mgmt->u.action.u.delba.reason_code = cpu_to_le16(WLAN_STATUS_REQUEST_DECLINED); + pattrib->pktlen = offsetofend(struct ieee80211_mgmt, u.action.u.delba.reason_code); + break; + default: + break; } pattrib->last_txcmdsz = pattrib->pktlen; @@ -5623,7 +5658,7 @@ unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr) if (initiator == 0) { /* recipient */ for (tid = 0; tid < MAXTID; tid++) { if (psta->recvreorder_ctrl[tid].enable) { - issue_action_BA(padapter, addr, RTW_WLAN_ACTION_DELBA, (((tid << 1) | initiator) & 0x1F)); + issue_action_BA(padapter, addr, WLAN_ACTION_DELBA, (((tid << 1) | initiator) & 0x1F)); psta->recvreorder_ctrl[tid].enable = false; psta->recvreorder_ctrl[tid].indicate_seq = 0xffff; } @@ -5631,7 +5666,7 @@ unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr) } else if (initiator == 1) { /* originator */ for (tid = 0; tid < MAXTID; tid++) { if (psta->htpriv.agg_enable_bitmap & BIT(tid)) { - issue_action_BA(padapter, addr, RTW_WLAN_ACTION_DELBA, (((tid << 1) | initiator) & 0x1F)); + issue_action_BA(padapter, addr, WLAN_ACTION_DELBA, (((tid << 1) | initiator) & 0x1F)); psta->htpriv.agg_enable_bitmap &= ~BIT(tid); psta->htpriv.candidate_tid_bitmap &= ~BIT(tid); } @@ -5667,14 +5702,129 @@ unsigned int send_beacon(struct adapter *padapter) bool get_beacon_valid_bit(struct adapter *adapter) { + int res; + u8 reg; + + res = rtw_read8(adapter, REG_TDECTRL + 2, ®); + if (res) + return false; + /* BIT(16) of REG_TDECTRL = BIT(0) of REG_TDECTRL+2 */ - return BIT(0) & rtw_read8(adapter, REG_TDECTRL + 2); + return BIT(0) & reg; } void clear_beacon_valid_bit(struct adapter *adapter) { + int res; + u8 reg; + + res = rtw_read8(adapter, REG_TDECTRL + 2, ®); + if (res) + return; + /* BIT(16) of REG_TDECTRL = BIT(0) of REG_TDECTRL+2, write 1 to clear, Clear by sw */ - rtw_write8(adapter, REG_TDECTRL + 2, rtw_read8(adapter, REG_TDECTRL + 2) | BIT(0)); + rtw_write8(adapter, REG_TDECTRL + 2, reg | BIT(0)); +} + +void rtw_resume_tx_beacon(struct adapter *adapt) +{ + struct hal_data_8188e *haldata = &adapt->haldata; + + /* 2010.03.01. Marked by tynli. No need to call workitem beacause we record the value */ + /* which should be read from register to a global variable. */ + + rtw_write8(adapt, REG_FWHW_TXQ_CTRL + 2, (haldata->RegFwHwTxQCtrl) | BIT(6)); + haldata->RegFwHwTxQCtrl |= BIT(6); + rtw_write8(adapt, REG_TBTT_PROHIBIT + 1, 0xff); + haldata->RegReg542 |= BIT(0); + rtw_write8(adapt, REG_TBTT_PROHIBIT + 2, haldata->RegReg542); +} + +void rtw_stop_tx_beacon(struct adapter *adapt) +{ + struct hal_data_8188e *haldata = &adapt->haldata; + + /* 2010.03.01. Marked by tynli. No need to call workitem beacause we record the value */ + /* which should be read from register to a global variable. */ + + rtw_write8(adapt, REG_FWHW_TXQ_CTRL + 2, (haldata->RegFwHwTxQCtrl) & (~BIT(6))); + haldata->RegFwHwTxQCtrl &= (~BIT(6)); + rtw_write8(adapt, REG_TBTT_PROHIBIT + 1, 0x64); + haldata->RegReg542 &= ~(BIT(0)); + rtw_write8(adapt, REG_TBTT_PROHIBIT + 2, haldata->RegReg542); + + /* todo: CheckFwRsvdPageContent(Adapter); 2010.06.23. Added by tynli. */ +} + +static void rtw_set_opmode(struct adapter *adapter, u8 mode) +{ + u8 val8; + int res; + + /* disable Port0 TSF update */ + res = rtw_read8(adapter, REG_BCN_CTRL, &val8); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL, val8 | BIT(4)); + + /* set net_type */ + res = rtw_read8(adapter, MSR, &val8); + if (res) + return; + + val8 &= 0x0c; + val8 |= mode; + rtw_write8(adapter, MSR, val8); + + if ((mode == _HW_STATE_STATION_) || (mode == _HW_STATE_NOLINK_)) { + rtw_stop_tx_beacon(adapter); + + rtw_write8(adapter, REG_BCN_CTRL, 0x19);/* disable atim wnd */ + } else if (mode == _HW_STATE_ADHOC_) { + rtw_resume_tx_beacon(adapter); + rtw_write8(adapter, REG_BCN_CTRL, 0x1a); + } else if (mode == _HW_STATE_AP_) { + rtw_resume_tx_beacon(adapter); + + rtw_write8(adapter, REG_BCN_CTRL, 0x12); + + /* Set RCR */ + rtw_write32(adapter, REG_RCR, 0x7000208e);/* CBSSID_DATA must set to 0,reject ICV_ERR packet */ + /* enable to rx data frame */ + rtw_write16(adapter, REG_RXFLTMAP2, 0xFFFF); + /* enable to rx ps-poll */ + rtw_write16(adapter, REG_RXFLTMAP1, 0x0400); + + /* Beacon Control related register for first time */ + rtw_write8(adapter, REG_BCNDMATIM, 0x02); /* 2ms */ + + rtw_write8(adapter, REG_ATIMWND, 0x0a); /* 10ms */ + rtw_write16(adapter, REG_BCNTCFG, 0x00); + rtw_write16(adapter, REG_TBTT_PROHIBIT, 0xff04); + rtw_write16(adapter, REG_TSFTR_SYN_OFFSET, 0x7fff);/* +32767 (~32ms) */ + + /* reset TSF */ + rtw_write8(adapter, REG_DUAL_TSF_RST, BIT(0)); + + /* BIT(3) - If set 0, hw will clr bcnq when tx becon ok/fail or port 0 */ + res = rtw_read8(adapter, REG_MBID_NUM, &val8); + if (res) + return; + + rtw_write8(adapter, REG_MBID_NUM, val8 | BIT(3) | BIT(4)); + + /* enable BCN0 Function for if1 */ + /* don't enable update TSF0 for if1 (due to TSF update when beacon/probe rsp are received) */ + rtw_write8(adapter, REG_BCN_CTRL, (DIS_TSF_UDT0_NORMAL_CHIP | EN_BCN_FUNCTION | BIT(1))); + + /* dis BCN1 ATIM WND if if2 is station */ + res = rtw_read8(adapter, REG_BCN_CTRL_1, &val8); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL_1, val8 | BIT(0)); + } } /**************************************************************************** @@ -5698,9 +5848,70 @@ static void rtw_set_initial_gain(struct adapter *adapter, u8 gain) } } +void rtw_mlme_under_site_survey(struct adapter *adapter) +{ + /* config RCR to receive different BSSID & not to receive data frame */ + + int res; + u8 reg; + u32 v; + + res = rtw_read32(adapter, REG_RCR, &v); + if (res) + return; + + v &= ~(RCR_CBSSID_BCN); + rtw_write32(adapter, REG_RCR, v); + /* reject all data frame */ + rtw_write16(adapter, REG_RXFLTMAP2, 0x00); + + /* disable update TSF */ + res = rtw_read8(adapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL, reg | BIT(4)); +} + +void rtw_mlme_site_survey_done(struct adapter *adapter) +{ + struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; + struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; + u32 reg32; + int res; + u8 reg; + + if ((is_client_associated_to_ap(adapter)) || + ((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE)) { + /* enable to rx data frame */ + rtw_write16(adapter, REG_RXFLTMAP2, 0xFFFF); + + /* enable update TSF */ + res = rtw_read8(adapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL, reg & (~BIT(4))); + } else if ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) { + rtw_write16(adapter, REG_RXFLTMAP2, 0xFFFF); + /* enable update TSF */ + res = rtw_read8(adapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL, reg & (~BIT(4))); + } + + res = rtw_read32(adapter, REG_RCR, ®32); + if (res) + return; + + rtw_write32(adapter, REG_RCR, reg32 | RCR_CBSSID_BCN); +} + void site_survey(struct adapter *padapter) { - unsigned char survey_channel = 0, val8; + unsigned char survey_channel = 0; enum rt_scan_type ScanType = SCAN_PASSIVE; struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; @@ -5824,8 +6035,7 @@ void site_survey(struct adapter *padapter) if (is_client_associated_to_ap(padapter)) issue_nulldata(padapter, NULL, 0, 3, 500); - val8 = 0; /* survey done */ - SetHwReg8188EU(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); + rtw_mlme_site_survey_done(padapter); report_surveydone_event(padapter); @@ -6002,7 +6212,9 @@ static void rtw_set_bssid(struct adapter *adapter, u8 *bssid) static void mlme_join(struct adapter *adapter, int type) { struct mlme_priv *mlmepriv = &adapter->mlmepriv; - u8 retry_limit = 0x30; + u8 retry_limit = 0x30, reg; + u32 reg32; + int res; switch (type) { case 0: @@ -6010,8 +6222,12 @@ static void mlme_join(struct adapter *adapter, int type) /* enable to rx data frame, accept all data frame */ rtw_write16(adapter, REG_RXFLTMAP2, 0xFFFF); + res = rtw_read32(adapter, REG_RCR, ®32); + if (res) + return; + rtw_write32(adapter, REG_RCR, - rtw_read32(adapter, REG_RCR) | RCR_CBSSID_DATA | RCR_CBSSID_BCN); + reg32 | RCR_CBSSID_DATA | RCR_CBSSID_BCN); if (check_fwstate(mlmepriv, WIFI_STATION_STATE)) { retry_limit = 48; @@ -6027,7 +6243,11 @@ static void mlme_join(struct adapter *adapter, int type) case 2: /* sta add event call back */ /* enable update TSF */ - rtw_write8(adapter, REG_BCN_CTRL, rtw_read8(adapter, REG_BCN_CTRL) & (~BIT(4))); + res = rtw_read8(adapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL, reg & (~BIT(4))); if (check_fwstate(mlmepriv, WIFI_ADHOC_STATE | WIFI_ADHOC_MASTER_STATE)) retry_limit = 0x7; @@ -6184,14 +6404,14 @@ void start_clnt_assoc(struct adapter *padapter) set_link_timer(pmlmeext, REASSOC_TO); } -unsigned int receive_disconnect(struct adapter *padapter, unsigned char *MacAddr, unsigned short reason) +void receive_disconnect(struct adapter *padapter, unsigned char *MacAddr, unsigned short reason) { struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; /* check A3 */ if (!(!memcmp(MacAddr, get_my_bssid(&pmlmeinfo->network), ETH_ALEN))) - return _SUCCESS; + return; if ((pmlmeinfo->state & 0x03) == WIFI_FW_STATION_STATE) { if (pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) { @@ -6202,7 +6422,6 @@ unsigned int receive_disconnect(struct adapter *padapter, unsigned char *MacAddr report_join_res(padapter, -2); } } - return _SUCCESS; } static void process_80211d(struct adapter *padapter, struct wlan_bssid_ex *bssid) @@ -6640,6 +6859,23 @@ void update_sta_info(struct adapter *padapter, struct sta_info *psta) psta->state = _FW_LINKED; } +static void rtw_reset_dm_func_flag(struct adapter *adapter) +{ + struct hal_data_8188e *haldata = &adapter->haldata; + struct dm_priv *dmpriv = &haldata->dmpriv; + struct odm_dm_struct *odmpriv = &haldata->odmpriv; + + odmpriv->SupportAbility = dmpriv->InitODMFlag; +} + +static void rtw_clear_dm_func_flag(struct adapter *adapter) +{ + struct hal_data_8188e *haldata = &adapter->haldata; + struct odm_dm_struct *odmpriv = &haldata->odmpriv; + + odmpriv->SupportAbility = 0; +} + void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res) { struct sta_info *psta, *psta_bmc; @@ -6670,12 +6906,12 @@ void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res) } /* turn on dynamic functions */ - SetHwReg8188EU(padapter, HW_VAR_DM_FUNC_RESET, NULL); + rtw_reset_dm_func_flag(padapter); /* update IOT-releated issue */ update_IOT_info(padapter); - SetHwReg8188EU(padapter, HW_VAR_BASIC_RATE, cur_network->SupportedRates); + rtw_set_basic_rate(padapter, cur_network->SupportedRates); /* BCN interval */ rtw_write16(padapter, REG_BCN_INTERVAL, pmlmeinfo->bcn_interval); @@ -6702,14 +6938,14 @@ void mlmeext_joinbss_event_callback(struct adapter *padapter, int join_res) rtw_set_max_rpt_macid(padapter, psta->mac_id); media_status = (psta->mac_id << 8) | 1; /* MACID|OPMODE: 1 means connect */ - SetHwReg8188EU(padapter, HW_VAR_H2C_MEDIA_STATUS_RPT, (u8 *)&media_status); + rtl8188e_set_FwMediaStatus_cmd(padapter, media_status); } mlme_join(padapter, 2); if ((pmlmeinfo->state & 0x03) == WIFI_FW_STATION_STATE) { /* correcting TSF */ - correct_TSF(padapter, pmlmeext); + correct_TSF(padapter); } rtw_lps_ctrl_wk_cmd(padapter, LPS_CTRL_CONNECT, 0); } @@ -6724,7 +6960,7 @@ void mlmeext_sta_add_event_callback(struct adapter *padapter, struct sta_info *p /* nothing to do */ } else { /* adhoc client */ /* correcting TSF */ - correct_TSF(padapter, pmlmeext); + correct_TSF(padapter); /* start beacon */ if (send_beacon(padapter) == _FAIL) { @@ -6748,6 +6984,9 @@ void mlmeext_sta_add_event_callback(struct adapter *padapter, struct sta_info *p static void mlme_disconnect(struct adapter *adapter) { + int res; + u8 reg; + /* Set RCR to not to receive data frame when NO LINK state */ /* reject all data frames */ rtw_write16(adapter, REG_RXFLTMAP2, 0x00); @@ -6756,7 +6995,12 @@ static void mlme_disconnect(struct adapter *adapter) rtw_write8(adapter, REG_DUAL_TSF_RST, (BIT(0) | BIT(1))); /* disable update TSF */ - rtw_write8(adapter, REG_BCN_CTRL, rtw_read8(adapter, REG_BCN_CTRL) | BIT(4)); + + res = rtw_read8(adapter, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapter, REG_BCN_CTRL, reg | BIT(4)); } void mlmeext_sta_del_event_callback(struct adapter *padapter) @@ -6810,14 +7054,20 @@ static u8 chk_ap_is_alive(struct sta_info *psta) return ret; } -static void rtl8188e_sreset_linked_status_check(struct adapter *padapter) +static int rtl8188e_sreset_linked_status_check(struct adapter *padapter) { - u32 rx_dma_status = rtw_read32(padapter, REG_RXDMA_STATUS); + u32 rx_dma_status; + int res; + u8 reg; + + res = rtw_read32(padapter, REG_RXDMA_STATUS, &rx_dma_status); + if (res) + return res; if (rx_dma_status != 0x00) rtw_write32(padapter, REG_RXDMA_STATUS, rx_dma_status); - rtw_read8(padapter, REG_FMETHR); + return rtw_read8(padapter, REG_FMETHR, ®); } void linked_status_chk(struct adapter *padapter) @@ -7045,7 +7295,7 @@ u8 setopmode_hdl(struct adapter *padapter, u8 *pbuf) type = _HW_STATE_NOLINK_; } - SetHwReg8188EU(padapter, HW_VAR_SET_OPMODE, (u8 *)(&type)); + rtw_set_opmode(padapter, type); return H2C_SUCCESS; } @@ -7081,7 +7331,7 @@ u8 createbss_hdl(struct adapter *padapter, u8 *pbuf) /* disable dynamic functions, such as high power, DIG */ Save_DM_Func_Flag(padapter); - SetHwReg8188EU(padapter, HW_VAR_DM_FUNC_CLR, NULL); + rtw_clear_dm_func_flag(padapter); /* cancel link timer */ _cancel_timer_ex(&pmlmeext->link_timer); @@ -7089,7 +7339,7 @@ u8 createbss_hdl(struct adapter *padapter, u8 *pbuf) /* clear CAM */ flush_all_cam_entry(padapter); - memcpy(pnetwork, pbuf, FIELD_OFFSET(struct wlan_bssid_ex, IELength)); + memcpy(pnetwork, pbuf, offsetof(struct wlan_bssid_ex, IELength)); pnetwork->IELength = ((struct wlan_bssid_ex *)pbuf)->IELength; if (pnetwork->IELength > MAX_IE_SZ)/* Check pbuf->IELength */ @@ -7146,7 +7396,7 @@ u8 join_cmd_hdl(struct adapter *padapter, u8 *pbuf) pmlmeinfo->candidate_tid_bitmap = 0; pmlmeinfo->bwmode_updated = false; - memcpy(pnetwork, pbuf, FIELD_OFFSET(struct wlan_bssid_ex, IELength)); + memcpy(pnetwork, pbuf, offsetof(struct wlan_bssid_ex, IELength)); pnetwork->IELength = ((struct wlan_bssid_ex *)pbuf)->IELength; if (pnetwork->IELength > MAX_IE_SZ)/* Check pbuf->IELength */ @@ -7219,6 +7469,7 @@ u8 disconnect_hdl(struct adapter *padapter, unsigned char *pbuf) struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; struct wlan_bssid_ex *pnetwork = (struct wlan_bssid_ex *)(&pmlmeinfo->network); u8 val8; + int res; if (is_client_associated_to_ap(padapter)) issue_deauth_ex(padapter, pnetwork->MacAddress, WLAN_REASON_DEAUTH_LEAVING, param->deauth_timeout_ms / 100, 100); @@ -7231,7 +7482,10 @@ u8 disconnect_hdl(struct adapter *padapter, unsigned char *pbuf) if (((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) || ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE)) { /* Stop BCN */ - val8 = rtw_read8(padapter, REG_BCN_CTRL); + res = rtw_read8(padapter, REG_BCN_CTRL, &val8); + if (res) + return H2C_DROPPED; + rtw_write8(padapter, REG_BCN_CTRL, val8 & (~(EN_BCN_FUNCTION | EN_TXBCN_RPT))); } @@ -7302,7 +7556,6 @@ u8 sitesurvey_cmd_hdl(struct adapter *padapter, u8 *pbuf) struct mlme_ext_priv *pmlmeext = &padapter->mlmeextpriv; struct sitesurvey_parm *pparm = (struct sitesurvey_parm *)pbuf; u8 bdelayscan = false; - u8 val8; u32 i; struct wifidirect_info *pwdinfo = &padapter->wdinfo; @@ -7347,7 +7600,7 @@ u8 sitesurvey_cmd_hdl(struct adapter *padapter, u8 *pbuf) if ((pmlmeext->sitesurvey_res.state == SCAN_START) || (pmlmeext->sitesurvey_res.state == SCAN_TXNULL)) { /* disable dynamic functions, such as high power, DIG */ Save_DM_Func_Flag(padapter); - SetHwReg8188EU(padapter, HW_VAR_DM_FUNC_CLR, NULL); + rtw_clear_dm_func_flag(padapter); /* config the initial gain under scanning, need to write the BB registers */ if (rtw_p2p_chk_state(pwdinfo, P2P_STATE_NONE)) @@ -7359,8 +7612,7 @@ u8 sitesurvey_cmd_hdl(struct adapter *padapter, u8 *pbuf) /* set MSR to no link state */ Set_MSR(padapter, _HW_STATE_NOLINK_); - val8 = 1; /* under site survey */ - SetHwReg8188EU(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); + rtw_mlme_under_site_survey(padapter); pmlmeext->sitesurvey_res.state = SCAN_PROCESS; } @@ -7475,7 +7727,7 @@ u8 add_ba_hdl(struct adapter *padapter, unsigned char *pbuf) if (((pmlmeinfo->state & WIFI_FW_ASSOC_SUCCESS) && (pmlmeinfo->HT_enable)) || ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE)) { - issue_action_BA(padapter, pparm->addr, RTW_WLAN_ACTION_ADDBA_REQ, (u16)pparm->tid); + issue_action_BA(padapter, pparm->addr, WLAN_ACTION_ADDBA_REQ, (u16)pparm->tid); _set_timer(&psta->addba_retry_timer, ADDBA_TO); } else { psta->htpriv.candidate_tid_bitmap &= ~BIT(pparm->tid); diff --git a/drivers/staging/r8188eu/core/rtw_p2p.c b/drivers/staging/r8188eu/core/rtw_p2p.c index beffe5b16f1e..bd654d4ff8b4 100644 --- a/drivers/staging/r8188eu/core/rtw_p2p.c +++ b/drivers/staging/r8188eu/core/rtw_p2p.c @@ -1450,10 +1450,9 @@ static void restore_p2p_state_handler(struct adapter *padapter) static void pre_tx_invitereq_handler(struct adapter *padapter) { struct wifidirect_info *pwdinfo = &padapter->wdinfo; - u8 val8 = 1; set_channel_bwmode(padapter, pwdinfo->invitereq_info.peer_ch, HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); - SetHwReg8188EU(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); + rtw_mlme_under_site_survey(padapter); issue_probereq_p2p(padapter, NULL); _set_timer(&pwdinfo->pre_tx_scan_timer, P2P_TX_PRESCAN_TIMEOUT); @@ -1462,10 +1461,9 @@ static void pre_tx_invitereq_handler(struct adapter *padapter) static void pre_tx_provdisc_handler(struct adapter *padapter) { struct wifidirect_info *pwdinfo = &padapter->wdinfo; - u8 val8 = 1; set_channel_bwmode(padapter, pwdinfo->tx_prov_disc_info.peer_channel_num[0], HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); - SetHwReg8188EU(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); + rtw_mlme_under_site_survey(padapter); issue_probereq_p2p(padapter, NULL); _set_timer(&pwdinfo->pre_tx_scan_timer, P2P_TX_PRESCAN_TIMEOUT); @@ -1474,10 +1472,9 @@ static void pre_tx_provdisc_handler(struct adapter *padapter) static void pre_tx_negoreq_handler(struct adapter *padapter) { struct wifidirect_info *pwdinfo = &padapter->wdinfo; - u8 val8 = 1; set_channel_bwmode(padapter, pwdinfo->nego_req_info.peer_channel_num[0], HAL_PRIME_CHNL_OFFSET_DONT_CARE, HT_CHANNEL_WIDTH_20); - SetHwReg8188EU(padapter, HW_VAR_MLME_SITESURVEY, (u8 *)(&val8)); + rtw_mlme_under_site_survey(padapter); issue_probereq_p2p(padapter, NULL); _set_timer(&pwdinfo->pre_tx_scan_timer, P2P_TX_PRESCAN_TIMEOUT); @@ -1891,7 +1888,7 @@ int rtw_p2p_enable(struct adapter *padapter, enum P2P_ROLE role) if (role == P2P_ROLE_DEVICE || role == P2P_ROLE_CLIENT || role == P2P_ROLE_GO) { /* leave IPS/Autosuspend */ - if (rtw_pwr_wakeup(padapter) == _FAIL) { + if (rtw_pwr_wakeup(padapter)) { ret = _FAIL; goto exit; } @@ -1905,7 +1902,7 @@ int rtw_p2p_enable(struct adapter *padapter, enum P2P_ROLE role) init_wifidirect_info(padapter, role); } else if (role == P2P_ROLE_DISABLE) { - if (rtw_pwr_wakeup(padapter) == _FAIL) { + if (rtw_pwr_wakeup(padapter)) { ret = _FAIL; goto exit; } diff --git a/drivers/staging/r8188eu/core/rtw_pwrctrl.c b/drivers/staging/r8188eu/core/rtw_pwrctrl.c index 7b816b824947..10550bd2c16d 100644 --- a/drivers/staging/r8188eu/core/rtw_pwrctrl.c +++ b/drivers/staging/r8188eu/core/rtw_pwrctrl.c @@ -229,6 +229,9 @@ void rtw_set_ps_mode(struct adapter *padapter, u8 ps_mode, u8 smart_ps, u8 bcn_a static bool lps_rf_on(struct adapter *adapter) { + int res; + u32 reg; + /* When we halt NIC, we should check if FW LPS is leave. */ if (adapter->pwrctrlpriv.rf_pwrstate == rf_off) { /* If it is in HW/SW Radio OFF or IPS state, we do not check Fw LPS Leave, */ @@ -236,7 +239,11 @@ static bool lps_rf_on(struct adapter *adapter) return true; } - if (rtw_read32(adapter, REG_RCR) & 0x00070000) + res = rtw_read32(adapter, REG_RCR, ®); + if (res) + return false; + + if (reg & 0x00070000) return false; return true; @@ -266,7 +273,7 @@ static s32 LPS_RF_ON_check(struct adapter *padapter, u32 delay_ms) err = -1; break; } - rtw_usleep_os(100); + msleep(1); } return err; @@ -374,24 +381,24 @@ int rtw_pwr_wakeup(struct adapter *padapter) struct mlme_priv *pmlmepriv = &padapter->mlmepriv; unsigned long timeout = jiffies + msecs_to_jiffies(3000); unsigned long deny_time; - int ret = _SUCCESS; + int ret; while (pwrpriv->ps_processing && time_before(jiffies, timeout)) msleep(10); /* I think this should be check in IPS, LPS, autosuspend functions... */ - if (check_fwstate(pmlmepriv, _FW_LINKED)) { - ret = _SUCCESS; + /* Below goto is a success path taken for already linked devices */ + ret = 0; + if (check_fwstate(pmlmepriv, _FW_LINKED)) goto exit; - } if (pwrpriv->rf_pwrstate == rf_off && ips_leave(padapter) == _FAIL) { - ret = _FAIL; + ret = -ENOMEM; goto exit; } if (padapter->bDriverStopped || !padapter->bup || !padapter->hw_init_completed) { - ret = _FAIL; + ret = -EBUSY; goto exit; } @@ -432,7 +439,7 @@ int rtw_pm_set_ips(struct adapter *padapter, u8 mode) return 0; } else if (mode == IPS_NONE) { rtw_ips_mode_req(pwrctrlpriv, mode); - if ((padapter->bSurpriseRemoved == 0) && (rtw_pwr_wakeup(padapter) == _FAIL)) + if ((padapter->bSurpriseRemoved == 0) && rtw_pwr_wakeup(padapter)) return -EFAULT; } else { return -EINVAL; diff --git a/drivers/staging/r8188eu/core/rtw_recv.c b/drivers/staging/r8188eu/core/rtw_recv.c index df518439aea2..e5a7b7dfc387 100644 --- a/drivers/staging/r8188eu/core/rtw_recv.c +++ b/drivers/staging/r8188eu/core/rtw_recv.c @@ -17,14 +17,14 @@ static u8 SNAP_ETH_TYPE_APPLETALK_AARP[2] = {0x80, 0xf3}; /* Bridge-Tunnel header (for EtherTypes ETH_P_AARP and ETH_P_IPX) */ static u8 rtw_bridge_tunnel_header[] = { - 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0xf8 }; static u8 rtw_rfc1042_header[] = { - 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 + 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; -void rtw_signal_stat_timer_hdl(struct timer_list *); +static void rtw_signal_stat_timer_hdl(struct timer_list *t); void _rtw_init_sta_recv_priv(struct sta_recv_priv *psta_recvpriv) { @@ -62,7 +62,7 @@ int _rtw_init_recv_priv(struct recv_priv *precvpriv, struct adapter *padapter) goto exit; } - precvpriv->precv_frame_buf = (u8 *)N_BYTE_ALIGMENT((size_t)(precvpriv->pallocated_frame_buf), RXFRAME_ALIGN_SZ); + precvpriv->precv_frame_buf = (u8 *)ALIGN((size_t)(precvpriv->pallocated_frame_buf), RXFRAME_ALIGN_SZ); precvframe = (struct recv_frame *)precvpriv->precv_frame_buf; @@ -166,10 +166,8 @@ int rtw_free_recvframe(struct recv_frame *precvframe, struct __queue *pfree_recv list_add_tail(&precvframe->list, get_list_head(pfree_recv_queue)); - if (padapter) { - if (pfree_recv_queue == &precvpriv->free_recv_queue) - precvpriv->free_recvframe_cnt++; - } + if (padapter && (pfree_recv_queue == &precvpriv->free_recv_queue)) + precvpriv->free_recvframe_cnt++; spin_unlock_bh(&pfree_recv_queue->lock); @@ -204,12 +202,12 @@ int rtw_enqueue_recvframe(struct recv_frame *precvframe, struct __queue *queue) } /* -caller : defrag ; recvframe_chk_defrag in recv_thread (passive) -pframequeue: defrag_queue : will be accessed in recv_thread (passive) - -using spinlock to protect - -*/ + * caller : defrag ; recvframe_chk_defrag in recv_thread (passive) + * pframequeue: defrag_queue : will be accessed in recv_thread (passive) + * + * using spinlock to protect + * + */ void rtw_free_recvframe_queue(struct __queue *pframequeue, struct __queue *pfree_recv_queue) { @@ -237,6 +235,7 @@ u32 rtw_free_uc_swdec_pending_queue(struct adapter *adapter) { u32 cnt = 0; struct recv_frame *pending_frame; + while ((pending_frame = rtw_alloc_recvframe(&adapter->recvpriv.uc_swdec_pending_queue))) { rtw_free_recvframe(pending_frame, &adapter->recvpriv.free_recv_queue); cnt++; @@ -327,6 +326,7 @@ static struct recv_frame *decryptor(struct adapter *padapter, struct recv_frame if (prxattrib->encrypt > 0) { u8 *iv = precv_frame->rx_data + prxattrib->hdrlen; + prxattrib->key_index = (((iv[3]) >> 6) & 0x3); if (prxattrib->key_index > WEP_KEYS) { @@ -452,8 +452,7 @@ static int recv_decache(struct recv_frame *precv_frame, u8 bretry, struct stainf return _SUCCESS; } -void process_pwrbit_data(struct adapter *padapter, struct recv_frame *precv_frame); -void process_pwrbit_data(struct adapter *padapter, struct recv_frame *precv_frame) +static void process_pwrbit_data(struct adapter *padapter, struct recv_frame *precv_frame) { unsigned char pwrbit; u8 *ptr = precv_frame->rx_data; @@ -557,15 +556,9 @@ static void count_rx_stats(struct adapter *padapter, struct recv_frame *prframe, } } -int sta2sta_data_frame( - struct adapter *adapter, - struct recv_frame *precv_frame, - struct sta_info **psta -); - -int sta2sta_data_frame(struct adapter *adapter, struct recv_frame *precv_frame, struct sta_info **psta) +static int sta2sta_data_frame(struct adapter *adapter, + struct recv_frame *precv_frame, struct sta_info **psta) { - u8 *ptr = precv_frame->rx_data; int ret = _SUCCESS; struct rx_pkt_attrib *pattrib = &precv_frame->attrib; struct sta_priv *pstapriv = &adapter->stapriv; @@ -620,12 +613,6 @@ int sta2sta_data_frame(struct adapter *adapter, struct recv_frame *precv_frame, sta_addr = pattrib->src; } } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE)) { - memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); - memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); - memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - sta_addr = mybssid; } else { ret = _FAIL; @@ -650,6 +637,7 @@ static int ap2sta_data_frame( struct sta_info **psta) { u8 *ptr = precv_frame->rx_data; + struct ieee80211_hdr *hdr = (struct ieee80211_hdr *)precv_frame->rx_data; struct rx_pkt_attrib *pattrib = &precv_frame->attrib; int ret = _SUCCESS; struct sta_priv *pstapriv = &adapter->stapriv; @@ -694,24 +682,16 @@ static int ap2sta_data_frame( goto exit; } - /* if ((GetFrameSubType(ptr) & WIFI_QOS_DATA_TYPE) == WIFI_QOS_DATA_TYPE) { */ - /* */ - - if (GetFrameSubType(ptr) & BIT(6)) { - /* No data, will not indicate to upper layer, temporily count it here */ + if (ieee80211_is_nullfunc(hdr->frame_control)) { + /* We count the nullfunc frame, but we'll not pass it on to higher layers. */ count_rx_stats(adapter, precv_frame, *psta); ret = RTW_RX_HANDLED; goto exit; } } else if (check_fwstate(pmlmepriv, WIFI_MP_STATE) && check_fwstate(pmlmepriv, _FW_LINKED)) { - memcpy(pattrib->dst, GetAddr1Ptr(ptr), ETH_ALEN); memcpy(pattrib->src, GetAddr2Ptr(ptr), ETH_ALEN); - memcpy(pattrib->bssid, GetAddr3Ptr(ptr), ETH_ALEN); - memcpy(pattrib->ra, pattrib->dst, ETH_ALEN); - memcpy(pattrib->ta, pattrib->src, ETH_ALEN); - /* */ memcpy(pattrib->bssid, mybssid, ETH_ALEN); *psta = rtw_get_stainfo(pstapriv, pattrib->bssid); /* get sta_info */ @@ -778,6 +758,7 @@ static int sta2ap_data_frame(struct adapter *adapter, } } else { u8 *myhwaddr = myid(&adapter->eeprompriv); + if (memcmp(pattrib->ra, myhwaddr, ETH_ALEN)) { ret = RTW_RX_HANDLED; goto exit; @@ -1023,6 +1004,7 @@ static int validate_recv_frame(struct adapter *adapter, struct recv_frame *precv if (pmlmeext->sitesurvey_res.state == SCAN_PROCESS) { int ch_set_idx = rtw_ch_set_search_ch(pmlmeext->channel_set, rtw_get_oper_ch(adapter)); + if (ch_set_idx >= 0) pmlmeext->channel_set[ch_set_idx].rx_count++; } @@ -1050,6 +1032,7 @@ static int validate_recv_frame(struct adapter *adapter, struct recv_frame *precv retval = validate_recv_data_frame(adapter, precv_frame); if (retval == _FAIL) { struct recv_priv *precvpriv = &adapter->recvpriv; + precvpriv->rx_drop++; } } @@ -1313,9 +1296,11 @@ static int amsdu_to_msdu(struct adapter *padapter, struct recv_frame *prframe) struct rx_pkt_attrib *pattrib; unsigned char *data_ptr; struct sk_buff *sub_skb, *subframes[MAX_SUBFRAME_COUNT]; + struct recv_priv *precvpriv = &padapter->recvpriv; struct __queue *pfree_recv_queue = &precvpriv->free_recv_queue; int ret = _SUCCESS; + nr_subframes = 0; pattrib = &prframe->attrib; @@ -1366,13 +1351,12 @@ static int amsdu_to_msdu(struct adapter *padapter, struct recv_frame *prframe) a_len -= nSubframe_Length; if (a_len != 0) { padding_len = 4 - ((nSubframe_Length + ETH_HLEN) & (4 - 1)); - if (padding_len == 4) { + if (padding_len == 4) padding_len = 0; - } - if (a_len < padding_len) { + if (a_len < padding_len) goto exit; - } + pdata += padding_len; a_len -= padding_len; } @@ -1747,9 +1731,11 @@ static int recv_func(struct adapter *padapter, struct recv_frame *rframe) !psecuritypriv->busetkipkey) { rtw_enqueue_recvframe(rframe, &padapter->recvpriv.uc_swdec_pending_queue); if (recvpriv->free_recvframe_cnt < NR_RECVFRAME / 4) { - /* to prevent from recvframe starvation, + /* + * to prevent from recvframe starvation, * get recvframe from uc_swdec_pending_queue to - * free_recvframe_cnt */ + * free_recvframe_cnt + */ rframe = rtw_alloc_recvframe(&padapter->recvpriv.uc_swdec_pending_queue); if (rframe) goto do_posthandle; @@ -1787,7 +1773,7 @@ _recv_entry_drop: return ret; } -void rtw_signal_stat_timer_hdl(struct timer_list *t) +static void rtw_signal_stat_timer_hdl(struct timer_list *t) { struct adapter *adapter = from_timer(adapter, t, recvpriv.signal_stat_timer); struct recv_priv *recvpriv = &adapter->recvpriv; diff --git a/drivers/staging/r8188eu/core/rtw_wlan_util.c b/drivers/staging/r8188eu/core/rtw_wlan_util.c index 392a65783f32..3a002cb6834f 100644 --- a/drivers/staging/r8188eu/core/rtw_wlan_util.c +++ b/drivers/staging/r8188eu/core/rtw_wlan_util.c @@ -264,23 +264,30 @@ void UpdateBrateTblForSoftAP(u8 *bssrateset, u32 bssratelen) void Save_DM_Func_Flag(struct adapter *padapter) { - u8 saveflag = true; + struct hal_data_8188e *haldata = &padapter->haldata; + struct odm_dm_struct *odmpriv = &haldata->odmpriv; - SetHwReg8188EU(padapter, HW_VAR_DM_FUNC_OP, (u8 *)(&saveflag)); + odmpriv->BK_SupportAbility = odmpriv->SupportAbility; } void Restore_DM_Func_Flag(struct adapter *padapter) { - u8 saveflag = false; + struct hal_data_8188e *haldata = &padapter->haldata; + struct odm_dm_struct *odmpriv = &haldata->odmpriv; - SetHwReg8188EU(padapter, HW_VAR_DM_FUNC_OP, (u8 *)(&saveflag)); + odmpriv->SupportAbility = odmpriv->BK_SupportAbility; } void Set_MSR(struct adapter *padapter, u8 type) { u8 val8; + int res; - val8 = rtw_read8(padapter, MSR) & 0x0c; + res = rtw_read8(padapter, MSR, &val8); + if (res) + return; + + val8 &= 0x0c; val8 |= type; rtw_write8(padapter, MSR, val8); } @@ -505,7 +512,11 @@ int WMM_param_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE) static void set_acm_ctrl(struct adapter *adapter, u8 acm_mask) { - u8 acmctrl = rtw_read8(adapter, REG_ACMHWCTRL); + u8 acmctrl; + int res = rtw_read8(adapter, REG_ACMHWCTRL, &acmctrl); + + if (res) + return; if (acm_mask > 1) acmctrl = acmctrl | 0x1; @@ -765,6 +776,7 @@ void HT_info_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE) static void set_min_ampdu_spacing(struct adapter *adapter, u8 spacing) { u8 sec_spacing; + int res; if (spacing <= 7) { switch (adapter->securitypriv.dot11PrivacyAlgrthm) { @@ -786,8 +798,38 @@ static void set_min_ampdu_spacing(struct adapter *adapter, u8 spacing) if (spacing < sec_spacing) spacing = sec_spacing; + res = rtw_read8(adapter, REG_AMPDU_MIN_SPACE, &sec_spacing); + if (res) + return; + rtw_write8(adapter, REG_AMPDU_MIN_SPACE, - (rtw_read8(adapter, REG_AMPDU_MIN_SPACE) & 0xf8) | spacing); + (sec_spacing & 0xf8) | spacing); + } +} + +static void set_ampdu_factor(struct adapter *adapter, u8 factor) +{ + u8 RegToSet_Normal[4] = {0x41, 0xa8, 0x72, 0xb9}; + u8 FactorToSet; + u8 *pRegToSet; + u8 index = 0; + + pRegToSet = RegToSet_Normal; /* 0xb972a841; */ + FactorToSet = factor; + if (FactorToSet <= 3) { + FactorToSet = (1 << (FactorToSet + 2)); + if (FactorToSet > 0xf) + FactorToSet = 0xf; + + for (index = 0; index < 4; index++) { + if ((pRegToSet[index] & 0xf0) > (FactorToSet << 4)) + pRegToSet[index] = (pRegToSet[index] & 0x0f) | (FactorToSet << 4); + + if ((pRegToSet[index] & 0x0f) > FactorToSet) + pRegToSet[index] = (pRegToSet[index] & 0xf0) | (FactorToSet); + + rtw_write8(adapter, (REG_AGGLEN_LMT + index), pRegToSet[index]); + } } } @@ -817,7 +859,7 @@ void HTOnAssocRsp(struct adapter *padapter) set_min_ampdu_spacing(padapter, min_MPDU_spacing); - SetHwReg8188EU(padapter, HW_VAR_AMPDU_FACTOR, (u8 *)(&max_AMPDU_len)); + set_ampdu_factor(padapter, max_AMPDU_len); } void ERP_IE_handler(struct adapter *padapter, struct ndis_802_11_var_ie *pIE) @@ -1225,6 +1267,45 @@ void set_sta_rate(struct adapter *padapter, struct sta_info *psta) enable_rate_adaptive(padapter, psta->mac_id); } +void rtw_set_basic_rate(struct adapter *adapter, u8 *rates) +{ + u16 BrateCfg = 0; + u8 RateIndex = 0; + int res; + u8 reg; + + /* 2007.01.16, by Emily */ + /* Select RRSR (in Legacy-OFDM and CCK) */ + /* For 8190, we select only 24M, 12M, 6M, 11M, 5.5M, 2M, and 1M from the Basic rate. */ + /* We do not use other rates. */ + HalSetBrateCfg(adapter, rates, &BrateCfg); + + /* 2011.03.30 add by Luke Lee */ + /* CCK 2M ACK should be disabled for some BCM and Atheros AP IOT */ + /* because CCK 2M has poor TXEVM */ + /* CCK 5.5M & 11M ACK should be enabled for better performance */ + + BrateCfg = (BrateCfg | 0xd) & 0x15d; + + BrateCfg |= 0x01; /* default enable 1M ACK rate */ + /* Set RRSR rate table. */ + rtw_write8(adapter, REG_RRSR, BrateCfg & 0xff); + rtw_write8(adapter, REG_RRSR + 1, (BrateCfg >> 8) & 0xff); + res = rtw_read8(adapter, REG_RRSR + 2, ®); + if (res) + return; + + rtw_write8(adapter, REG_RRSR + 2, reg & 0xf0); + + /* Set RTS initial rate */ + while (BrateCfg > 0x1) { + BrateCfg = (BrateCfg >> 1); + RateIndex++; + } + /* Ziv - Check */ + rtw_write8(adapter, REG_INIRTS_RATE_SEL, RateIndex); +} + /* Update RRSR and Rate for USERATE */ void update_tx_basic_rate(struct adapter *padapter, u8 wirelessmode) { @@ -1250,7 +1331,7 @@ void update_tx_basic_rate(struct adapter *padapter, u8 wirelessmode) else update_mgnt_tx_rate(padapter, IEEE80211_OFDM_RATE_6MB); - SetHwReg8188EU(padapter, HW_VAR_BASIC_RATE, supported_rates); + rtw_set_basic_rate(padapter, supported_rates); } unsigned char check_assoc_AP(u8 *pframe, uint len) @@ -1348,6 +1429,30 @@ static void set_ack_preamble(struct adapter *adapter, bool short_preamble) rtw_write8(adapter, REG_RRSR + 2, val8); }; +static void set_slot_time(struct adapter *adapter, u8 slot_time) +{ + u8 u1bAIFS, aSifsTime; + struct mlme_ext_priv *pmlmeext = &adapter->mlmeextpriv; + struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; + + rtw_write8(adapter, REG_SLOT, slot_time); + + if (pmlmeinfo->WMM_enable == 0) { + if (pmlmeext->cur_wireless_mode == WIRELESS_11B) + aSifsTime = 10; + else + aSifsTime = 16; + + u1bAIFS = aSifsTime + (2 * pmlmeinfo->slotTime); + + /* Temporary removed, 2008.06.20. */ + rtw_write8(adapter, REG_EDCA_VO_PARAM, u1bAIFS); + rtw_write8(adapter, REG_EDCA_VI_PARAM, u1bAIFS); + rtw_write8(adapter, REG_EDCA_BE_PARAM, u1bAIFS); + rtw_write8(adapter, REG_EDCA_BK_PARAM, u1bAIFS); + } +} + void update_capinfo(struct adapter *Adapter, u16 updateCap) { struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv; @@ -1386,7 +1491,7 @@ void update_capinfo(struct adapter *Adapter, u16 updateCap) } } - SetHwReg8188EU(Adapter, HW_VAR_SLOT_TIME, &pmlmeinfo->slotTime); + set_slot_time(Adapter, pmlmeinfo->slotTime); } void update_wireless_mode(struct adapter *padapter) @@ -1466,26 +1571,6 @@ int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie, uint var_ie_l return _SUCCESS; } -void update_TSF(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len) -{ - u8 *pIE; - __le32 *pbuf; - - pIE = pframe + sizeof(struct ieee80211_hdr_3addr); - pbuf = (__le32 *)pIE; - - pmlmeext->TSFValue = le32_to_cpu(*(pbuf + 1)); - - pmlmeext->TSFValue = pmlmeext->TSFValue << 32; - - pmlmeext->TSFValue |= le32_to_cpu(*pbuf); -} - -void correct_TSF(struct adapter *padapter, struct mlme_ext_priv *pmlmeext) -{ - SetHwReg8188EU(padapter, HW_VAR_CORRECT_TSF, NULL); -} - void beacon_timing_control(struct adapter *padapter) { SetBeaconRelatedRegisters8188EUsb(padapter); diff --git a/drivers/staging/r8188eu/core/rtw_xmit.c b/drivers/staging/r8188eu/core/rtw_xmit.c index 7135d89caac1..24401f3ae2a0 100644 --- a/drivers/staging/r8188eu/core/rtw_xmit.c +++ b/drivers/staging/r8188eu/core/rtw_xmit.c @@ -16,16 +16,13 @@ static u8 RFC1042_OUI[P80211_OUI_LEN] = { 0x00, 0x00, 0x00 }; static void _init_txservq(struct tx_servq *ptxservq) { - INIT_LIST_HEAD(&ptxservq->tx_pending); rtw_init_queue(&ptxservq->sta_pending); ptxservq->qcnt = 0; - } void _rtw_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv) { - memset((unsigned char *)psta_xmitpriv, 0, sizeof(struct sta_xmit_priv)); spin_lock_init(&psta_xmitpriv->lock); _init_txservq(&psta_xmitpriv->be_q); @@ -34,7 +31,6 @@ void _rtw_init_sta_xmit_priv(struct sta_xmit_priv *psta_xmitpriv) _init_txservq(&psta_xmitpriv->vo_q); INIT_LIST_HEAD(&psta_xmitpriv->legacy_dz); INIT_LIST_HEAD(&psta_xmitpriv->apsd); - } s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) @@ -78,7 +74,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) res = _FAIL; goto exit; } - pxmitpriv->pxmit_frame_buf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitpriv->pallocated_frame_buf), 4); + pxmitpriv->pxmit_frame_buf = (u8 *)ALIGN((size_t)(pxmitpriv->pallocated_frame_buf), 4); /* pxmitpriv->pxmit_frame_buf = pxmitpriv->pallocated_frame_buf + 4 - */ /* ((size_t) (pxmitpriv->pallocated_frame_buf) &3); */ @@ -115,7 +111,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) goto exit; } - pxmitpriv->pxmitbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitpriv->pallocated_xmitbuf), 4); + pxmitpriv->pxmitbuf = (u8 *)ALIGN((size_t)(pxmitpriv->pallocated_xmitbuf), 4); /* pxmitpriv->pxmitbuf = pxmitpriv->pallocated_xmitbuf + 4 - */ /* ((size_t) (pxmitpriv->pallocated_xmitbuf) &3); */ @@ -155,7 +151,7 @@ s32 _rtw_init_xmit_priv(struct xmit_priv *pxmitpriv, struct adapter *padapter) goto exit; } - pxmitpriv->pxmit_extbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitpriv->pallocated_xmit_extbuf), 4); + pxmitpriv->pxmit_extbuf = (u8 *)ALIGN((size_t)(pxmitpriv->pallocated_xmit_extbuf), 4); pxmitbuf = (struct xmit_buf *)pxmitpriv->pxmit_extbuf; @@ -299,6 +295,7 @@ static void update_attrib_vcs_info(struct adapter *padapter, struct xmit_frame * /* check HT op mode */ if (pattrib->ht_en) { u8 htopmode = pmlmeinfo->HT_protection; + if ((pmlmeext->cur_bwmode && (htopmode == 2 || htopmode == 3)) || (!pmlmeext->cur_bwmode && htopmode == 3)) { pattrib->vcs_mode = RTS_CTS; @@ -445,10 +442,11 @@ static s32 update_attrib(struct adapter *padapter, struct sk_buff *pkt, struct p pattrib->pktlen = pktfile.pkt_len; - if (ETH_P_IP == pattrib->ether_type) { + if (pattrib->ether_type == ETH_P_IP) { /* The following is for DHCP and ARP packet, we use cck1M to tx these packets and let LPS awake some time */ /* to prevent DHCP protocol fail */ u8 tmp[24]; + _rtw_pktfile_read(&pktfile, &tmp[0], 24); pattrib->dhcp_pkt = 0; if (pktfile.pkt_len > 282) {/* MINIMUM_DHCP_PACKET_SIZE) { */ @@ -627,7 +625,7 @@ static s32 xmitframe_addmic(struct adapter *padapter, struct xmit_frame *pxmitfr if (pframe[1] & 2) /* From Ds == 1 */ rtw_secmicappend(&micdata, &pframe[24], 6); else - rtw_secmicappend(&micdata, &pframe[10], 6); + rtw_secmicappend(&micdata, &pframe[10], 6); } else { /* ToDS == 0 */ rtw_secmicappend(&micdata, &pframe[4], 6); /* DA */ if (pframe[1] & 2) /* From Ds == 1 */ @@ -953,12 +951,11 @@ s32 rtw_xmitframe_coalesce(struct adapter *padapter, struct sk_buff *pkt, struct mpdu_len -= llc_sz; } - if ((pattrib->icv_len > 0) && (pattrib->bswenc)) { + if ((pattrib->icv_len > 0) && (pattrib->bswenc)) mpdu_len -= pattrib->icv_len; - } if (bmcst) { - /* don't do fragment to broadcat/multicast packets */ + /* don't do fragment to broadcast/multicast packets */ mem_sz = _rtw_pktfile_read(&pktfile, pframe, pattrib->pktlen); } else { mem_sz = _rtw_pktfile_read(&pktfile, pframe, mpdu_len); @@ -1068,7 +1065,6 @@ void rtw_update_protection(struct adapter *padapter, u8 *ie, uint ie_len) } break; } - } void rtw_count_tx_stats(struct adapter *padapter, struct xmit_frame *pxmitframe, int sz) @@ -1315,7 +1311,6 @@ void rtw_free_xmitframe_queue(struct xmit_priv *pxmitpriv, struct __queue *pfram rtw_free_xmitframe(pxmitpriv, pxmitframe); } spin_unlock_bh(&pframequeue->lock); - } s32 rtw_xmitframe_enqueue(struct adapter *padapter, struct xmit_frame *pxmitframe) @@ -1505,7 +1500,6 @@ void rtw_init_hwxmits(struct hw_xmit *phwxmit, int entry) for (i = 0; i < entry; i++, phwxmit++) phwxmit->accnt = 0; - } static int rtw_br_client_tx(struct adapter *padapter, struct sk_buff **pskb) @@ -1732,7 +1726,7 @@ int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct xmit_fra bool bmcst = is_multicast_ether_addr(pattrib->ra); if (!check_fwstate(pmlmepriv, WIFI_AP_STATE)) - return ret; + return ret; if (pattrib->psta) psta = pattrib->psta; @@ -1760,8 +1754,8 @@ int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct xmit_fra pstapriv->tim_bitmap |= BIT(0);/* */ pstapriv->sta_dz_bitmap |= BIT(0); - - update_beacon(padapter, _TIM_IE_, NULL, false);/* tx bc/mc packets after upate bcn */ + /* tx bc/mc packets after update bcn */ + update_beacon(padapter, _TIM_IE_, NULL, false); ret = true; } @@ -1811,7 +1805,7 @@ int xmitframe_enqueue_for_sleeping_sta(struct adapter *padapter, struct xmit_fra pstapriv->tim_bitmap |= BIT(psta->aid); if (psta->sleepq_len == 1) { - /* upate BCN for TIM IE */ + /* update BCN for TIM IE */ update_beacon(padapter, _TIM_IE_, NULL, false); } } @@ -2080,7 +2074,7 @@ void xmit_delivery_enabled_frames(struct adapter *padapter, struct sta_info *pst if ((psta->sleepq_ac_len == 0) && (!psta->has_legacy_ac) && (wmmps_ac)) { pstapriv->tim_bitmap &= ~BIT(psta->aid); - /* upate BCN for TIM IE */ + /* update BCN for TIM IE */ update_beacon(padapter, _TIM_IE_, NULL, false); } } diff --git a/drivers/staging/r8188eu/hal/Hal8188EPwrSeq.c b/drivers/staging/r8188eu/hal/Hal8188EPwrSeq.c deleted file mode 100644 index 6505e1fcb070..000000000000 --- a/drivers/staging/r8188eu/hal/Hal8188EPwrSeq.c +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* Copyright(c) 2007 - 2011 Realtek Corporation. */ - -#include "../include/Hal8188EPwrSeq.h" -#include "../include/rtl8188e_hal.h" - -struct wl_pwr_cfg rtl8188E_power_on_flow[] = { - { 0x0006, PWR_CMD_POLLING, BIT(1), BIT(1) }, - { 0x0002, PWR_CMD_WRITE, BIT(0) | BIT(1), 0 }, /* reset BB */ - { 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */ - { 0x0005, PWR_CMD_WRITE, BIT(7), 0 }, /* disable HWPDN (control by DRV)*/ - { 0x0005, PWR_CMD_WRITE, BIT(4) | BIT(3), 0 }, /* disable WL suspend*/ - { 0x0005, PWR_CMD_WRITE, BIT(0), BIT(0) }, - { 0x0005, PWR_CMD_POLLING, BIT(0), 0 }, - { 0x0023, PWR_CMD_WRITE, BIT(4), 0 }, - { 0xFFFF, PWR_CMD_END, 0, 0 }, -}; - -struct wl_pwr_cfg rtl8188E_card_disable_flow[] = { - { 0x001F, PWR_CMD_WRITE, 0xFF, 0 }, /* turn off RF */ - { 0x0023, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* LDO Sleep mode */ - { 0x0005, PWR_CMD_WRITE, BIT(1), BIT(1) }, /* turn off MAC by HW state machine */ - { 0x0005, PWR_CMD_POLLING, BIT(1), 0 }, - { 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */ - { 0x0005, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) }, /* enable WL suspend */ - { 0x0007, PWR_CMD_WRITE, 0xFF, 0 }, /* enable bandgap mbias in suspend */ - { 0x0041, PWR_CMD_WRITE, BIT(4), 0 }, /* Clear SIC_EN register */ - { 0xfe10, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* Set USB suspend enable local register */ - { 0xFFFF, PWR_CMD_END, 0, 0 }, -}; - -/* This is used by driver for LPSRadioOff Procedure, not for FW LPS Step */ -struct wl_pwr_cfg rtl8188E_enter_lps_flow[] = { - { 0x0522, PWR_CMD_WRITE, 0xFF, 0x7F },/* Tx Pause */ - { 0x05F8, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ - { 0x05F9, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ - { 0x05FA, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ - { 0x05FB, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ - { 0x0002, PWR_CMD_WRITE, BIT(0), 0 }, /* CCK and OFDM are disabled, clocks are gated */ - { 0x0002, PWR_CMD_DELAY, 0, PWRSEQ_DELAY_US }, - { 0x0100, PWR_CMD_WRITE, 0xFF, 0x3F }, /* Reset MAC TRX */ - { 0x0101, PWR_CMD_WRITE, BIT(1), 0 }, /* check if removed later */ - { 0x0553, PWR_CMD_WRITE, BIT(5), BIT(5) }, /* Respond TxOK to scheduler */ - { 0xFFFF, PWR_CMD_END, 0, 0 }, -}; diff --git a/drivers/staging/r8188eu/hal/Hal8188ERateAdaptive.c b/drivers/staging/r8188eu/hal/Hal8188ERateAdaptive.c index 57e8f5573846..1e04de3a6622 100644 --- a/drivers/staging/r8188eu/hal/Hal8188ERateAdaptive.c +++ b/drivers/staging/r8188eu/hal/Hal8188ERateAdaptive.c @@ -279,6 +279,7 @@ static int odm_ARFBRefresh_8188E(struct odm_dm_struct *dm_odm, struct odm_ra_inf { /* Wilson 2011/10/26 */ u32 MaskFromReg; s8 i; + int res; switch (pRaInfo->RateID) { case RATR_INX_WIRELESS_NGB: @@ -303,19 +304,31 @@ static int odm_ARFBRefresh_8188E(struct odm_dm_struct *dm_odm, struct odm_ra_inf pRaInfo->RAUseRate = (pRaInfo->RateMask) & 0x0000000d; break; case 12: - MaskFromReg = rtw_read32(dm_odm->Adapter, REG_ARFR0); + res = rtw_read32(dm_odm->Adapter, REG_ARFR0, &MaskFromReg); + if (res) + return res; + pRaInfo->RAUseRate = (pRaInfo->RateMask) & MaskFromReg; break; case 13: - MaskFromReg = rtw_read32(dm_odm->Adapter, REG_ARFR1); + res = rtw_read32(dm_odm->Adapter, REG_ARFR1, &MaskFromReg); + if (res) + return res; + pRaInfo->RAUseRate = (pRaInfo->RateMask) & MaskFromReg; break; case 14: - MaskFromReg = rtw_read32(dm_odm->Adapter, REG_ARFR2); + res = rtw_read32(dm_odm->Adapter, REG_ARFR2, &MaskFromReg); + if (res) + return res; + pRaInfo->RAUseRate = (pRaInfo->RateMask) & MaskFromReg; break; case 15: - MaskFromReg = rtw_read32(dm_odm->Adapter, REG_ARFR3); + res = rtw_read32(dm_odm->Adapter, REG_ARFR3, &MaskFromReg); + if (res) + return res; + pRaInfo->RAUseRate = (pRaInfo->RateMask) & MaskFromReg; break; default: @@ -601,12 +614,12 @@ void ODM_RA_TxRPT2Handle_8188E(struct odm_dm_struct *dm_odm, u8 *TxRPT_Buf, u16 pRAInfo = &dm_odm->RAInfo[MacId]; if (valid) { - pRAInfo->RTY[0] = (u16)GET_TX_REPORT_TYPE1_RERTY_0(pBuffer); - pRAInfo->RTY[1] = (u16)GET_TX_REPORT_TYPE1_RERTY_1(pBuffer); - pRAInfo->RTY[2] = (u16)GET_TX_REPORT_TYPE1_RERTY_2((u8 *)pBuffer); - pRAInfo->RTY[3] = (u16)GET_TX_REPORT_TYPE1_RERTY_3(pBuffer); - pRAInfo->RTY[4] = (u16)GET_TX_REPORT_TYPE1_RERTY_4(pBuffer); - pRAInfo->DROP = (u16)GET_TX_REPORT_TYPE1_DROP_0(pBuffer); + pRAInfo->RTY[0] = le16_to_cpup((__le16 *)pBuffer); + pRAInfo->RTY[1] = pBuffer[2]; + pRAInfo->RTY[2] = pBuffer[3]; + pRAInfo->RTY[3] = pBuffer[4]; + pRAInfo->RTY[4] = pBuffer[5]; + pRAInfo->DROP = pBuffer[6]; pRAInfo->TOTAL = pRAInfo->RTY[0] + pRAInfo->RTY[1] + pRAInfo->RTY[2] + pRAInfo->RTY[3] + pRAInfo->RTY[4] + pRAInfo->DROP; diff --git a/drivers/staging/r8188eu/hal/HalPhyRf_8188e.c b/drivers/staging/r8188eu/hal/HalPhyRf_8188e.c index b944c8071a3b..525deab10820 100644 --- a/drivers/staging/r8188eu/hal/HalPhyRf_8188e.c +++ b/drivers/staging/r8188eu/hal/HalPhyRf_8188e.c @@ -463,6 +463,7 @@ void _PHY_SaveADDARegisters(struct adapter *adapt, u32 *ADDAReg, u32 *ADDABackup } } +/* FIXME: return an error to caller */ static void _PHY_SaveMACRegisters( struct adapter *adapt, u32 *MACReg, @@ -470,11 +471,20 @@ static void _PHY_SaveMACRegisters( ) { u32 i; + int res; - for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) - MACBackup[i] = rtw_read8(adapt, MACReg[i]); + for (i = 0; i < (IQK_MAC_REG_NUM - 1); i++) { + u8 reg; - MACBackup[i] = rtw_read32(adapt, MACReg[i]); + res = rtw_read8(adapt, MACReg[i], ®); + if (res) + return; + + MACBackup[i] = reg; + } + + res = rtw_read32(adapt, MACReg[i], MACBackup + i); + (void)res; } static void reload_adda_reg(struct adapter *adapt, u32 *ADDAReg, u32 *ADDABackup, u32 RegiesterNum) @@ -739,9 +749,12 @@ static void phy_LCCalibrate_8188E(struct adapter *adapt) { u8 tmpreg; u32 RF_Amode = 0, LC_Cal; + int res; /* Check continuous TX and Packet TX */ - tmpreg = rtw_read8(adapt, 0xd03); + res = rtw_read8(adapt, 0xd03, &tmpreg); + if (res) + return; if ((tmpreg & 0x70) != 0) /* Deal with contisuous TX case */ rtw_write8(adapt, 0xd03, tmpreg & 0x8F); /* disable all continuous TX */ diff --git a/drivers/staging/r8188eu/hal/HalPwrSeqCmd.c b/drivers/staging/r8188eu/hal/HalPwrSeqCmd.c index 150ea380c39e..6c0b1368383d 100644 --- a/drivers/staging/r8188eu/hal/HalPwrSeqCmd.c +++ b/drivers/staging/r8188eu/hal/HalPwrSeqCmd.c @@ -3,25 +3,116 @@ #include "../include/HalPwrSeqCmd.h" -u8 HalPwrSeqCmdParsing(struct adapter *padapter, struct wl_pwr_cfg pwrseqcmd[]) +#define PWR_CMD_WRITE 0x01 + /* offset: the read register offset */ + /* msk: the mask of the write bits */ + /* value: write value */ + /* note: driver shall implement this cmd by read & msk after write */ + +#define PWR_CMD_POLLING 0x02 + /* offset: the read register offset */ + /* msk: the mask of the polled value */ + /* value: the value to be polled, masked by the msd field. */ + /* note: driver shall implement this cmd by */ + /* do{ */ + /* if ( (Read(offset) & msk) == (value & msk) ) */ + /* break; */ + /* } while (not timeout); */ + +#define PWR_CMD_DELAY 0x03 + /* offset: the value to delay (in us) */ + /* msk: N/A */ + /* value: N/A */ + +struct wl_pwr_cfg { + u16 offset; + u8 cmd:4; + u8 msk; + u8 value; +}; + +#define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset +#define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd +#define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk +#define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value + +static struct wl_pwr_cfg rtl8188E_power_on_flow[] = { + { 0x0006, PWR_CMD_POLLING, BIT(1), BIT(1) }, + { 0x0002, PWR_CMD_WRITE, BIT(0) | BIT(1), 0 }, /* reset BB */ + { 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */ + { 0x0005, PWR_CMD_WRITE, BIT(7), 0 }, /* disable HWPDN (control by DRV)*/ + { 0x0005, PWR_CMD_WRITE, BIT(4) | BIT(3), 0 }, /* disable WL suspend*/ + { 0x0005, PWR_CMD_WRITE, BIT(0), BIT(0) }, + { 0x0005, PWR_CMD_POLLING, BIT(0), 0 }, + { 0x0023, PWR_CMD_WRITE, BIT(4), 0 }, +}; + +static struct wl_pwr_cfg rtl8188E_card_disable_flow[] = { + { 0x001F, PWR_CMD_WRITE, 0xFF, 0 }, /* turn off RF */ + { 0x0023, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* LDO Sleep mode */ + { 0x0005, PWR_CMD_WRITE, BIT(1), BIT(1) }, /* turn off MAC by HW state machine */ + { 0x0005, PWR_CMD_POLLING, BIT(1), 0 }, + { 0x0026, PWR_CMD_WRITE, BIT(7), BIT(7) }, /* schmitt trigger */ + { 0x0005, PWR_CMD_WRITE, BIT(3) | BIT(4), BIT(3) }, /* enable WL suspend */ + { 0x0007, PWR_CMD_WRITE, 0xFF, 0 }, /* enable bandgap mbias in suspend */ + { 0x0041, PWR_CMD_WRITE, BIT(4), 0 }, /* Clear SIC_EN register */ + { 0xfe10, PWR_CMD_WRITE, BIT(4), BIT(4) }, /* Set USB suspend enable local register */ +}; + +/* This is used by driver for LPSRadioOff Procedure, not for FW LPS Step */ +static struct wl_pwr_cfg rtl8188E_enter_lps_flow[] = { + { 0x0522, PWR_CMD_WRITE, 0xFF, 0x7F },/* Tx Pause */ + { 0x05F8, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ + { 0x05F9, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ + { 0x05FA, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ + { 0x05FB, PWR_CMD_POLLING, 0xFF, 0 }, /* Should be zero if no packet is transmitted */ + { 0x0002, PWR_CMD_WRITE, BIT(0), 0 }, /* CCK and OFDM are disabled, clocks are gated */ + { 0x0002, PWR_CMD_DELAY, 0, 0 }, + { 0x0100, PWR_CMD_WRITE, 0xFF, 0x3F }, /* Reset MAC TRX */ + { 0x0101, PWR_CMD_WRITE, BIT(1), 0 }, /* check if removed later */ + { 0x0553, PWR_CMD_WRITE, BIT(5), BIT(5) }, /* Respond TxOK to scheduler */ +}; + +u8 HalPwrSeqCmdParsing(struct adapter *padapter, enum r8188eu_pwr_seq seq) { struct wl_pwr_cfg pwrcfgcmd = {0}; + struct wl_pwr_cfg *pwrseqcmd; u8 poll_bit = false; - u32 aryidx = 0; + u8 idx, num_steps; u8 value = 0; u32 offset = 0; u32 poll_count = 0; /* polling autoload done. */ u32 max_poll_count = 5000; + int res; - do { - pwrcfgcmd = pwrseqcmd[aryidx]; + switch (seq) { + case PWR_ON_FLOW: + pwrseqcmd = rtl8188E_power_on_flow; + num_steps = ARRAY_SIZE(rtl8188E_power_on_flow); + break; + case DISABLE_FLOW: + pwrseqcmd = rtl8188E_card_disable_flow; + num_steps = ARRAY_SIZE(rtl8188E_card_disable_flow); + break; + case LPS_ENTER_FLOW: + pwrseqcmd = rtl8188E_enter_lps_flow; + num_steps = ARRAY_SIZE(rtl8188E_enter_lps_flow); + break; + default: + return false; + } + + for (idx = 0; idx < num_steps; idx++) { + pwrcfgcmd = pwrseqcmd[idx]; switch (GET_PWR_CFG_CMD(pwrcfgcmd)) { case PWR_CMD_WRITE: offset = GET_PWR_CFG_OFFSET(pwrcfgcmd); /* Read the value from system register */ - value = rtw_read8(padapter, offset); + res = rtw_read8(padapter, offset, &value); + if (res) + return false; value &= ~(GET_PWR_CFG_MASK(pwrcfgcmd)); value |= (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd)); @@ -33,7 +124,9 @@ u8 HalPwrSeqCmdParsing(struct adapter *padapter, struct wl_pwr_cfg pwrseqcmd[]) poll_bit = false; offset = GET_PWR_CFG_OFFSET(pwrcfgcmd); do { - value = rtw_read8(padapter, offset); + res = rtw_read8(padapter, offset, &value); + if (res) + return false; value &= GET_PWR_CFG_MASK(pwrcfgcmd); if (value == (GET_PWR_CFG_VALUE(pwrcfgcmd) & GET_PWR_CFG_MASK(pwrcfgcmd))) @@ -46,20 +139,11 @@ u8 HalPwrSeqCmdParsing(struct adapter *padapter, struct wl_pwr_cfg pwrseqcmd[]) } while (!poll_bit); break; case PWR_CMD_DELAY: - if (GET_PWR_CFG_VALUE(pwrcfgcmd) == PWRSEQ_DELAY_US) - udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd)); - else - udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd) * 1000); - break; - case PWR_CMD_END: - /* When this command is parsed, end the process */ - return true; + udelay(GET_PWR_CFG_OFFSET(pwrcfgcmd)); break; default: break; } - - aryidx++;/* Add Array Index */ - } while (1); + } return true; } diff --git a/drivers/staging/r8188eu/hal/hal_com.c b/drivers/staging/r8188eu/hal/hal_com.c index 910cc07f656c..6a1cdc67335b 100644 --- a/drivers/staging/r8188eu/hal/hal_com.c +++ b/drivers/staging/r8188eu/hal/hal_com.c @@ -10,45 +10,6 @@ #define _HAL_INIT_C_ -void dump_chip_info(struct HAL_VERSION chip_vers) -{ - uint cnt = 0; - char buf[128]; - - cnt += sprintf((buf + cnt), "Chip Version Info: CHIP_8188E_"); - cnt += sprintf((buf + cnt), "%s_", IS_NORMAL_CHIP(chip_vers) ? - "Normal_Chip" : "Test_Chip"); - cnt += sprintf((buf + cnt), "%s_", IS_CHIP_VENDOR_TSMC(chip_vers) ? - "TSMC" : "UMC"); - - switch (chip_vers.CUTVersion) { - case A_CUT_VERSION: - cnt += sprintf((buf + cnt), "A_CUT_"); - break; - case B_CUT_VERSION: - cnt += sprintf((buf + cnt), "B_CUT_"); - break; - case C_CUT_VERSION: - cnt += sprintf((buf + cnt), "C_CUT_"); - break; - case D_CUT_VERSION: - cnt += sprintf((buf + cnt), "D_CUT_"); - break; - case E_CUT_VERSION: - cnt += sprintf((buf + cnt), "E_CUT_"); - break; - default: - cnt += sprintf((buf + cnt), "UNKNOWN_CUT(%d)_", chip_vers.CUTVersion); - break; - } - - cnt += sprintf((buf + cnt), "1T1R_"); - - cnt += sprintf((buf + cnt), "RomVer(%d)\n", 0); - - pr_info("%s", buf); -} - #define CHAN_PLAN_HW 0x80 u8 /* return the final channel plan decision */ @@ -303,7 +264,9 @@ s32 c2h_evt_read(struct adapter *adapter, u8 *buf) if (!buf) goto exit; - trigger = rtw_read8(adapter, REG_C2HEVT_CLEAR); + ret = rtw_read8(adapter, REG_C2HEVT_CLEAR, &trigger); + if (ret) + return _FAIL; if (trigger == C2H_EVT_HOST_CLOSE) goto exit; /* Not ready */ @@ -314,13 +277,26 @@ s32 c2h_evt_read(struct adapter *adapter, u8 *buf) memset(c2h_evt, 0, 16); - *buf = rtw_read8(adapter, REG_C2HEVT_MSG_NORMAL); - *(buf + 1) = rtw_read8(adapter, REG_C2HEVT_MSG_NORMAL + 1); + ret = rtw_read8(adapter, REG_C2HEVT_MSG_NORMAL, buf); + if (ret) { + ret = _FAIL; + goto clear_evt; + } + ret = rtw_read8(adapter, REG_C2HEVT_MSG_NORMAL + 1, buf + 1); + if (ret) { + ret = _FAIL; + goto clear_evt; + } /* Read the content */ - for (i = 0; i < c2h_evt->plen; i++) - c2h_evt->payload[i] = rtw_read8(adapter, REG_C2HEVT_MSG_NORMAL + - sizeof(*c2h_evt) + i); + for (i = 0; i < c2h_evt->plen; i++) { + ret = rtw_read8(adapter, REG_C2HEVT_MSG_NORMAL + + sizeof(*c2h_evt) + i, c2h_evt->payload + i); + if (ret) { + ret = _FAIL; + goto clear_evt; + } + } ret = _SUCCESS; diff --git a/drivers/staging/r8188eu/hal/rtl8188e_cmd.c b/drivers/staging/r8188eu/hal/rtl8188e_cmd.c index 475650dc7301..b01ee1695fee 100644 --- a/drivers/staging/r8188eu/hal/rtl8188e_cmd.c +++ b/drivers/staging/r8188eu/hal/rtl8188e_cmd.c @@ -18,13 +18,18 @@ static u8 _is_fw_read_cmd_down(struct adapter *adapt, u8 msgbox_num) { - u8 read_down = false; + u8 read_down = false, reg; int retry_cnts = 100; + int res; u8 valid; do { - valid = rtw_read8(adapt, REG_HMETFR) & BIT(msgbox_num); + res = rtw_read8(adapt, REG_HMETFR, ®); + if (res) + continue; + + valid = reg & BIT(msgbox_num); if (0 == valid) read_down = true; } while ((!read_down) && (retry_cnts--)); @@ -533,6 +538,8 @@ void rtl8188e_set_FwJoinBssReport_cmd(struct adapter *adapt, u8 mstatus) bool bcn_valid = false; u8 DLBcnCount = 0; u32 poll = 0; + u8 reg; + int res; if (mstatus == 1) { /* We should set AID, correct TSF, HW seq enable before set JoinBssReport to Fw in 88/92C. */ @@ -547,8 +554,17 @@ void rtl8188e_set_FwJoinBssReport_cmd(struct adapter *adapt, u8 mstatus) /* Disable Hw protection for a time which revserd for Hw sending beacon. */ /* Fix download reserved page packet fail that access collision with the protection time. */ /* 2010.05.11. Added by tynli. */ - rtw_write8(adapt, REG_BCN_CTRL, rtw_read8(adapt, REG_BCN_CTRL) & (~BIT(3))); - rtw_write8(adapt, REG_BCN_CTRL, rtw_read8(adapt, REG_BCN_CTRL) | BIT(4)); + res = rtw_read8(adapt, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapt, REG_BCN_CTRL, reg & (~BIT(3))); + + res = rtw_read8(adapt, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapt, REG_BCN_CTRL, reg | BIT(4)); if (haldata->RegFwHwTxQCtrl & BIT(6)) bSendBeacon = true; @@ -581,8 +597,17 @@ void rtl8188e_set_FwJoinBssReport_cmd(struct adapter *adapt, u8 mstatus) /* */ /* Enable Bcn */ - rtw_write8(adapt, REG_BCN_CTRL, rtw_read8(adapt, REG_BCN_CTRL) | BIT(3)); - rtw_write8(adapt, REG_BCN_CTRL, rtw_read8(adapt, REG_BCN_CTRL) & (~BIT(4))); + res = rtw_read8(adapt, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapt, REG_BCN_CTRL, reg | BIT(3)); + + res = rtw_read8(adapt, REG_BCN_CTRL, ®); + if (res) + return; + + rtw_write8(adapt, REG_BCN_CTRL, reg & (~BIT(4))); /* To make sure that if there exists an adapter which would like to send beacon. */ /* If exists, the origianl value of 0x422[6] will be 1, we should check this to */ diff --git a/drivers/staging/r8188eu/hal/rtl8188e_dm.c b/drivers/staging/r8188eu/hal/rtl8188e_dm.c index 6d28e3dc0d26..0399872c4546 100644 --- a/drivers/staging/r8188eu/hal/rtl8188e_dm.c +++ b/drivers/staging/r8188eu/hal/rtl8188e_dm.c @@ -12,8 +12,12 @@ static void dm_InitGPIOSetting(struct adapter *Adapter) { u8 tmp1byte; + int res; + + res = rtw_read8(Adapter, REG_GPIO_MUXCFG, &tmp1byte); + if (res) + return; - tmp1byte = rtw_read8(Adapter, REG_GPIO_MUXCFG); tmp1byte &= (GPIOSEL_GPIO | ~GPIOSEL_ENBT); rtw_write8(Adapter, REG_GPIO_MUXCFG, tmp1byte); diff --git a/drivers/staging/r8188eu/hal/rtl8188e_hal_init.c b/drivers/staging/r8188eu/hal/rtl8188e_hal_init.c index e17375a74f17..5b8f1a912bbb 100644 --- a/drivers/staging/r8188eu/hal/rtl8188e_hal_init.c +++ b/drivers/staging/r8188eu/hal/rtl8188e_hal_init.c @@ -13,10 +13,14 @@ static void iol_mode_enable(struct adapter *padapter, u8 enable) { u8 reg_0xf0 = 0; + int res; if (enable) { /* Enable initial offload */ - reg_0xf0 = rtw_read8(padapter, REG_SYS_CFG); + res = rtw_read8(padapter, REG_SYS_CFG, ®_0xf0); + if (res) + return; + rtw_write8(padapter, REG_SYS_CFG, reg_0xf0 | SW_OFFLOAD_EN); if (!padapter->bFWReady) @@ -24,7 +28,10 @@ static void iol_mode_enable(struct adapter *padapter, u8 enable) } else { /* disable initial offload */ - reg_0xf0 = rtw_read8(padapter, REG_SYS_CFG); + res = rtw_read8(padapter, REG_SYS_CFG, ®_0xf0); + if (res) + return; + rtw_write8(padapter, REG_SYS_CFG, reg_0xf0 & ~SW_OFFLOAD_EN); } } @@ -34,17 +41,31 @@ static s32 iol_execute(struct adapter *padapter, u8 control) s32 status = _FAIL; u8 reg_0x88 = 0; unsigned long timeout; + int res; control = control & 0x0f; - reg_0x88 = rtw_read8(padapter, REG_HMEBOX_E0); + res = rtw_read8(padapter, REG_HMEBOX_E0, ®_0x88); + if (res) + return _FAIL; + rtw_write8(padapter, REG_HMEBOX_E0, reg_0x88 | control); timeout = jiffies + msecs_to_jiffies(1000); - while ((reg_0x88 = rtw_read8(padapter, REG_HMEBOX_E0)) & control && - time_before(jiffies, timeout)) - ; - reg_0x88 = rtw_read8(padapter, REG_HMEBOX_E0); + do { + res = rtw_read8(padapter, REG_HMEBOX_E0, ®_0x88); + if (res) + continue; + + if (!(reg_0x88 & control)) + break; + + } while (time_before(jiffies, timeout)); + + res = rtw_read8(padapter, REG_HMEBOX_E0, ®_0x88); + if (res) + return _FAIL; + status = (reg_0x88 & control) ? _FAIL : _SUCCESS; if (reg_0x88 & control << 4) status = _FAIL; @@ -62,7 +83,7 @@ static s32 iol_InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy) } static void -efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf) +efuse_phymap_to_logical(u8 *phymap, u16 _size_byte, u8 *pbuf) { u8 *efuseTbl = NULL; u8 rtemp8; @@ -70,7 +91,6 @@ efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf) u8 offset, wren; u16 i, j; u16 **eFuseWord = NULL; - u16 efuse_utilized = 0; u8 u1temp = 0; efuseTbl = kzalloc(EFUSE_MAP_LEN_88E, GFP_KERNEL); @@ -92,7 +112,6 @@ efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf) /* */ rtemp8 = *(phymap + eFuse_Addr); if (rtemp8 != 0xFF) { - efuse_utilized++; eFuse_Addr++; } else { goto exit; @@ -130,13 +149,11 @@ efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf) if (!(wren & 0x01)) { rtemp8 = *(phymap + eFuse_Addr); eFuse_Addr++; - efuse_utilized++; eFuseWord[offset][i] = (rtemp8 & 0xff); if (eFuse_Addr >= EFUSE_REAL_CONTENT_LEN_88E) break; rtemp8 = *(phymap + eFuse_Addr); eFuse_Addr++; - efuse_utilized++; eFuseWord[offset][i] |= (((u16)rtemp8 << 8) & 0xff00); if (eFuse_Addr >= EFUSE_REAL_CONTENT_LEN_88E) @@ -149,7 +166,6 @@ efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf) rtemp8 = *(phymap + eFuse_Addr); if (rtemp8 != 0xFF && (eFuse_Addr < EFUSE_REAL_CONTENT_LEN_88E)) { - efuse_utilized++; eFuse_Addr++; } } @@ -167,59 +183,70 @@ efuse_phymap_to_logical(u8 *phymap, u16 _offset, u16 _size_byte, u8 *pbuf) /* */ /* 4. Copy from Efuse map to output pointer memory!!! */ /* */ - for (i = 0; i < _size_byte; i++) - pbuf[i] = efuseTbl[_offset + i]; - - /* */ - /* 5. Calculate Efuse utilization. */ - /* */ + memcpy(pbuf, efuseTbl, _size_byte); exit: kfree(efuseTbl); kfree(eFuseWord); } -static void efuse_read_phymap_from_txpktbuf( +/* FIXME: add error handling in callers */ +static int efuse_read_phymap_from_txpktbuf( struct adapter *adapter, - int bcnhead, /* beacon head, where FW store len(2-byte) and efuse physical map. */ u8 *content, /* buffer to store efuse physical map */ u16 *size /* for efuse content: the max byte to read. will update to byte read */ ) { unsigned long timeout; - u16 dbg_addr = 0; __le32 lo32 = 0, hi32 = 0; u16 len = 0, count = 0; - int i = 0; + int i = 0, res; u16 limit = *size; - + u8 reg; u8 *pos = content; - - if (bcnhead < 0) /* if not valid */ - bcnhead = rtw_read8(adapter, REG_TDECTRL + 1); + u32 reg32; rtw_write8(adapter, REG_PKT_BUFF_ACCESS_CTRL, TXPKT_BUF_SELECT); - dbg_addr = bcnhead * 128 / 8; /* 8-bytes addressing */ - while (1) { - rtw_write16(adapter, REG_PKTBUF_DBG_ADDR, dbg_addr + i); + rtw_write16(adapter, REG_PKTBUF_DBG_ADDR, i); rtw_write8(adapter, REG_TXPKTBUF_DBG, 0); timeout = jiffies + msecs_to_jiffies(1000); - while (!rtw_read8(adapter, REG_TXPKTBUF_DBG) && time_before(jiffies, timeout)) - rtw_usleep_os(100); + do { + res = rtw_read8(adapter, REG_TXPKTBUF_DBG, ®); + if (res) + continue; + + if (reg) + break; + + msleep(1); + } while (time_before(jiffies, timeout)); /* data from EEPROM needs to be in LE */ - lo32 = cpu_to_le32(rtw_read32(adapter, REG_PKTBUF_DBG_DATA_L)); - hi32 = cpu_to_le32(rtw_read32(adapter, REG_PKTBUF_DBG_DATA_H)); + res = rtw_read32(adapter, REG_PKTBUF_DBG_DATA_L, ®32); + if (res) + return res; + + lo32 = cpu_to_le32(reg32); + + res = rtw_read32(adapter, REG_PKTBUF_DBG_DATA_H, ®32); + if (res) + return res; + + hi32 = cpu_to_le32(reg32); if (i == 0) { + u16 reg; + /* Although lenc is only used in a debug statement, * do not remove it as the rtw_read16() call consumes * 2 bytes from the EEPROM source. */ - rtw_read16(adapter, REG_PKTBUF_DBG_DATA_L); + res = rtw_read16(adapter, REG_PKTBUF_DBG_DATA_L, ®); + if (res) + return res; len = le32_to_cpu(lo32) & 0x0000ffff; @@ -246,21 +273,23 @@ static void efuse_read_phymap_from_txpktbuf( } rtw_write8(adapter, REG_PKT_BUFF_ACCESS_CTRL, DISABLE_TRXPKT_BUF_ACCESS); *size = count; + + return 0; } -static s32 iol_read_efuse(struct adapter *padapter, u8 txpktbuf_bndy, u16 offset, u16 size_byte, u8 *logical_map) +static s32 iol_read_efuse(struct adapter *padapter, u16 size_byte, u8 *logical_map) { s32 status = _FAIL; u8 physical_map[512]; u16 size = 512; - rtw_write8(padapter, REG_TDECTRL + 1, txpktbuf_bndy); + rtw_write8(padapter, REG_TDECTRL + 1, 0); memset(physical_map, 0xFF, 512); rtw_write8(padapter, REG_PKT_BUFF_ACCESS_CTRL, TXPKT_BUF_SELECT); status = iol_execute(padapter, CMD_READ_EFUSE_MAP); if (status == _SUCCESS) - efuse_read_phymap_from_txpktbuf(padapter, txpktbuf_bndy, physical_map, &size); - efuse_phymap_to_logical(physical_map, offset, size_byte, logical_map); + efuse_read_phymap_from_txpktbuf(padapter, physical_map, &size); + efuse_phymap_to_logical(physical_map, size_byte, logical_map); return status; } @@ -321,25 +350,35 @@ exit: void rtl8188e_EfusePowerSwitch(struct adapter *pAdapter, u8 PwrState) { u16 tmpV16; + int res; if (PwrState) { rtw_write8(pAdapter, REG_EFUSE_ACCESS, EFUSE_ACCESS_ON); /* 1.2V Power: From VDDON with Power Cut(0x0000h[15]), defualt valid */ - tmpV16 = rtw_read16(pAdapter, REG_SYS_ISO_CTRL); + res = rtw_read16(pAdapter, REG_SYS_ISO_CTRL, &tmpV16); + if (res) + return; + if (!(tmpV16 & PWC_EV12V)) { tmpV16 |= PWC_EV12V; rtw_write16(pAdapter, REG_SYS_ISO_CTRL, tmpV16); } /* Reset: 0x0000h[28], default valid */ - tmpV16 = rtw_read16(pAdapter, REG_SYS_FUNC_EN); + res = rtw_read16(pAdapter, REG_SYS_FUNC_EN, &tmpV16); + if (res) + return; + if (!(tmpV16 & FEN_ELDR)) { tmpV16 |= FEN_ELDR; rtw_write16(pAdapter, REG_SYS_FUNC_EN, tmpV16); } /* Clock: Gated(0x0008h[5]) 8M(0x0008h[1]) clock from ANA, default valid */ - tmpV16 = rtw_read16(pAdapter, REG_SYS_CLKR); + res = rtw_read16(pAdapter, REG_SYS_CLKR, &tmpV16); + if (res) + return; + if ((!(tmpV16 & LOADER_CLK_EN)) || (!(tmpV16 & ANA8M))) { tmpV16 |= (LOADER_CLK_EN | ANA8M); rtw_write16(pAdapter, REG_SYS_CLKR, tmpV16); @@ -470,26 +509,60 @@ exit: kfree(eFuseWord); } -static void ReadEFuseByIC(struct adapter *Adapter, u16 _offset, u16 _size_byte, u8 *pbuf) +void rtl8188e_ReadEFuse(struct adapter *Adapter, u16 _size_byte, u8 *pbuf) { int ret = _FAIL; if (rtw_IOL_applied(Adapter)) { rtl8188eu_InitPowerOn(Adapter); iol_mode_enable(Adapter, 1); - ret = iol_read_efuse(Adapter, 0, _offset, _size_byte, pbuf); + ret = iol_read_efuse(Adapter, _size_byte, pbuf); iol_mode_enable(Adapter, 0); if (_SUCCESS == ret) return; } - Hal_EfuseReadEFuse88E(Adapter, _offset, _size_byte, pbuf); + Hal_EfuseReadEFuse88E(Adapter, 0, _size_byte, pbuf); } -void rtl8188e_ReadEFuse(struct adapter *Adapter, u16 _offset, u16 _size_byte, u8 *pbuf) +static void dump_chip_info(struct HAL_VERSION chip_vers) { - ReadEFuseByIC(Adapter, _offset, _size_byte, pbuf); + uint cnt = 0; + char buf[128]; + + cnt += sprintf((buf + cnt), "Chip Version Info: CHIP_8188E_"); + cnt += sprintf((buf + cnt), "%s_", IS_NORMAL_CHIP(chip_vers) ? + "Normal_Chip" : "Test_Chip"); + cnt += sprintf((buf + cnt), "%s_", IS_CHIP_VENDOR_TSMC(chip_vers) ? + "TSMC" : "UMC"); + + switch (chip_vers.CUTVersion) { + case A_CUT_VERSION: + cnt += sprintf((buf + cnt), "A_CUT_"); + break; + case B_CUT_VERSION: + cnt += sprintf((buf + cnt), "B_CUT_"); + break; + case C_CUT_VERSION: + cnt += sprintf((buf + cnt), "C_CUT_"); + break; + case D_CUT_VERSION: + cnt += sprintf((buf + cnt), "D_CUT_"); + break; + case E_CUT_VERSION: + cnt += sprintf((buf + cnt), "E_CUT_"); + break; + default: + cnt += sprintf((buf + cnt), "UNKNOWN_CUT(%d)_", chip_vers.CUTVersion); + break; + } + + cnt += sprintf((buf + cnt), "1T1R_"); + + cnt += sprintf((buf + cnt), "RomVer(%d)\n", 0); + + pr_info("%s", buf); } void rtl8188e_read_chip_version(struct adapter *padapter) @@ -497,8 +570,12 @@ void rtl8188e_read_chip_version(struct adapter *padapter) u32 value32; struct HAL_VERSION ChipVersion; struct hal_data_8188e *pHalData = &padapter->haldata; + int res; + + res = rtw_read32(padapter, REG_SYS_CFG, &value32); + if (res) + return; - value32 = rtw_read32(padapter, REG_SYS_CFG); ChipVersion.ChipType = ((value32 & RTL_ID) ? TEST_CHIP : NORMAL_CHIP); ChipVersion.VendorType = ((value32 & VENDOR_ID) ? CHIP_VENDOR_UMC : CHIP_VENDOR_TSMC); @@ -525,10 +602,17 @@ void rtl8188e_SetHalODMVar(struct adapter *Adapter, void *pValue1, bool bSet) void hal_notch_filter_8188e(struct adapter *adapter, bool enable) { + int res; + u8 reg; + + res = rtw_read8(adapter, rOFDM0_RxDSP + 1, ®); + if (res) + return; + if (enable) - rtw_write8(adapter, rOFDM0_RxDSP + 1, rtw_read8(adapter, rOFDM0_RxDSP + 1) | BIT(1)); + rtw_write8(adapter, rOFDM0_RxDSP + 1, reg | BIT(1)); else - rtw_write8(adapter, rOFDM0_RxDSP + 1, rtw_read8(adapter, rOFDM0_RxDSP + 1) & ~BIT(1)); + rtw_write8(adapter, rOFDM0_RxDSP + 1, reg & ~BIT(1)); } /* */ @@ -538,26 +622,24 @@ void hal_notch_filter_8188e(struct adapter *adapter, bool enable) /* */ static s32 _LLTWrite(struct adapter *padapter, u32 address, u32 data) { - s32 status = _SUCCESS; - s32 count = 0; + s32 count; u32 value = _LLT_INIT_ADDR(address) | _LLT_INIT_DATA(data) | _LLT_OP(_LLT_WRITE_ACCESS); u16 LLTReg = REG_LLT_INIT; + int res; rtw_write32(padapter, LLTReg, value); /* polling */ - do { - value = rtw_read32(padapter, LLTReg); + for (count = 0; count <= POLLING_LLT_THRESHOLD; count++) { + res = rtw_read32(padapter, LLTReg, &value); + if (res) + continue; + if (_LLT_NO_ACTIVE == _LLT_OP_VALUE(value)) break; + } - if (count > POLLING_LLT_THRESHOLD) { - status = _FAIL; - break; - } - } while (count++); - - return status; + return count > POLLING_LLT_THRESHOLD ? _FAIL : _SUCCESS; } s32 InitLLTTable(struct adapter *padapter, u8 txpktbuf_bndy) diff --git a/drivers/staging/r8188eu/hal/rtl8188e_phycfg.c b/drivers/staging/r8188eu/hal/rtl8188e_phycfg.c index 4864dafd887b..dea6d915a1f4 100644 --- a/drivers/staging/r8188eu/hal/rtl8188e_phycfg.c +++ b/drivers/staging/r8188eu/hal/rtl8188e_phycfg.c @@ -56,8 +56,12 @@ rtl8188e_PHY_QueryBBReg( ) { u32 ReturnValue = 0, OriginalValue, BitShift; + int res; + + res = rtw_read32(Adapter, RegAddr, &OriginalValue); + if (res) + return 0; - OriginalValue = rtw_read32(Adapter, RegAddr); BitShift = phy_CalculateBitShift(BitMask); ReturnValue = (OriginalValue & BitMask) >> BitShift; return ReturnValue; @@ -84,9 +88,13 @@ rtl8188e_PHY_QueryBBReg( void rtl8188e_PHY_SetBBReg(struct adapter *Adapter, u32 RegAddr, u32 BitMask, u32 Data) { u32 OriginalValue, BitShift; + int res; if (BitMask != bMaskDWord) { /* if not "double word" write */ - OriginalValue = rtw_read32(Adapter, RegAddr); + res = rtw_read32(Adapter, RegAddr, &OriginalValue); + if (res) + return; + BitShift = phy_CalculateBitShift(BitMask); Data = ((OriginalValue & (~BitMask)) | (Data << BitShift)); } @@ -484,13 +492,17 @@ PHY_BBConfig8188E( { int rtStatus = _SUCCESS; struct hal_data_8188e *pHalData = &Adapter->haldata; - u32 RegVal; + u16 RegVal; u8 CrystalCap; + int res; phy_InitBBRFRegisterDefinition(Adapter); /* Enable BB and RF */ - RegVal = rtw_read16(Adapter, REG_SYS_FUNC_EN); + res = rtw_read16(Adapter, REG_SYS_FUNC_EN, &RegVal); + if (res) + return _FAIL; + rtw_write16(Adapter, REG_SYS_FUNC_EN, (u16)(RegVal | BIT(13) | BIT(0) | BIT(1))); /* 20090923 Joseph: Advised by Steven and Jenyu. Power sequence before init RF. */ @@ -594,6 +606,7 @@ _PHY_SetBWMode92C( struct hal_data_8188e *pHalData = &Adapter->haldata; u8 regBwOpMode; u8 regRRSR_RSC; + int res; if (Adapter->bDriverStopped) return; @@ -602,8 +615,13 @@ _PHY_SetBWMode92C( /* 3<1>Set MAC register */ /* 3 */ - regBwOpMode = rtw_read8(Adapter, REG_BWOPMODE); - regRRSR_RSC = rtw_read8(Adapter, REG_RRSR + 2); + res = rtw_read8(Adapter, REG_BWOPMODE, ®BwOpMode); + if (res) + return; + + res = rtw_read8(Adapter, REG_RRSR + 2, ®RRSR_RSC); + if (res) + return; switch (pHalData->CurrentChannelBW) { case HT_CHANNEL_WIDTH_20: diff --git a/drivers/staging/r8188eu/hal/rtl8188eu_recv.c b/drivers/staging/r8188eu/hal/rtl8188eu_recv.c index 727e1adce1dc..def6d0d6e402 100644 --- a/drivers/staging/r8188eu/hal/rtl8188eu_recv.c +++ b/drivers/staging/r8188eu/hal/rtl8188eu_recv.c @@ -32,7 +32,7 @@ int rtl8188eu_init_recv_priv(struct adapter *padapter) goto exit; } - precvpriv->precv_buf = (u8 *)N_BYTE_ALIGMENT((size_t)(precvpriv->pallocated_recv_buf), 4); + precvpriv->precv_buf = (u8 *)ALIGN((size_t)(precvpriv->pallocated_recv_buf), 4); precvbuf = (struct recv_buf *)precvpriv->precv_buf; diff --git a/drivers/staging/r8188eu/hal/usb_halinit.c b/drivers/staging/r8188eu/hal/usb_halinit.c index a217272a07f8..ff074d246dab 100644 --- a/drivers/staging/r8188eu/hal/usb_halinit.c +++ b/drivers/staging/r8188eu/hal/usb_halinit.c @@ -11,7 +11,7 @@ #include "../include/rtw_iol.h" #include "../include/usb_ops.h" #include "../include/usb_osintf.h" -#include "../include/Hal8188EPwrSeq.h" +#include "../include/HalPwrSeqCmd.h" static void _ConfigNormalChipOutEP_8188E(struct adapter *adapt, u8 NumOutPipe) { @@ -52,12 +52,14 @@ void rtl8188eu_interface_configure(struct adapter *adapt) u32 rtl8188eu_InitPowerOn(struct adapter *adapt) { u16 value16; + int res; + /* HW Power on sequence */ struct hal_data_8188e *haldata = &adapt->haldata; if (haldata->bMacPwrCtrlOn) return _SUCCESS; - if (!HalPwrSeqCmdParsing(adapt, Rtl8188E_NIC_PWR_ON_FLOW)) + if (!HalPwrSeqCmdParsing(adapt, PWR_ON_FLOW)) return _FAIL; /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */ @@ -65,7 +67,10 @@ u32 rtl8188eu_InitPowerOn(struct adapter *adapt) rtw_write16(adapt, REG_CR, 0x00); /* suggseted by zhouzhou, by page, 20111230 */ /* Enable MAC DMA/WMAC/SCHEDULE/SEC block */ - value16 = rtw_read16(adapt, REG_CR); + res = rtw_read16(adapt, REG_CR, &value16); + if (res) + return _FAIL; + value16 |= (HCI_TXDMA_EN | HCI_RXDMA_EN | TXDMA_EN | RXDMA_EN | PROTOCOL_EN | SCHEDULE_EN | ENSEC | CALTMR_EN); /* for SDIO - Set CR bit10 to enable 32k calibration. Suggested by SD1 Gimmy. Added by tynli. 2011.08.31. */ @@ -81,6 +86,7 @@ static void _InitInterrupt(struct adapter *Adapter) { u32 imr, imr_ex; u8 usb_opt; + int res; /* HISR write one to clear */ rtw_write32(Adapter, REG_HISR_88E, 0xFFFFFFFF); @@ -94,7 +100,9 @@ static void _InitInterrupt(struct adapter *Adapter) /* REG_USB_SPECIAL_OPTION - BIT(4) */ /* 0; Use interrupt endpoint to upload interrupt pkt */ /* 1; Use bulk endpoint to upload interrupt pkt, */ - usb_opt = rtw_read8(Adapter, REG_USB_SPECIAL_OPTION); + res = rtw_read8(Adapter, REG_USB_SPECIAL_OPTION, &usb_opt); + if (res) + return; if (adapter_to_dvobj(Adapter)->pusbdev->speed == USB_SPEED_HIGH) usb_opt = usb_opt | (INT_BULK_SEL); @@ -163,7 +171,14 @@ static void _InitNormalChipRegPriority(struct adapter *Adapter, u16 beQ, u16 bkQ, u16 viQ, u16 voQ, u16 mgtQ, u16 hiQ) { - u16 value16 = (rtw_read16(Adapter, REG_TRXDMA_CTRL) & 0x7); + u16 value16; + int res; + + res = rtw_read16(Adapter, REG_TRXDMA_CTRL, &value16); + if (res) + return; + + value16 &= 0x7; value16 |= _TXDMA_BEQ_MAP(beQ) | _TXDMA_BKQ_MAP(bkQ) | _TXDMA_VIQ_MAP(viQ) | _TXDMA_VOQ_MAP(voQ) | @@ -282,8 +297,12 @@ static void _InitQueuePriority(struct adapter *Adapter) static void _InitNetworkType(struct adapter *Adapter) { u32 value32; + int res; + + res = rtw_read32(Adapter, REG_CR, &value32); + if (res) + return; - value32 = rtw_read32(Adapter, REG_CR); /* TODO: use the other function to set network type */ value32 = (value32 & ~MASK_NETTYPE) | _NETTYPE(NT_LINK_AP); @@ -323,9 +342,13 @@ static void _InitAdaptiveCtrl(struct adapter *Adapter) { u16 value16; u32 value32; + int res; /* Response Rate Set */ - value32 = rtw_read32(Adapter, REG_RRSR); + res = rtw_read32(Adapter, REG_RRSR, &value32); + if (res) + return; + value32 &= ~RATE_BITMAP_ALL; value32 |= RATE_RRSR_CCK_ONLY_1M; rtw_write32(Adapter, REG_RRSR, value32); @@ -363,8 +386,12 @@ static void _InitEDCA(struct adapter *Adapter) static void _InitRetryFunction(struct adapter *Adapter) { u8 value8; + int res; + + res = rtw_read8(Adapter, REG_FWHW_TXQ_CTRL, &value8); + if (res) + return; - value8 = rtw_read8(Adapter, REG_FWHW_TXQ_CTRL); value8 |= EN_AMPDU_RTY_NEW; rtw_write8(Adapter, REG_FWHW_TXQ_CTRL, value8); @@ -390,11 +417,15 @@ static void _InitRetryFunction(struct adapter *Adapter) static void usb_AggSettingTxUpdate(struct adapter *Adapter) { u32 value32; + int res; if (Adapter->registrypriv.wifi_spec) return; - value32 = rtw_read32(Adapter, REG_TDECTRL); + res = rtw_read32(Adapter, REG_TDECTRL, &value32); + if (res) + return; + value32 = value32 & ~(BLK_DESC_NUM_MASK << BLK_DESC_NUM_SHIFT); value32 |= ((USB_TXAGG_DESC_NUM & BLK_DESC_NUM_MASK) << BLK_DESC_NUM_SHIFT); @@ -423,9 +454,15 @@ usb_AggSettingRxUpdate( { u8 valueDMA; u8 valueUSB; + int res; - valueDMA = rtw_read8(Adapter, REG_TRXDMA_CTRL); - valueUSB = rtw_read8(Adapter, REG_USB_SPECIAL_OPTION); + res = rtw_read8(Adapter, REG_TRXDMA_CTRL, &valueDMA); + if (res) + return; + + res = rtw_read8(Adapter, REG_USB_SPECIAL_OPTION, &valueUSB); + if (res) + return; valueDMA |= RXDMA_AGG_EN; valueUSB &= ~USB_AGG_EN; @@ -446,9 +483,11 @@ static void InitUsbAggregationSetting(struct adapter *Adapter) usb_AggSettingRxUpdate(Adapter); } -static void _InitBeaconParameters(struct adapter *Adapter) +/* FIXME: add error handling in callers */ +static int _InitBeaconParameters(struct adapter *Adapter) { struct hal_data_8188e *haldata = &Adapter->haldata; + int res; rtw_write16(Adapter, REG_BCN_CTRL, 0x1010); @@ -461,9 +500,19 @@ static void _InitBeaconParameters(struct adapter *Adapter) /* beacause test chip does not contension before sending beacon. by tynli. 2009.11.03 */ rtw_write16(Adapter, REG_BCNTCFG, 0x660F); - haldata->RegFwHwTxQCtrl = rtw_read8(Adapter, REG_FWHW_TXQ_CTRL + 2); - haldata->RegReg542 = rtw_read8(Adapter, REG_TBTT_PROHIBIT + 2); - haldata->RegCR_1 = rtw_read8(Adapter, REG_CR + 1); + res = rtw_read8(Adapter, REG_FWHW_TXQ_CTRL + 2, &haldata->RegFwHwTxQCtrl); + if (res) + return res; + + res = rtw_read8(Adapter, REG_TBTT_PROHIBIT + 2, &haldata->RegReg542); + if (res) + return res; + + res = rtw_read8(Adapter, REG_CR + 1, &haldata->RegCR_1); + if (res) + return res; + + return 0; } static void _BeaconFunctionEnable(struct adapter *Adapter, @@ -484,11 +533,17 @@ static void _BBTurnOnBlock(struct adapter *Adapter) static void _InitAntenna_Selection(struct adapter *Adapter) { struct hal_data_8188e *haldata = &Adapter->haldata; + int res; + u32 reg; if (haldata->AntDivCfg == 0) return; - rtw_write32(Adapter, REG_LEDCFG0, rtw_read32(Adapter, REG_LEDCFG0) | BIT(23)); + res = rtw_read32(Adapter, REG_LEDCFG0, ®); + if (res) + return; + + rtw_write32(Adapter, REG_LEDCFG0, reg | BIT(23)); rtl8188e_PHY_SetBBReg(Adapter, rFPGA0_XAB_RFParameter, BIT(13), 0x01); if (rtl8188e_PHY_QueryBBReg(Adapter, rFPGA0_XA_RFInterfaceOE, 0x300) == Antenna_A) @@ -514,9 +569,11 @@ u32 rtl8188eu_hal_init(struct adapter *Adapter) u16 value16; u8 txpktbuf_bndy; u32 status = _SUCCESS; + int res; struct hal_data_8188e *haldata = &Adapter->haldata; struct pwrctrl_priv *pwrctrlpriv = &Adapter->pwrctrlpriv; struct registry_priv *pregistrypriv = &Adapter->registrypriv; + u32 reg; if (Adapter->pwrctrlpriv.bkeepfwalive) { if (haldata->odmpriv.RFCalibrateInfo.bIQKInitialized) { @@ -614,13 +671,19 @@ u32 rtl8188eu_hal_init(struct adapter *Adapter) /* Hw bug which Hw initials RxFF boundary size to a value which is larger than the real Rx buffer size in 88E. */ /* */ /* Enable MACTXEN/MACRXEN block */ - value16 = rtw_read16(Adapter, REG_CR); + res = rtw_read16(Adapter, REG_CR, &value16); + if (res) + return _FAIL; + value16 |= (MACTXEN | MACRXEN); rtw_write8(Adapter, REG_CR, value16); /* Enable TX Report */ /* Enable Tx Report Timer */ - value8 = rtw_read8(Adapter, REG_TX_RPT_CTRL); + res = rtw_read8(Adapter, REG_TX_RPT_CTRL, &value8); + if (res) + return _FAIL; + rtw_write8(Adapter, REG_TX_RPT_CTRL, (value8 | BIT(1) | BIT(0))); /* Set MAX RPT MACID */ rtw_write8(Adapter, REG_TX_RPT_CTRL + 1, 2);/* FOR sta mode ,0: bc/mc ,1:AP */ @@ -684,7 +747,11 @@ u32 rtl8188eu_hal_init(struct adapter *Adapter) rtw_write16(Adapter, REG_TX_RPT_TIME, 0x3DF0); /* enable tx DMA to drop the redundate data of packet */ - rtw_write16(Adapter, REG_TXDMA_OFFSET_CHK, (rtw_read16(Adapter, REG_TXDMA_OFFSET_CHK) | DROP_DATA_EN)); + res = rtw_read16(Adapter, REG_TXDMA_OFFSET_CHK, &value16); + if (res) + return _FAIL; + + rtw_write16(Adapter, REG_TXDMA_OFFSET_CHK, (value16 | DROP_DATA_EN)); /* 2010/08/26 MH Merge from 8192CE. */ if (pwrctrlpriv->rf_pwrstate == rf_on) { @@ -704,7 +771,11 @@ u32 rtl8188eu_hal_init(struct adapter *Adapter) rtw_write8(Adapter, REG_USB_HRPWM, 0); /* ack for xmit mgmt frames. */ - rtw_write32(Adapter, REG_FWHW_TXQ_CTRL, rtw_read32(Adapter, REG_FWHW_TXQ_CTRL) | BIT(12)); + res = rtw_read32(Adapter, REG_FWHW_TXQ_CTRL, ®); + if (res) + return _FAIL; + + rtw_write32(Adapter, REG_FWHW_TXQ_CTRL, reg | BIT(12)); exit: return status; @@ -714,23 +785,33 @@ static void CardDisableRTL8188EU(struct adapter *Adapter) { u8 val8; struct hal_data_8188e *haldata = &Adapter->haldata; + int res; /* Stop Tx Report Timer. 0x4EC[Bit1]=b'0 */ - val8 = rtw_read8(Adapter, REG_TX_RPT_CTRL); + res = rtw_read8(Adapter, REG_TX_RPT_CTRL, &val8); + if (res) + return; + rtw_write8(Adapter, REG_TX_RPT_CTRL, val8 & (~BIT(1))); /* stop rx */ rtw_write8(Adapter, REG_CR, 0x0); /* Run LPS WL RFOFF flow */ - HalPwrSeqCmdParsing(Adapter, Rtl8188E_NIC_LPS_ENTER_FLOW); + HalPwrSeqCmdParsing(Adapter, LPS_ENTER_FLOW); /* 2. 0x1F[7:0] = 0 turn off RF */ - val8 = rtw_read8(Adapter, REG_MCUFWDL); + res = rtw_read8(Adapter, REG_MCUFWDL, &val8); + if (res) + return; + if ((val8 & RAM_DL_SEL) && Adapter->bFWReady) { /* 8051 RAM code */ /* Reset MCU 0x2[10]=0. */ - val8 = rtw_read8(Adapter, REG_SYS_FUNC_EN + 1); + res = rtw_read8(Adapter, REG_SYS_FUNC_EN + 1, &val8); + if (res) + return; + val8 &= ~BIT(2); /* 0x2[10], FEN_CPUEN */ rtw_write8(Adapter, REG_SYS_FUNC_EN + 1, val8); } @@ -740,26 +821,45 @@ static void CardDisableRTL8188EU(struct adapter *Adapter) /* YJ,add,111212 */ /* Disable 32k */ - val8 = rtw_read8(Adapter, REG_32K_CTRL); + res = rtw_read8(Adapter, REG_32K_CTRL, &val8); + if (res) + return; + rtw_write8(Adapter, REG_32K_CTRL, val8 & (~BIT(0))); /* Card disable power action flow */ - HalPwrSeqCmdParsing(Adapter, Rtl8188E_NIC_DISABLE_FLOW); + HalPwrSeqCmdParsing(Adapter, DISABLE_FLOW); /* Reset MCU IO Wrapper */ - val8 = rtw_read8(Adapter, REG_RSV_CTRL + 1); + res = rtw_read8(Adapter, REG_RSV_CTRL + 1, &val8); + if (res) + return; + rtw_write8(Adapter, REG_RSV_CTRL + 1, (val8 & (~BIT(3)))); - val8 = rtw_read8(Adapter, REG_RSV_CTRL + 1); + + res = rtw_read8(Adapter, REG_RSV_CTRL + 1, &val8); + if (res) + return; + rtw_write8(Adapter, REG_RSV_CTRL + 1, val8 | BIT(3)); /* YJ,test add, 111207. For Power Consumption. */ - val8 = rtw_read8(Adapter, GPIO_IN); + res = rtw_read8(Adapter, GPIO_IN, &val8); + if (res) + return; + rtw_write8(Adapter, GPIO_OUT, val8); rtw_write8(Adapter, GPIO_IO_SEL, 0xFF);/* Reg0x46 */ - val8 = rtw_read8(Adapter, REG_GPIO_IO_SEL); + res = rtw_read8(Adapter, REG_GPIO_IO_SEL, &val8); + if (res) + return; + rtw_write8(Adapter, REG_GPIO_IO_SEL, (val8 << 4)); - val8 = rtw_read8(Adapter, REG_GPIO_IO_SEL + 1); + res = rtw_read8(Adapter, REG_GPIO_IO_SEL + 1, &val8); + if (res) + return; + rtw_write8(Adapter, REG_GPIO_IO_SEL + 1, val8 | 0x0F);/* Reg0x43 */ rtw_write32(Adapter, REG_BB_PAD_CTRL, 0x00080808);/* set LNA ,TRSW,EX_PA Pin to output mode */ haldata->bMacPwrCtrlOn = false; @@ -812,13 +912,10 @@ exit: static void Hal_EfuseParseMACAddr_8188EU(struct adapter *adapt, u8 *hwinfo, bool AutoLoadFail) { - u16 i; - u8 sMacAddr[6] = {0x00, 0xE0, 0x4C, 0x81, 0x88, 0x02}; struct eeprom_priv *eeprom = &adapt->eeprompriv; if (AutoLoadFail) { - for (i = 0; i < 6; i++) - eeprom->mac_addr[i] = sMacAddr[i]; + eth_random_addr(eeprom->mac_addr); } else { /* Read Permanent MAC address */ memcpy(eeprom->mac_addr, &hwinfo[EEPROM_MAC_ADDR_88EU], ETH_ALEN); @@ -829,285 +926,41 @@ void ReadAdapterInfo8188EU(struct adapter *Adapter) { struct eeprom_priv *eeprom = &Adapter->eeprompriv; struct led_priv *ledpriv = &Adapter->ledpriv; + u8 *efuse_buf; u8 eeValue; + int res; /* check system boot selection */ - eeValue = rtw_read8(Adapter, REG_9346CR); - eeprom->EepromOrEfuse = (eeValue & BOOT_FROM_EEPROM); + res = rtw_read8(Adapter, REG_9346CR, &eeValue); + if (res) + return; + eeprom->bautoload_fail_flag = !(eeValue & EEPROM_EN); - if (!is_boot_from_eeprom(Adapter)) - EFUSE_ShadowMapUpdate(Adapter); + efuse_buf = kmalloc(EFUSE_MAP_LEN_88E, GFP_KERNEL); + if (!efuse_buf) + return; + memset(efuse_buf, 0xFF, EFUSE_MAP_LEN_88E); + + if (!(eeValue & BOOT_FROM_EEPROM) && !eeprom->bautoload_fail_flag) { + rtl8188e_EfusePowerSwitch(Adapter, true); + rtl8188e_ReadEFuse(Adapter, EFUSE_MAP_LEN_88E, efuse_buf); + rtl8188e_EfusePowerSwitch(Adapter, false); + } /* parse the eeprom/efuse content */ - Hal_EfuseParseIDCode88E(Adapter, eeprom->efuse_eeprom_data); - Hal_EfuseParseMACAddr_8188EU(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); + Hal_EfuseParseIDCode88E(Adapter, efuse_buf); + Hal_EfuseParseMACAddr_8188EU(Adapter, efuse_buf, eeprom->bautoload_fail_flag); - Hal_ReadPowerSavingMode88E(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); - Hal_ReadTxPowerInfo88E(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); - rtl8188e_EfuseParseChnlPlan(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); - Hal_EfuseParseXtal_8188E(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); - Hal_ReadAntennaDiversity88E(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); - Hal_ReadThermalMeter_88E(Adapter, eeprom->efuse_eeprom_data, eeprom->bautoload_fail_flag); + Hal_ReadPowerSavingMode88E(Adapter, efuse_buf, eeprom->bautoload_fail_flag); + Hal_ReadTxPowerInfo88E(Adapter, efuse_buf, eeprom->bautoload_fail_flag); + rtl8188e_EfuseParseChnlPlan(Adapter, efuse_buf, eeprom->bautoload_fail_flag); + Hal_EfuseParseXtal_8188E(Adapter, efuse_buf, eeprom->bautoload_fail_flag); + Hal_ReadAntennaDiversity88E(Adapter, efuse_buf, eeprom->bautoload_fail_flag); + Hal_ReadThermalMeter_88E(Adapter, efuse_buf, eeprom->bautoload_fail_flag); ledpriv->bRegUseLed = true; -} - -static void ResumeTxBeacon(struct adapter *adapt) -{ - struct hal_data_8188e *haldata = &adapt->haldata; - - /* 2010.03.01. Marked by tynli. No need to call workitem beacause we record the value */ - /* which should be read from register to a global variable. */ - - rtw_write8(adapt, REG_FWHW_TXQ_CTRL + 2, (haldata->RegFwHwTxQCtrl) | BIT(6)); - haldata->RegFwHwTxQCtrl |= BIT(6); - rtw_write8(adapt, REG_TBTT_PROHIBIT + 1, 0xff); - haldata->RegReg542 |= BIT(0); - rtw_write8(adapt, REG_TBTT_PROHIBIT + 2, haldata->RegReg542); -} - -static void StopTxBeacon(struct adapter *adapt) -{ - struct hal_data_8188e *haldata = &adapt->haldata; - - /* 2010.03.01. Marked by tynli. No need to call workitem beacause we record the value */ - /* which should be read from register to a global variable. */ - - rtw_write8(adapt, REG_FWHW_TXQ_CTRL + 2, (haldata->RegFwHwTxQCtrl) & (~BIT(6))); - haldata->RegFwHwTxQCtrl &= (~BIT(6)); - rtw_write8(adapt, REG_TBTT_PROHIBIT + 1, 0x64); - haldata->RegReg542 &= ~(BIT(0)); - rtw_write8(adapt, REG_TBTT_PROHIBIT + 2, haldata->RegReg542); - - /* todo: CheckFwRsvdPageContent(Adapter); 2010.06.23. Added by tynli. */ -} - -static void hw_var_set_opmode(struct adapter *Adapter, u8 *val) -{ - u8 val8; - u8 mode = *((u8 *)val); - - /* disable Port0 TSF update */ - rtw_write8(Adapter, REG_BCN_CTRL, rtw_read8(Adapter, REG_BCN_CTRL) | BIT(4)); - - /* set net_type */ - val8 = rtw_read8(Adapter, MSR) & 0x0c; - val8 |= mode; - rtw_write8(Adapter, MSR, val8); - - if ((mode == _HW_STATE_STATION_) || (mode == _HW_STATE_NOLINK_)) { - StopTxBeacon(Adapter); - - rtw_write8(Adapter, REG_BCN_CTRL, 0x19);/* disable atim wnd */ - } else if (mode == _HW_STATE_ADHOC_) { - ResumeTxBeacon(Adapter); - rtw_write8(Adapter, REG_BCN_CTRL, 0x1a); - } else if (mode == _HW_STATE_AP_) { - ResumeTxBeacon(Adapter); - - rtw_write8(Adapter, REG_BCN_CTRL, 0x12); - - /* Set RCR */ - rtw_write32(Adapter, REG_RCR, 0x7000208e);/* CBSSID_DATA must set to 0,reject ICV_ERR packet */ - /* enable to rx data frame */ - rtw_write16(Adapter, REG_RXFLTMAP2, 0xFFFF); - /* enable to rx ps-poll */ - rtw_write16(Adapter, REG_RXFLTMAP1, 0x0400); - - /* Beacon Control related register for first time */ - rtw_write8(Adapter, REG_BCNDMATIM, 0x02); /* 2ms */ - - rtw_write8(Adapter, REG_ATIMWND, 0x0a); /* 10ms */ - rtw_write16(Adapter, REG_BCNTCFG, 0x00); - rtw_write16(Adapter, REG_TBTT_PROHIBIT, 0xff04); - rtw_write16(Adapter, REG_TSFTR_SYN_OFFSET, 0x7fff);/* +32767 (~32ms) */ - - /* reset TSF */ - rtw_write8(Adapter, REG_DUAL_TSF_RST, BIT(0)); - - /* BIT(3) - If set 0, hw will clr bcnq when tx becon ok/fail or port 0 */ - rtw_write8(Adapter, REG_MBID_NUM, rtw_read8(Adapter, REG_MBID_NUM) | BIT(3) | BIT(4)); - - /* enable BCN0 Function for if1 */ - /* don't enable update TSF0 for if1 (due to TSF update when beacon/probe rsp are received) */ - rtw_write8(Adapter, REG_BCN_CTRL, (DIS_TSF_UDT0_NORMAL_CHIP | EN_BCN_FUNCTION | BIT(1))); - - /* dis BCN1 ATIM WND if if2 is station */ - rtw_write8(Adapter, REG_BCN_CTRL_1, rtw_read8(Adapter, REG_BCN_CTRL_1) | BIT(0)); - } -} - -void SetHwReg8188EU(struct adapter *Adapter, u8 variable, u8 *val) -{ - struct hal_data_8188e *haldata = &Adapter->haldata; - struct dm_priv *pdmpriv = &haldata->dmpriv; - struct odm_dm_struct *podmpriv = &haldata->odmpriv; - - switch (variable) { - case HW_VAR_SET_OPMODE: - hw_var_set_opmode(Adapter, val); - break; - case HW_VAR_BASIC_RATE: - { - u16 BrateCfg = 0; - u8 RateIndex = 0; - - /* 2007.01.16, by Emily */ - /* Select RRSR (in Legacy-OFDM and CCK) */ - /* For 8190, we select only 24M, 12M, 6M, 11M, 5.5M, 2M, and 1M from the Basic rate. */ - /* We do not use other rates. */ - HalSetBrateCfg(Adapter, val, &BrateCfg); - - /* 2011.03.30 add by Luke Lee */ - /* CCK 2M ACK should be disabled for some BCM and Atheros AP IOT */ - /* because CCK 2M has poor TXEVM */ - /* CCK 5.5M & 11M ACK should be enabled for better performance */ - - BrateCfg = (BrateCfg | 0xd) & 0x15d; - - BrateCfg |= 0x01; /* default enable 1M ACK rate */ - /* Set RRSR rate table. */ - rtw_write8(Adapter, REG_RRSR, BrateCfg & 0xff); - rtw_write8(Adapter, REG_RRSR + 1, (BrateCfg >> 8) & 0xff); - rtw_write8(Adapter, REG_RRSR + 2, rtw_read8(Adapter, REG_RRSR + 2) & 0xf0); - - /* Set RTS initial rate */ - while (BrateCfg > 0x1) { - BrateCfg = (BrateCfg >> 1); - RateIndex++; - } - /* Ziv - Check */ - rtw_write8(Adapter, REG_INIRTS_RATE_SEL, RateIndex); - } - break; - case HW_VAR_CORRECT_TSF: - { - u64 tsf; - struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv; - struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; - - tsf = pmlmeext->TSFValue - do_div(pmlmeext->TSFValue, - pmlmeinfo->bcn_interval * 1024) - 1024; /* us */ - - if (((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) || ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE)) - StopTxBeacon(Adapter); - - /* disable related TSF function */ - rtw_write8(Adapter, REG_BCN_CTRL, rtw_read8(Adapter, REG_BCN_CTRL) & (~BIT(3))); - - rtw_write32(Adapter, REG_TSFTR, tsf); - rtw_write32(Adapter, REG_TSFTR + 4, tsf >> 32); - - /* enable related TSF function */ - rtw_write8(Adapter, REG_BCN_CTRL, rtw_read8(Adapter, REG_BCN_CTRL) | BIT(3)); - - if (((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE) || ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE)) - ResumeTxBeacon(Adapter); - } - break; - case HW_VAR_MLME_SITESURVEY: - if (*((u8 *)val)) { /* under sitesurvey */ - /* config RCR to receive different BSSID & not to receive data frame */ - u32 v = rtw_read32(Adapter, REG_RCR); - v &= ~(RCR_CBSSID_BCN); - rtw_write32(Adapter, REG_RCR, v); - /* reject all data frame */ - rtw_write16(Adapter, REG_RXFLTMAP2, 0x00); - - /* disable update TSF */ - rtw_write8(Adapter, REG_BCN_CTRL, rtw_read8(Adapter, REG_BCN_CTRL) | BIT(4)); - } else { /* sitesurvey done */ - struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv; - struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; - - if ((is_client_associated_to_ap(Adapter)) || - ((pmlmeinfo->state & 0x03) == WIFI_FW_ADHOC_STATE)) { - /* enable to rx data frame */ - rtw_write16(Adapter, REG_RXFLTMAP2, 0xFFFF); - - /* enable update TSF */ - rtw_write8(Adapter, REG_BCN_CTRL, rtw_read8(Adapter, REG_BCN_CTRL) & (~BIT(4))); - } else if ((pmlmeinfo->state & 0x03) == WIFI_FW_AP_STATE) { - rtw_write16(Adapter, REG_RXFLTMAP2, 0xFFFF); - /* enable update TSF */ - rtw_write8(Adapter, REG_BCN_CTRL, rtw_read8(Adapter, REG_BCN_CTRL) & (~BIT(4))); - } - rtw_write32(Adapter, REG_RCR, rtw_read32(Adapter, REG_RCR) | RCR_CBSSID_BCN); - } - break; - case HW_VAR_SLOT_TIME: - { - u8 u1bAIFS, aSifsTime; - struct mlme_ext_priv *pmlmeext = &Adapter->mlmeextpriv; - struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; - - rtw_write8(Adapter, REG_SLOT, val[0]); - - if (pmlmeinfo->WMM_enable == 0) { - if (pmlmeext->cur_wireless_mode == WIRELESS_11B) - aSifsTime = 10; - else - aSifsTime = 16; - - u1bAIFS = aSifsTime + (2 * pmlmeinfo->slotTime); - - /* Temporary removed, 2008.06.20. */ - rtw_write8(Adapter, REG_EDCA_VO_PARAM, u1bAIFS); - rtw_write8(Adapter, REG_EDCA_VI_PARAM, u1bAIFS); - rtw_write8(Adapter, REG_EDCA_BE_PARAM, u1bAIFS); - rtw_write8(Adapter, REG_EDCA_BK_PARAM, u1bAIFS); - } - } - break; - case HW_VAR_DM_FLAG: - podmpriv->SupportAbility = *((u8 *)val); - break; - case HW_VAR_DM_FUNC_OP: - if (val[0]) - podmpriv->BK_SupportAbility = podmpriv->SupportAbility; - else - podmpriv->SupportAbility = podmpriv->BK_SupportAbility; - break; - case HW_VAR_DM_FUNC_RESET: - podmpriv->SupportAbility = pdmpriv->InitODMFlag; - break; - case HW_VAR_DM_FUNC_CLR: - podmpriv->SupportAbility = 0; - break; - case HW_VAR_AMPDU_FACTOR: - { - u8 RegToSet_Normal[4] = {0x41, 0xa8, 0x72, 0xb9}; - u8 FactorToSet; - u8 *pRegToSet; - u8 index = 0; - - pRegToSet = RegToSet_Normal; /* 0xb972a841; */ - FactorToSet = *((u8 *)val); - if (FactorToSet <= 3) { - FactorToSet = (1 << (FactorToSet + 2)); - if (FactorToSet > 0xf) - FactorToSet = 0xf; - - for (index = 0; index < 4; index++) { - if ((pRegToSet[index] & 0xf0) > (FactorToSet << 4)) - pRegToSet[index] = (pRegToSet[index] & 0x0f) | (FactorToSet << 4); - - if ((pRegToSet[index] & 0x0f) > FactorToSet) - pRegToSet[index] = (pRegToSet[index] & 0xf0) | (FactorToSet); - - rtw_write8(Adapter, (REG_AGGLEN_LMT + index), pRegToSet[index]); - } - } - } - break; - case HW_VAR_H2C_MEDIA_STATUS_RPT: - rtl8188e_set_FwMediaStatus_cmd(Adapter, (*(__le16 *)val)); - break; - default: - break; - } - + kfree(efuse_buf); } void UpdateHalRAMask8188EUsb(struct adapter *adapt, u32 mac_id, u8 rssi_level) @@ -1190,6 +1043,8 @@ void SetBeaconRelatedRegisters8188EUsb(struct adapter *adapt) struct mlme_ext_priv *pmlmeext = &adapt->mlmeextpriv; struct mlme_ext_info *pmlmeinfo = &pmlmeext->mlmext_info; u32 bcn_ctrl_reg = REG_BCN_CTRL; + int res; + u8 reg; /* reset TSF, enable update TSF, correcting TSF On Beacon */ /* BCN interval */ @@ -1200,7 +1055,10 @@ void SetBeaconRelatedRegisters8188EUsb(struct adapter *adapt) rtw_write8(adapt, REG_SLOT, 0x09); - value32 = rtw_read32(adapt, REG_TCR); + res = rtw_read32(adapt, REG_TCR, &value32); + if (res) + return; + value32 &= ~TSFRST; rtw_write32(adapt, REG_TCR, value32); @@ -1213,9 +1071,13 @@ void SetBeaconRelatedRegisters8188EUsb(struct adapter *adapt) _BeaconFunctionEnable(adapt, true, true); - ResumeTxBeacon(adapt); + rtw_resume_tx_beacon(adapt); - rtw_write8(adapt, bcn_ctrl_reg, rtw_read8(adapt, bcn_ctrl_reg) | BIT(1)); + res = rtw_read8(adapt, bcn_ctrl_reg, ®); + if (res) + return; + + rtw_write8(adapt, bcn_ctrl_reg, reg | BIT(1)); } void rtl8188eu_init_default_value(struct adapter *adapt) diff --git a/drivers/staging/r8188eu/hal/usb_ops_linux.c b/drivers/staging/r8188eu/hal/usb_ops_linux.c index d5e674542a78..c1a4d023f627 100644 --- a/drivers/staging/r8188eu/hal/usb_ops_linux.c +++ b/drivers/staging/r8188eu/hal/usb_ops_linux.c @@ -94,40 +94,47 @@ static int usb_write(struct intf_hdl *intf, u16 value, void *data, u8 size) return status; } -u8 rtw_read8(struct adapter *adapter, u32 addr) +int __must_check rtw_read8(struct adapter *adapter, u32 addr, u8 *data) { struct io_priv *io_priv = &adapter->iopriv; struct intf_hdl *intf = &io_priv->intf; u16 value = addr & 0xffff; - u8 data; - usb_read(intf, value, &data, 1); - - return data; + return usb_read(intf, value, data, 1); } -u16 rtw_read16(struct adapter *adapter, u32 addr) +int __must_check rtw_read16(struct adapter *adapter, u32 addr, u16 *data) { struct io_priv *io_priv = &adapter->iopriv; struct intf_hdl *intf = &io_priv->intf; u16 value = addr & 0xffff; - __le16 data; + __le16 le_data; + int res; - usb_read(intf, value, &data, 2); + res = usb_read(intf, value, &le_data, 2); + if (res) + return res; - return le16_to_cpu(data); + *data = le16_to_cpu(le_data); + + return 0; } -u32 rtw_read32(struct adapter *adapter, u32 addr) +int __must_check rtw_read32(struct adapter *adapter, u32 addr, u32 *data) { struct io_priv *io_priv = &adapter->iopriv; struct intf_hdl *intf = &io_priv->intf; u16 value = addr & 0xffff; - __le32 data; + __le32 le_data; + int res; - usb_read(intf, value, &data, 4); + res = usb_read(intf, value, &le_data, 4); + if (res) + return res; - return le32_to_cpu(data); + *data = le32_to_cpu(le_data); + + return 0; } int rtw_write8(struct adapter *adapter, u32 addr, u8 val) diff --git a/drivers/staging/r8188eu/include/Hal8188EPwrSeq.h b/drivers/staging/r8188eu/include/Hal8188EPwrSeq.h deleted file mode 100644 index e4c5b5d23cb4..000000000000 --- a/drivers/staging/r8188eu/include/Hal8188EPwrSeq.h +++ /dev/null @@ -1,13 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright(c) 2007 - 2011 Realtek Corporation. */ - -#ifndef __HAL8188EPWRSEQ_H__ -#define __HAL8188EPWRSEQ_H__ - -#include "HalPwrSeqCmd.h" - -extern struct wl_pwr_cfg rtl8188E_power_on_flow[]; -extern struct wl_pwr_cfg rtl8188E_card_disable_flow[]; -extern struct wl_pwr_cfg rtl8188E_enter_lps_flow[]; - -#endif /* __HAL8188EPWRSEQ_H__ */ diff --git a/drivers/staging/r8188eu/include/Hal8188ERateAdaptive.h b/drivers/staging/r8188eu/include/Hal8188ERateAdaptive.h index 20d73ca781e8..c571ad9478ea 100644 --- a/drivers/staging/r8188eu/include/Hal8188ERateAdaptive.h +++ b/drivers/staging/r8188eu/include/Hal8188ERateAdaptive.h @@ -22,19 +22,6 @@ le32_to_cpu((*(__le32 *)(__rxstatusdesc + 16)) #define GET_TX_RPT2_DESC_MACID_VALID_2_88E(__rxstatusdesc) \ le32_to_cpu((*(__le32 *)(__rxstatusdesc + 20)) - -#define GET_TX_REPORT_TYPE1_RERTY_0(__paddr) \ - le16_get_bits(*(__le16 *)__paddr, GENMASK(15, 0)) -#define GET_TX_REPORT_TYPE1_RERTY_1(__paddr) \ - LE_BITS_TO_1BYTE(__paddr + 2, 0, 8) -#define GET_TX_REPORT_TYPE1_RERTY_2(__paddr) \ - LE_BITS_TO_1BYTE(__paddr + 3, 0, 8) -#define GET_TX_REPORT_TYPE1_RERTY_3(__paddr) \ - LE_BITS_TO_1BYTE(__paddr + 4, 0, 8) -#define GET_TX_REPORT_TYPE1_RERTY_4(__paddr) \ - LE_BITS_TO_1BYTE(__paddr + 5, 0, 8) -#define GET_TX_REPORT_TYPE1_DROP_0(__paddr) \ - LE_BITS_TO_1BYTE(__paddr + 6, 0, 8) /* End rate adaptive define */ int ODM_RAInfo_Init_all(struct odm_dm_struct *dm_odm); diff --git a/drivers/staging/r8188eu/include/HalPwrSeqCmd.h b/drivers/staging/r8188eu/include/HalPwrSeqCmd.h index 49c02cce569e..0886300d26bf 100644 --- a/drivers/staging/r8188eu/include/HalPwrSeqCmd.h +++ b/drivers/staging/r8188eu/include/HalPwrSeqCmd.h @@ -6,54 +6,13 @@ #include "drv_types.h" -/*---------------------------------------------*/ -/* 3 The value of cmd: 4 bits */ -/*---------------------------------------------*/ - -#define PWR_CMD_WRITE 0x01 - /* offset: the read register offset */ - /* msk: the mask of the write bits */ - /* value: write value */ - /* note: driver shall implement this cmd by read & msk after write */ - -#define PWR_CMD_POLLING 0x02 - /* offset: the read register offset */ - /* msk: the mask of the polled value */ - /* value: the value to be polled, masked by the msd field. */ - /* note: driver shall implement this cmd by */ - /* do{ */ - /* if ( (Read(offset) & msk) == (value & msk) ) */ - /* break; */ - /* } while (not timeout); */ - -#define PWR_CMD_DELAY 0x03 - /* offset: the value to delay */ - /* msk: N/A */ - /* value: the unit of delay, 0: us, 1: ms */ - -#define PWR_CMD_END 0x04 - /* offset: N/A */ - /* msk: N/A */ - /* value: N/A */ - -enum pwrseq_cmd_delat_unit { - PWRSEQ_DELAY_US, - PWRSEQ_DELAY_MS, +enum r8188eu_pwr_seq { + PWR_ON_FLOW, + DISABLE_FLOW, + LPS_ENTER_FLOW, }; -struct wl_pwr_cfg { - u16 offset; - u8 cmd:4; - u8 msk; - u8 value; -}; - -#define GET_PWR_CFG_OFFSET(__PWR_CMD) __PWR_CMD.offset -#define GET_PWR_CFG_CMD(__PWR_CMD) __PWR_CMD.cmd -#define GET_PWR_CFG_MASK(__PWR_CMD) __PWR_CMD.msk -#define GET_PWR_CFG_VALUE(__PWR_CMD) __PWR_CMD.value - /* Prototype of protected function. */ -u8 HalPwrSeqCmdParsing(struct adapter *padapter, struct wl_pwr_cfg PwrCfgCmd[]); +u8 HalPwrSeqCmdParsing(struct adapter *padapter, enum r8188eu_pwr_seq seq); #endif diff --git a/drivers/staging/r8188eu/include/basic_types.h b/drivers/staging/r8188eu/include/basic_types.h deleted file mode 100644 index ffb21170e898..000000000000 --- a/drivers/staging/r8188eu/include/basic_types.h +++ /dev/null @@ -1,52 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ -/* Copyright(c) 2007 - 2011 Realtek Corporation. */ - -#ifndef __BASIC_TYPES_H__ -#define __BASIC_TYPES_H__ - -#include -#define NDIS_OID uint - -typedef void (*proc_t)(void *); - -#define FIELD_OFFSET(s, field) ((ssize_t)&((s *)(0))->field) - -/* port from fw */ -/* TODO: Macros Below are Sync from SD7-Driver. It is necessary - * to check correctness */ - -/* - * Call endian free function when - * 1. Read/write packet content. - * 2. Before write integer to IO. - * 3. After read integer from IO. -*/ - -/* Convert little data endian to host ordering */ -#define EF1BYTE(_val) \ - ((u8)(_val)) - -/* Create a bit mask */ -#define BIT_LEN_MASK_8(__bitlen) \ - (0xFF >> (8 - (__bitlen))) - -/*Description: - * Return 4-byte value in host byte ordering from - * 4-byte pointer in little-endian system. - */ -#define LE_P1BYTE_TO_HOST_1BYTE(__pstart) \ - (EF1BYTE(*((u8 *)(__pstart)))) - -/*Description: -Translate subfield (continuous bits in little-endian) of 4-byte -value to host byte ordering.*/ -#define LE_BITS_TO_1BYTE(__pstart, __bitoffset, __bitlen) \ - ( \ - (LE_P1BYTE_TO_HOST_1BYTE(__pstart) >> (__bitoffset)) & \ - BIT_LEN_MASK_8(__bitlen) \ - ) - -#define N_BYTE_ALIGMENT(__value, __aligment) ((__aligment == 1) ? \ - (__value) : (((__value + __aligment - 1) / __aligment) * __aligment)) - -#endif /* __BASIC_TYPES_H__ */ diff --git a/drivers/staging/r8188eu/include/hal_com.h b/drivers/staging/r8188eu/include/hal_com.h index 56ba356b5371..d7e333f6ce39 100644 --- a/drivers/staging/r8188eu/include/hal_com.h +++ b/drivers/staging/r8188eu/include/hal_com.h @@ -131,9 +131,6 @@ #define REG_NOA_DESC_START 0x05E8 #define REG_NOA_DESC_COUNT 0x05EC -#include "HalVerDef.h" -void dump_chip_info(struct HAL_VERSION ChipVersion); - /* return the final channel plan decision */ u8 hal_com_get_channel_plan(struct adapter *padapter, u8 hw_channel_plan, diff --git a/drivers/staging/r8188eu/include/hal_intf.h b/drivers/staging/r8188eu/include/hal_intf.h index a56f3d6ca399..ab6856d8a090 100644 --- a/drivers/staging/r8188eu/include/hal_intf.h +++ b/drivers/staging/r8188eu/include/hal_intf.h @@ -8,31 +8,15 @@ #include "drv_types.h" #include "Hal8188EPhyCfg.h" -enum hw_variables { - HW_VAR_SET_OPMODE, - HW_VAR_BASIC_RATE, - HW_VAR_CORRECT_TSF, - HW_VAR_MLME_SITESURVEY, - HW_VAR_SLOT_TIME, - HW_VAR_DM_FLAG, - HW_VAR_DM_FUNC_OP, - HW_VAR_DM_FUNC_RESET, - HW_VAR_DM_FUNC_CLR, - HW_VAR_AMPDU_FACTOR, - HW_VAR_H2C_MEDIA_STATUS_RPT, -}; - typedef s32 (*c2h_id_filter)(u8 id); -#define is_boot_from_eeprom(adapter) (adapter->eeprompriv.EepromOrEfuse) - void rtl8188eu_interface_configure(struct adapter *adapt); void ReadAdapterInfo8188EU(struct adapter *Adapter); void rtl8188eu_init_default_value(struct adapter *adapt); void rtl8188e_SetHalODMVar(struct adapter *Adapter, void *pValue1, bool bSet); u32 rtl8188eu_InitPowerOn(struct adapter *adapt); void rtl8188e_EfusePowerSwitch(struct adapter *pAdapter, u8 PwrState); -void rtl8188e_ReadEFuse(struct adapter *Adapter, u16 _offset, u16 _size_byte, u8 *pbuf); +void rtl8188e_ReadEFuse(struct adapter *Adapter, u16 _size_byte, u8 *pbuf); void hal_notch_filter_8188e(struct adapter *adapter, bool enable); @@ -44,8 +28,6 @@ int rtl8188e_IOL_exec_cmds_sync(struct adapter *adapter, unsigned int rtl8188eu_inirp_init(struct adapter *Adapter); -void SetHwReg8188EU(struct adapter *Adapter, u8 variable, u8 *val); - uint rtw_hal_init(struct adapter *padapter); uint rtw_hal_deinit(struct adapter *padapter); void rtw_hal_stop(struct adapter *padapter); diff --git a/drivers/staging/r8188eu/include/ieee80211.h b/drivers/staging/r8188eu/include/ieee80211.h index 15636a808f52..e7a4f8af497a 100644 --- a/drivers/staging/r8188eu/include/ieee80211.h +++ b/drivers/staging/r8188eu/include/ieee80211.h @@ -624,13 +624,6 @@ enum _PUBLIC_ACTION { ACT_PUBLIC_MAX }; -/* BACK action code */ -enum rtw_ieee80211_back_actioncode { - RTW_WLAN_ACTION_ADDBA_REQ = 0, - RTW_WLAN_ACTION_ADDBA_RESP = 1, - RTW_WLAN_ACTION_DELBA = 2, -}; - #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ #define WME_OUI_TYPE 2 diff --git a/drivers/staging/r8188eu/include/osdep_service.h b/drivers/staging/r8188eu/include/osdep_service.h index f1a703643e74..72990a1cdc66 100644 --- a/drivers/staging/r8188eu/include/osdep_service.h +++ b/drivers/staging/r8188eu/include/osdep_service.h @@ -5,7 +5,6 @@ #define __OSDEP_SERVICE_H_ #include -#include "basic_types.h" #define _FAIL 0 #define _SUCCESS 1 @@ -77,8 +76,6 @@ void *rtw_malloc2d(int h, int w, int size); spin_lock_init(&((q)->lock)); \ } while (0) -void rtw_usleep_os(int us); - static inline unsigned char _cancel_timer_ex(struct timer_list *ptimer) { return del_timer_sync(ptimer); diff --git a/drivers/staging/r8188eu/include/rtl8188e_hal.h b/drivers/staging/r8188eu/include/rtl8188e_hal.h index d2a069d4e1cc..5cd62b216720 100644 --- a/drivers/staging/r8188eu/include/rtl8188e_hal.h +++ b/drivers/staging/r8188eu/include/rtl8188e_hal.h @@ -26,11 +26,6 @@ #include "odm_RegConfig8188E.h" #include "odm_RTL8188E.h" -/* RTL8188E Power Configuration CMDs for USB/SDIO interfaces */ -#define Rtl8188E_NIC_PWR_ON_FLOW rtl8188E_power_on_flow -#define Rtl8188E_NIC_DISABLE_FLOW rtl8188E_card_disable_flow -#define Rtl8188E_NIC_LPS_ENTER_FLOW rtl8188E_enter_lps_flow - #define DRVINFO_SZ 4 /* unit is 8bytes */ #define PageNum_128(_Len) (u32)(((_Len)>>7) + ((_Len) & 0x7F ? 1 : 0)) diff --git a/drivers/staging/r8188eu/include/rtl8188e_spec.h b/drivers/staging/r8188eu/include/rtl8188e_spec.h index ef42c4b2f20c..9e7b1f89037c 100644 --- a/drivers/staging/r8188eu/include/rtl8188e_spec.h +++ b/drivers/staging/r8188eu/include/rtl8188e_spec.h @@ -9,7 +9,6 @@ #define HAL_PS_TIMER_INT_DELAY 50 /* 50 microseconds */ #define HAL_92C_NAV_UPPER_UNIT 128 /* micro-second */ -#define MAC_ADDR_LEN 6 /* 8188E PKT_BUFF_ACCESS_CTRL value */ #define TXPKT_BUF_SELECT 0x69 #define RXPKT_BUF_SELECT 0xA5 @@ -427,12 +426,6 @@ #define MAX_MSS_DENSITY_2T 0x13 #define MAX_MSS_DENSITY_1T 0x0A -/* EEPROM enable when set 1 */ -#define CmdEEPROM_En BIT(5) -/* System EEPROM select, 0: boot from E-FUSE, 1: The EEPROM used is 9346 */ -#define CmdEERPOMSEL BIT(4) -#define Cmd9346CR_9356SEL BIT(4) - /* 8192C GPIO MUX Configuration Register (offset 0x40, 4 byte) */ #define GPIOSEL_GPIO 0 #define GPIOSEL_ENBT BIT(5) @@ -1059,142 +1052,6 @@ Current IOREG MAP #define SCR_TXBCUSEDK BIT(6) /* Force Tx Bcast pkt Use Default Key */ #define SCR_RXBCUSEDK BIT(7) /* Force Rx Bcast pkt Use Default Key */ -/* RTL8188E SDIO Configuration */ - -/* I/O bus domain address mapping */ -#define SDIO_LOCAL_BASE 0x10250000 -#define WLAN_IOREG_BASE 0x10260000 -#define FIRMWARE_FIFO_BASE 0x10270000 -#define TX_HIQ_BASE 0x10310000 -#define TX_MIQ_BASE 0x10320000 -#define TX_LOQ_BASE 0x10330000 -#define RX_RX0FF_BASE 0x10340000 - -/* SDIO host local register space mapping. */ -#define SDIO_LOCAL_MSK 0x0FFF -#define WLAN_IOREG_MSK 0x7FFF -#define WLAN_FIFO_MSK 0x1FFF /* Aggregation Length[12:0] */ -#define WLAN_RX0FF_MSK 0x0003 - -/* Without ref to the SDIO Device ID */ -#define SDIO_WITHOUT_REF_DEVICE_ID 0 -#define SDIO_LOCAL_DEVICE_ID 0 /* 0b[16], 000b[15:13] */ -#define WLAN_TX_HIQ_DEVICE_ID 4 /* 0b[16], 100b[15:13] */ -#define WLAN_TX_MIQ_DEVICE_ID 5 /* 0b[16], 101b[15:13] */ -#define WLAN_TX_LOQ_DEVICE_ID 6 /* 0b[16], 110b[15:13] */ -#define WLAN_RX0FF_DEVICE_ID 7 /* 0b[16], 111b[15:13] */ -#define WLAN_IOREG_DEVICE_ID 8 /* 1b[16] */ - -/* SDIO Tx Free Page Index */ -#define HI_QUEUE_IDX 0 -#define MID_QUEUE_IDX 1 -#define LOW_QUEUE_IDX 2 -#define PUBLIC_QUEUE_IDX 3 - -#define SDIO_MAX_TX_QUEUE 3 /* HIQ, MIQ and LOQ */ -#define SDIO_MAX_RX_QUEUE 1 - -/* SDIO Tx Control */ -#define SDIO_REG_TX_CTRL 0x0000 -/* SDIO Host Interrupt Mask */ -#define SDIO_REG_HIMR 0x0014 -/* SDIO Host Interrupt Service Routine */ -#define SDIO_REG_HISR 0x0018 -/* HCI Current Power Mode */ -#define SDIO_REG_HCPWM 0x0019 -/* RXDMA Request Length */ -#define SDIO_REG_RX0_REQ_LEN 0x001C -/* Free Tx Buffer Page */ -#define SDIO_REG_FREE_TXPG 0x0020 -/* HCI Current Power Mode 1 */ -#define SDIO_REG_HCPWM1 0x0024 -/* HCI Current Power Mode 2 */ -#define SDIO_REG_HCPWM2 0x0026 -/* HTSF Informaion */ -#define SDIO_REG_HTSFR_INFO 0x0030 -/* HCI Request Power Mode 1 */ -#define SDIO_REG_HRPWM1 0x0080 -/* HCI Request Power Mode 2 */ -#define SDIO_REG_HRPWM2 0x0082 -/* HCI Power Save Clock */ -#define SDIO_REG_HPS_CLKR 0x0084 -/* SDIO HCI Suspend Control */ -#define SDIO_REG_HSUS_CTRL 0x0086 -/* SDIO Host Extension Interrupt Mask Always */ -#define SDIO_REG_HIMR_ON 0x0090 -/* SDIO Host Extension Interrupt Status Always */ -#define SDIO_REG_HISR_ON 0x0091 - -#define SDIO_HIMR_DISABLED 0 - -/* RTL8188E SDIO Host Interrupt Mask Register */ -#define SDIO_HIMR_RX_REQUEST_MSK BIT(0) -#define SDIO_HIMR_AVAL_MSK BIT(1) -#define SDIO_HIMR_TXERR_MSK BIT(2) -#define SDIO_HIMR_RXERR_MSK BIT(3) -#define SDIO_HIMR_TXFOVW_MSK BIT(4) -#define SDIO_HIMR_RXFOVW_MSK BIT(5) -#define SDIO_HIMR_TXBCNOK_MSK BIT(6) -#define SDIO_HIMR_TXBCNERR_MSK BIT(7) -#define SDIO_HIMR_BCNERLY_INT_MSK BIT(16) -#define SDIO_HIMR_C2HCMD_MSK BIT(17) -#define SDIO_HIMR_CPWM1_MSK BIT(18) -#define SDIO_HIMR_CPWM2_MSK BIT(19) -#define SDIO_HIMR_HSISR_IND_MSK BIT(20) -#define SDIO_HIMR_GTINT3_IND_MSK BIT(21) -#define SDIO_HIMR_GTINT4_IND_MSK BIT(22) -#define SDIO_HIMR_PSTIMEOUT_MSK BIT(23) -#define SDIO_HIMR_OCPINT_MSK BIT(24) -#define SDIO_HIMR_ATIMEND_MSK BIT(25) -#define SDIO_HIMR_ATIMEND_E_MSK BIT(26) -#define SDIO_HIMR_CTWEND_MSK BIT(27) - -/* RTL8188E SDIO Specific */ -#define SDIO_HIMR_MCU_ERR_MSK BIT(28) -#define SDIO_HIMR_TSF_BIT32_TOGGLE_MSK BIT(29) - -/* SDIO Host Interrupt Service Routine */ -#define SDIO_HISR_RX_REQUEST BIT(0) -#define SDIO_HISR_AVAL BIT(1) -#define SDIO_HISR_TXERR BIT(2) -#define SDIO_HISR_RXERR BIT(3) -#define SDIO_HISR_TXFOVW BIT(4) -#define SDIO_HISR_RXFOVW BIT(5) -#define SDIO_HISR_TXBCNOK BIT(6) -#define SDIO_HISR_TXBCNERR BIT(7) -#define SDIO_HISR_BCNERLY_INT BIT(16) -#define SDIO_HISR_C2HCMD BIT(17) -#define SDIO_HISR_CPWM1 BIT(18) -#define SDIO_HISR_CPWM2 BIT(19) -#define SDIO_HISR_HSISR_IND BIT(20) -#define SDIO_HISR_GTINT3_IND BIT(21) -#define SDIO_HISR_GTINT4_IND BIT(22) -#define SDIO_HISR_PSTIME BIT(23) -#define SDIO_HISR_OCPINT BIT(24) -#define SDIO_HISR_ATIMEND BIT(25) -#define SDIO_HISR_ATIMEND_E BIT(26) -#define SDIO_HISR_CTWEND BIT(27) - -/* RTL8188E SDIO Specific */ -#define SDIO_HISR_MCU_ERR BIT(28) -#define SDIO_HISR_TSF_BIT32_TOGGLE BIT(29) - -#define MASK_SDIO_HISR_CLEAR \ - (SDIO_HISR_TXERR | SDIO_HISR_RXERR | SDIO_HISR_TXFOVW |\ - SDIO_HISR_RXFOVW | SDIO_HISR_TXBCNOK | SDIO_HISR_TXBCNERR |\ - SDIO_HISR_C2HCMD | SDIO_HISR_CPWM1 | SDIO_HISR_CPWM2 |\ - SDIO_HISR_HSISR_IND | SDIO_HISR_GTINT3_IND | SDIO_HISR_GTINT4_IND |\ - SDIO_HISR_PSTIMEOUT | SDIO_HISR_OCPINT) - -/* SDIO HCI Suspend Control Register */ -#define HCI_RESUME_PWR_RDY BIT(1) -#define HCI_SUS_CTRL BIT(0) - -/* SDIO Tx FIFO related */ -/* The number of Tx FIFO free page */ -#define SDIO_TX_FREE_PG_QUEUE 4 -#define SDIO_TX_FIFO_PAGE_SZ 128 - /* 0xFE00h ~ 0xFE55h USB Configuration */ /* 2 USB Information (0xFE17) */ diff --git a/drivers/staging/r8188eu/include/rtw_eeprom.h b/drivers/staging/r8188eu/include/rtw_eeprom.h index d8d48ace356c..94d735b1d0db 100644 --- a/drivers/staging/r8188eu/include/rtw_eeprom.h +++ b/drivers/staging/r8188eu/include/rtw_eeprom.h @@ -7,19 +7,9 @@ #include "osdep_service.h" #include "drv_types.h" -#define HWSET_MAX_SIZE_512 512 - struct eeprom_priv { u8 bautoload_fail_flag; u8 mac_addr[ETH_ALEN] __aligned(2); /* PermanentAddress */ - u8 EepromOrEfuse; - u8 efuse_eeprom_data[HWSET_MAX_SIZE_512] __aligned(4); }; -void eeprom_write16(struct adapter *padapter, u16 reg, u16 data); -u16 eeprom_read16(struct adapter *padapter, u16 reg); -void read_eeprom_content(struct adapter *padapter); -void eeprom_read_sz(struct adapter *adapt, u16 reg, u8 *data, u32 sz); -void read_eeprom_content_by_attrib(struct adapter *padapter); - #endif /* __RTL871X_EEPROM_H__ */ diff --git a/drivers/staging/r8188eu/include/rtw_efuse.h b/drivers/staging/r8188eu/include/rtw_efuse.h index 2daf69f554d5..3d688a0e6dfb 100644 --- a/drivers/staging/r8188eu/include/rtw_efuse.h +++ b/drivers/staging/r8188eu/include/rtw_efuse.h @@ -8,6 +8,4 @@ void ReadEFuseByte(struct adapter *adapter, u16 _offset, u8 *pbuf); -void EFUSE_ShadowMapUpdate(struct adapter *adapter); - #endif diff --git a/drivers/staging/r8188eu/include/rtw_io.h b/drivers/staging/r8188eu/include/rtw_io.h index 6910e2b430e2..925c7967ac04 100644 --- a/drivers/staging/r8188eu/include/rtw_io.h +++ b/drivers/staging/r8188eu/include/rtw_io.h @@ -220,9 +220,9 @@ void unregister_intf_hdl(struct intf_hdl *pintfhdl); void _rtw_attrib_read(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem); void _rtw_attrib_write(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem); -u8 rtw_read8(struct adapter *adapter, u32 addr); -u16 rtw_read16(struct adapter *adapter, u32 addr); -u32 rtw_read32(struct adapter *adapter, u32 addr); +int __must_check rtw_read8(struct adapter *adapter, u32 addr, u8 *data); +int __must_check rtw_read16(struct adapter *adapter, u32 addr, u16 *data); +int __must_check rtw_read32(struct adapter *adapter, u32 addr, u32 *data); void _rtw_read_mem(struct adapter *adapter, u32 addr, u32 cnt, u8 *pmem); u32 rtw_read_port(struct adapter *adapter, u8 *pmem); void rtw_read_port_cancel(struct adapter *adapter); @@ -283,7 +283,7 @@ void free_io_queue(struct adapter *adapter); void async_bus_io(struct io_queue *pio_q); void bus_sync_io(struct io_queue *pio_q); u32 _ioreq2rwmem(struct io_queue *pio_q); -void dev_power_down(struct adapter * Adapter, u8 bpwrup); +void dev_power_down(struct adapter *Adapter, u8 bpwrup); #define PlatformEFIOWrite1Byte(_a,_b,_c) \ rtw_write8(_a,_b,_c) diff --git a/drivers/staging/r8188eu/include/rtw_iol.h b/drivers/staging/r8188eu/include/rtw_iol.h index fb88ebc1dabb..099f5a075274 100644 --- a/drivers/staging/r8188eu/include/rtw_iol.h +++ b/drivers/staging/r8188eu/include/rtw_iol.h @@ -41,22 +41,14 @@ int rtw_IOL_append_END_cmd(struct xmit_frame *xmit_frame); void read_efuse_from_txpktbuf(struct adapter *adapter, int bcnhead, u8 *content, u16 *size); -int _rtw_IOL_append_WB_cmd(struct xmit_frame *xmit_frame, u16 addr, - u8 value, u8 mask); -int _rtw_IOL_append_WW_cmd(struct xmit_frame *xmit_frame, u16 addr, - u16 value, u16 mask); -int _rtw_IOL_append_WD_cmd(struct xmit_frame *xmit_frame, u16 addr, - u32 value, u32 mask); -int _rtw_IOL_append_WRF_cmd(struct xmit_frame *xmit_frame, u8 rf_path, - u16 addr, u32 value, u32 mask); -#define rtw_IOL_append_WB_cmd(xmit_frame, addr, value, mask) \ - _rtw_IOL_append_WB_cmd((xmit_frame), (addr), (value) ,(mask)) -#define rtw_IOL_append_WW_cmd(xmit_frame, addr, value, mask) \ - _rtw_IOL_append_WW_cmd((xmit_frame), (addr), (value),(mask)) -#define rtw_IOL_append_WD_cmd(xmit_frame, addr, value, mask) \ - _rtw_IOL_append_WD_cmd((xmit_frame), (addr), (value), (mask)) -#define rtw_IOL_append_WRF_cmd(xmit_frame, rf_path, addr, value, mask) \ - _rtw_IOL_append_WRF_cmd((xmit_frame),(rf_path), (addr), (value), (mask)) +int rtw_IOL_append_WB_cmd(struct xmit_frame *xmit_frame, u16 addr, + u8 value, u8 mask); +int rtw_IOL_append_WW_cmd(struct xmit_frame *xmit_frame, u16 addr, + u16 value, u16 mask); +int rtw_IOL_append_WD_cmd(struct xmit_frame *xmit_frame, u16 addr, + u32 value, u32 mask); +int rtw_IOL_append_WRF_cmd(struct xmit_frame *xmit_frame, u8 rf_path, + u16 addr, u32 value, u32 mask); u8 rtw_IOL_cmd_boundary_handle(struct xmit_frame *pxmit_frame); diff --git a/drivers/staging/r8188eu/include/rtw_led.h b/drivers/staging/r8188eu/include/rtw_led.h index 2c14cb23d9ad..d6b0c1c2f9a2 100644 --- a/drivers/staging/r8188eu/include/rtw_led.h +++ b/drivers/staging/r8188eu/include/rtw_led.h @@ -37,9 +37,11 @@ enum LED_STATE_871x { LED_BLINK_RUNTOP = 13, /* Customized for RunTop */ }; -struct LED_871x { +struct led_priv { struct adapter *padapter; + bool bRegUseLed; + enum LED_STATE_871x CurrLedState; /* Current LED state. */ enum LED_STATE_871x BlinkingLedState; /* Next state for blinking, * either RTW_LED_ON or RTW_LED_OFF are. */ @@ -58,11 +60,6 @@ struct LED_871x { struct delayed_work blink_work; }; -struct led_priv{ - struct LED_871x SwLed0; - bool bRegUseLed; -}; - void rtl8188eu_InitSwLeds(struct adapter *padapter); void rtl8188eu_DeInitSwLeds(struct adapter *padapter); diff --git a/drivers/staging/r8188eu/include/rtw_mlme_ext.h b/drivers/staging/r8188eu/include/rtw_mlme_ext.h index 573d65b175cc..343ce1ce4b3d 100644 --- a/drivers/staging/r8188eu/include/rtw_mlme_ext.h +++ b/drivers/staging/r8188eu/include/rtw_mlme_ext.h @@ -424,6 +424,9 @@ void invalidate_cam_all(struct adapter *padapter); int allocate_fw_sta_entry(struct adapter *padapter); void flush_all_cam_entry(struct adapter *padapter); +void rtw_mlme_under_site_survey(struct adapter *adapter); +void rtw_mlme_site_survey_done(struct adapter *adapter); + void site_survey(struct adapter *padapter); u8 collect_bss_info(struct adapter *padapter, struct recv_frame *precv_frame, struct wlan_bssid_ex *bssid); @@ -455,6 +458,7 @@ int rtw_check_bcn_info(struct adapter *Adapter, u8 *pframe, u32 packet_len); void update_IOT_info(struct adapter *padapter); void update_capinfo(struct adapter *adapter, u16 updatecap); void update_wireless_mode(struct adapter *padapter); +void rtw_set_basic_rate(struct adapter *adapter, u8 *rates); void update_tx_basic_rate(struct adapter *padapter, u8 modulation); void update_bmc_sta_support_rate(struct adapter *padapter, u32 mac_id); int update_sta_support_rate(struct adapter *padapter, u8 *pvar_ie, @@ -468,8 +472,7 @@ unsigned int update_MSC_rate(struct HT_caps_element *pHT_caps); void Update_RA_Entry(struct adapter *padapter, u32 mac_id); void set_sta_rate(struct adapter *padapter, struct sta_info *psta); -unsigned int receive_disconnect(struct adapter *padapter, - unsigned char *macaddr, unsigned short reason); +void receive_disconnect(struct adapter *padapter, unsigned char *macaddr, unsigned short reason); unsigned char get_highest_rate_idx(u32 mask); int support_short_GI(struct adapter *padapter, struct HT_caps_element *caps); @@ -524,12 +527,13 @@ int issue_deauth(struct adapter *padapter, unsigned char *da, unsigned short reason); int issue_deauth_ex(struct adapter *padapter, u8 *da, unsigned short reason, int try_cnt, int wait_ms); -void issue_action_BA(struct adapter *padapter, unsigned char *raddr, - unsigned char action, unsigned short status); +void issue_action_BA(struct adapter *padapter, unsigned char *raddr, u8 action, u16 status); unsigned int send_delba(struct adapter *padapter, u8 initiator, u8 *addr); unsigned int send_beacon(struct adapter *padapter); bool get_beacon_valid_bit(struct adapter *adapter); void clear_beacon_valid_bit(struct adapter *adapter); +void rtw_resume_tx_beacon(struct adapter *adapt); +void rtw_stop_tx_beacon(struct adapter *adapt); void start_clnt_assoc(struct adapter *padapter); void start_clnt_auth(struct adapter *padapter); @@ -544,12 +548,8 @@ unsigned int OnProbeReq(struct adapter *padapter, struct recv_frame *precv_frame); unsigned int OnProbeRsp(struct adapter *padapter, struct recv_frame *precv_frame); -unsigned int DoReserved(struct adapter *padapter, - struct recv_frame *precv_frame); unsigned int OnBeacon(struct adapter *padapter, struct recv_frame *precv_frame); -unsigned int OnAtim(struct adapter *padapter, - struct recv_frame *precv_frame); unsigned int OnDisassoc(struct adapter *padapter, struct recv_frame *precv_frame); unsigned int OnAuth(struct adapter *padapter, @@ -592,9 +592,6 @@ void addba_timer_hdl(struct sta_info *psta); bool cckrates_included(unsigned char *rate, int ratelen); bool cckratesonly_included(unsigned char *rate, int ratelen); -void update_TSF(struct mlme_ext_priv *pmlmeext, u8 *pframe, uint len); -void correct_TSF(struct adapter *padapter, struct mlme_ext_priv *pmlmeext); - struct cmd_hdl { uint parmsize; u8 (*h2cfuns)(struct adapter *padapter, u8 *pbuf); diff --git a/drivers/staging/r8188eu/include/usb_ops_linux.h b/drivers/staging/r8188eu/include/usb_ops_linux.h index 641f059ffaf7..966688eedf66 100644 --- a/drivers/staging/r8188eu/include/usb_ops_linux.h +++ b/drivers/staging/r8188eu/include/usb_ops_linux.h @@ -26,6 +26,4 @@ #define usb_read_interrupt_complete(purb, regs) \ usb_read_interrupt_complete(purb) -unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr); - #endif diff --git a/drivers/staging/r8188eu/os_dep/ioctl_linux.c b/drivers/staging/r8188eu/os_dep/ioctl_linux.c index 8dd280e2739a..7f91dac2e41b 100644 --- a/drivers/staging/r8188eu/os_dep/ioctl_linux.c +++ b/drivers/staging/r8188eu/os_dep/ioctl_linux.c @@ -687,12 +687,9 @@ static int rtw_wx_set_mode(struct net_device *dev, struct iw_request_info *a, enum ndis_802_11_network_infra networkType; int ret = 0; - - - if (_FAIL == rtw_pwr_wakeup(padapter)) { - ret = -EPERM; + ret = rtw_pwr_wakeup(padapter); + if (ret) goto exit; - } if (!padapter->hw_init_completed) { ret = -EPERM; @@ -931,12 +928,9 @@ static int rtw_wx_set_wap(struct net_device *dev, struct wlan_network *pnetwork = NULL; enum ndis_802_11_auth_mode authmode; - - - if (_FAIL == rtw_pwr_wakeup(padapter)) { - ret = -1; + ret = rtw_pwr_wakeup(padapter); + if (ret) goto exit; - } if (!padapter->bup) { ret = -1; @@ -1049,10 +1043,9 @@ static int rtw_wx_set_scan(struct net_device *dev, struct iw_request_info *a, struct ndis_802_11_ssid ssid[RTW_SSID_SCAN_AMOUNT]; struct wifidirect_info *pwdinfo = &padapter->wdinfo; - if (_FAIL == rtw_pwr_wakeup(padapter)) { - ret = -1; + ret = rtw_pwr_wakeup(padapter); + if (ret) goto exit; - } if (padapter->bDriverStopped) { ret = -1; @@ -1252,10 +1245,9 @@ static int rtw_wx_set_essid(struct net_device *dev, uint ret = 0, len; - if (_FAIL == rtw_pwr_wakeup(padapter)) { - ret = -1; + ret = rtw_pwr_wakeup(padapter); + if (ret) goto exit; - } if (!padapter->bup) { ret = -1; @@ -1593,7 +1585,7 @@ static int rtw_wx_set_enc(struct net_device *dev, if (erq->length > 0) { wep.KeyLength = erq->length <= 5 ? 5 : 13; - wep.Length = wep.KeyLength + FIELD_OFFSET(struct ndis_802_11_wep, KeyMaterial); + wep.Length = wep.KeyLength + offsetof(struct ndis_802_11_wep, KeyMaterial); } else { wep.KeyLength = 0; @@ -3126,18 +3118,29 @@ exit: static void mac_reg_dump(struct adapter *padapter) { int i, j = 1; + u32 reg; + int res; + pr_info("\n ======= MAC REG =======\n"); for (i = 0x0; i < 0x300; i += 4) { if (j % 4 == 1) pr_info("0x%02x", i); - pr_info(" 0x%08x ", rtw_read32(padapter, i)); + + res = rtw_read32(padapter, i, ®); + if (!res) + pr_info(" 0x%08x ", reg); + if ((j++) % 4 == 0) pr_info("\n"); } for (i = 0x400; i < 0x800; i += 4) { if (j % 4 == 1) pr_info("0x%02x", i); - pr_info(" 0x%08x ", rtw_read32(padapter, i)); + + res = rtw_read32(padapter, i, ®); + if (!res) + pr_info(" 0x%08x ", reg); + if ((j++) % 4 == 0) pr_info("\n"); } @@ -3145,13 +3148,18 @@ static void mac_reg_dump(struct adapter *padapter) static void bb_reg_dump(struct adapter *padapter) { - int i, j = 1; + int i, j = 1, res; + u32 reg; + pr_info("\n ======= BB REG =======\n"); for (i = 0x800; i < 0x1000; i += 4) { if (j % 4 == 1) pr_info("0x%02x", i); - pr_info(" 0x%08x ", rtw_read32(padapter, i)); + res = rtw_read32(padapter, i, ®); + if (!res) + pr_info(" 0x%08x ", reg); + if ((j++) % 4 == 0) pr_info("\n"); } @@ -3178,6 +3186,7 @@ static void rtw_set_dynamic_functions(struct adapter *adapter, u8 dm_func) { struct hal_data_8188e *haldata = &adapter->haldata; struct odm_dm_struct *odmpriv = &haldata->odmpriv; + int res; switch (dm_func) { case 0: @@ -3193,7 +3202,9 @@ static void rtw_set_dynamic_functions(struct adapter *adapter, u8 dm_func) if (!(odmpriv->SupportAbility & DYNAMIC_BB_DIG)) { struct rtw_dig *digtable = &odmpriv->DM_DigTable; - digtable->CurIGValue = rtw_read8(adapter, 0xc50); + res = rtw_read8(adapter, 0xc50, &digtable->CurIGValue); + (void)res; + /* FIXME: return an error to caller */ } odmpriv->SupportAbility = DYNAMIC_ALL_FUNC_ENABLE; break; @@ -3202,6 +3213,14 @@ static void rtw_set_dynamic_functions(struct adapter *adapter, u8 dm_func) } } +static void rtw_set_dm_func_flag(struct adapter *adapter, u32 odm_flag) +{ + struct hal_data_8188e *haldata = &adapter->haldata; + struct odm_dm_struct *odmpriv = &haldata->odmpriv; + + odmpriv->SupportAbility = odm_flag; +} + static int rtw_dbg_port(struct net_device *dev, struct iw_request_info *info, union iwreq_data *wrqu, char *extra) @@ -3329,8 +3348,9 @@ static int rtw_dbg_port(struct net_device *dev, u16 reg = arg; u16 start_value = 0; u32 write_num = extra_arg; - int i; + int i, res; struct xmit_frame *xmit_frame; + u8 val8; xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); if (!xmit_frame) { @@ -3343,7 +3363,9 @@ static int rtw_dbg_port(struct net_device *dev, if (rtl8188e_IOL_exec_cmds_sync(padapter, xmit_frame, 5000, 0) != _SUCCESS) ret = -EPERM; - rtw_read8(padapter, reg); + /* FIXME: is this read necessary? */ + res = rtw_read8(padapter, reg, &val8); + (void)res; } break; @@ -3352,8 +3374,8 @@ static int rtw_dbg_port(struct net_device *dev, u16 reg = arg; u16 start_value = 200; u32 write_num = extra_arg; - - int i; + u16 val16; + int i, res; struct xmit_frame *xmit_frame; xmit_frame = rtw_IOL_accquire_xmit_frame(padapter); @@ -3367,7 +3389,9 @@ static int rtw_dbg_port(struct net_device *dev, if (rtl8188e_IOL_exec_cmds_sync(padapter, xmit_frame, 5000, 0) != _SUCCESS) ret = -EPERM; - rtw_read16(padapter, reg); + /* FIXME: is this read necessary? */ + res = rtw_read16(padapter, reg, &val16); + (void)res; } break; case 0x08: /* continuous write dword test */ @@ -3390,7 +3414,8 @@ static int rtw_dbg_port(struct net_device *dev, if (rtl8188e_IOL_exec_cmds_sync(padapter, xmit_frame, 5000, 0) != _SUCCESS) ret = -EPERM; - rtw_read32(padapter, reg); + /* FIXME: is this read necessary? */ + ret = rtw_read32(padapter, reg, &write_num); } break; } @@ -3434,7 +3459,7 @@ static int rtw_dbg_port(struct net_device *dev, case 0x06: { u32 ODMFlag = (u32)(0x0f & arg); - SetHwReg8188EU(padapter, HW_VAR_DM_FLAG, (u8 *)(&ODMFlag)); + rtw_set_dm_func_flag(padapter, ODMFlag); } break; case 0x07: diff --git a/drivers/staging/r8188eu/os_dep/os_intfs.c b/drivers/staging/r8188eu/os_dep/os_intfs.c index 891c85b088ca..cac9553666e6 100644 --- a/drivers/staging/r8188eu/os_dep/os_intfs.c +++ b/drivers/staging/r8188eu/os_dep/os_intfs.c @@ -740,19 +740,32 @@ static void rtw_fifo_cleanup(struct adapter *adapter) { struct pwrctrl_priv *pwrpriv = &adapter->pwrctrlpriv; u8 trycnt = 100; + int res; + u32 reg; /* pause tx */ rtw_write8(adapter, REG_TXPAUSE, 0xff); /* keep sn */ - adapter->xmitpriv.nqos_ssn = rtw_read16(adapter, REG_NQOS_SEQ); + /* FIXME: return an error to caller */ + res = rtw_read16(adapter, REG_NQOS_SEQ, &adapter->xmitpriv.nqos_ssn); + if (res) + return; if (!pwrpriv->bkeepfwalive) { /* RX DMA stop */ + res = rtw_read32(adapter, REG_RXPKT_NUM, ®); + if (res) + return; + rtw_write32(adapter, REG_RXPKT_NUM, - (rtw_read32(adapter, REG_RXPKT_NUM) | RW_RELEASE_EN)); + (reg | RW_RELEASE_EN)); do { - if (!(rtw_read32(adapter, REG_RXPKT_NUM) & RXDMA_IDLE)) + res = rtw_read32(adapter, REG_RXPKT_NUM, ®); + if (res) + continue; + + if (!(reg & RXDMA_IDLE)) break; } while (trycnt--); diff --git a/drivers/staging/r8188eu/os_dep/osdep_service.c b/drivers/staging/r8188eu/os_dep/osdep_service.c index 812acd59be79..3504a0a9ba87 100644 --- a/drivers/staging/r8188eu/os_dep/osdep_service.c +++ b/drivers/staging/r8188eu/os_dep/osdep_service.c @@ -42,14 +42,6 @@ Otherwise, there will be racing condition. Caller must check if the list is empty before calling rtw_list_delete */ -void rtw_usleep_os(int us) -{ - if (1 < (us / 1000)) - msleep(1); - else - msleep((us / 1000) + 1); -} - static const struct device_type wlan_type = { .name = "wlan", }; diff --git a/drivers/staging/r8188eu/os_dep/usb_intf.c b/drivers/staging/r8188eu/os_dep/usb_intf.c index 68869c5daeff..cc2b44f60c46 100644 --- a/drivers/staging/r8188eu/os_dep/usb_intf.c +++ b/drivers/staging/r8188eu/os_dep/usb_intf.c @@ -372,7 +372,7 @@ handle_dualmac: free_adapter: if (pnetdev) rtw_free_netdev(pnetdev); - else if (padapter) + else vfree(padapter); return NULL; diff --git a/drivers/staging/r8188eu/os_dep/usb_ops_linux.c b/drivers/staging/r8188eu/os_dep/usb_ops_linux.c index 0269e602b217..220e592b757c 100644 --- a/drivers/staging/r8188eu/os_dep/usb_ops_linux.c +++ b/drivers/staging/r8188eu/os_dep/usb_ops_linux.c @@ -7,7 +7,7 @@ #include "../include/usb_ops_linux.h" #include "../include/rtl8188e_recv.h" -unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr) +static unsigned int ffaddr2pipehdl(struct dvobj_priv *pdvobj, u32 addr) { unsigned int pipe = 0, ep_num = 0; struct usb_device *pusbd = pdvobj->pusbdev; diff --git a/drivers/staging/r8188eu/os_dep/xmit_linux.c b/drivers/staging/r8188eu/os_dep/xmit_linux.c index e430c64e9068..91a1e4e3219a 100644 --- a/drivers/staging/r8188eu/os_dep/xmit_linux.c +++ b/drivers/staging/r8188eu/os_dep/xmit_linux.c @@ -71,7 +71,7 @@ int rtw_os_xmit_resource_alloc(struct adapter *padapter, struct xmit_buf *pxmitb if (!pxmitbuf->pallocated_buf) return _FAIL; - pxmitbuf->pbuf = (u8 *)N_BYTE_ALIGMENT((size_t)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); + pxmitbuf->pbuf = (u8 *)ALIGN((size_t)(pxmitbuf->pallocated_buf), XMITBUF_ALIGN_SZ); pxmitbuf->dma_transfer_addr = 0; pxmitbuf->pxmit_urb = usb_alloc_urb(0, GFP_KERNEL); diff --git a/drivers/staging/rtl8192e/rtllib_tx.c b/drivers/staging/rtl8192e/rtllib_tx.c index 37715afb0210..42f81b23a144 100644 --- a/drivers/staging/rtl8192e/rtllib_tx.c +++ b/drivers/staging/rtl8192e/rtllib_tx.c @@ -205,30 +205,28 @@ static struct rtllib_txb *rtllib_alloc_txb(int nr_frags, int txb_size, struct rtllib_txb *txb; int i; - txb = kmalloc(sizeof(struct rtllib_txb) + (sizeof(u8 *) * nr_frags), - gfp_mask); + txb = kzalloc(struct_size(txb, fragments, nr_frags), gfp_mask); if (!txb) return NULL; - memset(txb, 0, sizeof(struct rtllib_txb)); txb->nr_frags = nr_frags; txb->frag_size = cpu_to_le16(txb_size); for (i = 0; i < nr_frags; i++) { txb->fragments[i] = dev_alloc_skb(txb_size); - if (unlikely(!txb->fragments[i])) { - i--; - break; - } + if (unlikely(!txb->fragments[i])) + goto err_free; memset(txb->fragments[i]->cb, 0, sizeof(txb->fragments[i]->cb)); } - if (unlikely(i != nr_frags)) { - while (i >= 0) - dev_kfree_skb_any(txb->fragments[i--]); - kfree(txb); - return NULL; - } + return txb; + +err_free: + while (--i >= 0) + dev_kfree_skb_any(txb->fragments[i]); + kfree(txb); + + return NULL; } static int rtllib_classify(struct sk_buff *skb, u8 bIsAmsdu) diff --git a/drivers/staging/rtl8192e/rtllib_wx.c b/drivers/staging/rtl8192e/rtllib_wx.c index cf9a240924f2..da2c41c9b92f 100644 --- a/drivers/staging/rtl8192e/rtllib_wx.c +++ b/drivers/staging/rtl8192e/rtllib_wx.c @@ -17,17 +17,9 @@ #include #include #include "rtllib.h" -struct modes_unit { - char *mode_string; - int mode_size; -}; -static struct modes_unit rtllib_modes[] = { - {"a", 1}, - {"b", 1}, - {"g", 1}, - {"?", 1}, - {"N-24G", 5}, - {"N-5G", 4}, + +static const char * const rtllib_modes[] = { + "a", "b", "g", "?", "N-24G", "N-5G" }; #define MAX_CUSTOM_LEN 64 @@ -72,10 +64,9 @@ static inline char *rtl819x_translate_scan(struct rtllib_device *ieee, /* Add the protocol name */ iwe.cmd = SIOCGIWNAME; for (i = 0; i < ARRAY_SIZE(rtllib_modes); i++) { - if (network->mode&(1<mode & BIT(i)) { + strcpy(pname, rtllib_modes[i]); + pname += strlen(rtllib_modes[i]); } } *pname = '\0'; @@ -158,7 +149,8 @@ static inline char *rtl819x_translate_scan(struct rtllib_device *ieee, max_rate = rate; } iwe.cmd = SIOCGIWRATE; - iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.disabled = 0; + iwe.u.bitrate.fixed = 0; iwe.u.bitrate.value = max_rate * 500000; start = iwe_stream_add_event_rsl(info, start, stop, &iwe, IW_EV_PARAM_LEN); iwe.cmd = IWEVCUSTOM; @@ -285,7 +277,7 @@ int rtllib_wx_set_encode(struct rtllib_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) { - struct iw_point *erq = &(wrqu->encoding); + struct iw_point *erq = &wrqu->encoding; struct net_device *dev = ieee->dev; struct rtllib_security sec = { .flags = 0 @@ -312,8 +304,9 @@ int rtllib_wx_set_encode(struct rtllib_device *ieee, netdev_dbg(ieee->dev, "Disabling encryption on key %d.\n", key); lib80211_crypt_delayed_deinit(&ieee->crypt_info, crypt); - } else + } else { netdev_dbg(ieee->dev, "Disabling encryption.\n"); + } /* Check all the keys to see if any are still configured, * and if no key index was provided, de-init them all @@ -457,7 +450,7 @@ int rtllib_wx_get_encode(struct rtllib_device *ieee, struct iw_request_info *info, union iwreq_data *wrqu, char *keybuf) { - struct iw_point *erq = &(wrqu->encoding); + struct iw_point *erq = &wrqu->encoding; int len, key; struct lib80211_crypt_data *crypt; @@ -608,7 +601,6 @@ int rtllib_wx_set_encode_ext(struct rtllib_device *ieee, goto done; } *crypt = new_crypt; - } if (ext->key_len > 0 && (*crypt)->ops->set_key && @@ -732,8 +724,9 @@ int rtllib_wx_set_auth(struct rtllib_device *ieee, } else if (data->value & IW_AUTH_ALG_LEAP) { ieee->open_wep = 1; ieee->auth_mode = 2; - } else + } else { return -EINVAL; + } break; case IW_AUTH_WPA_ENABLED: @@ -776,7 +769,7 @@ int rtllib_wx_set_gen_ie(struct rtllib_device *ieee, u8 *ie, size_t len) kfree(ieee->wps_ie); ieee->wps_ie = NULL; if (len) { - if (len != ie[1]+2) + if (len != ie[1] + 2) return -EINVAL; buf = kmemdup(ie, len, GFP_KERNEL); if (!buf) diff --git a/drivers/staging/rtl8192u/r8192U.h b/drivers/staging/rtl8192u/r8192U.h index 14ca00a2789b..1942cb849374 100644 --- a/drivers/staging/rtl8192u/r8192U.h +++ b/drivers/staging/rtl8192u/r8192U.h @@ -1013,7 +1013,7 @@ typedef struct r8192_priv { bool bis_any_nonbepkts; bool bcurrent_turbo_EDCA; bool bis_cur_rdlstate; - struct timer_list fsync_timer; + struct delayed_work fsync_work; bool bfsync_processing; /* 500ms Fsync timer is active or not */ u32 rate_record; u32 rateCountDiffRecord; diff --git a/drivers/staging/rtl8192u/r8192U_dm.c b/drivers/staging/rtl8192u/r8192U_dm.c index 725bf5ca9e34..00fc8fd344db 100644 --- a/drivers/staging/rtl8192u/r8192U_dm.c +++ b/drivers/staging/rtl8192u/r8192U_dm.c @@ -2578,19 +2578,20 @@ static void dm_init_fsync(struct net_device *dev) priv->ieee80211->fsync_seconddiff_ratethreshold = 200; priv->ieee80211->fsync_state = Default_Fsync; priv->framesyncMonitor = 1; /* current default 0xc38 monitor on */ - timer_setup(&priv->fsync_timer, dm_fsync_timer_callback, 0); + INIT_DELAYED_WORK(&priv->fsync_work, dm_fsync_work_callback); } static void dm_deInit_fsync(struct net_device *dev) { struct r8192_priv *priv = ieee80211_priv(dev); - del_timer_sync(&priv->fsync_timer); + cancel_delayed_work_sync(&priv->fsync_work); } -void dm_fsync_timer_callback(struct timer_list *t) +void dm_fsync_work_callback(struct work_struct *work) { - struct r8192_priv *priv = from_timer(priv, t, fsync_timer); + struct r8192_priv *priv = + container_of(work, struct r8192_priv, fsync_work.work); struct net_device *dev = priv->ieee80211->dev; u32 rate_index, rate_count = 0, rate_count_diff = 0; bool bSwitchFromCountDiff = false; @@ -2657,17 +2658,16 @@ void dm_fsync_timer_callback(struct timer_list *t) } } if (bDoubleTimeInterval) { - if (timer_pending(&priv->fsync_timer)) - del_timer_sync(&priv->fsync_timer); - priv->fsync_timer.expires = jiffies + - msecs_to_jiffies(priv->ieee80211->fsync_time_interval*priv->ieee80211->fsync_multiple_timeinterval); - add_timer(&priv->fsync_timer); + cancel_delayed_work_sync(&priv->fsync_work); + schedule_delayed_work(&priv->fsync_work, + msecs_to_jiffies(priv + ->ieee80211->fsync_time_interval * + priv->ieee80211->fsync_multiple_timeinterval)); } else { - if (timer_pending(&priv->fsync_timer)) - del_timer_sync(&priv->fsync_timer); - priv->fsync_timer.expires = jiffies + - msecs_to_jiffies(priv->ieee80211->fsync_time_interval); - add_timer(&priv->fsync_timer); + cancel_delayed_work_sync(&priv->fsync_work); + schedule_delayed_work(&priv->fsync_work, + msecs_to_jiffies(priv + ->ieee80211->fsync_time_interval)); } } else { /* Let Register return to default value; */ @@ -2695,7 +2695,7 @@ static void dm_EndSWFsync(struct net_device *dev) struct r8192_priv *priv = ieee80211_priv(dev); RT_TRACE(COMP_HALDM, "%s\n", __func__); - del_timer_sync(&(priv->fsync_timer)); + cancel_delayed_work_sync(&priv->fsync_work); /* Let Register return to default value; */ if (priv->bswitch_fsync) { @@ -2736,11 +2736,9 @@ static void dm_StartSWFsync(struct net_device *dev) if (priv->ieee80211->fsync_rate_bitmap & rateBitmap) priv->rate_record += priv->stats.received_rate_histogram[1][rateIndex]; } - if (timer_pending(&priv->fsync_timer)) - del_timer_sync(&priv->fsync_timer); - priv->fsync_timer.expires = jiffies + - msecs_to_jiffies(priv->ieee80211->fsync_time_interval); - add_timer(&priv->fsync_timer); + cancel_delayed_work_sync(&priv->fsync_work); + schedule_delayed_work(&priv->fsync_work, + msecs_to_jiffies(priv->ieee80211->fsync_time_interval)); write_nic_dword(dev, rOFDM0_RxDetector2, 0x465c12cd); } @@ -3002,7 +3000,7 @@ static void dm_check_txrateandretrycount(struct net_device *dev) /* for initial tx rate */ /*priv->stats.last_packet_rate = read_nic_byte(dev, INITIAL_TX_RATE_REG);*/ read_nic_byte(dev, INITIAL_TX_RATE_REG, &ieee->softmac_stats.last_packet_rate); - /* for tx tx retry count */ + /* for tx retry count */ /*priv->stats.txretrycount = read_nic_dword(dev, TX_RETRY_COUNT_REG);*/ read_nic_dword(dev, TX_RETRY_COUNT_REG, &ieee->softmac_stats.txretrycount); } diff --git a/drivers/staging/rtl8192u/r8192U_dm.h b/drivers/staging/rtl8192u/r8192U_dm.h index 0b2a1c688597..2159018b4e38 100644 --- a/drivers/staging/rtl8192u/r8192U_dm.h +++ b/drivers/staging/rtl8192u/r8192U_dm.h @@ -166,7 +166,7 @@ void dm_force_tx_fw_info(struct net_device *dev, void dm_init_edca_turbo(struct net_device *dev); void dm_rf_operation_test_callback(unsigned long data); void dm_rf_pathcheck_workitemcallback(struct work_struct *work); -void dm_fsync_timer_callback(struct timer_list *t); +void dm_fsync_work_callback(struct work_struct *work); void dm_cck_txpower_adjust(struct net_device *dev, bool binch14); void dm_shadow_init(struct net_device *dev); void dm_initialize_txpower_tracking(struct net_device *dev); diff --git a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c index 1bdbd0971f73..f878b04076d8 100644 --- a/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c +++ b/drivers/staging/rtl8723bs/core/rtw_mlme_ext.c @@ -960,7 +960,7 @@ unsigned int OnAssocReq(struct adapter *padapter, union recv_frame *precv_frame) return _FAIL; frame_type = GetFrameSubType(pframe); - if (frame_type == WIFI_ASSOCREQ) + if (frame_type == WIFI_ASSOCREQ) ie_offset = _ASOCREQ_IE_OFFSET_; else /* WIFI_REASSOCREQ */ ie_offset = _REASOCREQ_IE_OFFSET_; diff --git a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c index cf35125b7891..cb6d287f580d 100644 --- a/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c +++ b/drivers/staging/rtl8723bs/os_dep/ioctl_cfg80211.c @@ -366,9 +366,8 @@ void rtw_cfg80211_ibss_indicate_connect(struct adapter *padapter) int freq = (int)cur_network->network.configuration.ds_config; struct ieee80211_channel *chan; - if (pwdev->iftype != NL80211_IFTYPE_ADHOC) { + if (pwdev->iftype != NL80211_IFTYPE_ADHOC) return; - } if (!rtw_cfg80211_check_bss(padapter)) { struct wlan_bssid_ex *pnetwork = &(padapter->mlmeextpriv.mlmext_info.network); @@ -544,9 +543,8 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa goto exit; } - if (wep_key_len > 0) { + if (wep_key_len > 0) wep_key_len = wep_key_len <= 5 ? 5 : 13; - } if (psecuritypriv->bWepDefaultKeyIdxSet == 0) { /* wep default key has not been set, so use this key index as default key. */ @@ -582,9 +580,8 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); psecuritypriv->dot118021XGrpPrivacy = _WEP40_; - if (param->u.crypt.key_len == 13) { + if (param->u.crypt.key_len == 13) psecuritypriv->dot118021XGrpPrivacy = _WEP104_; - } } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { psecuritypriv->dot118021XGrpPrivacy = _TKIP_; @@ -626,24 +623,16 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa } - if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) /* psk/802_1x */ - { - if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) - { - if (param->u.crypt.set_tx == 1) /* pairwise key */ - { + if (psecuritypriv->dot11AuthAlgrthm == dot11AuthAlgrthm_8021X && psta) { /* psk/802_1x */ + if (check_fwstate(pmlmepriv, WIFI_AP_STATE)) { + if (param->u.crypt.set_tx == 1) { /* pairwise key */ memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - if (strcmp(param->u.crypt.alg, "WEP") == 0) - { + if (strcmp(param->u.crypt.alg, "WEP") == 0) { psta->dot118021XPrivacy = _WEP40_; if (param->u.crypt.key_len == 13) - { psta->dot118021XPrivacy = _WEP104_; - } - } - else if (strcmp(param->u.crypt.alg, "TKIP") == 0) - { + } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { psta->dot118021XPrivacy = _TKIP_; /* DEBUG_ERR("set key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len); */ @@ -653,14 +642,10 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa psecuritypriv->busetkipkey = true; - } - else if (strcmp(param->u.crypt.alg, "CCMP") == 0) - { + } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { psta->dot118021XPrivacy = _AES_; - } - else - { + } else { psta->dot118021XPrivacy = _NO_PRIVACY_; } @@ -670,21 +655,14 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa psta->bpairwise_key_installed = true; - } - else/* group key??? */ - { - if (strcmp(param->u.crypt.alg, "WEP") == 0) - { + } else { /* group key??? */ + if (strcmp(param->u.crypt.alg, "WEP") == 0) { memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); psecuritypriv->dot118021XGrpPrivacy = _WEP40_; if (param->u.crypt.key_len == 13) - { psecuritypriv->dot118021XGrpPrivacy = _WEP104_; - } - } - else if (strcmp(param->u.crypt.alg, "TKIP") == 0) - { + } else if (strcmp(param->u.crypt.alg, "TKIP") == 0) { psecuritypriv->dot118021XGrpPrivacy = _TKIP_; memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); @@ -696,15 +674,11 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa psecuritypriv->busetkipkey = true; - } - else if (strcmp(param->u.crypt.alg, "CCMP") == 0) - { + } else if (strcmp(param->u.crypt.alg, "CCMP") == 0) { psecuritypriv->dot118021XGrpPrivacy = _AES_; memcpy(grpkey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - } - else - { + } else { psecuritypriv->dot118021XGrpPrivacy = _NO_PRIVACY_; } @@ -717,8 +691,7 @@ static int rtw_cfg80211_ap_set_encryption(struct net_device *dev, struct ieee_pa rtw_ap_set_group_key(padapter, param->u.crypt.key, psecuritypriv->dot118021XGrpPrivacy, param->u.crypt.idx); pbcmc_sta = rtw_get_bcmc_stainfo(padapter); - if (pbcmc_sta) - { + if (pbcmc_sta) { pbcmc_sta->ieee8021x_blocked = false; pbcmc_sta->dot118021XPrivacy = psecuritypriv->dot118021XGrpPrivacy;/* rx will use bmc_sta's dot118021XPrivacy */ } @@ -746,20 +719,16 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param param->u.crypt.err = 0; param->u.crypt.alg[IEEE_CRYPT_ALG_NAME_LEN - 1] = '\0'; - if (param_len < (u32) ((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) - { + if (param_len < (u32) ((u8 *) param->u.crypt.key - (u8 *) param) + param->u.crypt.key_len) { ret = -EINVAL; goto exit; } if (param->sta_addr[0] == 0xff && param->sta_addr[1] == 0xff && param->sta_addr[2] == 0xff && param->sta_addr[3] == 0xff && - param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) - { + param->sta_addr[4] == 0xff && param->sta_addr[5] == 0xff) { if (param->u.crypt.idx >= WEP_KEYS - || param->u.crypt.idx >= BIP_MAX_KEYID - ) - { + || param->u.crypt.idx >= BIP_MAX_KEYID) { ret = -EINVAL; goto exit; } @@ -770,19 +739,16 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param } } - if (strcmp(param->u.crypt.alg, "WEP") == 0) - { + if (strcmp(param->u.crypt.alg, "WEP") == 0) { wep_key_idx = param->u.crypt.idx; wep_key_len = param->u.crypt.key_len; - if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) - { + if ((wep_key_idx >= WEP_KEYS) || (wep_key_len <= 0)) { ret = -EINVAL; goto exit; } - if (psecuritypriv->bWepDefaultKeyIdxSet == 0) - { + if (psecuritypriv->bWepDefaultKeyIdxSet == 0) { /* wep default key has not been set, so use this key index as default key. */ wep_key_len = wep_key_len <= 5 ? 5 : 13; @@ -791,8 +757,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param psecuritypriv->dot11PrivacyAlgrthm = _WEP40_; psecuritypriv->dot118021XGrpPrivacy = _WEP40_; - if (wep_key_len == 13) - { + if (wep_key_len == 13) { psecuritypriv->dot11PrivacyAlgrthm = _WEP104_; psecuritypriv->dot118021XGrpPrivacy = _WEP104_; } @@ -809,13 +774,11 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param goto exit; } - if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) /* 802_1x */ - { + if (padapter->securitypriv.dot11AuthAlgrthm == dot11AuthAlgrthm_8021X) { /* 802_1x */ struct sta_info *psta, *pbcmc_sta; struct sta_priv *pstapriv = &padapter->stapriv; - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE) == true) /* sta mode */ - { + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE | WIFI_MP_STATE) == true) { /* sta mode */ psta = rtw_get_stainfo(pstapriv, get_bssid(pmlmepriv)); if (psta) { /* Jeff: don't disable ieee8021x_blocked while clearing key */ @@ -824,18 +787,15 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || - (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) - { + (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) { psta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; } - if (param->u.crypt.set_tx == 1)/* pairwise key */ - { + if (param->u.crypt.set_tx == 1) { /* pairwise key */ memcpy(psta->dot118021x_UncstKey.skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); - if (strcmp(param->u.crypt.alg, "TKIP") == 0)/* set mic key */ - { + if (strcmp(param->u.crypt.alg, "TKIP") == 0) { /* set mic key */ /* DEBUG_ERR(("\nset key length :param->u.crypt.key_len =%d\n", param->u.crypt.key_len)); */ memcpy(psta->dot11tkiptxmickey.skey, &(param->u.crypt.key[16]), 8); memcpy(psta->dot11tkiprxmickey.skey, &(param->u.crypt.key[24]), 8); @@ -845,11 +805,8 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param } rtw_setstakey_cmd(padapter, psta, true, true); - } - else/* group key */ - { - if (strcmp(param->u.crypt.alg, "TKIP") == 0 || strcmp(param->u.crypt.alg, "CCMP") == 0) - { + } else { /* group key */ + if (strcmp(param->u.crypt.alg, "TKIP") == 0 || strcmp(param->u.crypt.alg, "CCMP") == 0) { memcpy(padapter->securitypriv.dot118021XGrpKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); memcpy(padapter->securitypriv.dot118021XGrptxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[16]), 8); memcpy(padapter->securitypriv.dot118021XGrprxmickey[param->u.crypt.idx].skey, &(param->u.crypt.key[24]), 8); @@ -857,9 +814,7 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param padapter->securitypriv.dot118021XGrpKeyid = param->u.crypt.idx; rtw_set_key(padapter, &padapter->securitypriv, param->u.crypt.idx, 1, true); - } - else if (strcmp(param->u.crypt.alg, "BIP") == 0) - { + } else if (strcmp(param->u.crypt.alg, "BIP") == 0) { /* save the IGTK key, length 16 bytes */ memcpy(padapter->securitypriv.dot11wBIPKey[param->u.crypt.idx].skey, param->u.crypt.key, (param->u.crypt.key_len > 16 ? 16 : param->u.crypt.key_len)); /* @@ -873,25 +828,19 @@ static int rtw_cfg80211_set_encryption(struct net_device *dev, struct ieee_param } pbcmc_sta = rtw_get_bcmc_stainfo(padapter); - if (!pbcmc_sta) - { + if (!pbcmc_sta) { /* DEBUG_ERR(("Set OID_802_11_ADD_KEY: bcmc stainfo is null\n")); */ - } - else - { + } else { /* Jeff: don't disable ieee8021x_blocked while clearing key */ if (strcmp(param->u.crypt.alg, "none") != 0) pbcmc_sta->ieee8021x_blocked = false; if ((padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption2Enabled) || - (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) - { + (padapter->securitypriv.ndisencryptstatus == Ndis802_11Encryption3Enabled)) { pbcmc_sta->dot118021XPrivacy = padapter->securitypriv.dot11PrivacyAlgrthm; } } - } - else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) /* adhoc mode */ - { + } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE)) { /* adhoc mode */ } } @@ -949,39 +898,29 @@ static int cfg80211_rtw_add_key(struct wiphy *wiphy, struct net_device *ndev, if (!mac_addr || is_broadcast_ether_addr(mac_addr)) - { param->u.crypt.set_tx = 0; /* for wpa/wpa2 group key */ - } else { + else param->u.crypt.set_tx = 1; /* for wpa/wpa2 pairwise key */ - } param->u.crypt.idx = key_index; if (params->seq_len && params->seq) - { memcpy(param->u.crypt.seq, (u8 *)params->seq, params->seq_len); - } - if (params->key_len && params->key) - { + if (params->key_len && params->key) { param->u.crypt.key_len = params->key_len; memcpy(param->u.crypt.key, (u8 *)params->key, params->key_len); } - if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) - { + if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) == true) { ret = rtw_cfg80211_set_encryption(ndev, param, param_len); - } - else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) - { + } else if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { if (mac_addr) memcpy(param->sta_addr, (void *)mac_addr, ETH_ALEN); ret = rtw_cfg80211_ap_set_encryption(ndev, param, param_len); - } - else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true - || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) - { + } else if (check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) == true + || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) == true) { ret = rtw_cfg80211_set_encryption(ndev, param, param_len); } @@ -1007,8 +946,7 @@ static int cfg80211_rtw_del_key(struct wiphy *wiphy, struct net_device *ndev, struct adapter *padapter = rtw_netdev_priv(ndev); struct security_priv *psecuritypriv = &padapter->securitypriv; - if (key_index == psecuritypriv->dot11PrivacyKeyIndex) - { + if (key_index == psecuritypriv->dot11PrivacyKeyIndex) { /* clear the flag of wep default key set. */ psecuritypriv->bWepDefaultKeyIdxSet = 0; } @@ -1024,16 +962,14 @@ static int cfg80211_rtw_set_default_key(struct wiphy *wiphy, struct adapter *padapter = rtw_netdev_priv(ndev); struct security_priv *psecuritypriv = &padapter->securitypriv; - if ((key_index < WEP_KEYS) && ((psecuritypriv->dot11PrivacyAlgrthm == _WEP40_) || (psecuritypriv->dot11PrivacyAlgrthm == _WEP104_))) /* set wep default key */ - { + if ((key_index < WEP_KEYS) && ((psecuritypriv->dot11PrivacyAlgrthm == _WEP40_) || (psecuritypriv->dot11PrivacyAlgrthm == _WEP104_))) { /* set wep default key */ psecuritypriv->ndisencryptstatus = Ndis802_11Encryption1Enabled; psecuritypriv->dot11PrivacyKeyIndex = key_index; psecuritypriv->dot11PrivacyAlgrthm = _WEP40_; psecuritypriv->dot118021XGrpPrivacy = _WEP40_; - if (psecuritypriv->dot11DefKeylen[key_index] == 13) - { + if (psecuritypriv->dot11DefKeylen[key_index] == 13) { psecuritypriv->dot11PrivacyAlgrthm = _WEP104_; psecuritypriv->dot118021XGrpPrivacy = _WEP104_; } @@ -1071,9 +1007,7 @@ static int cfg80211_rtw_get_station(struct wiphy *wiphy, /* for infra./P2PClient mode */ if (check_fwstate(pmlmepriv, WIFI_STATION_STATE) - && check_fwstate(pmlmepriv, _FW_LINKED) - ) - { + && check_fwstate(pmlmepriv, _FW_LINKED)) { struct wlan_network *cur_network = &(pmlmepriv->cur_network); if (memcmp((u8 *)mac, cur_network->network.mac_address, ETH_ALEN)) { @@ -1099,9 +1033,7 @@ static int cfg80211_rtw_get_station(struct wiphy *wiphy, if ((check_fwstate(pmlmepriv, WIFI_ADHOC_STATE) || check_fwstate(pmlmepriv, WIFI_ADHOC_MASTER_STATE) || check_fwstate(pmlmepriv, WIFI_AP_STATE)) - && check_fwstate(pmlmepriv, _FW_LINKED) - ) - { + && check_fwstate(pmlmepriv, _FW_LINKED)) { /* TODO: should acquire station info... */ } @@ -1121,8 +1053,7 @@ static int cfg80211_rtw_change_iface(struct wiphy *wiphy, struct mlme_ext_priv *pmlmeext = &(padapter->mlmeextpriv); int ret = 0; - if (adapter_to_dvobj(padapter)->processing_dev_remove == true) - { + if (adapter_to_dvobj(padapter)->processing_dev_remove == true) { ret = -EPERM; goto exit; } @@ -1141,8 +1072,7 @@ static int cfg80211_rtw_change_iface(struct wiphy *wiphy, old_type = rtw_wdev->iftype; - if (old_type != type) - { + if (old_type != type) { pmlmeext->action_public_rxseq = 0xffff; pmlmeext->action_public_dialog_token = 0xff; } @@ -1164,8 +1094,7 @@ static int cfg80211_rtw_change_iface(struct wiphy *wiphy, rtw_wdev->iftype = type; - if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) - { + if (rtw_set_802_11_infrastructure_mode(padapter, networkType) == false) { rtw_wdev->iftype = old_type; ret = -EPERM; goto exit; @@ -1230,9 +1159,7 @@ void rtw_cfg80211_surveydone_event_callback(struct adapter *padapter) /* report network only if the current channel set contains the channel to which this network belongs */ if (rtw_ch_set_search_ch(padapter->mlmeextpriv.channel_set, pnetwork->network.configuration.ds_config) >= 0 - && true == rtw_validate_ssid(&(pnetwork->network.ssid)) - ) - { + && true == rtw_validate_ssid(&(pnetwork->network.ssid))) { /* ev =translate_scan(padapter, a, pnetwork, ev, stop); */ rtw_cfg80211_inform_bss(padapter, pnetwork); } @@ -1249,13 +1176,10 @@ static int rtw_cfg80211_set_probe_req_wpsp2pie(struct adapter *padapter, char *b u8 *wps_ie; struct mlme_priv *pmlmepriv = &(padapter->mlmepriv); - if (len > 0) - { + if (len > 0) { wps_ie = rtw_get_wps_ie(buf, len, NULL, &wps_ielen); - if (wps_ie) - { - if (pmlmepriv->wps_probe_req_ie) - { + if (wps_ie) { + if (pmlmepriv->wps_probe_req_ie) { pmlmepriv->wps_probe_req_ie_len = 0; kfree(pmlmepriv->wps_probe_req_ie); pmlmepriv->wps_probe_req_ie = NULL; @@ -1307,10 +1231,8 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy pwdev_priv->scan_request = request; spin_unlock_bh(&pwdev_priv->scan_req_lock); - if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) - { - if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS|_FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) - { + if (check_fwstate(pmlmepriv, WIFI_AP_STATE) == true) { + if (check_fwstate(pmlmepriv, WIFI_UNDER_WPS|_FW_UNDER_SURVEY|_FW_UNDER_LINKING) == true) { need_indicate_scan_done = true; goto check_need_indicate_scan_done; } @@ -1333,15 +1255,13 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy goto check_need_indicate_scan_done; } - if (pmlmepriv->LinkDetectInfo.bBusyTraffic == true) - { - static unsigned long lastscantime = 0; + if (pmlmepriv->LinkDetectInfo.bBusyTraffic == true) { + static unsigned long lastscantime; unsigned long passtime; passtime = jiffies_to_msecs(jiffies - lastscantime); lastscantime = jiffies; - if (passtime > 12000) - { + if (passtime > 12000) { need_indicate_scan_done = true; goto check_need_indicate_scan_done; } @@ -1380,9 +1300,7 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy } else if (request->n_channels <= 4) { for (j = request->n_channels - 1; j >= 0; j--) for (i = 0; i < survey_times; i++) - { memcpy(&ch[j*survey_times+i], &ch[j], sizeof(struct rtw_ieee80211_channel)); - } _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, ch, survey_times * request->n_channels); } else { _status = rtw_sitesurvey_cmd(padapter, ssid, RTW_SSID_SCAN_AMOUNT, NULL, 0); @@ -1391,14 +1309,11 @@ static int cfg80211_rtw_scan(struct wiphy *wiphy if (_status == false) - { ret = -1; - } check_need_indicate_scan_done: kfree(ssid); - if (need_indicate_scan_done) - { + if (need_indicate_scan_done) { rtw_cfg80211_surveydone_event_callback(padapter); rtw_cfg80211_indicate_scan_done(padapter, false); } @@ -1424,9 +1339,7 @@ static int rtw_cfg80211_set_wpa_version(struct security_priv *psecuritypriv, u32 if (wpa_version & (NL80211_WPA_VERSION_1 | NL80211_WPA_VERSION_2)) - { psecuritypriv->ndisauthtype = Ndis802_11AuthModeWPAPSK; - } return 0; @@ -1585,8 +1498,7 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel if (pairwise_cipher == 0) pairwise_cipher = WPA_CIPHER_NONE; - switch (group_cipher) - { + switch (group_cipher) { case WPA_CIPHER_NONE: padapter->securitypriv.dot118021XGrpPrivacy = _NO_PRIVACY_; padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; @@ -1609,8 +1521,7 @@ static int rtw_cfg80211_set_wpa_ie(struct adapter *padapter, u8 *pie, size_t iel break; } - switch (pairwise_cipher) - { + switch (pairwise_cipher) { case WPA_CIPHER_NONE: padapter->securitypriv.dot11PrivacyAlgrthm = _NO_PRIVACY_; padapter->securitypriv.ndisencryptstatus = Ndis802_11EncryptionDisabled; @@ -1731,8 +1642,7 @@ static int cfg80211_rtw_leave_ibss(struct wiphy *wiphy, struct net_device *ndev) rtw_wdev->iftype = NL80211_IFTYPE_STATION; - if (rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure) == false) - { + if (rtw_set_802_11_infrastructure_mode(padapter, Ndis802_11Infrastructure) == false) { rtw_wdev->iftype = old_type; ret = -EPERM; goto leave_ibss; @@ -1792,9 +1702,8 @@ static int cfg80211_rtw_connect(struct wiphy *wiphy, struct net_device *ndev, ret = -EBUSY; goto exit; } - if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) { + if (check_fwstate(pmlmepriv, _FW_UNDER_SURVEY) == true) rtw_scan_abort(padapter); - } psecuritypriv->ndisencryptstatus = Ndis802_11EncryptionDisabled; psecuritypriv->dot11PrivacyAlgrthm = _NO_PRIVACY_; @@ -2288,9 +2197,8 @@ static int rtw_cfg80211_add_monitor_if(struct adapter *padapter, char *name, str mon_ndev->ieee80211_ptr = mon_wdev; ret = cfg80211_register_netdevice(mon_ndev); - if (ret) { + if (ret) goto out; - } *ndev = pwdev_priv->pmon_ndev = mon_ndev; memcpy(pwdev_priv->ifname_mon, name, IFNAMSIZ+1); @@ -2403,11 +2311,10 @@ static int rtw_add_beacon(struct adapter *adapter, const u8 *head, size_t head_l rtw_ies_remove_ie(pbuf, &len, _BEACON_IE_OFFSET_, WLAN_EID_VENDOR_SPECIFIC, P2P_OUI, 4); rtw_ies_remove_ie(pbuf, &len, _BEACON_IE_OFFSET_, WLAN_EID_VENDOR_SPECIFIC, WFD_OUI, 4); - if (rtw_check_beacon_data(adapter, pbuf, len) == _SUCCESS) { + if (rtw_check_beacon_data(adapter, pbuf, len) == _SUCCESS) ret = 0; - } else { + else ret = -EINVAL; - } kfree(pbuf); diff --git a/drivers/staging/rts5208/spi.c b/drivers/staging/rts5208/spi.c index f1e9e80044ed..e88fe1a998f8 100644 --- a/drivers/staging/rts5208/spi.c +++ b/drivers/staging/rts5208/spi.c @@ -460,10 +460,8 @@ int spi_set_parameter(struct scsi_cmnd *srb, struct rtsx_chip *chip) spi->clk_div = ((u16)(srb->cmnd[4]) << 8) | srb->cmnd[5]; spi->write_en = srb->cmnd[6]; - dev_dbg(rtsx_dev(chip), "%s: ", __func__); - dev_dbg(rtsx_dev(chip), "spi_clock = %d, ", spi->spi_clock); - dev_dbg(rtsx_dev(chip), "clk_div = %d, ", spi->clk_div); - dev_dbg(rtsx_dev(chip), "write_en = %d\n", spi->write_en); + dev_dbg(rtsx_dev(chip), "spi_clock = %d, clk_div = %d, write_en = %d\n", + spi->spi_clock, spi->clk_div, spi->write_en); return STATUS_SUCCESS; } diff --git a/drivers/staging/sm750fb/ddk750_dvi.c b/drivers/staging/sm750fb/ddk750_dvi.c index 029d9acec47d..e0c7ff3352bf 100644 --- a/drivers/staging/sm750fb/ddk750_dvi.c +++ b/drivers/staging/sm750fb/ddk750_dvi.c @@ -15,7 +15,7 @@ static struct dvi_ctrl_device dcft_supported_dvi_controller[] = { #ifdef DVI_CTRL_SII164 { .init = sii164InitChip, - .get_vendor_id = sii164GetVendorID, + .get_vendor_id = sii164_get_vendor_id, .get_device_id = sii164GetDeviceID, #ifdef SII164_FULL_FUNCTIONS .reset_chip = sii164ResetChip, diff --git a/drivers/staging/sm750fb/ddk750_power.h b/drivers/staging/sm750fb/ddk750_power.h index 7002567a47d2..63c9e8b6ffb3 100644 --- a/drivers/staging/sm750fb/ddk750_power.h +++ b/drivers/staging/sm750fb/ddk750_power.h @@ -15,7 +15,7 @@ enum dpms { } void ddk750_set_dpms(enum dpms state); -void sm750_set_power_mode(unsigned int powerMode); +void sm750_set_power_mode(unsigned int mode); void sm750_set_current_gate(unsigned int gate); /* diff --git a/drivers/staging/sm750fb/ddk750_sii164.c b/drivers/staging/sm750fb/ddk750_sii164.c index 73e0e9f41ec5..3da1796cd7aa 100644 --- a/drivers/staging/sm750fb/ddk750_sii164.c +++ b/drivers/staging/sm750fb/ddk750_sii164.c @@ -29,13 +29,13 @@ static char *gDviCtrlChipName = "Silicon Image SiI 164"; #endif /* - * sii164GetVendorID + * sii164_get_vendor_id * This function gets the vendor ID of the DVI controller chip. * * Output: * Vendor ID */ -unsigned short sii164GetVendorID(void) +unsigned short sii164_get_vendor_id(void) { unsigned short vendorID; @@ -140,7 +140,7 @@ long sii164InitChip(unsigned char edge_select, #endif /* Check if SII164 Chip exists */ - if ((sii164GetVendorID() == SII164_VENDOR_ID) && + if ((sii164_get_vendor_id() == SII164_VENDOR_ID) && (sii164GetDeviceID() == SII164_DEVICE_ID)) { /* * Initialize SII164 controller chip. diff --git a/drivers/staging/sm750fb/ddk750_sii164.h b/drivers/staging/sm750fb/ddk750_sii164.h index d940cb729066..ca330f6a43e2 100644 --- a/drivers/staging/sm750fb/ddk750_sii164.h +++ b/drivers/staging/sm750fb/ddk750_sii164.h @@ -27,7 +27,7 @@ long sii164InitChip(unsigned char edgeSelect, unsigned char pllFilterEnable, unsigned char pllFilterValue); -unsigned short sii164GetVendorID(void); +unsigned short sii164_get_vendor_id(void); unsigned short sii164GetDeviceID(void); #ifdef SII164_FULL_FUNCTIONS diff --git a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c index e429b33b4d39..f4c2c9506d86 100644 --- a/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c +++ b/drivers/staging/vc04_services/bcm2835-audio/bcm2835-vchiq.c @@ -25,12 +25,14 @@ MODULE_PARM_DESC(force_bulk, "Force use of vchiq bulk for audio"); static void bcm2835_audio_lock(struct bcm2835_audio_instance *instance) { mutex_lock(&instance->vchi_mutex); - vchiq_use_service(instance->service_handle); + vchiq_use_service(instance->alsa_stream->chip->vchi_ctx->instance, + instance->service_handle); } static void bcm2835_audio_unlock(struct bcm2835_audio_instance *instance) { - vchiq_release_service(instance->service_handle); + vchiq_release_service(instance->alsa_stream->chip->vchi_ctx->instance, + instance->service_handle); mutex_unlock(&instance->vchi_mutex); } @@ -44,8 +46,8 @@ static int bcm2835_audio_send_msg_locked(struct bcm2835_audio_instance *instance init_completion(&instance->msg_avail_comp); } - status = vchiq_queue_kernel_message(instance->service_handle, - m, sizeof(*m)); + status = vchiq_queue_kernel_message(instance->alsa_stream->chip->vchi_ctx->instance, + instance->service_handle, m, sizeof(*m)); if (status) { dev_err(instance->dev, "vchi message queue failed: %d, msg=%d\n", @@ -89,11 +91,13 @@ static int bcm2835_audio_send_simple(struct bcm2835_audio_instance *instance, return bcm2835_audio_send_msg(instance, &m, wait); } -static enum vchiq_status audio_vchi_callback(enum vchiq_reason reason, +static enum vchiq_status audio_vchi_callback(struct vchiq_instance *vchiq_instance, + enum vchiq_reason reason, struct vchiq_header *header, unsigned int handle, void *userdata) { - struct bcm2835_audio_instance *instance = vchiq_get_service_userdata(handle); + struct bcm2835_audio_instance *instance = vchiq_get_service_userdata(vchiq_instance, + handle); struct vc_audio_msg *m; if (reason != VCHIQ_MESSAGE_AVAILABLE) @@ -114,7 +118,7 @@ static enum vchiq_status audio_vchi_callback(enum vchiq_reason reason, dev_err(instance->dev, "unexpected callback type=%d\n", m->type); } - vchiq_release_message(handle, header); + vchiq_release_message(vchiq_instance, instance->service_handle, header); return VCHIQ_SUCCESS; } @@ -143,7 +147,8 @@ vc_vchi_audio_init(struct vchiq_instance *vchiq_instance, } /* Finished with the service for now */ - vchiq_release_service(instance->service_handle); + vchiq_release_service(instance->alsa_stream->chip->vchi_ctx->instance, + instance->service_handle); return 0; } @@ -153,10 +158,12 @@ static void vc_vchi_audio_deinit(struct bcm2835_audio_instance *instance) int status; mutex_lock(&instance->vchi_mutex); - vchiq_use_service(instance->service_handle); + vchiq_use_service(instance->alsa_stream->chip->vchi_ctx->instance, + instance->service_handle); /* Close all VCHI service connections */ - status = vchiq_close_service(instance->service_handle); + status = vchiq_close_service(instance->alsa_stream->chip->vchi_ctx->instance, + instance->service_handle); if (status) { dev_err(instance->dev, "failed to close VCHI service connection (status=%d)\n", @@ -226,7 +233,7 @@ int bcm2835_audio_open(struct bcm2835_alsa_stream *alsa_stream) goto deinit; bcm2835_audio_lock(instance); - vchiq_get_peer_version(instance->service_handle, + vchiq_get_peer_version(vchi_ctx->instance, instance->service_handle, &instance->peer_version); bcm2835_audio_unlock(instance); if (instance->peer_version < 2 || force_bulk) @@ -322,6 +329,8 @@ int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, unsigned int size, void *src) { struct bcm2835_audio_instance *instance = alsa_stream->instance; + struct bcm2835_vchi_ctx *vchi_ctx = alsa_stream->chip->vchi_ctx; + struct vchiq_instance *vchiq_instance = vchi_ctx->instance; struct vc_audio_msg m = { .type = VC_AUDIO_MSG_TYPE_WRITE, .write.count = size, @@ -343,15 +352,14 @@ int bcm2835_audio_write(struct bcm2835_alsa_stream *alsa_stream, count = size; if (!instance->max_packet) { /* Send the message to the videocore */ - status = vchiq_bulk_transmit(instance->service_handle, src, - count, NULL, - VCHIQ_BULK_MODE_BLOCKING); + status = vchiq_bulk_transmit(vchiq_instance, instance->service_handle, src, count, + NULL, VCHIQ_BULK_MODE_BLOCKING); } else { while (count > 0) { int bytes = min(instance->max_packet, count); - status = vchiq_queue_kernel_message(instance->service_handle, - src, bytes); + status = vchiq_queue_kernel_message(vchiq_instance, + instance->service_handle, src, bytes); src += bytes; count -= bytes; } diff --git a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h index c93f2f3e87bb..db1441c0cc66 100644 --- a/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h +++ b/drivers/staging/vc04_services/include/linux/raspberrypi/vchiq.h @@ -53,9 +53,12 @@ struct vchiq_element { unsigned int size; }; +struct vchiq_instance; + struct vchiq_service_base { int fourcc; - enum vchiq_status (*callback)(enum vchiq_reason reason, + enum vchiq_status (*callback)(struct vchiq_instance *instance, + enum vchiq_reason reason, struct vchiq_header *header, unsigned int handle, void *bulk_userdata); @@ -71,7 +74,8 @@ struct vchiq_completion_data_kernel { struct vchiq_service_params_kernel { int fourcc; - enum vchiq_status (*callback)(enum vchiq_reason reason, + enum vchiq_status (*callback)(struct vchiq_instance *instance, + enum vchiq_reason reason, struct vchiq_header *header, unsigned int handle, void *bulk_userdata); @@ -88,23 +92,27 @@ extern enum vchiq_status vchiq_connect(struct vchiq_instance *instance); extern enum vchiq_status vchiq_open_service(struct vchiq_instance *instance, const struct vchiq_service_params_kernel *params, unsigned int *pservice); -extern enum vchiq_status vchiq_close_service(unsigned int service); -extern enum vchiq_status vchiq_use_service(unsigned int service); -extern enum vchiq_status vchiq_release_service(unsigned int service); -extern void vchiq_msg_queue_push(unsigned int handle, struct vchiq_header *header); -extern void vchiq_release_message(unsigned int service, - struct vchiq_header *header); -extern int vchiq_queue_kernel_message(unsigned int handle, void *data, - unsigned int size); -extern enum vchiq_status vchiq_bulk_transmit(unsigned int service, - const void *data, unsigned int size, void *userdata, - enum vchiq_bulk_mode mode); -extern enum vchiq_status vchiq_bulk_receive(unsigned int service, - void *data, unsigned int size, void *userdata, - enum vchiq_bulk_mode mode); -extern void *vchiq_get_service_userdata(unsigned int service); -extern enum vchiq_status vchiq_get_peer_version(unsigned int handle, - short *peer_version); -extern struct vchiq_header *vchiq_msg_hold(unsigned int handle); +extern enum vchiq_status vchiq_close_service(struct vchiq_instance *instance, + unsigned int service); +extern enum vchiq_status vchiq_use_service(struct vchiq_instance *instance, unsigned int service); +extern enum vchiq_status vchiq_release_service(struct vchiq_instance *instance, + unsigned int service); +extern void vchiq_msg_queue_push(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_header *header); +extern void vchiq_release_message(struct vchiq_instance *instance, unsigned int service, + struct vchiq_header *header); +extern int vchiq_queue_kernel_message(struct vchiq_instance *instance, unsigned int handle, + void *data, unsigned int size); +extern enum vchiq_status vchiq_bulk_transmit(struct vchiq_instance *instance, unsigned int service, + const void *data, unsigned int size, void *userdata, + enum vchiq_bulk_mode mode); +extern enum vchiq_status vchiq_bulk_receive(struct vchiq_instance *instance, unsigned int service, + void *data, unsigned int size, void *userdata, + enum vchiq_bulk_mode mode); +extern void *vchiq_get_service_userdata(struct vchiq_instance *instance, unsigned int service); +extern enum vchiq_status vchiq_get_peer_version(struct vchiq_instance *instance, + unsigned int handle, + short *peer_version); +extern struct vchiq_header *vchiq_msg_hold(struct vchiq_instance *instance, unsigned int handle); #endif /* VCHIQ_H */ diff --git a/drivers/staging/vc04_services/interface/TESTING b/drivers/staging/vc04_services/interface/TESTING new file mode 100644 index 000000000000..a6d63efcbcb9 --- /dev/null +++ b/drivers/staging/vc04_services/interface/TESTING @@ -0,0 +1,82 @@ +This document contains some hints to test the function of the VCHIQ driver +without having additional hardware to the Raspberry Pi. + +* Requirements & limitations + +Testing the VCHIQ driver requires a Raspberry Pi with one of the following SoC: + - BCM2835 ( e.g. Raspberry Pi Zero W ) + - BCM2836 ( e.g. Raspberry Pi 2 ) + - BCM2837 ( e.g. Raspberry Pi 3 B+ ) + +The BCM2711 used in the Raspberry Pi 4 is currently not supported in the +mainline kernel. + +There are no specific requirements to the VideoCore firmware to get VCHIQ +working. + +The test scenarios described in this document based on the tool vchiq_test. +Its source code is available here: https://github.com/raspberrypi/userland + +* Configuration + +Here are the most common kernel configurations: + + 1. BCM2835 target SoC (ARM 32 bit) + + Just use bcm2835_defconfig which already has VCHIQ enabled. + + 2. BCM2836/7 target SoC (ARM 32 bit) + + Use the multi_v7_defconfig as a base and then enable all VCHIQ options. + + 3. BCM2837 target SoC (ARM 64 bit) + + Use the defconfig as a base and then enable all VCHIQ options. + +* Scenarios + + * Initial test + + Check the driver is probed and /dev/vchiq is created + + * Functional test + + Command: vchiq_test -f 10 + + Expected output: + Functional test - iters:10 + ======== iteration 1 ======== + Testing bulk transfer for alignment. + Testing bulk transfer at PAGE_SIZE. + ... + + * Ping test + + Command: vchiq_test -p 1 + + Expected output: + Ping test - service:echo, iters:1, version 3 + vchi ping (size 0) -> 57.000000us + vchi ping (size 0, 0 async, 0 oneway) -> 122.000000us + vchi bulk (size 0, 0 async, 0 oneway) -> 546.000000us + vchi bulk (size 0, 0 oneway) -> 230.000000us + vchi ping (size 0) -> 49.000000us + vchi ping (size 0, 0 async, 0 oneway) -> 70.000000us + vchi bulk (size 0, 0 async, 0 oneway) -> 296.000000us + vchi bulk (size 0, 0 oneway) -> 266.000000us + vchi ping (size 0, 1 async, 0 oneway) -> 65.000000us + vchi bulk (size 0, 0 oneway) -> 456.000000us + vchi ping (size 0, 2 async, 0 oneway) -> 74.000000us + vchi bulk (size 0, 0 oneway) -> 640.000000us + vchi ping (size 0, 10 async, 0 oneway) -> 125.000000us + vchi bulk (size 0, 0 oneway) -> 2309.000000us + vchi ping (size 0, 0 async, 1 oneway) -> 70.000000us + vchi ping (size 0, 0 async, 2 oneway) -> 76.000000us + vchi ping (size 0, 0 async, 10 oneway) -> 105.000000us + vchi ping (size 0, 10 async, 10 oneway) -> 165.000000us + vchi ping (size 0, 100 async, 0 oneway) -> nanus + vchi bulk (size 0, 0 oneway) -> nanus + vchi ping (size 0, 0 async, 100 oneway) -> nanus + vchi ping (size 0, 100 async, 100 oneway) -> infus + vchi ping (size 0, 200 async, 0 oneway) -> infus + ... diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c index 0596ac61e286..dc33490ba7fb 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.c @@ -148,12 +148,11 @@ static unsigned int g_fragments_size; static char *g_fragments_base; static char *g_free_fragments; static struct semaphore g_free_fragments_sema; -static struct device *g_dev; static DEFINE_SEMAPHORE(g_free_fragments_mutex); static enum vchiq_status -vchiq_blocking_bulk_transfer(unsigned int handle, void *data, +vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void *data, unsigned int size, enum vchiq_bulk_dir dir); static irqreturn_t @@ -175,17 +174,17 @@ vchiq_doorbell_irq(int irq, void *dev_id) } static void -cleanup_pagelistinfo(struct vchiq_pagelist_info *pagelistinfo) +cleanup_pagelistinfo(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo) { if (pagelistinfo->scatterlist_mapped) { - dma_unmap_sg(g_dev, pagelistinfo->scatterlist, + dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, pagelistinfo->num_pages, pagelistinfo->dma_dir); } if (pagelistinfo->pages_need_release) unpin_user_pages(pagelistinfo->pages, pagelistinfo->num_pages); - dma_free_coherent(g_dev, pagelistinfo->pagelist_buffer_size, + dma_free_coherent(instance->state->dev, pagelistinfo->pagelist_buffer_size, pagelistinfo->pagelist, pagelistinfo->dma_addr); } @@ -212,7 +211,7 @@ is_adjacent_block(u32 *addrs, u32 addr, unsigned int k) */ static struct vchiq_pagelist_info * -create_pagelist(char *buf, char __user *ubuf, +create_pagelist(struct vchiq_instance *instance, char *buf, char __user *ubuf, size_t count, unsigned short type) { struct pagelist *pagelist; @@ -250,7 +249,7 @@ create_pagelist(char *buf, char __user *ubuf, /* Allocate enough storage to hold the page pointers and the page * list */ - pagelist = dma_alloc_coherent(g_dev, pagelist_size, &dma_addr, + pagelist = dma_alloc_coherent(instance->state->dev, pagelist_size, &dma_addr, GFP_KERNEL); vchiq_log_trace(vchiq_arm_log_level, "%s - %pK", __func__, pagelist); @@ -292,7 +291,7 @@ create_pagelist(char *buf, char __user *ubuf, size_t bytes = PAGE_SIZE - off; if (!pg) { - cleanup_pagelistinfo(pagelistinfo); + cleanup_pagelistinfo(instance, pagelistinfo); return NULL; } @@ -315,7 +314,7 @@ create_pagelist(char *buf, char __user *ubuf, /* This is probably due to the process being killed */ if (actual_pages > 0) unpin_user_pages(pages, actual_pages); - cleanup_pagelistinfo(pagelistinfo); + cleanup_pagelistinfo(instance, pagelistinfo); return NULL; } /* release user pages */ @@ -338,13 +337,13 @@ create_pagelist(char *buf, char __user *ubuf, count -= len; } - dma_buffers = dma_map_sg(g_dev, + dma_buffers = dma_map_sg(instance->state->dev, scatterlist, num_pages, pagelistinfo->dma_dir); if (dma_buffers == 0) { - cleanup_pagelistinfo(pagelistinfo); + cleanup_pagelistinfo(instance, pagelistinfo); return NULL; } @@ -378,7 +377,7 @@ create_pagelist(char *buf, char __user *ubuf, char *fragments; if (down_interruptible(&g_free_fragments_sema)) { - cleanup_pagelistinfo(pagelistinfo); + cleanup_pagelistinfo(instance, pagelistinfo); return NULL; } @@ -397,7 +396,7 @@ create_pagelist(char *buf, char __user *ubuf, } static void -free_pagelist(struct vchiq_pagelist_info *pagelistinfo, +free_pagelist(struct vchiq_instance *instance, struct vchiq_pagelist_info *pagelistinfo, int actual) { struct pagelist *pagelist = pagelistinfo->pagelist; @@ -411,7 +410,7 @@ free_pagelist(struct vchiq_pagelist_info *pagelistinfo, * NOTE: dma_unmap_sg must be called before the * cpu can touch any of the data/pages. */ - dma_unmap_sg(g_dev, pagelistinfo->scatterlist, + dma_unmap_sg(instance->state->dev, pagelistinfo->scatterlist, pagelistinfo->num_pages, pagelistinfo->dma_dir); pagelistinfo->scatterlist_mapped = 0; @@ -460,7 +459,7 @@ free_pagelist(struct vchiq_pagelist_info *pagelistinfo, set_page_dirty(pages[i]); } - cleanup_pagelistinfo(pagelistinfo); + cleanup_pagelistinfo(instance, pagelistinfo); } int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) @@ -519,7 +518,7 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) *(char **)&g_fragments_base[i * g_fragments_size] = NULL; sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); - err = vchiq_init_state(state, vchiq_slot_zero); + err = vchiq_init_state(state, vchiq_slot_zero, dev); if (err) return err; @@ -547,7 +546,6 @@ int vchiq_platform_init(struct platform_device *pdev, struct vchiq_state *state) return err ? : -ENXIO; } - g_dev = dev; vchiq_log_info(vchiq_arm_log_level, "vchiq_init - done (slots %pK, phys %pad)", vchiq_slot_zero, &slot_phys); @@ -604,6 +602,10 @@ static struct vchiq_arm_state *vchiq_platform_get_arm_state(struct vchiq_state * void remote_event_signal(struct remote_event *event) { + /* + * Ensure that all writes to shared data structures have completed + * before signalling the peer. + */ wmb(); event->fired = 1; @@ -615,12 +617,12 @@ remote_event_signal(struct remote_event *event) } int -vchiq_prepare_bulk_data(struct vchiq_bulk *bulk, void *offset, +vchiq_prepare_bulk_data(struct vchiq_instance *instance, struct vchiq_bulk *bulk, void *offset, void __user *uoffset, int size, int dir) { struct vchiq_pagelist_info *pagelistinfo; - pagelistinfo = create_pagelist(offset, uoffset, size, + pagelistinfo = create_pagelist(instance, offset, uoffset, size, (dir == VCHIQ_BULK_RECEIVE) ? PAGELIST_READ : PAGELIST_WRITE); @@ -640,10 +642,10 @@ vchiq_prepare_bulk_data(struct vchiq_bulk *bulk, void *offset, } void -vchiq_complete_bulk(struct vchiq_bulk *bulk) +vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk) { if (bulk && bulk->remote_data && bulk->actual) - free_pagelist((struct vchiq_pagelist_info *)bulk->remote_data, + free_pagelist(instance, (struct vchiq_pagelist_info *)bulk->remote_data, bulk->actual); } @@ -821,7 +823,7 @@ vchiq_open_service(struct vchiq_instance *instance, *phandle = service->handle; status = vchiq_open_service_internal(service, current->pid); if (status != VCHIQ_SUCCESS) { - vchiq_remove_service(service->handle); + vchiq_remove_service(instance, service->handle); *phandle = VCHIQ_SERVICE_HANDLE_INVALID; } } @@ -834,8 +836,8 @@ failed: EXPORT_SYMBOL(vchiq_open_service); enum vchiq_status -vchiq_bulk_transmit(unsigned int handle, const void *data, unsigned int size, - void *userdata, enum vchiq_bulk_mode mode) +vchiq_bulk_transmit(struct vchiq_instance *instance, unsigned int handle, const void *data, + unsigned int size, void *userdata, enum vchiq_bulk_mode mode) { enum vchiq_status status; @@ -843,13 +845,13 @@ vchiq_bulk_transmit(unsigned int handle, const void *data, unsigned int size, switch (mode) { case VCHIQ_BULK_MODE_NOCALLBACK: case VCHIQ_BULK_MODE_CALLBACK: - status = vchiq_bulk_transfer(handle, + status = vchiq_bulk_transfer(instance, handle, (void *)data, NULL, size, userdata, mode, VCHIQ_BULK_TRANSMIT); break; case VCHIQ_BULK_MODE_BLOCKING: - status = vchiq_blocking_bulk_transfer(handle, (void *)data, size, + status = vchiq_blocking_bulk_transfer(instance, handle, (void *)data, size, VCHIQ_BULK_TRANSMIT); break; default: @@ -871,8 +873,8 @@ vchiq_bulk_transmit(unsigned int handle, const void *data, unsigned int size, } EXPORT_SYMBOL(vchiq_bulk_transmit); -enum vchiq_status vchiq_bulk_receive(unsigned int handle, void *data, - unsigned int size, void *userdata, +enum vchiq_status vchiq_bulk_receive(struct vchiq_instance *instance, unsigned int handle, + void *data, unsigned int size, void *userdata, enum vchiq_bulk_mode mode) { enum vchiq_status status; @@ -881,12 +883,12 @@ enum vchiq_status vchiq_bulk_receive(unsigned int handle, void *data, switch (mode) { case VCHIQ_BULK_MODE_NOCALLBACK: case VCHIQ_BULK_MODE_CALLBACK: - status = vchiq_bulk_transfer(handle, data, NULL, + status = vchiq_bulk_transfer(instance, handle, data, NULL, size, userdata, mode, VCHIQ_BULK_RECEIVE); break; case VCHIQ_BULK_MODE_BLOCKING: - status = vchiq_blocking_bulk_transfer(handle, (void *)data, size, + status = vchiq_blocking_bulk_transfer(instance, handle, (void *)data, size, VCHIQ_BULK_RECEIVE); break; default: @@ -909,20 +911,17 @@ enum vchiq_status vchiq_bulk_receive(unsigned int handle, void *data, EXPORT_SYMBOL(vchiq_bulk_receive); static enum vchiq_status -vchiq_blocking_bulk_transfer(unsigned int handle, void *data, unsigned int size, - enum vchiq_bulk_dir dir) +vchiq_blocking_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void *data, + unsigned int size, enum vchiq_bulk_dir dir) { - struct vchiq_instance *instance; struct vchiq_service *service; enum vchiq_status status; struct bulk_waiter_node *waiter = NULL, *iter; - service = find_service_by_handle(handle); + service = find_service_by_handle(instance, handle); if (!service) return VCHIQ_ERROR; - instance = service->instance; - vchiq_service_put(service); mutex_lock(&instance->bulk_waiter_list_mutex); @@ -959,7 +958,7 @@ vchiq_blocking_bulk_transfer(unsigned int handle, void *data, unsigned int size, } } - status = vchiq_bulk_transfer(handle, data, NULL, size, + status = vchiq_bulk_transfer(instance, handle, data, NULL, size, &waiter->bulk_waiter, VCHIQ_BULK_MODE_BLOCKING, dir); if ((status != VCHIQ_RETRY) || fatal_signal_pending(current) || !waiter->bulk_waiter.bulk) { @@ -1046,8 +1045,8 @@ add_completion(struct vchiq_instance *instance, enum vchiq_reason reason, } enum vchiq_status -service_callback(enum vchiq_reason reason, struct vchiq_header *header, - unsigned int handle, void *bulk_userdata) +service_callback(struct vchiq_instance *instance, enum vchiq_reason reason, + struct vchiq_header *header, unsigned int handle, void *bulk_userdata) { /* * How do we ensure the callback goes to the right client? @@ -1057,7 +1056,6 @@ service_callback(enum vchiq_reason reason, struct vchiq_header *header, */ struct user_service *user_service; struct vchiq_service *service; - struct vchiq_instance *instance; bool skip_completion = false; DEBUG_INITIALISE(g_state.local); @@ -1065,14 +1063,13 @@ service_callback(enum vchiq_reason reason, struct vchiq_header *header, DEBUG_TRACE(SERVICE_CALLBACK_LINE); rcu_read_lock(); - service = handle_to_service(handle); + service = handle_to_service(instance, handle); if (WARN_ON(!service)) { rcu_read_unlock(); return VCHIQ_SUCCESS; } user_service = (struct user_service *)service->base.userdata; - instance = user_service->instance; if (!instance || instance->closing) { rcu_read_unlock(); @@ -1318,7 +1315,8 @@ vchiq_get_state(void) */ static enum vchiq_status -vchiq_keepalive_vchiq_callback(enum vchiq_reason reason, +vchiq_keepalive_vchiq_callback(struct vchiq_instance *instance, + enum vchiq_reason reason, struct vchiq_header *header, unsigned int service_user, void *bulk_user) { @@ -1387,14 +1385,14 @@ vchiq_keepalive_thread_func(void *v) */ while (uc--) { atomic_inc(&arm_state->ka_use_ack_count); - status = vchiq_use_service(ka_handle); + status = vchiq_use_service(instance, ka_handle); if (status != VCHIQ_SUCCESS) { vchiq_log_error(vchiq_susp_log_level, "%s vchiq_use_service error %d", __func__, status); } } while (rc--) { - status = vchiq_release_service(ka_handle); + status = vchiq_release_service(instance, ka_handle); if (status != VCHIQ_SUCCESS) { vchiq_log_error(vchiq_susp_log_level, "%s vchiq_release_service error %d", __func__, @@ -1590,10 +1588,10 @@ vchiq_instance_set_trace(struct vchiq_instance *instance, int trace) } enum vchiq_status -vchiq_use_service(unsigned int handle) +vchiq_use_service(struct vchiq_instance *instance, unsigned int handle) { enum vchiq_status ret = VCHIQ_ERROR; - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); if (service) { ret = vchiq_use_internal(service->state, service, USE_TYPE_SERVICE); @@ -1604,10 +1602,10 @@ vchiq_use_service(unsigned int handle) EXPORT_SYMBOL(vchiq_use_service); enum vchiq_status -vchiq_release_service(unsigned int handle) +vchiq_release_service(struct vchiq_instance *instance, unsigned int handle) { enum vchiq_status ret = VCHIQ_ERROR; - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); if (service) { ret = vchiq_release_internal(service->state, service); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h index 2aa46b119a46..2851ef6b9cd0 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_arm.h @@ -86,10 +86,10 @@ extern struct vchiq_state * vchiq_get_state(void); enum vchiq_status -vchiq_use_service(unsigned int handle); +vchiq_use_service(struct vchiq_instance *instance, unsigned int handle); extern enum vchiq_status -vchiq_release_service(unsigned int handle); +vchiq_release_service(struct vchiq_instance *instance, unsigned int handle); extern enum vchiq_status vchiq_check_service(struct vchiq_service *service); @@ -138,8 +138,8 @@ static inline int vchiq_register_chrdev(struct device *parent) { return 0; } #endif /* IS_ENABLED(CONFIG_VCHIQ_CDEV) */ extern enum vchiq_status -service_callback(enum vchiq_reason reason, struct vchiq_header *header, - unsigned int handle, void *bulk_userdata); +service_callback(struct vchiq_instance *vchiq_instance, enum vchiq_reason reason, + struct vchiq_header *header, unsigned int handle, void *bulk_userdata); extern void free_bulk_waiter(struct vchiq_instance *instance); diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c index 8f99272dbd6f..45ed30bfdbf5 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.c @@ -13,6 +13,7 @@ #include #include +#include "vchiq_arm.h" #include "vchiq_core.h" #define VCHIQ_SLOT_HANDLER_STACK 8192 @@ -161,7 +162,6 @@ int vchiq_sync_log_level = VCHIQ_LOG_DEFAULT; DEFINE_SPINLOCK(bulk_waiter_spinlock); static DEFINE_SPINLOCK(quota_spinlock); -struct vchiq_state *vchiq_states[VCHIQ_MAX_STATES]; static unsigned int handle_seq; static const char *const srvstate_names[] = { @@ -234,13 +234,19 @@ set_service_state(struct vchiq_service *service, int newstate) service->srvstate = newstate; } +struct vchiq_service *handle_to_service(struct vchiq_instance *instance, unsigned int handle) +{ + int idx = handle & (VCHIQ_MAX_SERVICES - 1); + + return rcu_dereference(instance->state->services[idx]); +} struct vchiq_service * -find_service_by_handle(unsigned int handle) +find_service_by_handle(struct vchiq_instance *instance, unsigned int handle) { struct vchiq_service *service; rcu_read_lock(); - service = handle_to_service(handle); + service = handle_to_service(instance, handle); if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && service->handle == handle && kref_get_unless_zero(&service->ref_count)) { @@ -281,7 +287,7 @@ find_service_for_instance(struct vchiq_instance *instance, unsigned int handle) struct vchiq_service *service; rcu_read_lock(); - service = handle_to_service(handle); + service = handle_to_service(instance, handle); if (service && service->srvstate != VCHIQ_SRVSTATE_FREE && service->handle == handle && service->instance == instance && @@ -302,7 +308,7 @@ find_closed_service_for_instance(struct vchiq_instance *instance, unsigned int h struct vchiq_service *service; rcu_read_lock(); - service = handle_to_service(handle); + service = handle_to_service(instance, handle); if (service && (service->srvstate == VCHIQ_SRVSTATE_FREE || service->srvstate == VCHIQ_SRVSTATE_CLOSED) && @@ -398,26 +404,26 @@ vchiq_service_put(struct vchiq_service *service) } int -vchiq_get_client_id(unsigned int handle) +vchiq_get_client_id(struct vchiq_instance *instance, unsigned int handle) { struct vchiq_service *service; int id; rcu_read_lock(); - service = handle_to_service(handle); + service = handle_to_service(instance, handle); id = service ? service->client_id : 0; rcu_read_unlock(); return id; } void * -vchiq_get_service_userdata(unsigned int handle) +vchiq_get_service_userdata(struct vchiq_instance *instance, unsigned int handle) { void *userdata; struct vchiq_service *service; rcu_read_lock(); - service = handle_to_service(handle); + service = handle_to_service(instance, handle); userdata = service ? service->base.userdata : NULL; rcu_read_unlock(); return userdata; @@ -466,7 +472,8 @@ make_service_callback(struct vchiq_service *service, enum vchiq_reason reason, vchiq_log_trace(vchiq_core_log_level, "%d: callback:%d (%s, %pK, %pK)", service->state->id, service->localport, reason_names[reason], header, bulk_userdata); - status = service->base.callback(reason, header, service->handle, bulk_userdata); + status = service->base.callback(service->instance, reason, header, service->handle, + bulk_userdata); if (status == VCHIQ_ERROR) { vchiq_log_warning(vchiq_core_log_level, "%d: ignoring ERROR from callback to service %x", @@ -475,7 +482,7 @@ make_service_callback(struct vchiq_service *service, enum vchiq_reason reason, } if (reason != VCHIQ_MESSAGE_AVAILABLE) - vchiq_release_message(service->handle, header); + vchiq_release_message(service->instance, service->handle, header); return status; } @@ -521,6 +528,7 @@ remote_event_wait(wait_queue_head_t *wq, struct remote_event *event) return 0; } event->armed = 0; + /* Ensure that the peer sees that we are not waiting (armed == 0). */ wmb(); } @@ -643,6 +651,7 @@ request_poll(struct vchiq_state *state, struct vchiq_service *service, skip_service: state->poll_needed = 1; + /* Ensure the slot handler thread sees the poll_needed flag. */ wmb(); /* ... and ensure the slot handler runs. */ @@ -1149,6 +1158,7 @@ queue_message_sync(struct vchiq_state *state, struct vchiq_service *service, remote_event_wait(&state->sync_release_event, &local->sync_release); + /* Ensure that reads don't overtake the remote_event_wait. */ rmb(); header = (struct vchiq_header *)SLOT_DATA_FROM_INDEX(state, @@ -1441,7 +1451,7 @@ abort_outstanding_bulks(struct vchiq_service *service, } if (queue->process != queue->local_insert) { - vchiq_complete_bulk(bulk); + vchiq_complete_bulk(service->instance, bulk); vchiq_log_info(SRVTRACE_LEVEL(service), "%s %c%c%c%c d:%d ABORTED - tx len:%d, rx len:%d", @@ -1769,7 +1779,7 @@ parse_message(struct vchiq_state *state, struct vchiq_header *header) DEBUG_TRACE(PARSE_LINE); WARN_ON(queue->process == queue->local_insert); - vchiq_complete_bulk(bulk); + vchiq_complete_bulk(service->instance, bulk); queue->process++; mutex_unlock(&service->bulk_mutex); DEBUG_TRACE(PARSE_LINE); @@ -1952,6 +1962,7 @@ slot_handler_func(void *v) DEBUG_TRACE(SLOT_HANDLER_LINE); remote_event_wait(&state->trigger_event, &local->trigger); + /* Ensure that reads don't overtake the remote_event_wait. */ rmb(); DEBUG_TRACE(SLOT_HANDLER_LINE); @@ -2014,6 +2025,7 @@ sync_func(void *v) remote_event_wait(&state->sync_trigger_event, &local->sync_trigger); + /* Ensure that reads don't overtake the remote_event_wait. */ rmb(); msgid = header->msgid; @@ -2142,18 +2154,13 @@ vchiq_init_slots(void *mem_base, int mem_size) } int -vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero) +vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero, struct device *dev) { struct vchiq_shared_state *local; struct vchiq_shared_state *remote; char threadname[16]; int i, ret; - if (vchiq_states[0]) { - pr_err("%s: VCHIQ state already initialized\n", __func__); - return -EINVAL; - } - local = &slot_zero->slave; remote = &slot_zero->master; @@ -2169,6 +2176,8 @@ vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero) memset(state, 0, sizeof(struct vchiq_state)); + state->dev = dev; + /* * initialize shared state pointers */ @@ -2272,8 +2281,6 @@ vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero) wake_up_process(state->recycle_thread); wake_up_process(state->sync_thread); - vchiq_states[0] = state; - /* Indicate readiness to the other side */ local->initialised = 1; @@ -2287,9 +2294,10 @@ fail_free_handler_thread: return ret; } -void vchiq_msg_queue_push(unsigned int handle, struct vchiq_header *header) +void vchiq_msg_queue_push(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_header *header) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); int pos; if (!service) @@ -2309,9 +2317,9 @@ void vchiq_msg_queue_push(unsigned int handle, struct vchiq_header *header) } EXPORT_SYMBOL(vchiq_msg_queue_push); -struct vchiq_header *vchiq_msg_hold(unsigned int handle) +struct vchiq_header *vchiq_msg_hold(struct vchiq_instance *instance, unsigned int handle) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); struct vchiq_header *header; int pos; @@ -2866,16 +2874,16 @@ vchiq_shutdown_internal(struct vchiq_state *state, struct vchiq_instance *instan /* Find all services registered to this client and remove them. */ i = 0; while ((service = next_service_by_instance(state, instance, &i)) != NULL) { - (void)vchiq_remove_service(service->handle); + (void)vchiq_remove_service(instance, service->handle); vchiq_service_put(service); } } enum vchiq_status -vchiq_close_service(unsigned int handle) +vchiq_close_service(struct vchiq_instance *instance, unsigned int handle) { /* Unregister the service */ - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); enum vchiq_status status = VCHIQ_SUCCESS; if (!service) @@ -2930,10 +2938,10 @@ vchiq_close_service(unsigned int handle) EXPORT_SYMBOL(vchiq_close_service); enum vchiq_status -vchiq_remove_service(unsigned int handle) +vchiq_remove_service(struct vchiq_instance *instance, unsigned int handle) { /* Unregister the service */ - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); enum vchiq_status status = VCHIQ_SUCCESS; if (!service) @@ -2996,11 +3004,11 @@ vchiq_remove_service(unsigned int handle) * When called in blocking mode, the userdata field points to a bulk_waiter * structure. */ -enum vchiq_status vchiq_bulk_transfer(unsigned int handle, void *offset, void __user *uoffset, - int size, void *userdata, enum vchiq_bulk_mode mode, - enum vchiq_bulk_dir dir) +enum vchiq_status vchiq_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, + void *offset, void __user *uoffset, int size, void *userdata, + enum vchiq_bulk_mode mode, enum vchiq_bulk_dir dir) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); struct vchiq_bulk_queue *queue; struct vchiq_bulk *bulk; struct vchiq_state *state; @@ -3075,9 +3083,13 @@ enum vchiq_status vchiq_bulk_transfer(unsigned int handle, void *offset, void __ bulk->size = size; bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; - if (vchiq_prepare_bulk_data(bulk, offset, uoffset, size, dir)) + if (vchiq_prepare_bulk_data(instance, bulk, offset, uoffset, size, dir)) goto unlock_error_exit; + /* + * Ensure that the bulk data record is visible to the peer + * before proceeding. + */ wmb(); vchiq_log_info(vchiq_core_log_level, "%d: bt (%d->%d) %cx %x@%pad %pK", @@ -3139,7 +3151,7 @@ waiting: unlock_both_error_exit: mutex_unlock(&state->slot_mutex); cancel_bulk_error_exit: - vchiq_complete_bulk(bulk); + vchiq_complete_bulk(service->instance, bulk); unlock_error_exit: mutex_unlock(&service->bulk_mutex); @@ -3150,13 +3162,13 @@ error_exit: } enum vchiq_status -vchiq_queue_message(unsigned int handle, +vchiq_queue_message(struct vchiq_instance *instance, unsigned int handle, ssize_t (*copy_callback)(void *context, void *dest, size_t offset, size_t maxsize), void *context, size_t size) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); enum vchiq_status status = VCHIQ_ERROR; int data_id; @@ -3199,12 +3211,13 @@ error_exit: return status; } -int vchiq_queue_kernel_message(unsigned int handle, void *data, unsigned int size) +int vchiq_queue_kernel_message(struct vchiq_instance *instance, unsigned int handle, void *data, + unsigned int size) { enum vchiq_status status; while (1) { - status = vchiq_queue_message(handle, memcpy_copy_callback, + status = vchiq_queue_message(instance, handle, memcpy_copy_callback, data, size); /* @@ -3223,10 +3236,10 @@ int vchiq_queue_kernel_message(unsigned int handle, void *data, unsigned int siz EXPORT_SYMBOL(vchiq_queue_kernel_message); void -vchiq_release_message(unsigned int handle, +vchiq_release_message(struct vchiq_instance *instance, unsigned int handle, struct vchiq_header *header) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); struct vchiq_shared_state *remote; struct vchiq_state *state; int slot_index; @@ -3265,10 +3278,10 @@ release_message_sync(struct vchiq_state *state, struct vchiq_header *header) } enum vchiq_status -vchiq_get_peer_version(unsigned int handle, short *peer_version) +vchiq_get_peer_version(struct vchiq_instance *instance, unsigned int handle, short *peer_version) { enum vchiq_status status = VCHIQ_ERROR; - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); if (!service) goto exit; @@ -3300,9 +3313,10 @@ void vchiq_get_config(struct vchiq_config *config) } int -vchiq_set_service_option(unsigned int handle, enum vchiq_service_option option, int value) +vchiq_set_service_option(struct vchiq_instance *instance, unsigned int handle, + enum vchiq_service_option option, int value) { - struct vchiq_service *service = find_service_by_handle(handle); + struct vchiq_service *service = find_service_by_handle(instance, handle); struct vchiq_service_quota *quota; int ret = -EINVAL; diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h index 1ddc661642a9..8b4a38f5b3f2 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_core.h @@ -314,6 +314,7 @@ struct vchiq_slot_zero { }; struct vchiq_state { + struct device *dev; int id; int initialised; enum vchiq_connstate conn_state; @@ -448,8 +449,6 @@ extern int vchiq_core_log_level; extern int vchiq_core_msg_log_level; extern int vchiq_sync_log_level; -extern struct vchiq_state *vchiq_states[VCHIQ_MAX_STATES]; - extern const char * get_conn_state_name(enum vchiq_connstate conn_state); @@ -457,7 +456,7 @@ extern struct vchiq_slot_zero * vchiq_init_slots(void *mem_base, int mem_size); extern int -vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero); +vchiq_init_state(struct vchiq_state *state, struct vchiq_slot_zero *slot_zero, struct device *dev); extern enum vchiq_status vchiq_connect_internal(struct vchiq_state *state, struct vchiq_instance *instance); @@ -487,8 +486,8 @@ extern void remote_event_pollall(struct vchiq_state *state); extern enum vchiq_status -vchiq_bulk_transfer(unsigned int handle, void *offset, void __user *uoffset, - int size, void *userdata, enum vchiq_bulk_mode mode, +vchiq_bulk_transfer(struct vchiq_instance *instance, unsigned int handle, void *offset, + void __user *uoffset, int size, void *userdata, enum vchiq_bulk_mode mode, enum vchiq_bulk_dir dir); extern int @@ -507,20 +506,10 @@ extern void request_poll(struct vchiq_state *state, struct vchiq_service *service, int poll_type); -static inline struct vchiq_service * -handle_to_service(unsigned int handle) -{ - int idx = handle & (VCHIQ_MAX_SERVICES - 1); - struct vchiq_state *state = vchiq_states[(handle / VCHIQ_MAX_SERVICES) & - (VCHIQ_MAX_STATES - 1)]; - - if (!state) - return NULL; - return rcu_dereference(state->services[idx]); -} +struct vchiq_service *handle_to_service(struct vchiq_instance *instance, unsigned int handle); extern struct vchiq_service * -find_service_by_handle(unsigned int handle); +find_service_by_handle(struct vchiq_instance *instance, unsigned int handle); extern struct vchiq_service * find_service_by_port(struct vchiq_state *state, unsigned int localport); @@ -548,16 +537,16 @@ extern void vchiq_service_put(struct vchiq_service *service); extern enum vchiq_status -vchiq_queue_message(unsigned int handle, +vchiq_queue_message(struct vchiq_instance *instance, unsigned int handle, ssize_t (*copy_callback)(void *context, void *dest, size_t offset, size_t maxsize), void *context, size_t size); -int vchiq_prepare_bulk_data(struct vchiq_bulk *bulk, void *offset, void __user *uoffset, - int size, int dir); +int vchiq_prepare_bulk_data(struct vchiq_instance *instance, struct vchiq_bulk *bulk, void *offset, + void __user *uoffset, int size, int dir); -void vchiq_complete_bulk(struct vchiq_bulk *bulk); +void vchiq_complete_bulk(struct vchiq_instance *instance, struct vchiq_bulk *bulk); void remote_event_signal(struct remote_event *event); @@ -595,12 +584,13 @@ void vchiq_set_conn_state(struct vchiq_state *state, enum vchiq_connstate newsta void vchiq_log_dump_mem(const char *label, u32 addr, const void *void_mem, size_t num_bytes); -enum vchiq_status vchiq_remove_service(unsigned int service); +enum vchiq_status vchiq_remove_service(struct vchiq_instance *instance, unsigned int service); -int vchiq_get_client_id(unsigned int service); +int vchiq_get_client_id(struct vchiq_instance *instance, unsigned int service); void vchiq_get_config(struct vchiq_config *config); -int vchiq_set_service_option(unsigned int service, enum vchiq_service_option option, int value); +int vchiq_set_service_option(struct vchiq_instance *instance, unsigned int service, + enum vchiq_service_option option, int value); #endif diff --git a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c index 66bbfec332ba..7e297494437e 100644 --- a/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c +++ b/drivers/staging/vc04_services/interface/vchiq_arm/vchiq_dev.c @@ -108,8 +108,8 @@ static ssize_t vchiq_ioc_copy_element_data(void *context, void *dest, } static int -vchiq_ioc_queue_message(unsigned int handle, struct vchiq_element *elements, - unsigned long count) +vchiq_ioc_queue_message(struct vchiq_instance *instance, unsigned int handle, + struct vchiq_element *elements, unsigned long count) { struct vchiq_io_copy_callback_context context; enum vchiq_status status = VCHIQ_SUCCESS; @@ -127,7 +127,7 @@ vchiq_ioc_queue_message(unsigned int handle, struct vchiq_element *elements, total_size += elements[i].size; } - status = vchiq_queue_message(handle, vchiq_ioc_copy_element_data, + status = vchiq_queue_message(instance, handle, vchiq_ioc_copy_element_data, &context, total_size); if (status == VCHIQ_ERROR) @@ -191,7 +191,7 @@ static int vchiq_ioc_create_service(struct vchiq_instance *instance, if (args->is_open) { status = vchiq_open_service_internal(service, instance->pid); if (status != VCHIQ_SUCCESS) { - vchiq_remove_service(service->handle); + vchiq_remove_service(instance, service->handle); return (status == VCHIQ_RETRY) ? -EINTR : -EIO; } @@ -266,7 +266,7 @@ static int vchiq_ioc_dequeue_message(struct vchiq_instance *instance, /* Copy to user space if msgbuf is not NULL */ if (!args->buf || (copy_to_user(args->buf, header->data, header->size) == 0)) { ret = header->size; - vchiq_release_message(service->handle, header); + vchiq_release_message(instance, service->handle, header); } else { ret = -EFAULT; } @@ -330,7 +330,7 @@ static int vchiq_irq_queue_bulk_tx_rx(struct vchiq_instance *instance, userdata = args->userdata; } - status = vchiq_bulk_transfer(args->handle, NULL, args->data, args->size, + status = vchiq_bulk_transfer(instance, args->handle, NULL, args->data, args->size, userdata, args->mode, dir); if (!waiter) { @@ -529,7 +529,7 @@ static int vchiq_ioc_await_completion(struct vchiq_instance *instance, } /* Now it has been copied, the message can be released. */ - vchiq_release_message(service->handle, header); + vchiq_release_message(instance, service->handle, header); /* The completion must point to the msgbuf. */ user_completion.header = msgbuf; @@ -596,7 +596,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) i = 0; while ((service = next_service_by_instance(instance->state, instance, &i))) { - status = vchiq_remove_service(service->handle); + status = vchiq_remove_service(instance, service->handle); vchiq_service_put(service); if (status != VCHIQ_SUCCESS) break; @@ -649,7 +649,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; if (put_user(args.handle, &argp->handle)) { - vchiq_remove_service(args.handle); + vchiq_remove_service(instance, args.handle); ret = -EFAULT; } } break; @@ -673,8 +673,8 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) */ if (!user_service->close_pending) { status = (cmd == VCHIQ_IOC_CLOSE_SERVICE) ? - vchiq_close_service(service->handle) : - vchiq_remove_service(service->handle); + vchiq_close_service(instance, service->handle) : + vchiq_remove_service(instance, service->handle); if (status != VCHIQ_SUCCESS) break; } @@ -731,7 +731,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) if (copy_from_user(elements, args.elements, args.count * sizeof(struct vchiq_element)) == 0) - ret = vchiq_ioc_queue_message(args.handle, elements, + ret = vchiq_ioc_queue_message(instance, args.handle, elements, args.count); else ret = -EFAULT; @@ -788,7 +788,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case VCHIQ_IOC_GET_CLIENT_ID: { unsigned int handle = (unsigned int)arg; - ret = vchiq_get_client_id(handle); + ret = vchiq_get_client_id(instance, handle); } break; case VCHIQ_IOC_GET_CONFIG: { @@ -827,7 +827,7 @@ vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) break; } - ret = vchiq_set_service_option(args.handle, args.option, + ret = vchiq_set_service_option(instance, args.handle, args.option, args.value); } break; @@ -908,6 +908,7 @@ vchiq_compat_ioctl_create_service(struct file *file, unsigned int cmd, { struct vchiq_create_service args; struct vchiq_create_service32 args32; + struct vchiq_instance *instance = file->private_data; long ret; if (copy_from_user(&args32, ptrargs32, sizeof(args32))) @@ -926,12 +927,12 @@ vchiq_compat_ioctl_create_service(struct file *file, unsigned int cmd, .handle = args32.handle, }; - ret = vchiq_ioc_create_service(file->private_data, &args); + ret = vchiq_ioc_create_service(instance, &args); if (ret < 0) return ret; if (put_user(args.handle, &ptrargs32->handle)) { - vchiq_remove_service(args.handle); + vchiq_remove_service(instance, args.handle); return -EFAULT; } @@ -960,6 +961,7 @@ vchiq_compat_ioctl_queue_message(struct file *file, struct vchiq_queue_message args; struct vchiq_queue_message32 args32; struct vchiq_service *service; + struct vchiq_instance *instance = file->private_data; int ret; if (copy_from_user(&args32, arg, sizeof(args32))) @@ -974,7 +976,7 @@ vchiq_compat_ioctl_queue_message(struct file *file, if (args32.count > MAX_ELEMENTS) return -EINVAL; - service = find_service_for_instance(file->private_data, args.handle); + service = find_service_for_instance(instance, args.handle); if (!service) return -EINVAL; @@ -994,7 +996,7 @@ vchiq_compat_ioctl_queue_message(struct file *file, compat_ptr(element32[count].data); elements[count].size = element32[count].size; } - ret = vchiq_ioc_queue_message(args.handle, elements, + ret = vchiq_ioc_queue_message(instance, args.handle, elements, args.count); } else { ret = -EINVAL; @@ -1261,7 +1263,7 @@ static int vchiq_release(struct inode *inode, struct file *file) spin_unlock(&msg_queue_spinlock); if (header) - vchiq_release_message(service->handle, header); + vchiq_release_message(instance, service->handle, header); spin_lock(&msg_queue_spinlock); } diff --git a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c index 845b20e4d05a..cb921c94996a 100644 --- a/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c +++ b/drivers/staging/vc04_services/vchiq-mmal/mmal-vchiq.c @@ -292,8 +292,8 @@ static void buffer_to_host_work_cb(struct work_struct *work) /* Dummy receive to ensure the buffers remain in order */ len = 8; /* queue the bulk submission */ - vchiq_use_service(instance->service_handle); - ret = vchiq_bulk_receive(instance->service_handle, + vchiq_use_service(instance->vchiq_instance, instance->service_handle); + ret = vchiq_bulk_receive(instance->vchiq_instance, instance->service_handle, msg_context->u.bulk.buffer->buffer, /* Actual receive needs to be a multiple * of 4 bytes @@ -302,7 +302,7 @@ static void buffer_to_host_work_cb(struct work_struct *work) msg_context, VCHIQ_BULK_MODE_CALLBACK); - vchiq_release_service(instance->service_handle); + vchiq_release_service(instance->vchiq_instance, instance->service_handle); if (ret != 0) pr_err("%s: ctx: %p, vchiq_bulk_receive failed %d\n", @@ -436,15 +436,15 @@ buffer_from_host(struct vchiq_mmal_instance *instance, /* no payload in message */ m.u.buffer_from_host.payload_in_message = 0; - vchiq_use_service(instance->service_handle); + vchiq_use_service(instance->vchiq_instance, instance->service_handle); - ret = vchiq_queue_kernel_message(instance->service_handle, &m, + ret = vchiq_queue_kernel_message(instance->vchiq_instance, instance->service_handle, &m, sizeof(struct mmal_msg_header) + sizeof(m.u.buffer_from_host)); if (ret) atomic_dec(&port->buffers_with_vpu); - vchiq_release_service(instance->service_handle); + vchiq_release_service(instance->vchiq_instance, instance->service_handle); return ret; } @@ -548,11 +548,12 @@ static void bulk_abort_cb(struct vchiq_mmal_instance *instance, } /* incoming event service callback */ -static enum vchiq_status service_callback(enum vchiq_reason reason, +static enum vchiq_status service_callback(struct vchiq_instance *vchiq_instance, + enum vchiq_reason reason, struct vchiq_header *header, unsigned int handle, void *bulk_ctx) { - struct vchiq_mmal_instance *instance = vchiq_get_service_userdata(handle); + struct vchiq_mmal_instance *instance = vchiq_get_service_userdata(vchiq_instance, handle); u32 msg_len; struct mmal_msg *msg; struct mmal_msg_context *msg_context; @@ -572,25 +573,25 @@ static enum vchiq_status service_callback(enum vchiq_reason reason, /* handling is different for buffer messages */ switch (msg->h.type) { case MMAL_MSG_TYPE_BUFFER_FROM_HOST: - vchiq_release_message(handle, header); + vchiq_release_message(vchiq_instance, handle, header); break; case MMAL_MSG_TYPE_EVENT_TO_HOST: event_to_host_cb(instance, msg, msg_len); - vchiq_release_message(handle, header); + vchiq_release_message(vchiq_instance, handle, header); break; case MMAL_MSG_TYPE_BUFFER_TO_HOST: buffer_to_host_cb(instance, msg, msg_len); - vchiq_release_message(handle, header); + vchiq_release_message(vchiq_instance, handle, header); break; default: /* messages dependent on header context to complete */ if (!msg->h.context) { pr_err("received message context was null!\n"); - vchiq_release_message(handle, header); + vchiq_release_message(vchiq_instance, handle, header); break; } @@ -599,7 +600,7 @@ static enum vchiq_status service_callback(enum vchiq_reason reason, if (!msg_context) { pr_err("received invalid message context %u!\n", msg->h.context); - vchiq_release_message(handle, header); + vchiq_release_message(vchiq_instance, handle, header); break; } @@ -678,13 +679,13 @@ static int send_synchronous_mmal_msg(struct vchiq_mmal_instance *instance, DBG_DUMP_MSG(msg, (sizeof(struct mmal_msg_header) + payload_len), ">>> sync message"); - vchiq_use_service(instance->service_handle); + vchiq_use_service(instance->vchiq_instance, instance->service_handle); - ret = vchiq_queue_kernel_message(instance->service_handle, msg, + ret = vchiq_queue_kernel_message(instance->vchiq_instance, instance->service_handle, msg, sizeof(struct mmal_msg_header) + payload_len); - vchiq_release_service(instance->service_handle); + vchiq_release_service(instance->vchiq_instance, instance->service_handle); if (ret) { pr_err("error %d queuing message\n", ret); @@ -824,7 +825,7 @@ static int port_info_set(struct vchiq_mmal_instance *instance, port->component->handle, port->handle); release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -919,7 +920,7 @@ release_msg: pr_debug("%s:result:%d component:0x%x port:%d\n", __func__, ret, port->component->handle, port->handle); - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -967,7 +968,7 @@ static int create_component(struct vchiq_mmal_instance *instance, component->inputs, component->outputs, component->clocks); release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1000,7 +1001,7 @@ static int destroy_component(struct vchiq_mmal_instance *instance, release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1032,7 +1033,7 @@ static int enable_component(struct vchiq_mmal_instance *instance, ret = -rmsg->u.component_enable_reply.status; release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1065,7 +1066,7 @@ static int disable_component(struct vchiq_mmal_instance *instance, release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1097,7 +1098,7 @@ static int get_version(struct vchiq_mmal_instance *instance, *minor_out = rmsg->u.version.minor; release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1139,7 +1140,7 @@ static int port_action_port(struct vchiq_mmal_instance *instance, port_action_type_names[action_type], action_type); release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1187,7 +1188,7 @@ static int port_action_handle(struct vchiq_mmal_instance *instance, action_type, connect_component_handle, connect_port_handle); release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1228,7 +1229,7 @@ static int port_parameter_set(struct vchiq_mmal_instance *instance, ret, port->component->handle, port->handle, parameter_id); release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1287,7 +1288,7 @@ static int port_parameter_get(struct vchiq_mmal_instance *instance, ret, port->component->handle, port->handle, parameter_id); release_msg: - vchiq_release_message(instance->service_handle, rmsg_handle); + vchiq_release_message(instance->vchiq_instance, instance->service_handle, rmsg_handle); return ret; } @@ -1832,9 +1833,9 @@ int vchiq_mmal_finalise(struct vchiq_mmal_instance *instance) if (mutex_lock_interruptible(&instance->vchiq_mutex)) return -EINTR; - vchiq_use_service(instance->service_handle); + vchiq_use_service(instance->vchiq_instance, instance->service_handle); - status = vchiq_close_service(instance->service_handle); + status = vchiq_close_service(instance->vchiq_instance, instance->service_handle); if (status != 0) pr_err("mmal-vchiq: VCHIQ close failed\n"); @@ -1922,14 +1923,14 @@ int vchiq_mmal_init(struct vchiq_mmal_instance **out_instance) goto err_close_services; } - vchiq_release_service(instance->service_handle); + vchiq_release_service(instance->vchiq_instance, instance->service_handle); *out_instance = instance; return 0; err_close_services: - vchiq_close_service(instance->service_handle); + vchiq_close_service(instance->vchiq_instance, instance->service_handle); destroy_workqueue(instance->bulk_wq); err_free: kfree(instance); diff --git a/drivers/staging/vme_user/Kconfig b/drivers/staging/vme_user/Kconfig index e8b4461bf27f..c8eabf8f40f1 100644 --- a/drivers/staging/vme_user/Kconfig +++ b/drivers/staging/vme_user/Kconfig @@ -1,4 +1,29 @@ # SPDX-License-Identifier: GPL-2.0 +menuconfig VME_BUS + bool "VME bridge support" + depends on STAGING && PCI + help + If you say Y here you get support for the VME bridge Framework. + +if VME_BUS + +comment "VME Bridge Drivers" + +config VME_TSI148 + tristate "Tempe" + depends on HAS_DMA + help + If you say Y here you get support for the Tundra TSI148 VME bridge + chip. + +config VME_FAKE + tristate "Fake" + help + If you say Y here you get support for the fake VME bridge. This + provides a virtualised VME Bus for devices with no VME bridge. This + is mainly useful for VME development (in the absence of VME + hardware). + comment "VME Device Drivers" config VME_USER @@ -11,3 +36,5 @@ config VME_USER To compile this driver as a module, choose M here. The module will be called vme_user. If unsure, say N. + +endif diff --git a/drivers/staging/vme_user/Makefile b/drivers/staging/vme_user/Makefile index 5380115139b0..8dcc6938ce5c 100644 --- a/drivers/staging/vme_user/Makefile +++ b/drivers/staging/vme_user/Makefile @@ -3,4 +3,7 @@ # Makefile for the VME device drivers. # +obj-$(CONFIG_VME_BUS) += vme.o obj-$(CONFIG_VME_USER) += vme_user.o +obj-$(CONFIG_VME_TSI148) += vme_tsi148.o +obj-$(CONFIG_VME_FAKE) += vme_fake.o diff --git a/drivers/vme/vme.c b/drivers/staging/vme_user/vme.c similarity index 99% rename from drivers/vme/vme.c rename to drivers/staging/vme_user/vme.c index 8dba20186be3..b5555683a069 100644 --- a/drivers/vme/vme.c +++ b/drivers/staging/vme_user/vme.c @@ -26,8 +26,8 @@ #include #include #include -#include +#include "vme.h" #include "vme_bridge.h" /* Bitmask and list of registered buses both protected by common mutex */ diff --git a/include/linux/vme.h b/drivers/staging/vme_user/vme.h similarity index 100% rename from include/linux/vme.h rename to drivers/staging/vme_user/vme.h diff --git a/drivers/vme/vme_bridge.h b/drivers/staging/vme_user/vme_bridge.h similarity index 99% rename from drivers/vme/vme_bridge.h rename to drivers/staging/vme_user/vme_bridge.h index 42ecf961004e..0bbefe9851d7 100644 --- a/drivers/vme/vme_bridge.h +++ b/drivers/staging/vme_user/vme_bridge.h @@ -2,7 +2,7 @@ #ifndef _VME_BRIDGE_H_ #define _VME_BRIDGE_H_ -#include +#include "vme.h" #define VME_CRCSR_BUF_SIZE (508*1024) /* diff --git a/drivers/vme/bridges/vme_fake.c b/drivers/staging/vme_user/vme_fake.c similarity index 99% rename from drivers/vme/bridges/vme_fake.c rename to drivers/staging/vme_user/vme_fake.c index 6a1bc284f297..dd646b0c531d 100644 --- a/drivers/vme/bridges/vme_fake.c +++ b/drivers/staging/vme_user/vme_fake.c @@ -29,9 +29,9 @@ #include #include #include -#include -#include "../vme_bridge.h" +#include "vme.h" +#include "vme_bridge.h" /* * Define the number of each that the fake driver supports. diff --git a/drivers/vme/bridges/vme_tsi148.c b/drivers/staging/vme_user/vme_tsi148.c similarity index 99% rename from drivers/vme/bridges/vme_tsi148.c rename to drivers/staging/vme_user/vme_tsi148.c index be9051b02f24..956476213241 100644 --- a/drivers/vme/bridges/vme_tsi148.c +++ b/drivers/staging/vme_user/vme_tsi148.c @@ -26,9 +26,9 @@ #include #include #include -#include -#include "../vme_bridge.h" +#include "vme.h" +#include "vme_bridge.h" #include "vme_tsi148.h" static int tsi148_probe(struct pci_dev *, const struct pci_device_id *); diff --git a/drivers/vme/bridges/vme_tsi148.h b/drivers/staging/vme_user/vme_tsi148.h similarity index 100% rename from drivers/vme/bridges/vme_tsi148.h rename to drivers/staging/vme_user/vme_tsi148.h diff --git a/drivers/staging/vme_user/vme_user.c b/drivers/staging/vme_user/vme_user.c index 859af797630c..4e533c0bfe6d 100644 --- a/drivers/staging/vme_user/vme_user.c +++ b/drivers/staging/vme_user/vme_user.c @@ -33,8 +33,8 @@ #include #include -#include +#include "vme.h" #include "vme_user.h" static const char driver_name[] = "vme_user"; diff --git a/drivers/staging/vt6655/baseband.c b/drivers/staging/vt6655/baseband.c index 577a38fae369..5de841cb776c 100644 --- a/drivers/staging/vt6655/baseband.c +++ b/drivers/staging/vt6655/baseband.c @@ -1912,7 +1912,7 @@ bool bb_read_embedded(struct vnt_private *priv, unsigned char by_bb_addr, iowrite8(by_bb_addr, iobase + MAC_REG_BBREGADR); /* turn on REGR */ - MACvRegBitsOn(iobase, MAC_REG_BBREGCTL, BBREGCTL_REGR); + vt6655_mac_reg_bits_on(iobase, MAC_REG_BBREGCTL, BBREGCTL_REGR); /* W_MAX_TIMEOUT is the timeout period */ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { by_value = ioread8(iobase + MAC_REG_BBREGCTL); @@ -1957,7 +1957,7 @@ bool bb_write_embedded(struct vnt_private *priv, unsigned char by_bb_addr, iowrite8(by_data, iobase + MAC_REG_BBREGDATA); /* turn on BBREGCTL_REGW */ - MACvRegBitsOn(iobase, MAC_REG_BBREGCTL, BBREGCTL_REGW); + vt6655_mac_reg_bits_on(iobase, MAC_REG_BBREGCTL, BBREGCTL_REGW); /* W_MAX_TIMEOUT is the timeout period */ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { by_value = ioread8(iobase + MAC_REG_BBREGCTL); @@ -2013,8 +2013,8 @@ bool bb_vt3253_init(struct vnt_private *priv) byVT3253B0_AGC4_RFMD2959[ii][0], byVT3253B0_AGC4_RFMD2959[ii][1]); - VNSvOutPortD(iobase + MAC_REG_ITRTMSET, 0x23); - MACvRegBitsOn(iobase, MAC_REG_PAPEDELAY, BIT(0)); + iowrite32(0x23, iobase + MAC_REG_ITRTMSET); + vt6655_mac_reg_bits_on(iobase, MAC_REG_PAPEDELAY, BIT(0)); } priv->abyBBVGA[0] = 0x18; priv->abyBBVGA[1] = 0x0A; @@ -2054,7 +2054,7 @@ bool bb_vt3253_init(struct vnt_private *priv) byVT3253B0_AGC[ii][1]); iowrite8(0x23, iobase + MAC_REG_ITRTMSET); - MACvRegBitsOn(iobase, MAC_REG_PAPEDELAY, BIT(0)); + vt6655_mac_reg_bits_on(iobase, MAC_REG_PAPEDELAY, BIT(0)); priv->abyBBVGA[0] = 0x14; priv->abyBBVGA[1] = 0x0A; diff --git a/drivers/staging/vt6655/card.c b/drivers/staging/vt6655/card.c index 2cde0082fc03..846469cc06bb 100644 --- a/drivers/staging/vt6655/card.c +++ b/drivers/staging/vt6655/card.c @@ -293,12 +293,10 @@ bool CARDbUpdateTSF(struct vnt_private *priv, unsigned char byRxRate, qwTSFOffset = CARDqGetTSFOffset(byRxRate, qwBSSTimestamp, local_tsf); /* adjust TSF, HW's TSF add TSF Offset reg */ - VNSvOutPortD(priv->port_offset + MAC_REG_TSFOFST, - (u32)qwTSFOffset); - VNSvOutPortD(priv->port_offset + MAC_REG_TSFOFST + 4, - (u32)(qwTSFOffset >> 32)); - MACvRegBitsOn(priv->port_offset, MAC_REG_TFTCTL, - TFTCTL_TSFSYNCEN); + qwTSFOffset = le64_to_cpu(qwTSFOffset); + iowrite32((u32)qwTSFOffset, priv->port_offset + MAC_REG_TSFOFST); + iowrite32((u32)(qwTSFOffset >> 32), priv->port_offset + MAC_REG_TSFOFST + 4); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TSFSYNCEN); } return true; } @@ -326,13 +324,13 @@ bool CARDbSetBeaconPeriod(struct vnt_private *priv, qwNextTBTT = CARDqGetNextTBTT(qwNextTBTT, wBeaconInterval); /* set HW beacon interval */ - VNSvOutPortW(priv->port_offset + MAC_REG_BI, wBeaconInterval); + iowrite16(wBeaconInterval, priv->port_offset + MAC_REG_BI); priv->wBeaconInterval = wBeaconInterval; /* Set NextTBTT */ - VNSvOutPortD(priv->port_offset + MAC_REG_NEXTTBTT, (u32)qwNextTBTT); - VNSvOutPortD(priv->port_offset + MAC_REG_NEXTTBTT + 4, - (u32)(qwNextTBTT >> 32)); - MACvRegBitsOn(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); + qwNextTBTT = le64_to_cpu(qwNextTBTT); + iowrite32((u32)qwNextTBTT, priv->port_offset + MAC_REG_NEXTTBTT); + iowrite32((u32)(qwNextTBTT >> 32), priv->port_offset + MAC_REG_NEXTTBTT + 4); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); return true; } @@ -354,29 +352,28 @@ void CARDbRadioPowerOff(struct vnt_private *priv) switch (priv->byRFType) { case RF_RFMD2959: - MACvWordRegBitsOff(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_TXPEINV); - MACvWordRegBitsOn(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_SWPE1); + vt6655_mac_word_reg_bits_off(priv->port_offset, MAC_REG_SOFTPWRCTL, + SOFTPWRCTL_TXPEINV); + vt6655_mac_word_reg_bits_on(priv->port_offset, MAC_REG_SOFTPWRCTL, + SOFTPWRCTL_SWPE1); break; case RF_AIROHA: case RF_AL2230S: - MACvWordRegBitsOff(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_SWPE2); - MACvWordRegBitsOff(priv->port_offset, MAC_REG_SOFTPWRCTL, - SOFTPWRCTL_SWPE3); + vt6655_mac_word_reg_bits_off(priv->port_offset, MAC_REG_SOFTPWRCTL, + SOFTPWRCTL_SWPE2); + vt6655_mac_word_reg_bits_off(priv->port_offset, MAC_REG_SOFTPWRCTL, + SOFTPWRCTL_SWPE3); break; } - MACvRegBitsOff(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_RXON); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_RXON); bb_set_deep_sleep(priv, priv->local_id); priv->radio_off = true; pr_debug("chester power off\n"); - MACvRegBitsOn(priv->port_offset, MAC_REG_GPIOCTL0, - LED_ACTSET); /* LED issue */ + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_GPIOCTL0, LED_ACTSET); /* LED issue */ } void CARDvSafeResetTx(struct vnt_private *priv) @@ -411,8 +408,7 @@ void CARDvSafeResetTx(struct vnt_private *priv) MACvSetCurrTXDescAddr(TYPE_AC0DMA, priv, priv->td1_pool_dma); /* set MAC Beacon TX pointer */ - MACvSetCurrBCNTxDescAddr(priv->port_offset, - (priv->tx_beacon_dma)); + iowrite32((u32)priv->tx_beacon_dma, priv->port_offset + MAC_REG_BCNDMAPTR); } /* @@ -453,8 +449,8 @@ void CARDvSafeResetRx(struct vnt_private *priv) } /* set perPkt mode */ - MACvRx0PerPktMode(priv->port_offset); - MACvRx1PerPktMode(priv->port_offset); + iowrite32(RX_PERPKT, priv->port_offset + MAC_REG_RXDMACTL0); + iowrite32(RX_PERPKT, priv->port_offset + MAC_REG_RXDMACTL1); /* set MAC RD pointer */ MACvSetCurrRx0DescAddr(priv, priv->rd0_pool_dma); @@ -553,7 +549,7 @@ void CARDvSetRSPINF(struct vnt_private *priv, u8 bb_type) /* swap over to get correct write order */ swap(phy.swap[0], phy.swap[1]); - VNSvOutPortD(priv->port_offset + MAC_REG_RSPINF_B_1, phy.field_write); + iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_1); /* RSPINF_b_2 */ vnt_get_phy_field(priv, 14, @@ -562,7 +558,7 @@ void CARDvSetRSPINF(struct vnt_private *priv, u8 bb_type) swap(phy.swap[0], phy.swap[1]); - VNSvOutPortD(priv->port_offset + MAC_REG_RSPINF_B_2, phy.field_write); + iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_2); /* RSPINF_b_5 */ vnt_get_phy_field(priv, 14, @@ -571,7 +567,7 @@ void CARDvSetRSPINF(struct vnt_private *priv, u8 bb_type) swap(phy.swap[0], phy.swap[1]); - VNSvOutPortD(priv->port_offset + MAC_REG_RSPINF_B_5, phy.field_write); + iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_5); /* RSPINF_b_11 */ vnt_get_phy_field(priv, 14, @@ -580,75 +576,66 @@ void CARDvSetRSPINF(struct vnt_private *priv, u8 bb_type) swap(phy.swap[0], phy.swap[1]); - VNSvOutPortD(priv->port_offset + MAC_REG_RSPINF_B_11, phy.field_write); + iowrite32(phy.field_write, priv->port_offset + MAC_REG_RSPINF_B_11); /* RSPINF_a_6 */ s_vCalculateOFDMRParameter(RATE_6M, bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_6, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_6); /* RSPINF_a_9 */ s_vCalculateOFDMRParameter(RATE_9M, bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_9, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_9); /* RSPINF_a_12 */ s_vCalculateOFDMRParameter(RATE_12M, bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_12, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_12); /* RSPINF_a_18 */ s_vCalculateOFDMRParameter(RATE_18M, bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_18, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_18); /* RSPINF_a_24 */ s_vCalculateOFDMRParameter(RATE_24M, bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_24, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_24); /* RSPINF_a_36 */ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)priv, RATE_36M), bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_36, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_36); /* RSPINF_a_48 */ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)priv, RATE_48M), bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_48, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_48); /* RSPINF_a_54 */ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)priv, RATE_54M), bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_54, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_54); /* RSPINF_a_72 */ s_vCalculateOFDMRParameter(CARDwGetOFDMControlRate((void *)priv, RATE_54M), bb_type, &byTxRate, &byRsvTime); - VNSvOutPortW(priv->port_offset + MAC_REG_RSPINF_A_72, - MAKEWORD(byTxRate, byRsvTime)); + iowrite16(MAKEWORD(byTxRate, byRsvTime), priv->port_offset + MAC_REG_RSPINF_A_72); /* Set to Page0 */ MACvSelectPage0(priv->port_offset); @@ -734,9 +721,9 @@ u64 CARDqGetTSFOffset(unsigned char byRxRate, u64 qwTSF1, u64 qwTSF2) * In: * priv - The adapter to be read * Out: - * qwCurrTSF - Current TSF counter + * none * - * Return Value: true if success; otherwise false + * Return Value: Current TSF counter */ u64 vt6655_get_current_tsf(struct vnt_private *priv) { @@ -745,7 +732,7 @@ u64 vt6655_get_current_tsf(struct vnt_private *priv) unsigned char data; u32 low, high; - MACvRegBitsOn(iobase, MAC_REG_TFTCTL, TFTCTL_TSFCNTRRD); + vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TSFCNTRRD); for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { data = ioread8(iobase + MAC_REG_TFTCTL); if (!(data & TFTCTL_TSFCNTRRD)) @@ -808,9 +795,10 @@ void CARDvSetFirstNextTBTT(struct vnt_private *priv, qwNextTBTT = CARDqGetNextTBTT(qwNextTBTT, wBeaconInterval); /* Set NextTBTT */ - VNSvOutPortD(iobase + MAC_REG_NEXTTBTT, (u32)qwNextTBTT); - VNSvOutPortD(iobase + MAC_REG_NEXTTBTT + 4, (u32)(qwNextTBTT >> 32)); - MACvRegBitsOn(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); + qwNextTBTT = le64_to_cpu(qwNextTBTT); + iowrite32((u32)qwNextTBTT, iobase + MAC_REG_NEXTTBTT); + iowrite32((u32)(qwNextTBTT >> 32), iobase + MAC_REG_NEXTTBTT + 4); + vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); } /* @@ -834,8 +822,9 @@ void CARDvUpdateNextTBTT(struct vnt_private *priv, u64 qwTSF, qwTSF = CARDqGetNextTBTT(qwTSF, wBeaconInterval); /* Set NextTBTT */ - VNSvOutPortD(iobase + MAC_REG_NEXTTBTT, (u32)qwTSF); - VNSvOutPortD(iobase + MAC_REG_NEXTTBTT + 4, (u32)(qwTSF >> 32)); - MACvRegBitsOn(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); + qwTSF = le64_to_cpu(qwTSF); + iowrite32((u32)qwTSF, iobase + MAC_REG_NEXTTBTT); + iowrite32((u32)(qwTSF >> 32), iobase + MAC_REG_NEXTTBTT + 4); + vt6655_mac_reg_bits_on(iobase, MAC_REG_TFTCTL, TFTCTL_TBTTSYNCEN); pr_debug("Card:Update Next TBTT[%8llx]\n", qwTSF); } diff --git a/drivers/staging/vt6655/channel.c b/drivers/staging/vt6655/channel.c index 652dcaf61169..e926f9829a15 100644 --- a/drivers/staging/vt6655/channel.c +++ b/drivers/staging/vt6655/channel.c @@ -94,7 +94,7 @@ bool set_channel(struct vnt_private *priv, struct ieee80211_channel *ch) } /* clear NAV */ - MACvRegBitsOn(priv->port_offset, MAC_REG_MACCR, MACCR_CLRNAV); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_MACCR, MACCR_CLRNAV); /* TX_PE will reserve 3 us for MAX2829 A mode only, * it is for better TX throughput diff --git a/drivers/staging/vt6655/device_main.c b/drivers/staging/vt6655/device_main.c index e8ac7b93b58c..bab08a40fe66 100644 --- a/drivers/staging/vt6655/device_main.c +++ b/drivers/staging/vt6655/device_main.c @@ -122,6 +122,9 @@ static int vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent); static void device_free_info(struct vnt_private *priv); static void device_print_info(struct vnt_private *priv); +static void vt6655_mac_write_bssid_addr(void __iomem *iobase, const u8 *mac_addr); +static void vt6655_mac_read_ether_addr(void __iomem *iobase, u8 *mac_addr); + static int device_init_rd0_ring(struct vnt_private *priv); static int device_init_rd1_ring(struct vnt_private *priv); static int device_init_td0_ring(struct vnt_private *priv); @@ -186,6 +189,22 @@ device_set_options(struct vnt_private *priv) pr_debug(" byBBType= %d\n", (int)priv->byBBType); } +static void vt6655_mac_write_bssid_addr(void __iomem *iobase, const u8 *mac_addr) +{ + iowrite8(1, iobase + MAC_REG_PAGE1SEL); + for (int i = 0; i < 6; i++) + iowrite8(mac_addr[i], iobase + MAC_REG_BSSID0 + i); + iowrite8(0, iobase + MAC_REG_PAGE1SEL); +} + +static void vt6655_mac_read_ether_addr(void __iomem *iobase, u8 *mac_addr) +{ + iowrite8(1, iobase + MAC_REG_PAGE1SEL); + for (int i = 0; i < 6; i++) + mac_addr[i] = ioread8(iobase + MAC_REG_PAR0 + i); + iowrite8(0, iobase + MAC_REG_PAGE1SEL); +} + /* * Initialisation of MAC & BBP registers */ @@ -340,8 +359,8 @@ static void device_init_registers(struct vnt_private *priv) } /* use relative tx timeout and 802.11i D4 */ - MACvWordRegBitsOn(priv->port_offset, - MAC_REG_CFG, (CFG_TKIPOPT | CFG_NOTXTIMEOUT)); + vt6655_mac_word_reg_bits_on(priv->port_offset, MAC_REG_CFG, + (CFG_TKIPOPT | CFG_NOTXTIMEOUT)); /* set performance parameter by registry */ MACvSetShortRetryLimit(priv, priv->byShortRetryLimit); @@ -398,7 +417,7 @@ static void device_init_registers(struct vnt_private *priv) CARDvSafeResetTx(priv); if (priv->local_id <= REV_ID_VT3253_A1) - MACvRegBitsOn(priv->port_offset, MAC_REG_RCR, RCR_WPAERR); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_RCR, RCR_WPAERR); /* Turn On Rx DMA */ MACvReceive0(priv->port_offset); @@ -1055,13 +1074,12 @@ static void vnt_interrupt_process(struct vnt_private *priv) * update ISR counter */ while (isr && priv->vif) { - MACvWriteISR(priv->port_offset, isr); + iowrite32(isr, priv->port_offset + MAC_REG_ISR); if (isr & ISR_FETALERR) { pr_debug(" ISR_FETALERR\n"); iowrite8(0, priv->port_offset + MAC_REG_SOFTPWRCTL); - VNSvOutPortW(priv->port_offset + - MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPECTI); + iowrite16(SOFTPWRCTL_SWPECTI, priv->port_offset + MAC_REG_SOFTPWRCTL); device_error(priv, isr); } @@ -1135,7 +1153,7 @@ static void vnt_interrupt_work(struct work_struct *work) if (priv->vif) vnt_interrupt_process(priv); - MACvIntEnable(priv->port_offset, IMR_MASK_VALUE); + iowrite32(IMR_MASK_VALUE, priv->port_offset + MAC_REG_IMR); } static irqreturn_t vnt_interrupt(int irq, void *arg) @@ -1144,7 +1162,7 @@ static irqreturn_t vnt_interrupt(int irq, void *arg) schedule_work(&priv->interrupt_work); - MACvIntDisable(priv->port_offset); + iowrite32(0, priv->port_offset + MAC_REG_IMR); return IRQ_HANDLED; } @@ -1253,8 +1271,8 @@ static int vnt_start(struct ieee80211_hw *hw) device_init_registers(priv); - dev_dbg(&priv->pcid->dev, "call MACvIntEnable\n"); - MACvIntEnable(priv->port_offset, IMR_MASK_VALUE); + dev_dbg(&priv->pcid->dev, "enable MAC interrupt\n"); + iowrite32(IMR_MASK_VALUE, priv->port_offset + MAC_REG_IMR); ieee80211_wake_queues(hw); @@ -1304,15 +1322,15 @@ static int vnt_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif) case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_ADHOC: - MACvRegBitsOff(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); - MACvRegBitsOn(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); break; case NL80211_IFTYPE_AP: - MACvRegBitsOff(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_RCR, RCR_UNICAST); - MACvRegBitsOn(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); break; default: @@ -1333,16 +1351,16 @@ static void vnt_remove_interface(struct ieee80211_hw *hw, case NL80211_IFTYPE_STATION: break; case NL80211_IFTYPE_ADHOC: - MACvRegBitsOff(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); - MACvRegBitsOff(priv->port_offset, - MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - MACvRegBitsOff(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); + vt6655_mac_reg_bits_off(priv->port_offset, + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_ADHOC); break; case NL80211_IFTYPE_AP: - MACvRegBitsOff(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); - MACvRegBitsOff(priv->port_offset, - MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); - MACvRegBitsOff(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); + vt6655_mac_reg_bits_off(priv->port_offset, + MAC_REG_TFTCTL, TFTCTL_TSFCNTREN); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_HOSTCR, HOSTCR_AP); break; default: break; @@ -1406,7 +1424,7 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw, spin_lock_irqsave(&priv->lock, flags); - MACvWriteBSSIDAddress(priv->port_offset, conf->bssid); + vt6655_mac_write_bssid_addr(priv->port_offset, conf->bssid); spin_unlock_irqrestore(&priv->lock, flags); } @@ -1458,11 +1476,10 @@ static void vnt_bss_info_changed(struct ieee80211_hw *hw, if (conf->enable_beacon) { vnt_beacon_enable(priv, vif, conf); - MACvRegBitsOn(priv->port_offset, MAC_REG_TCR, - TCR_AUTOBCNTX); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); } else { - MACvRegBitsOff(priv->port_offset, MAC_REG_TCR, - TCR_AUTOBCNTX); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TCR, + TCR_AUTOBCNTX); } } @@ -1523,20 +1540,17 @@ static void vnt_configure(struct ieee80211_hw *hw, if (priv->mc_list_count > 2) { MACvSelectPage1(priv->port_offset); - VNSvOutPortD(priv->port_offset + - MAC_REG_MAR0, 0xffffffff); - VNSvOutPortD(priv->port_offset + - MAC_REG_MAR0 + 4, 0xffffffff); + iowrite32(0xffffffff, priv->port_offset + MAC_REG_MAR0); + iowrite32(0xffffffff, priv->port_offset + MAC_REG_MAR0 + 4); MACvSelectPage0(priv->port_offset); } else { MACvSelectPage1(priv->port_offset); - VNSvOutPortD(priv->port_offset + - MAC_REG_MAR0, (u32)multicast); - VNSvOutPortD(priv->port_offset + - MAC_REG_MAR0 + 4, - (u32)(multicast >> 32)); + multicast = le64_to_cpu(multicast); + iowrite32((u32)multicast, priv->port_offset + MAC_REG_MAR0); + iowrite32((u32)(multicast >> 32), + priv->port_offset + MAC_REG_MAR0 + 4); MACvSelectPage0(priv->port_offset); } @@ -1726,7 +1740,7 @@ vt6655_probe(struct pci_dev *pcid, const struct pci_device_id *ent) } /* initial to reload eeprom */ MACvInitialize(priv); - MACvReadEtherAddress(priv->port_offset, priv->abyCurrentNetAddr); + vt6655_mac_read_ether_addr(priv->port_offset, priv->abyCurrentNetAddr); /* Get RFType */ priv->byRFType = SROMbyReadEmbedded(priv->port_offset, EEP_OFS_RFTYPE); diff --git a/drivers/staging/vt6655/mac.c b/drivers/staging/vt6655/mac.c index 88ddd0676463..dcc649532737 100644 --- a/drivers/staging/vt6655/mac.c +++ b/drivers/staging/vt6655/mac.c @@ -38,6 +38,47 @@ #include "mac.h" +void vt6655_mac_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask) +{ + unsigned char reg_value; + + reg_value = ioread8(iobase + reg_offset); + iowrite8(reg_value | bit_mask, iobase + reg_offset); +} + +void vt6655_mac_word_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask) +{ + unsigned short reg_value; + + reg_value = ioread16(iobase + reg_offset); + iowrite16(reg_value | (bit_mask), iobase + reg_offset); +} + +void vt6655_mac_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask) +{ + unsigned char reg_value; + + reg_value = ioread8(iobase + reg_offset); + iowrite8(reg_value & ~(bit_mask), iobase + reg_offset); +} + +void vt6655_mac_word_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask) +{ + unsigned short reg_value; + + reg_value = ioread16(iobase + reg_offset); + iowrite16(reg_value & ~(bit_mask), iobase + reg_offset); +} + +static void vt6655_mac_clear_stck_ds(void __iomem *iobase) +{ + u8 reg_value; + + reg_value = ioread8(iobase + MAC_REG_STICKHW); + reg_value = reg_value & 0xFC; + iowrite8(reg_value, iobase + MAC_REG_STICKHW); +} + /* * Description: * Test if all test bits off @@ -337,7 +378,7 @@ bool MACbSafeRxOff(struct vnt_private *priv) } /* try to safe shutdown RX */ - MACvRegBitsOff(io_base, MAC_REG_HOSTCR, HOSTCR_RXON); + vt6655_mac_reg_bits_off(io_base, MAC_REG_HOSTCR, HOSTCR_RXON); /* W_MAX_TIMEOUT is the timeout period */ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { if (!(ioread8(io_base + MAC_REG_HOSTCR) & HOSTCR_RXONST)) @@ -392,7 +433,7 @@ bool MACbSafeTxOff(struct vnt_private *priv) } /* try to safe shutdown TX */ - MACvRegBitsOff(io_base, MAC_REG_HOSTCR, HOSTCR_TXON); + vt6655_mac_reg_bits_off(io_base, MAC_REG_HOSTCR, HOSTCR_TXON); /* W_MAX_TIMEOUT is the timeout period */ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { @@ -423,7 +464,7 @@ bool MACbSafeStop(struct vnt_private *priv) { void __iomem *io_base = priv->port_offset; - MACvRegBitsOff(io_base, MAC_REG_TCR, TCR_AUTOBCNTX); + vt6655_mac_reg_bits_off(io_base, MAC_REG_TCR, TCR_AUTOBCNTX); if (!MACbSafeRxOff(priv)) { pr_debug(" MACbSafeRxOff == false)\n"); @@ -436,7 +477,7 @@ bool MACbSafeStop(struct vnt_private *priv) return false; } - MACvRegBitsOff(io_base, MAC_REG_HOSTCR, HOSTCR_MACEN); + vt6655_mac_reg_bits_off(io_base, MAC_REG_HOSTCR, HOSTCR_MACEN); return true; } @@ -458,7 +499,7 @@ bool MACbShutdown(struct vnt_private *priv) { void __iomem *io_base = priv->port_offset; /* disable MAC IMR */ - MACvIntDisable(io_base); + iowrite32(0, io_base + MAC_REG_IMR); MACvSetLoopbackMode(priv, MAC_LB_INTERNAL); /* stop the adapter */ if (!MACbSafeStop(priv)) { @@ -486,7 +527,7 @@ void MACvInitialize(struct vnt_private *priv) { void __iomem *io_base = priv->port_offset; /* clear sticky bits */ - MACvClearStckDS(io_base); + vt6655_mac_clear_stck_ds(io_base); /* disable force PME-enable */ iowrite8(PME_OVR, io_base + MAC_REG_PMC1); /* only 3253 A */ @@ -730,7 +771,7 @@ bool MACbPSWakeup(struct vnt_private *priv) return true; /* Disable PS */ - MACvRegBitsOff(io_base, MAC_REG_PSCTL, PSCTL_PSEN); + vt6655_mac_reg_bits_off(io_base, MAC_REG_PSCTL, PSCTL_PSEN); /* Check if SyncFlushOK */ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { diff --git a/drivers/staging/vt6655/mac.h b/drivers/staging/vt6655/mac.h index 57ae3bdbdb2d..0122c4603c66 100644 --- a/drivers/staging/vt6655/mac.h +++ b/drivers/staging/vt6655/mac.h @@ -18,7 +18,7 @@ #ifndef __MAC_H__ #define __MAC_H__ -#include "upc.h" +#include "device.h" /*--------------------- Export Definitions -------------------------*/ /* Registers in the MAC */ @@ -537,82 +537,14 @@ /*--------------------- Export Macros ------------------------------*/ -#define MACvRegBitsOn(iobase, byRegOfs, byBits) \ -do { \ - unsigned char byData; \ - byData = ioread8(iobase + byRegOfs); \ - iowrite8(byData | (byBits), iobase + byRegOfs); \ -} while (0) - -#define MACvWordRegBitsOn(iobase, byRegOfs, wBits) \ -do { \ - unsigned short wData; \ - wData = ioread16(iobase + byRegOfs); \ - VNSvOutPortW(iobase + byRegOfs, wData | (wBits)); \ -} while (0) - -#define MACvRegBitsOff(iobase, byRegOfs, byBits) \ -do { \ - unsigned char byData; \ - byData = ioread8(iobase + byRegOfs); \ - iowrite8(byData & ~(byBits), iobase + byRegOfs); \ -} while (0) - -#define MACvWordRegBitsOff(iobase, byRegOfs, wBits) \ -do { \ - unsigned short wData; \ - wData = ioread16(iobase + byRegOfs); \ - VNSvOutPortW(iobase + byRegOfs, wData & ~(wBits)); \ -} while (0) - -/* set the chip with current BCN tx descriptor address */ -#define MACvSetCurrBCNTxDescAddr(iobase, dwCurrDescAddr) \ - VNSvOutPortD(iobase + MAC_REG_BCNDMAPTR, \ - dwCurrDescAddr) - -/* set the chip with current BCN length */ -#define MACvSetCurrBCNLength(iobase, wCurrBCNLength) \ - VNSvOutPortW(iobase + MAC_REG_BCNDMACTL + 2, \ - wCurrBCNLength) - -#define MACvWriteBSSIDAddress(iobase, pbyEtherAddr) \ -do { \ - iowrite8(1, iobase + MAC_REG_PAGE1SEL); \ - iowrite8(pbyEtherAddr[0], iobase + MAC_REG_BSSID0); \ - iowrite8(pbyEtherAddr[1], iobase + MAC_REG_BSSID0 + 1); \ - iowrite8(pbyEtherAddr[2], iobase + MAC_REG_BSSID0 + 2); \ - iowrite8(pbyEtherAddr[3], iobase + MAC_REG_BSSID0 + 3); \ - iowrite8(pbyEtherAddr[4], iobase + MAC_REG_BSSID0 + 4); \ - iowrite8(pbyEtherAddr[5], iobase + MAC_REG_BSSID0 + 5); \ - iowrite8(0, iobase + MAC_REG_PAGE1SEL); \ -} while (0) - -#define MACvReadEtherAddress(iobase, pbyEtherAddr) \ -do { \ - iowrite8(1, iobase + MAC_REG_PAGE1SEL); \ - pbyEtherAddr[0] = ioread8(iobase + MAC_REG_PAR0); \ - pbyEtherAddr[1] = ioread8(iobase + MAC_REG_PAR0 + 1); \ - pbyEtherAddr[2] = ioread8(iobase + MAC_REG_PAR0 + 2); \ - pbyEtherAddr[3] = ioread8(iobase + MAC_REG_PAR0 + 3); \ - pbyEtherAddr[4] = ioread8(iobase + MAC_REG_PAR0 + 4); \ - pbyEtherAddr[5] = ioread8(iobase + MAC_REG_PAR0 + 5); \ - iowrite8(0, iobase + MAC_REG_PAGE1SEL); \ -} while (0) - -#define MACvRx0PerPktMode(iobase) \ - VNSvOutPortD(iobase + MAC_REG_RXDMACTL0, RX_PERPKT) - -#define MACvRx1PerPktMode(iobase) \ - VNSvOutPortD(iobase + MAC_REG_RXDMACTL1, RX_PERPKT) - #define MACvReceive0(iobase) \ do { \ unsigned long dwData; \ dwData = ioread32(iobase + MAC_REG_RXDMACTL0); \ if (dwData & DMACTL_RUN) \ - VNSvOutPortD(iobase + MAC_REG_RXDMACTL0, DMACTL_WAKE); \ + iowrite32(DMACTL_WAKE, iobase + MAC_REG_RXDMACTL0); \ else \ - VNSvOutPortD(iobase + MAC_REG_RXDMACTL0, DMACTL_RUN); \ + iowrite32(DMACTL_RUN, iobase + MAC_REG_RXDMACTL0); \ } while (0) #define MACvReceive1(iobase) \ @@ -620,9 +552,9 @@ do { \ unsigned long dwData; \ dwData = ioread32(iobase + MAC_REG_RXDMACTL1); \ if (dwData & DMACTL_RUN) \ - VNSvOutPortD(iobase + MAC_REG_RXDMACTL1, DMACTL_WAKE); \ + iowrite32(DMACTL_WAKE, iobase + MAC_REG_RXDMACTL1); \ else \ - VNSvOutPortD(iobase + MAC_REG_RXDMACTL1, DMACTL_RUN); \ + iowrite32(DMACTL_RUN, iobase + MAC_REG_RXDMACTL1); \ } while (0) #define MACvTransmit0(iobase) \ @@ -630,9 +562,9 @@ do { \ unsigned long dwData; \ dwData = ioread32(iobase + MAC_REG_TXDMACTL0); \ if (dwData & DMACTL_RUN) \ - VNSvOutPortD(iobase + MAC_REG_TXDMACTL0, DMACTL_WAKE); \ + iowrite32(DMACTL_WAKE, iobase + MAC_REG_TXDMACTL0); \ else \ - VNSvOutPortD(iobase + MAC_REG_TXDMACTL0, DMACTL_RUN); \ + iowrite32(DMACTL_RUN, iobase + MAC_REG_TXDMACTL0); \ } while (0) #define MACvTransmitAC0(iobase) \ @@ -640,28 +572,11 @@ do { \ unsigned long dwData; \ dwData = ioread32(iobase + MAC_REG_AC0DMACTL); \ if (dwData & DMACTL_RUN) \ - VNSvOutPortD(iobase + MAC_REG_AC0DMACTL, DMACTL_WAKE); \ + iowrite32(DMACTL_WAKE, iobase + MAC_REG_AC0DMACTL); \ else \ - VNSvOutPortD(iobase + MAC_REG_AC0DMACTL, DMACTL_RUN); \ + iowrite32(DMACTL_RUN, iobase + MAC_REG_AC0DMACTL); \ } while (0) -#define MACvClearStckDS(iobase) \ -do { \ - unsigned char byOrgValue; \ - byOrgValue = ioread8(iobase + MAC_REG_STICKHW); \ - byOrgValue = byOrgValue & 0xFC; \ - iowrite8(byOrgValue, iobase + MAC_REG_STICKHW); \ -} while (0) - -#define MACvWriteISR(iobase, dwValue) \ - VNSvOutPortD(iobase + MAC_REG_ISR, dwValue) - -#define MACvIntEnable(iobase, dwMask) \ - VNSvOutPortD(iobase + MAC_REG_IMR, dwMask) - -#define MACvIntDisable(iobase) \ - VNSvOutPortD(iobase + MAC_REG_IMR, 0) - #define MACvSelectPage0(iobase) \ iowrite8(0, iobase + MAC_REG_PAGE1SEL) @@ -673,7 +588,7 @@ do { \ unsigned long dwOrgValue; \ dwOrgValue = ioread32(iobase + MAC_REG_ENCFG); \ dwOrgValue = dwOrgValue | ENCFG_PROTECTMD; \ - VNSvOutPortD(iobase + MAC_REG_ENCFG, dwOrgValue); \ + iowrite32((u32)dwOrgValue, iobase + MAC_REG_ENCFG); \ } while (0) #define MACvDisableProtectMD(iobase) \ @@ -681,7 +596,7 @@ do { \ unsigned long dwOrgValue; \ dwOrgValue = ioread32(iobase + MAC_REG_ENCFG); \ dwOrgValue = dwOrgValue & ~ENCFG_PROTECTMD; \ - VNSvOutPortD(iobase + MAC_REG_ENCFG, dwOrgValue); \ + iowrite32((u32)dwOrgValue, iobase + MAC_REG_ENCFG); \ } while (0) #define MACvEnableBarkerPreambleMd(iobase) \ @@ -689,7 +604,7 @@ do { \ unsigned long dwOrgValue; \ dwOrgValue = ioread32(iobase + MAC_REG_ENCFG); \ dwOrgValue = dwOrgValue | ENCFG_BARKERPREAM; \ - VNSvOutPortD(iobase + MAC_REG_ENCFG, dwOrgValue); \ + iowrite32((u32)dwOrgValue, iobase + MAC_REG_ENCFG); \ } while (0) #define MACvDisableBarkerPreambleMd(iobase) \ @@ -697,7 +612,7 @@ do { \ unsigned long dwOrgValue; \ dwOrgValue = ioread32(iobase + MAC_REG_ENCFG); \ dwOrgValue = dwOrgValue & ~ENCFG_BARKERPREAM; \ - VNSvOutPortD(iobase + MAC_REG_ENCFG, dwOrgValue); \ + iowrite32((u32)dwOrgValue, iobase + MAC_REG_ENCFG); \ } while (0) #define MACvSetBBType(iobase, byTyp) \ @@ -706,15 +621,20 @@ do { \ dwOrgValue = ioread32(iobase + MAC_REG_ENCFG); \ dwOrgValue = dwOrgValue & ~ENCFG_BBTYPE_MASK; \ dwOrgValue = dwOrgValue | (unsigned long)byTyp; \ - VNSvOutPortD(iobase + MAC_REG_ENCFG, dwOrgValue); \ + iowrite32((u32)dwOrgValue, iobase + MAC_REG_ENCFG); \ } while (0) #define MACvSetRFLE_LatchBase(iobase) \ - MACvWordRegBitsOn(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_RFLEOPT) + vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_RFLEOPT) #define MAKEWORD(lb, hb) \ ((unsigned short)(((unsigned char)(lb)) | (((unsigned short)((unsigned char)(hb))) << 8))) +void vt6655_mac_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask); +void vt6655_mac_word_reg_bits_on(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask); +void vt6655_mac_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u8 bit_mask); +void vt6655_mac_word_reg_bits_off(void __iomem *iobase, const u8 reg_offset, const u16 bit_mask); + bool MACbIsRegBitsOff(struct vnt_private *priv, unsigned char byRegOfs, unsigned char byTestBits); diff --git a/drivers/staging/vt6655/power.c b/drivers/staging/vt6655/power.c index 06066fa56dd5..8527ad3eff48 100644 --- a/drivers/staging/vt6655/power.c +++ b/drivers/staging/vt6655/power.c @@ -52,30 +52,30 @@ void PSvEnablePowerSaving(struct vnt_private *priv, u16 wAID = priv->current_aid | BIT(14) | BIT(15); /* set period of power up before TBTT */ - VNSvOutPortW(priv->port_offset + MAC_REG_PWBT, C_PWBT); + iowrite16(C_PWBT, priv->port_offset + MAC_REG_PWBT); if (priv->op_mode != NL80211_IFTYPE_ADHOC) { /* set AID */ - VNSvOutPortW(priv->port_offset + MAC_REG_AIDATIM, wAID); + iowrite16(wAID, priv->port_offset + MAC_REG_AIDATIM); } /* Set AutoSleep */ - MACvRegBitsOn(priv->port_offset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); /* Set HWUTSF */ - MACvRegBitsOn(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); if (wListenInterval >= 2) { /* clear always listen beacon */ - MACvRegBitsOff(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); /* first time set listen next beacon */ - MACvRegBitsOn(priv->port_offset, MAC_REG_PSCTL, PSCTL_LNBCN); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_LNBCN); } else { /* always listen beacon */ - MACvRegBitsOn(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); } /* enable power saving hw function */ - MACvRegBitsOn(priv->port_offset, MAC_REG_PSCTL, PSCTL_PSEN); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_PSEN); priv->bEnablePSMode = true; priv->bPWBitOn = true; @@ -98,13 +98,13 @@ void PSvDisablePowerSaving(struct vnt_private *priv) MACbPSWakeup(priv); /* clear AutoSleep */ - MACvRegBitsOff(priv->port_offset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_PSCFG, PSCFG_AUTOSLEEP); /* clear HWUTSF */ - MACvRegBitsOff(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); + vt6655_mac_reg_bits_off(priv->port_offset, MAC_REG_TFTCTL, TFTCTL_HWUTSF); /* set always listen beacon */ - MACvRegBitsOn(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_ALBCN); priv->bEnablePSMode = false; @@ -135,8 +135,7 @@ bool PSbIsNextTBTTWakeUp(struct vnt_private *priv) if (priv->wake_up_count == 1) { /* Turn on wake up to listen next beacon */ - MACvRegBitsOn(priv->port_offset, - MAC_REG_PSCTL, PSCTL_LNBCN); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_PSCTL, PSCTL_LNBCN); wake_up = true; } } diff --git a/drivers/staging/vt6655/rf.c b/drivers/staging/vt6655/rf.c index ee5e2e0d9a8c..1fadc2fc4412 100644 --- a/drivers/staging/vt6655/rf.c +++ b/drivers/staging/vt6655/rf.c @@ -171,7 +171,7 @@ bool IFRFbWriteEmbedded(struct vnt_private *priv, unsigned long dwData) unsigned short ww; unsigned long dwValue; - VNSvOutPortD(iobase + MAC_REG_IFREGCTL, dwData); + iowrite32((u32)dwData, iobase + MAC_REG_IFREGCTL); /* W_MAX_TIMEOUT is the timeout period */ for (ww = 0; ww < W_MAX_TIMEOUT; ww++) { @@ -209,10 +209,10 @@ static bool RFbAL2230Init(struct vnt_private *priv) /* 3-wire control for normal mode */ iowrite8(0, iobase + MAC_REG_SOFTPWRCTL); - MACvWordRegBitsOn(iobase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPECTI | - SOFTPWRCTL_TXPEINV)); + vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, + (SOFTPWRCTL_SWPECTI | SOFTPWRCTL_TXPEINV)); /* PLL Off */ - MACvWordRegBitsOff(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3); + vt6655_mac_word_reg_bits_off(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3); /* patch abnormal AL2230 frequency output */ IFRFbWriteEmbedded(priv, (0x07168700 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW)); @@ -222,7 +222,7 @@ static bool RFbAL2230Init(struct vnt_private *priv) MACvTimer0MicroSDelay(priv, 30); /* delay 30 us */ /* PLL On */ - MACvWordRegBitsOn(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3); + vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, SOFTPWRCTL_SWPE3); MACvTimer0MicroSDelay(priv, 150);/* 150us */ ret &= IFRFbWriteEmbedded(priv, (0x00d80f00 + (BY_AL2230_REG_LEN << 3) + IFREGCTL_REGW)); @@ -232,10 +232,10 @@ static bool RFbAL2230Init(struct vnt_private *priv) ret &= IFRFbWriteEmbedded(priv, al2230_init_table[CB_AL2230_INIT_SEQ - 1]); - MACvWordRegBitsOn(iobase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 | - SOFTPWRCTL_SWPE2 | - SOFTPWRCTL_SWPECTI | - SOFTPWRCTL_TXPEINV)); + vt6655_mac_word_reg_bits_on(iobase, MAC_REG_SOFTPWRCTL, (SOFTPWRCTL_SWPE3 | + SOFTPWRCTL_SWPE2 | + SOFTPWRCTL_SWPECTI | + SOFTPWRCTL_TXPEINV)); /* 3-wire control for power saving mode */ iowrite8(PSSIG_WPE3 | PSSIG_WPE2, iobase + MAC_REG_PSPWRSIG); @@ -350,7 +350,7 @@ bool rf_write_wake_prog_syn(struct vnt_private *priv, unsigned char rf_type, unsigned char sleep_count = 0; unsigned short idx = MISCFIFO_SYNDATA_IDX; - VNSvOutPortW(iobase + MAC_REG_MISCFFNDEX, 0); + iowrite16(0, iobase + MAC_REG_MISCFFNDEX); switch (rf_type) { case RF_AIROHA: case RF_AL2230S: diff --git a/drivers/staging/vt6655/rxtx.c b/drivers/staging/vt6655/rxtx.c index 85cd01c463e8..5bdb5176772c 100644 --- a/drivers/staging/vt6655/rxtx.c +++ b/drivers/staging/vt6655/rxtx.c @@ -1420,11 +1420,11 @@ static int vnt_beacon_xmit(struct vnt_private *priv, priv->wBCNBufLen = sizeof(*short_head) + skb->len; - MACvSetCurrBCNTxDescAddr(priv->port_offset, priv->tx_beacon_dma); + iowrite32((u32)priv->tx_beacon_dma, priv->port_offset + MAC_REG_BCNDMAPTR); - MACvSetCurrBCNLength(priv->port_offset, priv->wBCNBufLen); + iowrite16(priv->wBCNBufLen, priv->port_offset + MAC_REG_BCNDMACTL + 2); /* Set auto Transmit on */ - MACvRegBitsOn(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); + vt6655_mac_reg_bits_on(priv->port_offset, MAC_REG_TCR, TCR_AUTOBCNTX); /* Poll Transmit the adapter */ iowrite8(BEACON_READY, priv->port_offset + MAC_REG_BCNDMACTL); diff --git a/drivers/staging/vt6655/srom.c b/drivers/staging/vt6655/srom.c index 722a2cc9a473..ee5ca4db74dc 100644 --- a/drivers/staging/vt6655/srom.c +++ b/drivers/staging/vt6655/srom.c @@ -27,7 +27,7 @@ * */ -#include "upc.h" +#include "device.h" #include "mac.h" #include "srom.h" diff --git a/drivers/staging/vt6655/upc.h b/drivers/staging/vt6655/upc.h deleted file mode 100644 index 2a47f5782b71..000000000000 --- a/drivers/staging/vt6655/upc.h +++ /dev/null @@ -1,35 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (c) 1996, 2003 VIA Networking Technologies, Inc. - * All rights reserved. - * - * Purpose: Macros to access device - * - * Author: Tevin Chen - * - * Date: Mar 17, 1997 - * - */ - -#ifndef __UPC_H__ -#define __UPC_H__ - -#include "device.h" - -/*--------------------- Export Definitions -------------------------*/ - -/* For memory mapped IO */ - -#define VNSvOutPortW(dwIOAddress, wData) \ - iowrite16((u16)(wData), dwIOAddress) - -#define VNSvOutPortD(dwIOAddress, dwData) \ - iowrite32((u32)(dwData), dwIOAddress) - -/*--------------------- Export Classes ----------------------------*/ - -/*--------------------- Export Variables --------------------------*/ - -/*--------------------- Export Functions --------------------------*/ - -#endif /* __UPC_H__ */ diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index e368f038ff5c..baf4da7bb3b4 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -1004,8 +1004,10 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, unsigned char *buf) { int data_direction, payload_length; + struct iscsi_ecdb_ahdr *ecdb_ahdr; struct iscsi_scsi_req *hdr; int iscsi_task_attr; + unsigned char *cdb; int sam_task_attr; atomic_long_inc(&conn->sess->cmd_pdus); @@ -1106,6 +1108,27 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, ISCSI_REASON_BOOKMARK_INVALID, buf); } + cdb = hdr->cdb; + + if (hdr->hlength) { + ecdb_ahdr = (struct iscsi_ecdb_ahdr *) (hdr + 1); + if (ecdb_ahdr->ahstype != ISCSI_AHSTYPE_CDB) { + pr_err("Additional Header Segment type %d not supported!\n", + ecdb_ahdr->ahstype); + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_CMD_NOT_SUPPORTED, buf); + } + + cdb = kmalloc(be16_to_cpu(ecdb_ahdr->ahslength) + 15, + GFP_KERNEL); + if (cdb == NULL) + return iscsit_add_reject_cmd(cmd, + ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + memcpy(cdb, hdr->cdb, ISCSI_CDB_SIZE); + memcpy(cdb + ISCSI_CDB_SIZE, ecdb_ahdr->ecdb, + be16_to_cpu(ecdb_ahdr->ahslength) - 1); + } + data_direction = (hdr->flags & ISCSI_FLAG_CMD_WRITE) ? DMA_TO_DEVICE : (hdr->flags & ISCSI_FLAG_CMD_READ) ? DMA_FROM_DEVICE : DMA_NONE; @@ -1153,9 +1176,12 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, struct iscsi_datain_req *dr; dr = iscsit_allocate_datain_req(); - if (!dr) + if (!dr) { + if (cdb != hdr->cdb) + kfree(cdb); return iscsit_add_reject_cmd(cmd, ISCSI_REASON_BOOKMARK_NO_RESOURCES, buf); + } iscsit_attach_datain_req(cmd, dr); } @@ -1176,9 +1202,12 @@ int iscsit_setup_scsi_cmd(struct iscsit_conn *conn, struct iscsit_cmd *cmd, target_get_sess_cmd(&cmd->se_cmd, true); cmd->se_cmd.tag = (__force u32)cmd->init_task_tag; - cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, hdr->cdb, + cmd->sense_reason = target_cmd_init_cdb(&cmd->se_cmd, cdb, GFP_KERNEL); + if (cdb != hdr->cdb) + kfree(cdb); + if (cmd->sense_reason) { if (cmd->sense_reason == TCM_OUT_OF_RESOURCES) { return iscsit_add_reject_cmd(cmd, @@ -4036,8 +4065,9 @@ static bool iscsi_target_check_conn_state(struct iscsit_conn *conn) static void iscsit_get_rx_pdu(struct iscsit_conn *conn) { int ret; - u8 *buffer, opcode; + u8 *buffer, *tmp_buf, opcode; u32 checksum = 0, digest = 0; + struct iscsi_hdr *hdr; struct kvec iov; buffer = kcalloc(ISCSI_HDR_LEN, sizeof(*buffer), GFP_KERNEL); @@ -4062,6 +4092,25 @@ static void iscsit_get_rx_pdu(struct iscsit_conn *conn) break; } + hdr = (struct iscsi_hdr *) buffer; + if (hdr->hlength) { + iov.iov_len = hdr->hlength * 4; + tmp_buf = krealloc(buffer, + ISCSI_HDR_LEN + iov.iov_len, + GFP_KERNEL); + if (!tmp_buf) + break; + + buffer = tmp_buf; + iov.iov_base = &buffer[ISCSI_HDR_LEN]; + + ret = rx_data(conn, &iov, 1, iov.iov_len); + if (ret != iov.iov_len) { + iscsit_rx_thread_wait_for_tcp(conn); + break; + } + } + if (conn->conn_ops->HeaderDigest) { iov.iov_base = &digest; iov.iov_len = ISCSI_CRC_LEN; @@ -4361,7 +4410,7 @@ int iscsit_close_connection( spin_lock_bh(&sess->conn_lock); atomic_dec(&sess->nconn); - pr_debug("Decremented iSCSI connection count to %hu from node:" + pr_debug("Decremented iSCSI connection count to %d from node:" " %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); /* diff --git a/drivers/target/iscsi/iscsi_target_auth.c b/drivers/target/iscsi/iscsi_target_auth.c index 6e5611d8f51b..c8a248bd11be 100644 --- a/drivers/target/iscsi/iscsi_target_auth.c +++ b/drivers/target/iscsi/iscsi_target_auth.c @@ -205,6 +205,38 @@ static struct iscsi_chap *chap_server_open( return chap; } +static const char base64_lookup_table[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +static int chap_base64_decode(u8 *dst, const char *src, size_t len) +{ + int i, bits = 0, ac = 0; + const char *p; + u8 *cp = dst; + + for (i = 0; i < len; i++) { + if (src[i] == '=') + return cp - dst; + + p = strchr(base64_lookup_table, src[i]); + if (p == NULL || src[i] == 0) + return -2; + + ac <<= 6; + ac += (p - base64_lookup_table); + bits += 6; + if (bits >= 8) { + *cp++ = (ac >> (bits - 8)) & 0xff; + ac &= ~(BIT(16) - BIT(bits - 8)); + bits -= 8; + } + } + if (ac) + return -1; + + return cp - dst; +} + static int chap_server_compute_hash( struct iscsit_conn *conn, struct iscsi_node_auth *auth, @@ -295,16 +327,27 @@ static int chap_server_compute_hash( pr_err("Could not find CHAP_R.\n"); goto out; } - if (type != HEX) { - pr_err("Could not find CHAP_R.\n"); - goto out; - } - if (strlen(chap_r) != chap->digest_size * 2) { - pr_err("Malformed CHAP_R\n"); - goto out; - } - if (hex2bin(client_digest, chap_r, chap->digest_size) < 0) { - pr_err("Malformed CHAP_R\n"); + + switch (type) { + case HEX: + if (strlen(chap_r) != chap->digest_size * 2) { + pr_err("Malformed CHAP_R\n"); + goto out; + } + if (hex2bin(client_digest, chap_r, chap->digest_size) < 0) { + pr_err("Malformed CHAP_R: invalid HEX\n"); + goto out; + } + break; + case BASE64: + if (chap_base64_decode(client_digest, chap_r, strlen(chap_r)) != + chap->digest_size) { + pr_err("Malformed CHAP_R: invalid BASE64\n"); + goto out; + } + break; + default: + pr_err("Could not find CHAP_R\n"); goto out; } @@ -373,7 +416,13 @@ static int chap_server_compute_hash( /* * Get CHAP_I. */ - if (extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type) < 0) { + ret = extract_param(nr_in_ptr, "CHAP_I", 10, identifier, &type); + if (ret == -ENOENT) { + pr_debug("Could not find CHAP_I. Initiator uses One way authentication.\n"); + auth_ret = 0; + goto out; + } + if (ret < 0) { pr_err("Could not find CHAP_I.\n"); goto out; } @@ -404,23 +453,46 @@ static int chap_server_compute_hash( goto out; } - if (type != HEX) { + switch (type) { + case HEX: + initiatorchg_len = DIV_ROUND_UP(strlen(initiatorchg), 2); + if (!initiatorchg_len) { + pr_err("Unable to convert incoming challenge\n"); + goto out; + } + if (initiatorchg_len > 1024) { + pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); + goto out; + } + + if (hex2bin(initiatorchg_binhex, initiatorchg, + initiatorchg_len) < 0) { + pr_err("Malformed CHAP_C: invalid HEX\n"); + goto out; + } + break; + case BASE64: + initiatorchg_len = chap_base64_decode(initiatorchg_binhex, + initiatorchg, + strlen(initiatorchg)); + if (initiatorchg_len < 0) { + pr_err("Malformed CHAP_C: invalid BASE64\n"); + goto out; + } + if (!initiatorchg_len) { + pr_err("Unable to convert incoming challenge\n"); + goto out; + } + if (initiatorchg_len > 1024) { + pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); + goto out; + } + break; + default: pr_err("Could not find CHAP_C.\n"); goto out; } - initiatorchg_len = DIV_ROUND_UP(strlen(initiatorchg), 2); - if (!initiatorchg_len) { - pr_err("Unable to convert incoming challenge\n"); - goto out; - } - if (initiatorchg_len > 1024) { - pr_err("CHAP_C exceeds maximum binary size of 1024 bytes\n"); - goto out; - } - if (hex2bin(initiatorchg_binhex, initiatorchg, initiatorchg_len) < 0) { - pr_err("Malformed CHAP_C\n"); - goto out; - } + pr_debug("[server] Got CHAP_C=%s\n", initiatorchg); /* * During mutual authentication, the CHAP_C generated by the diff --git a/drivers/target/iscsi/iscsi_target_configfs.c b/drivers/target/iscsi/iscsi_target_configfs.c index ce14540ba650..5d0f51822414 100644 --- a/drivers/target/iscsi/iscsi_target_configfs.c +++ b/drivers/target/iscsi/iscsi_target_configfs.c @@ -210,7 +210,7 @@ static struct se_tpg_np *lio_target_call_addnptotpg( return ERR_PTR(ret); } - tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); + tpg = to_iscsi_tpg(se_tpg); ret = iscsit_get_tpg(tpg); if (ret < 0) return ERR_PTR(-EINVAL); @@ -281,9 +281,7 @@ static ssize_t iscsi_nacl_attrib_##name##_show(struct config_item *item,\ char *page) \ { \ struct se_node_acl *se_nacl = attrib_to_nacl(item); \ - struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \ - se_node_acl); \ - \ + struct iscsi_node_acl *nacl = to_iscsi_nacl(se_nacl); \ return sprintf(page, "%u\n", nacl->node_attrib.name); \ } \ \ @@ -291,8 +289,7 @@ static ssize_t iscsi_nacl_attrib_##name##_store(struct config_item *item,\ const char *page, size_t count) \ { \ struct se_node_acl *se_nacl = attrib_to_nacl(item); \ - struct iscsi_node_acl *nacl = container_of(se_nacl, struct iscsi_node_acl, \ - se_node_acl); \ + struct iscsi_node_acl *nacl = to_iscsi_nacl(se_nacl); \ u32 val; \ int ret; \ \ @@ -317,6 +314,36 @@ ISCSI_NACL_ATTR(random_datain_pdu_offsets); ISCSI_NACL_ATTR(random_datain_seq_offsets); ISCSI_NACL_ATTR(random_r2t_offsets); +static ssize_t iscsi_nacl_attrib_authentication_show(struct config_item *item, + char *page) +{ + struct se_node_acl *se_nacl = attrib_to_nacl(item); + struct iscsi_node_acl *nacl = to_iscsi_nacl(se_nacl); + + return sprintf(page, "%d\n", nacl->node_attrib.authentication); +} + +static ssize_t iscsi_nacl_attrib_authentication_store(struct config_item *item, + const char *page, size_t count) +{ + struct se_node_acl *se_nacl = attrib_to_nacl(item); + struct iscsi_node_acl *nacl = to_iscsi_nacl(se_nacl); + s32 val; + int ret; + + ret = kstrtos32(page, 0, &val); + if (ret) + return ret; + if (val != 0 && val != 1 && val != NA_AUTHENTICATION_INHERITED) + return -EINVAL; + + nacl->node_attrib.authentication = val; + + return count; +} + +CONFIGFS_ATTR(iscsi_nacl_attrib_, authentication); + static struct configfs_attribute *lio_target_nacl_attrib_attrs[] = { &iscsi_nacl_attrib_attr_dataout_timeout, &iscsi_nacl_attrib_attr_dataout_timeout_retries, @@ -326,6 +353,7 @@ static struct configfs_attribute *lio_target_nacl_attrib_attrs[] = { &iscsi_nacl_attrib_attr_random_datain_pdu_offsets, &iscsi_nacl_attrib_attr_random_datain_seq_offsets, &iscsi_nacl_attrib_attr_random_r2t_offsets, + &iscsi_nacl_attrib_attr_authentication, NULL, }; @@ -377,15 +405,14 @@ static ssize_t iscsi_nacl_auth_##name##_show(struct config_item *item, \ char *page) \ { \ struct se_node_acl *nacl = auth_to_nacl(item); \ - return __iscsi_nacl_auth_##name##_show(container_of(nacl, \ - struct iscsi_node_acl, se_node_acl), page); \ + return __iscsi_nacl_auth_##name##_show(to_iscsi_nacl(nacl), page); \ } \ static ssize_t iscsi_nacl_auth_##name##_store(struct config_item *item, \ const char *page, size_t count) \ { \ struct se_node_acl *nacl = auth_to_nacl(item); \ - return __iscsi_nacl_auth_##name##_store(container_of(nacl, \ - struct iscsi_node_acl, se_node_acl), page, count); \ + return __iscsi_nacl_auth_##name##_store(to_iscsi_nacl(nacl), \ + page, count); \ } \ \ CONFIGFS_ATTR(iscsi_nacl_auth_, name) @@ -417,8 +444,7 @@ static ssize_t iscsi_nacl_auth_##name##_show(struct config_item *item, \ char *page) \ { \ struct se_node_acl *nacl = auth_to_nacl(item); \ - return __iscsi_nacl_auth_##name##_show(container_of(nacl, \ - struct iscsi_node_acl, se_node_acl), page); \ + return __iscsi_nacl_auth_##name##_show(to_iscsi_nacl(nacl), page); \ } \ \ CONFIGFS_ATTR_RO(iscsi_nacl_auth_, name) @@ -623,8 +649,7 @@ static ssize_t lio_target_nacl_cmdsn_depth_store(struct config_item *item, { struct se_node_acl *se_nacl = acl_to_nacl(item); struct se_portal_group *se_tpg = se_nacl->se_tpg; - struct iscsi_portal_group *tpg = container_of(se_tpg, - struct iscsi_portal_group, tpg_se_tpg); + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); struct config_item *acl_ci, *tpg_ci, *wwn_ci; u32 cmdsn_depth = 0; int ret; @@ -700,8 +725,7 @@ static struct configfs_attribute *lio_target_initiator_attrs[] = { static int lio_target_init_nodeacl(struct se_node_acl *se_nacl, const char *name) { - struct iscsi_node_acl *acl = - container_of(se_nacl, struct iscsi_node_acl, se_node_acl); + struct iscsi_node_acl *acl = to_iscsi_nacl(se_nacl); config_group_init_type_name(&acl->node_stat_grps.iscsi_sess_stats_group, "iscsi_sess_stats", &iscsi_stat_sess_cit); @@ -720,8 +744,7 @@ static ssize_t iscsi_tpg_attrib_##name##_show(struct config_item *item, \ char *page) \ { \ struct se_portal_group *se_tpg = attrib_to_tpg(item); \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ ssize_t rb; \ \ if (iscsit_get_tpg(tpg) < 0) \ @@ -736,8 +759,7 @@ static ssize_t iscsi_tpg_attrib_##name##_store(struct config_item *item,\ const char *page, size_t count) \ { \ struct se_portal_group *se_tpg = attrib_to_tpg(item); \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ u32 val; \ int ret; \ \ @@ -800,8 +822,7 @@ static struct configfs_attribute *lio_target_tpg_attrib_attrs[] = { static ssize_t __iscsi_##prefix##_##name##_show(struct se_portal_group *se_tpg, \ char *page) \ { \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \ \ if (!capable(CAP_SYS_ADMIN)) \ @@ -813,8 +834,7 @@ static ssize_t __iscsi_##prefix##_##name##_show(struct se_portal_group *se_tpg, static ssize_t __iscsi_##prefix##_##name##_store(struct se_portal_group *se_tpg,\ const char *page, size_t count) \ { \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \ \ if (!capable(CAP_SYS_ADMIN)) \ @@ -861,8 +881,7 @@ DEF_TPG_AUTH_STR(password_mutual, NAF_PASSWORD_IN_SET); static ssize_t __iscsi_##prefix##_##name##_show(struct se_portal_group *se_tpg, \ char *page) \ { \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ struct iscsi_node_auth *auth = &tpg->tpg_demo_auth; \ \ if (!capable(CAP_SYS_ADMIN)) \ @@ -900,8 +919,7 @@ static ssize_t iscsi_tpg_param_##name##_show(struct config_item *item, \ char *page) \ { \ struct se_portal_group *se_tpg = param_to_tpg(item); \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ struct iscsi_param *param; \ ssize_t rb; \ \ @@ -923,8 +941,7 @@ static ssize_t iscsi_tpg_param_##name##_store(struct config_item *item, \ const char *page, size_t count) \ { \ struct se_portal_group *se_tpg = param_to_tpg(item); \ - struct iscsi_portal_group *tpg = container_of(se_tpg, \ - struct iscsi_portal_group, tpg_se_tpg); \ + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); \ char *buf; \ int ret, len; \ \ @@ -1073,8 +1090,7 @@ free_out: static int lio_target_tiqn_enabletpg(struct se_portal_group *se_tpg, bool enable) { - struct iscsi_portal_group *tpg = container_of(se_tpg, - struct iscsi_portal_group, tpg_se_tpg); + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); int ret; ret = iscsit_get_tpg(tpg); @@ -1106,7 +1122,7 @@ static void lio_target_tiqn_deltpg(struct se_portal_group *se_tpg) struct iscsi_portal_group *tpg; struct iscsi_tiqn *tiqn; - tpg = container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); + tpg = to_iscsi_tpg(se_tpg); tiqn = tpg->tpg_tiqn; /* * iscsit_tpg_del_portal_group() assumes force=1 @@ -1416,46 +1432,41 @@ static void lio_aborted_task(struct se_cmd *se_cmd) cmd->conn->conn_transport->iscsit_aborted_task(cmd->conn, cmd); } -static inline struct iscsi_portal_group *iscsi_tpg(struct se_portal_group *se_tpg) -{ - return container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); -} - static char *lio_tpg_get_endpoint_wwn(struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_tiqn->tiqn; + return to_iscsi_tpg(se_tpg)->tpg_tiqn->tiqn; } static u16 lio_tpg_get_tag(struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpgt; + return to_iscsi_tpg(se_tpg)->tpgt; } static u32 lio_tpg_get_default_depth(struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_attrib.default_cmdsn_depth; + return to_iscsi_tpg(se_tpg)->tpg_attrib.default_cmdsn_depth; } static int lio_tpg_check_demo_mode(struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_attrib.generate_node_acls; + return to_iscsi_tpg(se_tpg)->tpg_attrib.generate_node_acls; } static int lio_tpg_check_demo_mode_cache(struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_attrib.cache_dynamic_acls; + return to_iscsi_tpg(se_tpg)->tpg_attrib.cache_dynamic_acls; } static int lio_tpg_check_demo_mode_write_protect( struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_attrib.demo_mode_write_protect; + return to_iscsi_tpg(se_tpg)->tpg_attrib.demo_mode_write_protect; } static int lio_tpg_check_prod_mode_write_protect( struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_attrib.prod_mode_write_protect; + return to_iscsi_tpg(se_tpg)->tpg_attrib.prod_mode_write_protect; } static int lio_tpg_check_prot_fabric_only( @@ -1465,9 +1476,9 @@ static int lio_tpg_check_prot_fabric_only( * Only report fabric_prot_type if t10_pi has also been enabled * for incoming ib_isert sessions. */ - if (!iscsi_tpg(se_tpg)->tpg_attrib.t10_pi) + if (!to_iscsi_tpg(se_tpg)->tpg_attrib.t10_pi) return 0; - return iscsi_tpg(se_tpg)->tpg_attrib.fabric_prot_type; + return to_iscsi_tpg(se_tpg)->tpg_attrib.fabric_prot_type; } /* @@ -1504,16 +1515,14 @@ static void lio_tpg_close_session(struct se_session *se_sess) static u32 lio_tpg_get_inst_index(struct se_portal_group *se_tpg) { - return iscsi_tpg(se_tpg)->tpg_tiqn->tiqn_index; + return to_iscsi_tpg(se_tpg)->tpg_tiqn->tiqn_index; } static void lio_set_default_node_attributes(struct se_node_acl *se_acl) { - struct iscsi_node_acl *acl = container_of(se_acl, struct iscsi_node_acl, - se_node_acl); + struct iscsi_node_acl *acl = to_iscsi_nacl(se_acl); struct se_portal_group *se_tpg = se_acl->se_tpg; - struct iscsi_portal_group *tpg = container_of(se_tpg, - struct iscsi_portal_group, tpg_se_tpg); + struct iscsi_portal_group *tpg = to_iscsi_tpg(se_tpg); acl->node_attrib.nacl = acl; iscsit_set_default_node_attribues(acl, tpg); diff --git a/drivers/target/iscsi/iscsi_target_login.c b/drivers/target/iscsi/iscsi_target_login.c index 6b94eecc4790..27e448c2d066 100644 --- a/drivers/target/iscsi/iscsi_target_login.c +++ b/drivers/target/iscsi/iscsi_target_login.c @@ -341,6 +341,7 @@ static int iscsi_login_zero_tsih_s2( { struct iscsi_node_attrib *na; struct iscsit_session *sess = conn->sess; + struct iscsi_param *param; bool iser = false; sess->tpg = conn->tpg; @@ -374,6 +375,18 @@ static int iscsi_login_zero_tsih_s2( na = iscsit_tpg_get_node_attrib(sess); + /* + * If ACL allows non-authorized access in TPG with CHAP, + * then set None to AuthMethod. + */ + param = iscsi_find_param_from_key(AUTHMETHOD, conn->param_list); + if (param && !strstr(param->value, NONE)) { + if (!iscsi_conn_auth_required(conn)) + if (iscsi_change_param_sprintf(conn, "AuthMethod=%s", + NONE)) + return -1; + } + /* * Need to send TargetPortalGroupTag back in first login response * on any iSCSI connection where the Initiator provides TargetName. @@ -715,7 +728,7 @@ void iscsi_post_login_handler( list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); - pr_debug("Incremented iSCSI Connection count to %hu" + pr_debug("Incremented iSCSI Connection count to %d" " from node: %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); spin_unlock_bh(&sess->conn_lock); @@ -763,7 +776,7 @@ void iscsi_post_login_handler( spin_lock_bh(&sess->conn_lock); list_add_tail(&conn->conn_list, &sess->sess_conn_list); atomic_inc(&sess->nconn); - pr_debug("Incremented iSCSI Connection count to %hu from node:" + pr_debug("Incremented iSCSI Connection count to %d from node:" " %s\n", atomic_read(&sess->nconn), sess->sess_ops->InitiatorName); spin_unlock_bh(&sess->conn_lock); diff --git a/drivers/target/iscsi/iscsi_target_nego.c b/drivers/target/iscsi/iscsi_target_nego.c index b34ac9ecac31..f2919319ad38 100644 --- a/drivers/target/iscsi/iscsi_target_nego.c +++ b/drivers/target/iscsi/iscsi_target_nego.c @@ -62,31 +62,34 @@ int extract_param( int len; if (!in_buf || !pattern || !out_buf || !type) - return -1; + return -EINVAL; ptr = strstr(in_buf, pattern); if (!ptr) - return -1; + return -ENOENT; ptr = strstr(ptr, "="); if (!ptr) - return -1; + return -EINVAL; ptr += 1; if (*ptr == '0' && (*(ptr+1) == 'x' || *(ptr+1) == 'X')) { ptr += 2; /* skip 0x */ *type = HEX; + } else if (*ptr == '0' && (*(ptr+1) == 'b' || *(ptr+1) == 'B')) { + ptr += 2; /* skip 0b */ + *type = BASE64; } else *type = DECIMAL; len = strlen_semi(ptr); if (len < 0) - return -1; + return -EINVAL; if (len >= max_length) { pr_err("Length of input: %d exceeds max_length:" " %d\n", len, max_length); - return -1; + return -EINVAL; } memcpy(out_buf, ptr, len); out_buf[len] = '\0'; @@ -94,6 +97,31 @@ int extract_param( return 0; } +static struct iscsi_node_auth *iscsi_get_node_auth(struct iscsit_conn *conn) +{ + struct iscsi_portal_group *tpg; + struct iscsi_node_acl *nacl; + struct se_node_acl *se_nacl; + + if (conn->sess->sess_ops->SessionType) + return &iscsit_global->discovery_acl.node_auth; + + se_nacl = conn->sess->se_sess->se_node_acl; + if (!se_nacl) { + pr_err("Unable to locate struct se_node_acl for CHAP auth\n"); + return NULL; + } + + if (se_nacl->dynamic_node_acl) { + tpg = to_iscsi_tpg(se_nacl->se_tpg); + return &tpg->tpg_demo_auth; + } + + nacl = to_iscsi_nacl(se_nacl); + + return &nacl->node_auth; +} + static u32 iscsi_handle_authentication( struct iscsit_conn *conn, char *in_buf, @@ -102,40 +130,11 @@ static u32 iscsi_handle_authentication( int *out_length, unsigned char *authtype) { - struct iscsit_session *sess = conn->sess; struct iscsi_node_auth *auth; - struct iscsi_node_acl *iscsi_nacl; - struct iscsi_portal_group *iscsi_tpg; - struct se_node_acl *se_nacl; - if (!sess->sess_ops->SessionType) { - /* - * For SessionType=Normal - */ - se_nacl = conn->sess->se_sess->se_node_acl; - if (!se_nacl) { - pr_err("Unable to locate struct se_node_acl for" - " CHAP auth\n"); - return -1; - } - - if (se_nacl->dynamic_node_acl) { - iscsi_tpg = container_of(se_nacl->se_tpg, - struct iscsi_portal_group, tpg_se_tpg); - - auth = &iscsi_tpg->tpg_demo_auth; - } else { - iscsi_nacl = container_of(se_nacl, struct iscsi_node_acl, - se_node_acl); - - auth = &iscsi_nacl->node_auth; - } - } else { - /* - * For SessionType=Discovery - */ - auth = &iscsit_global->discovery_acl.node_auth; - } + auth = iscsi_get_node_auth(conn); + if (!auth) + return -1; if (strstr("CHAP", authtype)) strcpy(conn->sess->auth_type, "CHAP"); @@ -815,6 +814,42 @@ static int iscsi_target_do_authentication( return 0; } +bool iscsi_conn_auth_required(struct iscsit_conn *conn) +{ + struct iscsi_node_acl *nacl; + struct se_node_acl *se_nacl; + + if (conn->sess->sess_ops->SessionType) { + /* + * For SessionType=Discovery + */ + return conn->tpg->tpg_attrib.authentication; + } + /* + * For SessionType=Normal + */ + se_nacl = conn->sess->se_sess->se_node_acl; + if (!se_nacl) { + pr_debug("Unknown ACL is trying to connect\n"); + return true; + } + + if (se_nacl->dynamic_node_acl) { + pr_debug("Dynamic ACL %s is trying to connect\n", + se_nacl->initiatorname); + return conn->tpg->tpg_attrib.authentication; + } + + pr_debug("Known ACL %s is trying to connect\n", + se_nacl->initiatorname); + + nacl = to_iscsi_nacl(se_nacl); + if (nacl->node_attrib.authentication == NA_AUTHENTICATION_INHERITED) + return conn->tpg->tpg_attrib.authentication; + + return nacl->node_attrib.authentication; +} + static int iscsi_target_handle_csg_zero( struct iscsit_conn *conn, struct iscsi_login *login) @@ -876,23 +911,27 @@ static int iscsi_target_handle_csg_zero( return -1; if (!iscsi_check_negotiated_keys(conn->param_list)) { - if (conn->tpg->tpg_attrib.authentication && - !strncmp(param->value, NONE, 4)) { - pr_err("Initiator sent AuthMethod=None but" - " Target is enforcing iSCSI Authentication," - " login failed.\n"); - iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, - ISCSI_LOGIN_STATUS_AUTH_FAILED); - return -1; + bool auth_required = iscsi_conn_auth_required(conn); + + if (auth_required) { + if (!strncmp(param->value, NONE, 4)) { + pr_err("Initiator sent AuthMethod=None but" + " Target is enforcing iSCSI Authentication," + " login failed.\n"); + iscsit_tx_login_rsp(conn, + ISCSI_STATUS_CLS_INITIATOR_ERR, + ISCSI_LOGIN_STATUS_AUTH_FAILED); + return -1; + } + + if (!login->auth_complete) + return 0; + + if (strncmp(param->value, NONE, 4) && + !login->auth_complete) + return 0; } - if (conn->tpg->tpg_attrib.authentication && - !login->auth_complete) - return 0; - - if (strncmp(param->value, NONE, 4) && !login->auth_complete) - return 0; - if ((login_req->flags & ISCSI_FLAG_LOGIN_NEXT_STAGE1) && (login_req->flags & ISCSI_FLAG_LOGIN_TRANSIT)) { login_rsp->flags |= ISCSI_FLAG_LOGIN_NEXT_STAGE1 | @@ -906,6 +945,18 @@ do_auth: return iscsi_target_do_authentication(conn, login); } +static bool iscsi_conn_authenticated(struct iscsit_conn *conn, + struct iscsi_login *login) +{ + if (!iscsi_conn_auth_required(conn)) + return true; + + if (login->auth_complete) + return true; + + return false; +} + static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_login *login) { int ret; @@ -949,11 +1000,10 @@ static int iscsi_target_handle_csg_one(struct iscsit_conn *conn, struct iscsi_lo return -1; } - if (!login->auth_complete && - conn->tpg->tpg_attrib.authentication) { + if (!iscsi_conn_authenticated(conn, login)) { pr_err("Initiator is requesting CSG: 1, has not been" - " successfully authenticated, and the Target is" - " enforcing iSCSI Authentication, login failed.\n"); + " successfully authenticated, and the Target is" + " enforcing iSCSI Authentication, login failed.\n"); iscsit_tx_login_rsp(conn, ISCSI_STATUS_CLS_INITIATOR_ERR, ISCSI_LOGIN_STATUS_AUTH_FAILED); return -1; diff --git a/drivers/target/iscsi/iscsi_target_nego.h b/drivers/target/iscsi/iscsi_target_nego.h index ed30b9ee75e6..41c3db3ddeaa 100644 --- a/drivers/target/iscsi/iscsi_target_nego.h +++ b/drivers/target/iscsi/iscsi_target_nego.h @@ -4,6 +4,7 @@ #define DECIMAL 0 #define HEX 1 +#define BASE64 2 struct iscsit_conn; struct iscsi_login; @@ -21,5 +22,5 @@ extern int iscsi_target_locate_portal(struct iscsi_np *, struct iscsit_conn *, extern int iscsi_target_start_negotiation( struct iscsi_login *, struct iscsit_conn *); extern void iscsi_target_nego_release(struct iscsit_conn *); - +extern bool iscsi_conn_auth_required(struct iscsit_conn *conn); #endif /* ISCSI_TARGET_NEGO_H */ diff --git a/drivers/target/iscsi/iscsi_target_nodeattrib.c b/drivers/target/iscsi/iscsi_target_nodeattrib.c index 874cb33c9be0..d63efdefb18e 100644 --- a/drivers/target/iscsi/iscsi_target_nodeattrib.c +++ b/drivers/target/iscsi/iscsi_target_nodeattrib.c @@ -30,6 +30,7 @@ void iscsit_set_default_node_attribues( { struct iscsi_node_attrib *a = &acl->node_attrib; + a->authentication = NA_AUTHENTICATION_INHERITED; a->dataout_timeout = NA_DATAOUT_TIMEOUT; a->dataout_timeout_retries = NA_DATAOUT_TIMEOUT_RETRIES; a->nopin_timeout = NA_NOPIN_TIMEOUT; diff --git a/drivers/target/iscsi/iscsi_target_tpg.c b/drivers/target/iscsi/iscsi_target_tpg.c index 4339ee517434..3cac1aafef68 100644 --- a/drivers/target/iscsi/iscsi_target_tpg.c +++ b/drivers/target/iscsi/iscsi_target_tpg.c @@ -394,8 +394,7 @@ struct iscsi_node_attrib *iscsit_tpg_get_node_attrib( { struct se_session *se_sess = sess->se_sess; struct se_node_acl *se_nacl = se_sess->se_node_acl; - struct iscsi_node_acl *acl = container_of(se_nacl, struct iscsi_node_acl, - se_node_acl); + struct iscsi_node_acl *acl = to_iscsi_nacl(se_nacl); return &acl->node_attrib; } diff --git a/drivers/target/target_core_alua.c b/drivers/target/target_core_alua.c index b56ef8af66e7..58df0145e8d0 100644 --- a/drivers/target/target_core_alua.c +++ b/drivers/target/target_core_alua.c @@ -385,7 +385,7 @@ target_emulate_set_target_port_groups(struct se_cmd *cmd) /* * Extract the RELATIVE TARGET PORT IDENTIFIER to identify - * the Target Port in question for the the incoming + * the Target Port in question for the incoming * SET_TARGET_PORT_GROUPS op. */ rtpi = get_unaligned_be16(ptr + 2); diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index bbcbbfa72b07..416514c5c7ac 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -732,6 +732,7 @@ static ssize_t emulate_tpu_store(struct config_item *item, const char *page, size_t count) { struct se_dev_attrib *da = to_attrib(item); + struct se_device *dev = da->da_dev; bool flag; int ret; @@ -744,8 +745,11 @@ static ssize_t emulate_tpu_store(struct config_item *item, * Discard supported is detected iblock_create_virtdevice(). */ if (flag && !da->max_unmap_block_desc_count) { - pr_err("Generic Block Discard not supported\n"); - return -ENOSYS; + if (!dev->transport->configure_unmap || + !dev->transport->configure_unmap(dev)) { + pr_err("Generic Block Discard not supported\n"); + return -ENOSYS; + } } da->emulate_tpu = flag; @@ -758,6 +762,7 @@ static ssize_t emulate_tpws_store(struct config_item *item, const char *page, size_t count) { struct se_dev_attrib *da = to_attrib(item); + struct se_device *dev = da->da_dev; bool flag; int ret; @@ -770,8 +775,11 @@ static ssize_t emulate_tpws_store(struct config_item *item, * Discard supported is detected iblock_create_virtdevice(). */ if (flag && !da->max_unmap_block_desc_count) { - pr_err("Generic Block Discard not supported\n"); - return -ENOSYS; + if (!dev->transport->configure_unmap || + !dev->transport->configure_unmap(dev)) { + pr_err("Generic Block Discard not supported\n"); + return -ENOSYS; + } } da->emulate_tpws = flag; @@ -964,6 +972,7 @@ static ssize_t unmap_zeroes_data_store(struct config_item *item, const char *page, size_t count) { struct se_dev_attrib *da = to_attrib(item); + struct se_device *dev = da->da_dev; bool flag; int ret; @@ -982,10 +991,12 @@ static ssize_t unmap_zeroes_data_store(struct config_item *item, * Discard supported is detected iblock_configure_device(). */ if (flag && !da->max_unmap_block_desc_count) { - pr_err("dev[%p]: Thin Provisioning LBPRZ will not be set" - " because max_unmap_block_desc_count is zero\n", - da->da_dev); - return -ENOSYS; + if (!dev->transport->configure_unmap || + !dev->transport->configure_unmap(dev)) { + pr_err("dev[%p]: Thin Provisioning LBPRZ will not be set because max_unmap_block_desc_count is zero\n", + da->da_dev); + return -ENOSYS; + } } da->unmap_zeroes_data = flag; pr_debug("dev[%p]: SE Device Thin Provisioning LBPRZ bit: %d\n", diff --git a/drivers/target/target_core_device.c b/drivers/target/target_core_device.c index 25f33eb25337..086ac9c9343c 100644 --- a/drivers/target/target_core_device.c +++ b/drivers/target/target_core_device.c @@ -960,6 +960,12 @@ int target_configure_device(struct se_device *dev) ret = dev->transport->configure_device(dev); if (ret) goto out_free_index; + + if (dev->transport->configure_unmap && + dev->transport->configure_unmap(dev)) { + pr_debug("Discard support available, but disabled by default.\n"); + } + /* * XXX: there is not much point to have two different values here.. */ diff --git a/drivers/target/target_core_file.c b/drivers/target/target_core_file.c index 6c8d8b051bfd..28aa643be5d5 100644 --- a/drivers/target/target_core_file.c +++ b/drivers/target/target_core_file.c @@ -86,6 +86,24 @@ static struct se_device *fd_alloc_device(struct se_hba *hba, const char *name) return &fd_dev->dev; } +static bool fd_configure_unmap(struct se_device *dev) +{ + struct file *file = FD_DEV(dev)->fd_file; + struct inode *inode = file->f_mapping->host; + + if (S_ISBLK(inode->i_mode)) + return target_configure_unmap_from_queue(&dev->dev_attrib, + I_BDEV(inode)); + + /* Limit UNMAP emulation to 8k Number of LBAs (NoLB) */ + dev->dev_attrib.max_unmap_lba_count = 0x2000; + /* Currently hardcoded to 1 in Linux/SCSI code. */ + dev->dev_attrib.max_unmap_block_desc_count = 1; + dev->dev_attrib.unmap_granularity = 1; + dev->dev_attrib.unmap_granularity_alignment = 0; + return true; +} + static int fd_configure_device(struct se_device *dev) { struct fd_dev *fd_dev = FD_DEV(dev); @@ -149,10 +167,6 @@ static int fd_configure_device(struct se_device *dev) " block_device blocks: %llu logical_block_size: %d\n", dev_size, div_u64(dev_size, fd_dev->fd_block_size), fd_dev->fd_block_size); - - if (target_configure_unmap_from_queue(&dev->dev_attrib, bdev)) - pr_debug("IFILE: BLOCK Discard support available," - " disabled by default\n"); /* * Enable write same emulation for IBLOCK and use 0xFFFF as * the smaller WRITE_SAME(10) only has a two-byte block count. @@ -170,16 +184,6 @@ static int fd_configure_device(struct se_device *dev) } fd_dev->fd_block_size = FD_BLOCKSIZE; - /* - * Limit UNMAP emulation to 8k Number of LBAs (NoLB) - */ - dev->dev_attrib.max_unmap_lba_count = 0x2000; - /* - * Currently hardcoded to 1 in Linux/SCSI code.. - */ - dev->dev_attrib.max_unmap_block_desc_count = 1; - dev->dev_attrib.unmap_granularity = 1; - dev->dev_attrib.unmap_granularity_alignment = 0; /* * Limit WRITE_SAME w/ UNMAP=0 emulation to 8k Number of LBAs (NoLB) @@ -438,10 +442,6 @@ fd_execute_write_same(struct se_cmd *cmd) unsigned int len = 0, i; ssize_t ret; - if (!nolb) { - target_complete_cmd(cmd, SAM_STAT_GOOD); - return 0; - } if (cmd->prot_op) { pr_err("WRITE_SAME: Protection information with FILEIO" " backends not supported\n"); @@ -927,6 +927,7 @@ static const struct target_backend_ops fileio_ops = { .configure_device = fd_configure_device, .destroy_device = fd_destroy_device, .free_device = fd_free_device, + .configure_unmap = fd_configure_unmap, .parse_cdb = fd_parse_cdb, .set_configfs_dev_params = fd_set_configfs_dev_params, .show_configfs_dev_params = fd_show_configfs_dev_params, diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index 30712a12b151..8351c974cee3 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -76,6 +76,14 @@ free_dev: return NULL; } +static bool iblock_configure_unmap(struct se_device *dev) +{ + struct iblock_dev *ib_dev = IBLOCK_DEV(dev); + + return target_configure_unmap_from_queue(&dev->dev_attrib, + ib_dev->ibd_bd); +} + static int iblock_configure_device(struct se_device *dev) { struct iblock_dev *ib_dev = IBLOCK_DEV(dev); @@ -119,10 +127,6 @@ static int iblock_configure_device(struct se_device *dev) dev->dev_attrib.hw_max_sectors = queue_max_hw_sectors(q); dev->dev_attrib.hw_queue_depth = q->nr_requests; - if (target_configure_unmap_from_queue(&dev->dev_attrib, bd)) - pr_debug("IBLOCK: BLOCK Discard support available," - " disabled by default\n"); - /* * Enable write same emulation for IBLOCK and use 0xFFFF as * the smaller WRITE_SAME(10) only has a two-byte block count. @@ -903,6 +907,7 @@ static const struct target_backend_ops iblock_ops = { .configure_device = iblock_configure_device, .destroy_device = iblock_destroy_device, .free_device = iblock_free_device, + .configure_unmap = iblock_configure_unmap, .plug_device = iblock_plug_device, .unplug_device = iblock_unplug_device, .parse_cdb = iblock_parse_cdb, diff --git a/drivers/target/target_core_sbc.c b/drivers/target/target_core_sbc.c index f6132836eb38..1e3216de1e04 100644 --- a/drivers/target/target_core_sbc.c +++ b/drivers/target/target_core_sbc.c @@ -345,68 +345,6 @@ sbc_setup_write_same(struct se_cmd *cmd, unsigned char flags, struct sbc_ops *op return 0; } -static sense_reason_t xdreadwrite_callback(struct se_cmd *cmd, bool success, - int *post_ret) -{ - unsigned char *buf, *addr; - struct scatterlist *sg; - unsigned int offset; - sense_reason_t ret = TCM_NO_SENSE; - int i, count; - - if (!success) - return 0; - - /* - * From sbc3r22.pdf section 5.48 XDWRITEREAD (10) command - * - * 1) read the specified logical block(s); - * 2) transfer logical blocks from the data-out buffer; - * 3) XOR the logical blocks transferred from the data-out buffer with - * the logical blocks read, storing the resulting XOR data in a buffer; - * 4) if the DISABLE WRITE bit is set to zero, then write the logical - * blocks transferred from the data-out buffer; and - * 5) transfer the resulting XOR data to the data-in buffer. - */ - buf = kmalloc(cmd->data_length, GFP_KERNEL); - if (!buf) { - pr_err("Unable to allocate xor_callback buf\n"); - return TCM_OUT_OF_RESOURCES; - } - /* - * Copy the scatterlist WRITE buffer located at cmd->t_data_sg - * into the locally allocated *buf - */ - sg_copy_to_buffer(cmd->t_data_sg, - cmd->t_data_nents, - buf, - cmd->data_length); - - /* - * Now perform the XOR against the BIDI read memory located at - * cmd->t_mem_bidi_list - */ - - offset = 0; - for_each_sg(cmd->t_bidi_data_sg, sg, cmd->t_bidi_data_nents, count) { - addr = kmap_atomic(sg_page(sg)); - if (!addr) { - ret = TCM_OUT_OF_RESOURCES; - goto out; - } - - for (i = 0; i < sg->length; i++) - *(addr + sg->offset + i) ^= *(buf + offset + i); - - offset += sg->length; - kunmap_atomic(addr); - } - -out: - kfree(buf); - return ret; -} - static sense_reason_t sbc_execute_rw(struct se_cmd *cmd) { @@ -933,47 +871,10 @@ sbc_parse_cdb(struct se_cmd *cmd, struct sbc_ops *ops) cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; cmd->execute_cmd = sbc_execute_rw; break; - case XDWRITEREAD_10: - if (cmd->data_direction != DMA_TO_DEVICE || - !(cmd->se_cmd_flags & SCF_BIDI)) - return TCM_INVALID_CDB_FIELD; - sectors = transport_get_sectors_10(cdb); - - if (sbc_check_dpofua(dev, cmd, cdb)) - return TCM_INVALID_CDB_FIELD; - - cmd->t_task_lba = transport_lba_32(cdb); - cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; - - /* - * Setup BIDI XOR callback to be run after I/O completion. - */ - cmd->execute_cmd = sbc_execute_rw; - cmd->transport_complete_callback = &xdreadwrite_callback; - break; case VARIABLE_LENGTH_CMD: { u16 service_action = get_unaligned_be16(&cdb[8]); switch (service_action) { - case XDWRITEREAD_32: - sectors = transport_get_sectors_32(cdb); - - if (sbc_check_dpofua(dev, cmd, cdb)) - return TCM_INVALID_CDB_FIELD; - /* - * Use WRITE_32 and READ_32 opcodes for the emulated - * XDWRITE_READ_32 logic. - */ - cmd->t_task_lba = transport_lba_64_ext(cdb); - cmd->se_cmd_flags |= SCF_SCSI_DATA_CDB; - - /* - * Setup BIDI XOR callback to be run during after I/O - * completion. - */ - cmd->execute_cmd = sbc_execute_rw; - cmd->transport_complete_callback = &xdreadwrite_callback; - break; case WRITE_SAME_32: sectors = transport_get_sectors_32(cdb); if (!sectors) { diff --git a/drivers/thermal/hisi_thermal.c b/drivers/thermal/hisi_thermal.c index b29ab09040d5..19a242c69ce6 100644 --- a/drivers/thermal/hisi_thermal.c +++ b/drivers/thermal/hisi_thermal.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * HiSilicon thermal sensor driver * @@ -6,15 +7,6 @@ * * Xinwei Kong * Leo Yan - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/thunderbolt/ctl.c b/drivers/thunderbolt/ctl.c index e92c658dba1c..e5ede5debfb0 100644 --- a/drivers/thunderbolt/ctl.c +++ b/drivers/thunderbolt/ctl.c @@ -694,7 +694,7 @@ void tb_ctl_free(struct tb_ctl *ctl) } /** - * tb_cfg_start() - start/resume the control channel + * tb_ctl_start() - start/resume the control channel * @ctl: Control channel to start */ void tb_ctl_start(struct tb_ctl *ctl) @@ -710,7 +710,7 @@ void tb_ctl_start(struct tb_ctl *ctl) } /** - * tb_ctrl_stop() - pause the control channel + * tb_ctl_stop() - pause the control channel * @ctl: Control channel to stop * * All invocations of ctl->callback will have finished after this method @@ -912,7 +912,7 @@ struct tb_cfg_result tb_cfg_read_raw(struct tb_ctl *ctl, void *buffer, } /** - * tb_cfg_write() - write from buffer into config space + * tb_cfg_write_raw() - write from buffer into config space * @ctl: Pointer to the control channel * @buffer: Data to write * @route: Route string of the router diff --git a/drivers/thunderbolt/ctl.h b/drivers/thunderbolt/ctl.h index e8c64898dfce..7c7d80f96c0c 100644 --- a/drivers/thunderbolt/ctl.h +++ b/drivers/thunderbolt/ctl.h @@ -35,7 +35,7 @@ struct tb_cfg_result { * If err = 1 then this is the port that send the * error. * If err = 0 and if this was a cfg_read/write then - * this is the the upstream port of the responding + * this is the upstream port of the responding * switch. * Otherwise the field is set to zero. */ diff --git a/drivers/thunderbolt/icm.c b/drivers/thunderbolt/icm.c index fff0c740c8f3..ae38f0d25a8d 100644 --- a/drivers/thunderbolt/icm.c +++ b/drivers/thunderbolt/icm.c @@ -2516,6 +2516,8 @@ struct tb *icm_probe(struct tb_nhi *nhi) case PCI_DEVICE_ID_INTEL_TGL_H_NHI1: case PCI_DEVICE_ID_INTEL_ADL_NHI0: case PCI_DEVICE_ID_INTEL_ADL_NHI1: + case PCI_DEVICE_ID_INTEL_RPL_NHI0: + case PCI_DEVICE_ID_INTEL_RPL_NHI1: icm->is_supported = icm_tgl_is_supported; icm->driver_ready = icm_icl_driver_ready; icm->set_uuid = icm_icl_set_uuid; diff --git a/drivers/thunderbolt/nhi.c b/drivers/thunderbolt/nhi.c index 1333b158a95e..cb8c9c4ae93a 100644 --- a/drivers/thunderbolt/nhi.c +++ b/drivers/thunderbolt/nhi.c @@ -1410,6 +1410,10 @@ static struct pci_device_id nhi_ids[] = { .driver_data = (kernel_ulong_t)&icl_nhi_ops }, { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_ADL_NHI1), .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI0), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, + { PCI_VDEVICE(INTEL, PCI_DEVICE_ID_INTEL_RPL_NHI1), + .driver_data = (kernel_ulong_t)&icl_nhi_ops }, /* Any USB4 compliant host */ { PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_USB4, ~0) }, diff --git a/drivers/thunderbolt/nhi.h b/drivers/thunderbolt/nhi.h index 69083aab2736..f09da5b62233 100644 --- a/drivers/thunderbolt/nhi.h +++ b/drivers/thunderbolt/nhi.h @@ -80,6 +80,8 @@ extern const struct tb_nhi_ops icl_nhi_ops; #define PCI_DEVICE_ID_INTEL_TGL_NHI1 0x9a1d #define PCI_DEVICE_ID_INTEL_TGL_H_NHI0 0x9a1f #define PCI_DEVICE_ID_INTEL_TGL_H_NHI1 0x9a21 +#define PCI_DEVICE_ID_INTEL_RPL_NHI0 0xa73e +#define PCI_DEVICE_ID_INTEL_RPL_NHI1 0xa76d #define PCI_CLASS_SERIAL_USB_USB4 0x0c0340 diff --git a/drivers/thunderbolt/switch.c b/drivers/thunderbolt/switch.c index 561e1d77240e..244f8cd38b25 100644 --- a/drivers/thunderbolt/switch.c +++ b/drivers/thunderbolt/switch.c @@ -3133,9 +3133,13 @@ void tb_switch_suspend(struct tb_switch *sw, bool runtime) /* * Actually only needed for Titan Ridge but for simplicity can be * done for USB4 device too as CLx is re-enabled at resume. + * CL0s and CL1 are enabled and supported together. */ - if (tb_switch_disable_clx(sw, TB_CL0S)) - tb_sw_warn(sw, "failed to disable CLx on upstream port\n"); + if (tb_switch_is_clx_enabled(sw, TB_CL1)) { + if (tb_switch_disable_clx(sw, TB_CL1)) + tb_sw_warn(sw, "failed to disable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); + } err = tb_plug_events_active(sw, false); if (err) @@ -3426,13 +3430,12 @@ static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx) } switch (clx) { - case TB_CL0S: - /* CL0s support requires also CL1 support */ + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ mask = LANE_ADP_CS_0_CL0S_SUPPORT | LANE_ADP_CS_0_CL1_SUPPORT; break; - /* For now we support only CL0s. Not CL1, CL2 */ - case TB_CL1: + /* For now we support only CL0s and CL1. Not CL2 */ case TB_CL2: default: return false; @@ -3446,18 +3449,18 @@ static bool tb_port_clx_supported(struct tb_port *port, enum tb_clx clx) return !!(val & mask); } -static inline bool tb_port_cl0s_supported(struct tb_port *port) -{ - return tb_port_clx_supported(port, TB_CL0S); -} - -static int __tb_port_cl0s_set(struct tb_port *port, bool enable) +static int __tb_port_clx_set(struct tb_port *port, enum tb_clx clx, bool enable) { u32 phy, mask; int ret; - /* To enable CL0s also required to enable CL1 */ - mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; + /* CL0s and CL1 are enabled and supported together */ + if (clx == TB_CL1) + mask = LANE_ADP_CS_1_CL0S_ENABLE | LANE_ADP_CS_1_CL1_ENABLE; + else + /* For now we support only CL0s and CL1. Not CL2 */ + return -EOPNOTSUPP; + ret = tb_port_read(port, &phy, TB_CFG_PORT, port->cap_phy + LANE_ADP_CS_1, 1); if (ret) @@ -3472,20 +3475,20 @@ static int __tb_port_cl0s_set(struct tb_port *port, bool enable) port->cap_phy + LANE_ADP_CS_1, 1); } -static int tb_port_cl0s_disable(struct tb_port *port) +static int tb_port_clx_disable(struct tb_port *port, enum tb_clx clx) { - return __tb_port_cl0s_set(port, false); + return __tb_port_clx_set(port, clx, false); } -static int tb_port_cl0s_enable(struct tb_port *port) +static int tb_port_clx_enable(struct tb_port *port, enum tb_clx clx) { - return __tb_port_cl0s_set(port, true); + return __tb_port_clx_set(port, clx, true); } -static int tb_switch_enable_cl0s(struct tb_switch *sw) +static int __tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) { struct tb_switch *parent = tb_switch_parent(sw); - bool up_cl0s_support, down_cl0s_support; + bool up_clx_support, down_clx_support; struct tb_port *up, *down; int ret; @@ -3510,37 +3513,37 @@ static int tb_switch_enable_cl0s(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_port_at(tb_route(sw), parent); - up_cl0s_support = tb_port_cl0s_supported(up); - down_cl0s_support = tb_port_cl0s_supported(down); + up_clx_support = tb_port_clx_supported(up, clx); + down_clx_support = tb_port_clx_supported(down, clx); - tb_port_dbg(up, "CL0s %ssupported\n", - up_cl0s_support ? "" : "not "); - tb_port_dbg(down, "CL0s %ssupported\n", - down_cl0s_support ? "" : "not "); + tb_port_dbg(up, "%s %ssupported\n", tb_switch_clx_name(clx), + up_clx_support ? "" : "not "); + tb_port_dbg(down, "%s %ssupported\n", tb_switch_clx_name(clx), + down_clx_support ? "" : "not "); - if (!up_cl0s_support || !down_cl0s_support) + if (!up_clx_support || !down_clx_support) return -EOPNOTSUPP; - ret = tb_port_cl0s_enable(up); + ret = tb_port_clx_enable(up, clx); if (ret) return ret; - ret = tb_port_cl0s_enable(down); + ret = tb_port_clx_enable(down, clx); if (ret) { - tb_port_cl0s_disable(up); + tb_port_clx_disable(up, clx); return ret; } ret = tb_switch_mask_clx_objections(sw); if (ret) { - tb_port_cl0s_disable(up); - tb_port_cl0s_disable(down); + tb_port_clx_disable(up, clx); + tb_port_clx_disable(down, clx); return ret; } - sw->clx = TB_CL0S; + sw->clx = clx; - tb_port_dbg(up, "CL0s enabled\n"); + tb_port_dbg(up, "%s enabled\n", tb_switch_clx_name(clx)); return 0; } @@ -3554,7 +3557,7 @@ static int tb_switch_enable_cl0s(struct tb_switch *sw) * to improve performance. CLx is enabled only if both sides of the link * support CLx, and if both sides of the link are not configured as two * single lane links and only if the link is not inter-domain link. The - * complete set of conditions is descibed in CM Guide 1.0 section 8.1. + * complete set of conditions is described in CM Guide 1.0 section 8.1. * * Return: Returns 0 on success or an error code on failure. */ @@ -3573,15 +3576,16 @@ int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx) return 0; switch (clx) { - case TB_CL0S: - return tb_switch_enable_cl0s(sw); + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + return __tb_switch_enable_clx(sw, clx); default: return -EOPNOTSUPP; } } -static int tb_switch_disable_cl0s(struct tb_switch *sw) +static int __tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) { struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *up, *down; @@ -3603,17 +3607,17 @@ static int tb_switch_disable_cl0s(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_port_at(tb_route(sw), parent); - ret = tb_port_cl0s_disable(up); + ret = tb_port_clx_disable(up, clx); if (ret) return ret; - ret = tb_port_cl0s_disable(down); + ret = tb_port_clx_disable(down, clx); if (ret) return ret; sw->clx = TB_CLX_DISABLE; - tb_port_dbg(up, "CL0s disabled\n"); + tb_port_dbg(up, "%s disabled\n", tb_switch_clx_name(clx)); return 0; } @@ -3630,8 +3634,9 @@ int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx) return 0; switch (clx) { - case TB_CL0S: - return tb_switch_disable_cl0s(sw); + case TB_CL1: + /* CL0s and CL1 are enabled and supported together */ + return __tb_switch_disable_clx(sw, clx); default: return -EOPNOTSUPP; diff --git a/drivers/thunderbolt/tb.c b/drivers/thunderbolt/tb.c index 9a3214fb5038..9853f6c7e81d 100644 --- a/drivers/thunderbolt/tb.c +++ b/drivers/thunderbolt/tb.c @@ -118,6 +118,13 @@ static void tb_switch_discover_tunnels(struct tb_switch *sw, switch (port->config.type) { case TB_TYPE_DP_HDMI_IN: tunnel = tb_tunnel_discover_dp(tb, port, alloc_hopids); + /* + * In case of DP tunnel exists, change host router's + * 1st children TMU mode to HiFi for CL0s to work. + */ + if (tunnel) + tb_switch_enable_tmu_1st_child(tb->root_switch, + TB_SWITCH_TMU_RATE_HIFI); break; case TB_TYPE_PCIE_DOWN: @@ -215,7 +222,7 @@ static int tb_enable_tmu(struct tb_switch *sw) int ret; /* If it is already enabled in correct mode, don't touch it */ - if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request)) + if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) return 0; ret = tb_switch_tmu_disable(sw); @@ -575,6 +582,7 @@ static void tb_scan_port(struct tb_port *port) struct tb_cm *tcm = tb_priv(port->sw->tb); struct tb_port *upstream_port; struct tb_switch *sw; + int ret; if (tb_is_upstream_port(port)) return; @@ -663,11 +671,24 @@ static void tb_scan_port(struct tb_port *port) tb_switch_lane_bonding_enable(sw); /* Set the link configured */ tb_switch_configure_link(sw); - if (tb_switch_enable_clx(sw, TB_CL0S)) - tb_sw_warn(sw, "failed to enable CLx on upstream port\n"); + /* + * CL0s and CL1 are enabled and supported together. + * Silently ignore CLx enabling in case CLx is not supported. + */ + ret = tb_switch_enable_clx(sw, TB_CL1); + if (ret && ret != -EOPNOTSUPP) + tb_sw_warn(sw, "failed to enable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); - tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, - tb_switch_is_clx_enabled(sw)); + if (tb_switch_is_clx_enabled(sw, TB_CL1)) + /* + * To support highest CLx state, we set router's TMU to + * Normal-Uni mode. + */ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + else + /* If CLx disabled, configure router's TMU to HiFi-Bidir mode*/ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to enable TMU\n"); @@ -965,6 +986,12 @@ static void tb_tunnel_dp(struct tb *tb) list_add_tail(&tunnel->list, &tcm->tunnel_list); tb_reclaim_usb3_bandwidth(tb, in, out); + /* + * In case of DP tunnel exists, change host router's 1st children + * TMU mode to HiFi for CL0s to work. + */ + tb_switch_enable_tmu_1st_child(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI); + return; err_free: @@ -1407,7 +1434,12 @@ static int tb_start(struct tb *tb) return ret; } - tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_HIFI, false); + /* + * To support highest CLx state, we set host router's TMU to + * Normal mode. + */ + tb_switch_tmu_configure(tb->root_switch, TB_SWITCH_TMU_RATE_NORMAL, + false); /* Enable TMU if it is off */ tb_switch_tmu_enable(tb->root_switch); /* Full scan to discover devices added before the driver was loaded. */ @@ -1446,19 +1478,31 @@ static int tb_suspend_noirq(struct tb *tb) static void tb_restore_children(struct tb_switch *sw) { struct tb_port *port; + int ret; /* No need to restore if the router is already unplugged */ if (sw->is_unplugged) return; - if (tb_switch_enable_clx(sw, TB_CL0S)) - tb_sw_warn(sw, "failed to re-enable CLx on upstream port\n"); - /* - * tb_switch_tmu_configure() was already called when the switch was - * added before entering system sleep or runtime suspend, - * so no need to call it again before enabling TMU. + * CL0s and CL1 are enabled and supported together. + * Silently ignore CLx re-enabling in case CLx is not supported. */ + ret = tb_switch_enable_clx(sw, TB_CL1); + if (ret && ret != -EOPNOTSUPP) + tb_sw_warn(sw, "failed to re-enable %s on upstream port\n", + tb_switch_clx_name(TB_CL1)); + + if (tb_switch_is_clx_enabled(sw, TB_CL1)) + /* + * To support highest CLx state, we set router's TMU to + * Normal-Uni mode. + */ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_NORMAL, true); + else + /* If CLx disabled, configure router's TMU to HiFi-Bidir mode*/ + tb_switch_tmu_configure(sw, TB_SWITCH_TMU_RATE_HIFI, false); + if (tb_enable_tmu(sw)) tb_sw_warn(sw, "failed to restore TMU configuration\n"); diff --git a/drivers/thunderbolt/tb.h b/drivers/thunderbolt/tb.h index a831faa50f65..5db76de40cc1 100644 --- a/drivers/thunderbolt/tb.h +++ b/drivers/thunderbolt/tb.h @@ -13,6 +13,7 @@ #include #include #include +#include #include "tb_regs.h" #include "ctl.h" @@ -111,7 +112,7 @@ struct tb_switch_tmu { enum tb_clx { TB_CLX_DISABLE, - TB_CL0S, + /* CL0s and CL1 are enabled and supported together */ TB_CL1, TB_CL2, }; @@ -933,46 +934,49 @@ int tb_switch_tmu_enable(struct tb_switch *sw); void tb_switch_tmu_configure(struct tb_switch *sw, enum tb_switch_tmu_rate rate, bool unidirectional); +void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, + enum tb_switch_tmu_rate rate); /** - * tb_switch_tmu_hifi_is_enabled() - Checks if the specified TMU mode is enabled + * tb_switch_tmu_is_enabled() - Checks if the specified TMU mode is enabled * @sw: Router whose TMU mode to check * @unidirectional: If uni-directional (bi-directional otherwise) * * Return true if hardware TMU configuration matches the one passed in - * as parameter. That is HiFi and either uni-directional or bi-directional. + * as parameter. That is HiFi/Normal and either uni-directional or bi-directional. */ -static inline bool tb_switch_tmu_hifi_is_enabled(const struct tb_switch *sw, - bool unidirectional) +static inline bool tb_switch_tmu_is_enabled(const struct tb_switch *sw, + bool unidirectional) { - return sw->tmu.rate == TB_SWITCH_TMU_RATE_HIFI && + return sw->tmu.rate == sw->tmu.rate_request && sw->tmu.unidirectional == unidirectional; } +static inline const char *tb_switch_clx_name(enum tb_clx clx) +{ + switch (clx) { + /* CL0s and CL1 are enabled and supported together */ + case TB_CL1: + return "CL0s/CL1"; + default: + return "unknown"; + } +} + int tb_switch_enable_clx(struct tb_switch *sw, enum tb_clx clx); int tb_switch_disable_clx(struct tb_switch *sw, enum tb_clx clx); /** * tb_switch_is_clx_enabled() - Checks if the CLx is enabled - * @sw: Router to check the CLx state for + * @sw: Router to check for the CLx + * @clx: The CLx state to check for * - * Checks if the CLx is enabled on the router upstream link. + * Checks if the specified CLx is enabled on the router upstream link. * Not applicable for a host router. */ -static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw) +static inline bool tb_switch_is_clx_enabled(const struct tb_switch *sw, + enum tb_clx clx) { - return sw->clx != TB_CLX_DISABLE; -} - -/** - * tb_switch_is_cl0s_enabled() - Checks if the CL0s is enabled - * @sw: Router to check for the CL0s - * - * Checks if the CL0s is enabled on the router upstream link. - * Not applicable for a host router. - */ -static inline bool tb_switch_is_cl0s_enabled(const struct tb_switch *sw) -{ - return sw->clx == TB_CL0S; + return sw->clx == clx; } /** diff --git a/drivers/thunderbolt/tb_regs.h b/drivers/thunderbolt/tb_regs.h index 6a16f61a72a1..166054110388 100644 --- a/drivers/thunderbolt/tb_regs.h +++ b/drivers/thunderbolt/tb_regs.h @@ -234,6 +234,7 @@ enum usb4_switch_op { /* Router TMU configuration */ #define TMU_RTR_CS_0 0x00 +#define TMU_RTR_CS_0_FREQ_WIND_MASK GENMASK(26, 16) #define TMU_RTR_CS_0_TD BIT(27) #define TMU_RTR_CS_0_UCAP BIT(30) #define TMU_RTR_CS_1 0x01 @@ -244,6 +245,11 @@ enum usb4_switch_op { #define TMU_RTR_CS_3_LOCAL_TIME_NS_MASK GENMASK(15, 0) #define TMU_RTR_CS_3_TS_PACKET_INTERVAL_MASK GENMASK(31, 16) #define TMU_RTR_CS_3_TS_PACKET_INTERVAL_SHIFT 16 +#define TMU_RTR_CS_15 0xf +#define TMU_RTR_CS_15_FREQ_AVG_MASK GENMASK(5, 0) +#define TMU_RTR_CS_15_DELAY_AVG_MASK GENMASK(11, 6) +#define TMU_RTR_CS_15_OFFSET_AVG_MASK GENMASK(17, 12) +#define TMU_RTR_CS_15_ERROR_AVG_MASK GENMASK(23, 18) #define TMU_RTR_CS_22 0x16 #define TMU_RTR_CS_24 0x18 #define TMU_RTR_CS_25 0x19 diff --git a/drivers/thunderbolt/tmu.c b/drivers/thunderbolt/tmu.c index e4a07a26f693..626aca3124b1 100644 --- a/drivers/thunderbolt/tmu.c +++ b/drivers/thunderbolt/tmu.c @@ -11,6 +11,55 @@ #include "tb.h" +static int tb_switch_set_tmu_mode_params(struct tb_switch *sw, + enum tb_switch_tmu_rate rate) +{ + u32 freq_meas_wind[2] = { 30, 800 }; + u32 avg_const[2] = { 4, 8 }; + u32 freq, avg, val; + int ret; + + if (rate == TB_SWITCH_TMU_RATE_NORMAL) { + freq = freq_meas_wind[0]; + avg = avg_const[0]; + } else if (rate == TB_SWITCH_TMU_RATE_HIFI) { + freq = freq_meas_wind[1]; + avg = avg_const[1]; + } else { + return 0; + } + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); + if (ret) + return ret; + + val &= ~TMU_RTR_CS_0_FREQ_WIND_MASK; + val |= FIELD_PREP(TMU_RTR_CS_0_FREQ_WIND_MASK, freq); + + ret = tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_0, 1); + if (ret) + return ret; + + ret = tb_sw_read(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_15, 1); + if (ret) + return ret; + + val &= ~TMU_RTR_CS_15_FREQ_AVG_MASK & + ~TMU_RTR_CS_15_DELAY_AVG_MASK & + ~TMU_RTR_CS_15_OFFSET_AVG_MASK & + ~TMU_RTR_CS_15_ERROR_AVG_MASK; + val |= FIELD_PREP(TMU_RTR_CS_15_FREQ_AVG_MASK, avg) | + FIELD_PREP(TMU_RTR_CS_15_DELAY_AVG_MASK, avg) | + FIELD_PREP(TMU_RTR_CS_15_OFFSET_AVG_MASK, avg) | + FIELD_PREP(TMU_RTR_CS_15_ERROR_AVG_MASK, avg); + + return tb_sw_write(sw, &val, TB_CFG_SWITCH, + sw->tmu.cap + TMU_RTR_CS_15, 1); +} + static const char *tb_switch_tmu_mode_name(const struct tb_switch *sw) { bool root_switch = !tb_route(sw); @@ -348,7 +397,7 @@ int tb_switch_tmu_disable(struct tb_switch *sw) if (tb_route(sw)) { - bool unidirectional = tb_switch_tmu_hifi_is_enabled(sw, true); + bool unidirectional = sw->tmu.unidirectional; struct tb_switch *parent = tb_switch_parent(sw); struct tb_port *down, *up; int ret; @@ -359,13 +408,14 @@ int tb_switch_tmu_disable(struct tb_switch *sw) * In case of uni-directional time sync, TMU handshake is * initiated by upstream router. In case of bi-directional * time sync, TMU handshake is initiated by downstream router. - * Therefore, we change the rate to off in the respective - * router. + * We change downstream router's rate to off for both uni/bidir + * cases although it is needed only for the bi-directional mode. + * We avoid changing upstream router's mode since it might + * have another downstream router plugged, that is set to + * uni-directional mode and we don't want to change it's TMU + * mode. */ - if (unidirectional) - tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_OFF); - else - tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); tb_port_tmu_time_sync_disable(up); ret = tb_port_tmu_time_sync_disable(down); @@ -411,6 +461,7 @@ static void __tb_switch_tmu_off(struct tb_switch *sw, bool unidirectional) else tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_OFF); + tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); tb_port_tmu_unidirectional_disable(down); tb_port_tmu_unidirectional_disable(up); } @@ -492,7 +543,11 @@ static int __tb_switch_tmu_enable_unidirectional(struct tb_switch *sw) up = tb_upstream_port(sw); down = tb_port_at(tb_route(sw), parent); - ret = tb_switch_tmu_rate_write(parent, TB_SWITCH_TMU_RATE_HIFI); + ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); + if (ret) + return ret; + + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); if (ret) return ret; @@ -519,7 +574,83 @@ out: return ret; } -static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) +static void __tb_switch_tmu_change_mode_prev(struct tb_switch *sw) +{ + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *down, *up; + + down = tb_port_at(tb_route(sw), parent); + up = tb_upstream_port(sw); + /* + * In case of any failure in one of the steps when change mode, + * get back to the TMU configurations in previous mode. + * In case of additional failures in the functions below, + * ignore them since the caller shall already report a failure. + */ + tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional); + if (sw->tmu.unidirectional_request) + tb_switch_tmu_rate_write(parent, sw->tmu.rate); + else + tb_switch_tmu_rate_write(sw, sw->tmu.rate); + + tb_switch_set_tmu_mode_params(sw, sw->tmu.rate); + tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional); +} + +static int __tb_switch_tmu_change_mode(struct tb_switch *sw) +{ + struct tb_switch *parent = tb_switch_parent(sw); + struct tb_port *up, *down; + int ret; + + up = tb_upstream_port(sw); + down = tb_port_at(tb_route(sw), parent); + ret = tb_port_tmu_set_unidirectional(down, sw->tmu.unidirectional_request); + if (ret) + goto out; + + if (sw->tmu.unidirectional_request) + ret = tb_switch_tmu_rate_write(parent, sw->tmu.rate_request); + else + ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); + if (ret) + return ret; + + ret = tb_switch_set_tmu_mode_params(sw, sw->tmu.rate_request); + if (ret) + return ret; + + ret = tb_port_tmu_set_unidirectional(up, sw->tmu.unidirectional_request); + if (ret) + goto out; + + ret = tb_port_tmu_time_sync_enable(down); + if (ret) + goto out; + + ret = tb_port_tmu_time_sync_enable(up); + if (ret) + goto out; + + return 0; + +out: + __tb_switch_tmu_change_mode_prev(sw); + return ret; +} + +/** + * tb_switch_tmu_enable() - Enable TMU on a router + * @sw: Router whose TMU to enable + * + * Enables TMU of a router to be in uni-directional Normal/HiFi + * or bi-directional HiFi mode. Calling tb_switch_tmu_configure() is required + * before calling this function, to select the mode Normal/HiFi and + * directionality (uni-directional/bi-directional). + * In HiFi mode all tunneling should work. In Normal mode, DP tunneling can't + * work. Uni-directional mode is required for CLx (Link Low-Power) to work. + */ +int tb_switch_tmu_enable(struct tb_switch *sw) { bool unidirectional = sw->tmu.unidirectional_request; int ret; @@ -535,12 +666,15 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) if (!tb_switch_is_clx_supported(sw)) return 0; - if (tb_switch_tmu_hifi_is_enabled(sw, sw->tmu.unidirectional_request)) + if (tb_switch_tmu_is_enabled(sw, sw->tmu.unidirectional_request)) return 0; if (tb_switch_is_titan_ridge(sw) && unidirectional) { - /* Titan Ridge supports only CL0s */ - if (!tb_switch_is_cl0s_enabled(sw)) + /* + * Titan Ridge supports CL0s and CL1 only. CL0s and CL1 are + * enabled and supported together. + */ + if (!tb_switch_is_clx_enabled(sw, TB_CL1)) return -EOPNOTSUPP; ret = tb_switch_tmu_objection_mask(sw); @@ -557,7 +691,11 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) return ret; if (tb_route(sw)) { - /* The used mode changes are from OFF to HiFi-Uni/HiFi-BiDir */ + /* + * The used mode changes are from OFF to + * HiFi-Uni/HiFi-BiDir/Normal-Uni or from Normal-Uni to + * HiFi-Uni. + */ if (sw->tmu.rate == TB_SWITCH_TMU_RATE_OFF) { if (unidirectional) ret = __tb_switch_tmu_enable_unidirectional(sw); @@ -565,6 +703,10 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) ret = __tb_switch_tmu_enable_bidirectional(sw); if (ret) return ret; + } else if (sw->tmu.rate == TB_SWITCH_TMU_RATE_NORMAL) { + ret = __tb_switch_tmu_change_mode(sw); + if (ret) + return ret; } sw->tmu.unidirectional = unidirectional; } else { @@ -574,39 +716,21 @@ static int tb_switch_tmu_hifi_enable(struct tb_switch *sw) * of the child node - see above. * Here only the host router' rate configuration is written. */ - ret = tb_switch_tmu_rate_write(sw, TB_SWITCH_TMU_RATE_HIFI); + ret = tb_switch_tmu_rate_write(sw, sw->tmu.rate_request); if (ret) return ret; } - sw->tmu.rate = TB_SWITCH_TMU_RATE_HIFI; + sw->tmu.rate = sw->tmu.rate_request; tb_sw_dbg(sw, "TMU: mode set to: %s\n", tb_switch_tmu_mode_name(sw)); return tb_switch_tmu_set_time_disruption(sw, false); } -/** - * tb_switch_tmu_enable() - Enable TMU on a router - * @sw: Router whose TMU to enable - * - * Enables TMU of a router to be in uni-directional or bi-directional HiFi mode. - * Calling tb_switch_tmu_configure() is required before calling this function, - * to select the mode HiFi and directionality (uni-directional/bi-directional). - * In both modes all tunneling should work. Uni-directional mode is required for - * CLx (Link Low-Power) to work. - */ -int tb_switch_tmu_enable(struct tb_switch *sw) -{ - if (sw->tmu.rate_request == TB_SWITCH_TMU_RATE_NORMAL) - return -EOPNOTSUPP; - - return tb_switch_tmu_hifi_enable(sw); -} - /** * tb_switch_tmu_configure() - Configure the TMU rate and directionality * @sw: Router whose mode to change - * @rate: Rate to configure Off/LowRes/HiFi + * @rate: Rate to configure Off/Normal/HiFi * @unidirectional: If uni-directional (bi-directional otherwise) * * Selects the rate of the TMU and directionality (uni-directional or @@ -618,3 +742,32 @@ void tb_switch_tmu_configure(struct tb_switch *sw, sw->tmu.unidirectional_request = unidirectional; sw->tmu.rate_request = rate; } + +static int tb_switch_tmu_config_enable(struct device *dev, void *rate) +{ + if (tb_is_switch(dev)) { + struct tb_switch *sw = tb_to_switch(dev); + + tb_switch_tmu_configure(sw, *(enum tb_switch_tmu_rate *)rate, + tb_switch_is_clx_enabled(sw, TB_CL1)); + if (tb_switch_tmu_enable(sw)) + tb_sw_dbg(sw, "fail switching TMU mode for 1st depth router\n"); + } + + return 0; +} + +/** + * tb_switch_enable_tmu_1st_child - Configure and enable TMU for 1st chidren + * @sw: The router to configure and enable it's children TMU + * @rate: Rate of the TMU to configure the router's chidren to + * + * Configures and enables the TMU mode of 1st depth children of the specified + * router to the specified rate. + */ +void tb_switch_enable_tmu_1st_child(struct tb_switch *sw, + enum tb_switch_tmu_rate rate) +{ + device_for_each_child(&sw->dev, &rate, + tb_switch_tmu_config_enable); +} diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index ffb01fc6de75..8f67db202d7b 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -215,7 +215,7 @@ static inline void ufshcd_vops_config_scaling_param(struct ufs_hba *hba, hba->vops->config_scaling_param(hba, p, data); } -extern struct ufs_pm_lvl_states ufs_pm_lvl_states[]; +extern const struct ufs_pm_lvl_states ufs_pm_lvl_states[]; /** * ufshcd_scsi_to_upiu_lun - maps scsi LUN to UPIU LUN @@ -234,8 +234,8 @@ static inline u8 ufshcd_scsi_to_upiu_lun(unsigned int scsi_lun) int __ufshcd_write_ee_control(struct ufs_hba *hba, u32 ee_ctrl_mask); int ufshcd_write_ee_control(struct ufs_hba *hba); -int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, u16 *other_mask, - u16 set, u16 clr); +int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, + const u16 *other_mask, u16 set, u16 clr); static inline int ufshcd_update_ee_drv_mask(struct ufs_hba *hba, u16 set, u16 clr) diff --git a/drivers/ufs/core/ufshcd.c b/drivers/ufs/core/ufshcd.c index 30635a3529f1..d6a87fe8958a 100644 --- a/drivers/ufs/core/ufshcd.c +++ b/drivers/ufs/core/ufshcd.c @@ -67,9 +67,6 @@ /* maximum number of link-startup retries */ #define DME_LINKSTARTUP_RETRIES 3 -/* Maximum retries for Hibern8 enter */ -#define UIC_HIBERN8_ENTER_RETRIES 3 - /* maximum number of reset retries before giving up */ #define MAX_HOST_RESET_RETRIES 5 @@ -178,7 +175,7 @@ enum { #define ufshcd_clear_eh_in_progress(h) \ ((h)->eh_flags &= ~UFSHCD_EH_IN_PROGRESS) -struct ufs_pm_lvl_states ufs_pm_lvl_states[] = { +const struct ufs_pm_lvl_states ufs_pm_lvl_states[] = { [UFS_PM_LVL_0] = {UFS_ACTIVE_PWR_MODE, UIC_LINK_ACTIVE_STATE}, [UFS_PM_LVL_1] = {UFS_ACTIVE_PWR_MODE, UIC_LINK_HIBERN8_STATE}, [UFS_PM_LVL_2] = {UFS_SLEEP_PWR_MODE, UIC_LINK_ACTIVE_STATE}, @@ -368,7 +365,7 @@ static void ufshcd_add_tm_upiu_trace(struct ufs_hba *hba, unsigned int tag, } static void ufshcd_add_uic_command_trace(struct ufs_hba *hba, - struct uic_command *ucmd, + const struct uic_command *ucmd, enum ufs_trace_str_t str_t) { u32 cmd; @@ -450,11 +447,11 @@ static void ufshcd_print_clk_freqs(struct ufs_hba *hba) } static void ufshcd_print_evt(struct ufs_hba *hba, u32 id, - char *err_name) + const char *err_name) { int i; bool found = false; - struct ufs_event_hist *e; + const struct ufs_event_hist *e; if (id >= UFS_EVT_CNT) return; @@ -504,7 +501,7 @@ static void ufshcd_print_evt_hist(struct ufs_hba *hba) static void ufshcd_print_trs(struct ufs_hba *hba, unsigned long bitmap, bool pr_prdt) { - struct ufshcd_lrb *lrbp; + const struct ufshcd_lrb *lrbp; int prdt_length; int tag; @@ -560,7 +557,7 @@ static void ufshcd_print_tmrs(struct ufs_hba *hba, unsigned long bitmap) static void ufshcd_print_host_state(struct ufs_hba *hba) { - struct scsi_device *sdev_ufs = hba->ufs_device_wlun; + const struct scsi_device *sdev_ufs = hba->ufs_device_wlun; dev_err(hba->dev, "UFS Host state=%d\n", hba->ufshcd_state); dev_err(hba->dev, "outstanding reqs=0x%lx tasks=0x%lx\n", @@ -1116,7 +1113,7 @@ static bool ufshcd_is_devfreq_scaling_required(struct ufs_hba *hba, */ static u32 ufshcd_pending_cmds(struct ufs_hba *hba) { - struct scsi_device *sdev; + const struct scsi_device *sdev; u32 pending = 0; lockdep_assert_held(hba->host->host_lock); @@ -2087,14 +2084,15 @@ static inline int ufshcd_monitor_opcode2dir(u8 opcode) static inline bool ufshcd_should_inform_monitor(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) { - struct ufs_hba_monitor *m = &hba->monitor; + const struct ufs_hba_monitor *m = &hba->monitor; return (m->enabled && lrbp && lrbp->cmd && (!m->chunk_size || m->chunk_size == lrbp->cmd->sdb.length) && ktime_before(hba->monitor.enabled_ts, lrbp->issue_time_stamp)); } -static void ufshcd_start_monitor(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) +static void ufshcd_start_monitor(struct ufs_hba *hba, + const struct ufshcd_lrb *lrbp) { int dir = ufshcd_monitor_opcode2dir(*lrbp->cmd->cmnd); unsigned long flags; @@ -2105,14 +2103,14 @@ static void ufshcd_start_monitor(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) spin_unlock_irqrestore(hba->host->host_lock, flags); } -static void ufshcd_update_monitor(struct ufs_hba *hba, struct ufshcd_lrb *lrbp) +static void ufshcd_update_monitor(struct ufs_hba *hba, const struct ufshcd_lrb *lrbp) { int dir = ufshcd_monitor_opcode2dir(*lrbp->cmd->cmnd); unsigned long flags; spin_lock_irqsave(hba->host->host_lock, flags); if (dir >= 0 && hba->monitor.nr_queued[dir] > 0) { - struct request *req = scsi_cmd_to_rq(lrbp->cmd); + const struct request *req = scsi_cmd_to_rq(lrbp->cmd); struct ufs_hba_monitor *m = &hba->monitor; ktime_t now, inc, lat; @@ -2235,6 +2233,8 @@ static inline int ufshcd_hba_capabilities(struct ufs_hba *hba) int err; hba->capabilities = ufshcd_readl(hba, REG_CONTROLLER_CAPABILITIES); + if (hba->quirks & UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS) + hba->capabilities &= ~MASK_64_ADDRESSING_SUPPORT; /* nutrs and nutmrs are 0 based values */ hba->nutrs = (hba->capabilities & MASK_TRANSFER_REQUESTS_SLOTS) + 1; @@ -3115,7 +3115,7 @@ int ufshcd_query_flag_retry(struct ufs_hba *hba, if (ret) dev_err(hba->dev, - "%s: query attribute, opcode %d, idn %d, failed with error %d after %d retires\n", + "%s: query attribute, opcode %d, idn %d, failed with error %d after %d retries\n", __func__, opcode, idn, ret, retries); return ret; } @@ -3286,7 +3286,7 @@ int ufshcd_query_attr_retry(struct ufs_hba *hba, if (ret) dev_err(hba->dev, - "%s: query attribute, idn %d, failed with error %d after %d retires\n", + "%s: query attribute, idn %d, failed with error %d after %d retries\n", __func__, idn, ret, QUERY_REQ_RETRIES); return ret; } @@ -3860,7 +3860,7 @@ int ufshcd_dme_configure_adapt(struct ufs_hba *hba, { int ret; - if (agreed_gear != UFS_HS_G4) + if (agreed_gear < UFS_HS_G4) adapt_val = PA_NO_ADAPT; ret = ufshcd_dme_set(hba, @@ -4149,7 +4149,7 @@ out_unlock: * * Returns 0 on success, non-zero value on failure */ -static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) +int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) { struct uic_command uic_cmd = {0}; int ret; @@ -4174,6 +4174,7 @@ static int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode) out: return ret; } +EXPORT_SYMBOL_GPL(ufshcd_uic_change_pwr_mode); int ufshcd_link_recovery(struct ufs_hba *hba) { @@ -4316,8 +4317,13 @@ static int ufshcd_get_max_pwr_mode(struct ufs_hba *hba) if (hba->max_pwr_info.is_valid) return 0; - pwr_info->pwr_tx = FAST_MODE; - pwr_info->pwr_rx = FAST_MODE; + if (hba->quirks & UFSHCD_QUIRK_HIBERN_FASTAUTO) { + pwr_info->pwr_tx = FASTAUTO_MODE; + pwr_info->pwr_rx = FASTAUTO_MODE; + } else { + pwr_info->pwr_tx = FAST_MODE; + pwr_info->pwr_rx = FAST_MODE; + } pwr_info->hs_rate = PA_HS_MODE_B; /* Get the connected lane count */ @@ -4792,7 +4798,7 @@ link_startup: * but we can't be sure if the link is up until link startup * succeeds. So reset the local Uni-Pro and try again. */ - if (ret && ufshcd_hba_enable(hba)) { + if (ret && retries && ufshcd_hba_enable(hba)) { ufshcd_update_evt_hist(hba, UFS_EVT_LINK_STARTUP_FAIL, (u32)ret); @@ -4957,7 +4963,7 @@ static int ufshcd_get_lu_wp(struct ufs_hba *hba, * */ static inline void ufshcd_get_lu_power_on_wp_status(struct ufs_hba *hba, - struct scsi_device *sdev) + const struct scsi_device *sdev) { if (hba->dev_info.f_power_on_wp_en && !hba->dev_info.is_lu_power_on_wp) { @@ -5479,8 +5485,8 @@ int ufshcd_write_ee_control(struct ufs_hba *hba) return err; } -int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, u16 *other_mask, - u16 set, u16 clr) +int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, + const u16 *other_mask, u16 set, u16 clr) { u16 new_mask, ee_ctrl_mask; int err = 0; @@ -7420,7 +7426,8 @@ static int ufshcd_eh_host_reset_handler(struct scsi_cmnd *cmd) * * Returns calculated max ICC level for specific regulator */ -static u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, char *buff) +static u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, + const char *buff) { int i; int curr_uA; @@ -7467,7 +7474,7 @@ static u32 ufshcd_get_max_icc_level(int sup_curr_uA, u32 start_scan, char *buff) * Returns calculated ICC level */ static u32 ufshcd_find_max_sup_active_icc_level(struct ufs_hba *hba, - u8 *desc_buf, int len) + const u8 *desc_buf, int len) { u32 icc_level = 0; @@ -7617,7 +7624,7 @@ out: return ret; } -static void ufshcd_wb_probe(struct ufs_hba *hba, u8 *desc_buf) +static void ufshcd_wb_probe(struct ufs_hba *hba, const u8 *desc_buf) { struct ufs_dev_info *dev_info = &hba->dev_info; u8 lun; @@ -7688,7 +7695,7 @@ wb_disabled: hba->caps &= ~UFSHCD_CAP_WB_EN; } -static void ufshcd_temp_notif_probe(struct ufs_hba *hba, u8 *desc_buf) +static void ufshcd_temp_notif_probe(struct ufs_hba *hba, const u8 *desc_buf) { struct ufs_dev_info *dev_info = &hba->dev_info; u32 ext_ufs_feature; @@ -7922,7 +7929,7 @@ static int ufshcd_quirk_tune_host_pa_tactivate(struct ufs_hba *hba) u32 granularity, peer_granularity; u32 pa_tactivate, peer_pa_tactivate; u32 pa_tactivate_us, peer_pa_tactivate_us; - u8 gran_to_us_table[] = {1, 4, 8, 16, 32, 100}; + static const u8 gran_to_us_table[] = {1, 4, 8, 16, 32, 100}; ret = ufshcd_dme_get(hba, UIC_ARG_MIB(PA_GRANULARITY), &granularity); @@ -8039,7 +8046,7 @@ struct ufs_ref_clk { enum ufs_ref_clk_freq val; }; -static struct ufs_ref_clk ufs_ref_clk_freqs[] = { +static const struct ufs_ref_clk ufs_ref_clk_freqs[] = { {19200000, REF_CLK_FREQ_19_2_MHZ}, {26000000, REF_CLK_FREQ_26_MHZ}, {38400000, REF_CLK_FREQ_38_4_MHZ}, @@ -8481,7 +8488,7 @@ static int ufshcd_setup_hba_vreg(struct ufs_hba *hba, bool on) return ufshcd_toggle_vreg(hba->dev, info->vdd_hba, on); } -static int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg) +int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg) { int ret = 0; @@ -8497,6 +8504,7 @@ static int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg) out: return ret; } +EXPORT_SYMBOL_GPL(ufshcd_get_vreg); static int ufshcd_init_vreg(struct ufs_hba *hba) { @@ -8590,6 +8598,19 @@ out: return ret; } +static enum ufs_ref_clk_freq ufshcd_parse_ref_clk_property(struct ufs_hba *hba) +{ + u32 freq; + int ret = device_property_read_u32(hba->dev, "ref-clk-freq", &freq); + + if (ret) { + dev_dbg(hba->dev, "Cannot query 'ref-clk-freq' property = %d", ret); + return REF_CLK_FREQ_INVAL; + } + + return ufs_get_bref_clk_from_hz(freq); +} + static int ufshcd_init_clocks(struct ufs_hba *hba) { int ret = 0; @@ -8683,6 +8704,9 @@ static int ufshcd_hba_init(struct ufs_hba *hba) if (err) goto out_disable_hba_vreg; + if (hba->dev_ref_clk_freq == REF_CLK_FREQ_INVAL) + hba->dev_ref_clk_freq = ufshcd_parse_ref_clk_property(hba); + err = ufshcd_setup_clocks(hba, true); if (err) goto out_disable_hba_vreg; diff --git a/drivers/ufs/host/Kconfig b/drivers/ufs/host/Kconfig index 82590224da13..4cc2dbd79ed0 100644 --- a/drivers/ufs/host/Kconfig +++ b/drivers/ufs/host/Kconfig @@ -92,6 +92,18 @@ config SCSI_UFS_HISI Select this if you have UFS controller on Hisilicon chipset. If unsure, say N. +config SCSI_UFS_RENESAS + tristate "Renesas specific hooks to UFS controller platform driver" + depends on (ARCH_RENESAS || COMPILE_TEST) && SCSI_UFSHCD_PLATFORM + help + This selects the Renesas specific additions to UFSHCD platform driver. + UFS host on Renesas needs some vendor specific configuration before + accessing the hardware. + + Select this if you have UFS controller on Renesas chipset. + + If unsure, say N. + config SCSI_UFS_TI_J721E tristate "TI glue layer for Cadence UFS Controller" depends on OF && HAS_IOMEM && (ARCH_K3 || COMPILE_TEST) diff --git a/drivers/ufs/host/Makefile b/drivers/ufs/host/Makefile index e4be54273c98..7717ca93e7d5 100644 --- a/drivers/ufs/host/Makefile +++ b/drivers/ufs/host/Makefile @@ -11,4 +11,5 @@ obj-$(CONFIG_SCSI_UFSHCD_PCI) += ufshcd-pci.o obj-$(CONFIG_SCSI_UFSHCD_PLATFORM) += ufshcd-pltfrm.o obj-$(CONFIG_SCSI_UFS_HISI) += ufs-hisi.o obj-$(CONFIG_SCSI_UFS_MEDIATEK) += ufs-mediatek.o +obj-$(CONFIG_SCSI_UFS_RENESAS) += ufs-renesas.o obj-$(CONFIG_SCSI_UFS_TI_J721E) += ti-j721e-ufs.o diff --git a/drivers/ufs/host/ufs-exynos.c b/drivers/ufs/host/ufs-exynos.c index a81d8cbd542f..eced97538082 100644 --- a/drivers/ufs/host/ufs-exynos.c +++ b/drivers/ufs/host/ufs-exynos.c @@ -52,11 +52,12 @@ #define HCI_ERR_EN_DME_LAYER 0x88 #define HCI_CLKSTOP_CTRL 0xB0 #define REFCLKOUT_STOP BIT(4) +#define MPHY_APBCLK_STOP BIT(3) #define REFCLK_STOP BIT(2) #define UNIPRO_MCLK_STOP BIT(1) #define UNIPRO_PCLK_STOP BIT(0) #define CLK_STOP_MASK (REFCLKOUT_STOP | REFCLK_STOP |\ - UNIPRO_MCLK_STOP |\ + UNIPRO_MCLK_STOP | MPHY_APBCLK_STOP|\ UNIPRO_PCLK_STOP) #define HCI_MISC 0xB4 #define REFCLK_CTRL_EN BIT(7) @@ -135,15 +136,9 @@ enum { /* * UNIPRO registers */ -#define UNIPRO_COMP_VERSION 0x000 -#define UNIPRO_DME_PWR_REQ 0x090 -#define UNIPRO_DME_PWR_REQ_POWERMODE 0x094 -#define UNIPRO_DME_PWR_REQ_LOCALL2TIMER0 0x098 -#define UNIPRO_DME_PWR_REQ_LOCALL2TIMER1 0x09C -#define UNIPRO_DME_PWR_REQ_LOCALL2TIMER2 0x0A0 -#define UNIPRO_DME_PWR_REQ_REMOTEL2TIMER0 0x0A4 -#define UNIPRO_DME_PWR_REQ_REMOTEL2TIMER1 0x0A8 -#define UNIPRO_DME_PWR_REQ_REMOTEL2TIMER2 0x0AC +#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0 0x78B8 +#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1 0x78BC +#define UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2 0x78C0 /* * UFS Protector registers @@ -157,7 +152,6 @@ enum { #define CNTR_DIV_VAL 40 -static struct exynos_ufs_drv_data exynos_ufs_drvs; static void exynos_ufs_auto_ctrl_hcc(struct exynos_ufs *ufs, bool en); static void exynos_ufs_ctrl_clkstop(struct exynos_ufs *ufs, bool en); @@ -657,8 +651,9 @@ static void exynos_ufs_config_phy_cap_attr(struct exynos_ufs *ufs) if (attr->rx_min_actv_time_cap) ufshcd_dme_set(hba, - UIC_ARG_MIB_SEL(RX_MIN_ACTIVATETIME_CAP, - i), attr->rx_min_actv_time_cap); + UIC_ARG_MIB_SEL( + RX_MIN_ACTIVATETIME_CAPABILITY, i), + attr->rx_min_actv_time_cap); if (attr->rx_hibern8_time_cap) ufshcd_dme_set(hba, @@ -910,9 +905,13 @@ static int exynos_ufs_phy_init(struct exynos_ufs *ufs) if (ret) { dev_err(hba->dev, "%s: phy init failed, ret = %d\n", __func__, ret); - goto out_exit_phy; + return ret; } + ret = phy_power_on(generic_phy); + if (ret) + goto out_exit_phy; + return 0; out_exit_phy: @@ -1174,10 +1173,6 @@ static int exynos_ufs_init(struct ufs_hba *hba) goto out; } - ret = phy_power_on(ufs->phy); - if (ret) - goto phy_off; - exynos_ufs_priv_init(hba, ufs); if (ufs->drv_data->drv_init) { @@ -1195,8 +1190,6 @@ static int exynos_ufs_init(struct ufs_hba *hba) exynos_ufs_config_smu(ufs); return 0; -phy_off: - phy_power_off(ufs->phy); out: hba->priv = NULL; return ret; @@ -1473,7 +1466,100 @@ static int exynosauto_ufs_vh_init(struct ufs_hba *hba) return 0; } -static struct ufs_hba_variant_ops ufs_hba_exynos_ops = { +static int fsd_ufs_pre_link(struct exynos_ufs *ufs) +{ + int i; + struct ufs_hba *hba = ufs->hba; + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_CLK_PERIOD), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + ufshcd_dme_set(hba, UIC_ARG_MIB(0x201), 0x12); + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40); + + for_each_ufs_tx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0xAA, i), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x8F, i), 0x3F); + } + + for_each_ufs_rx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x12, i), + DIV_ROUND_UP(NSEC_PER_SEC, ufs->mclk_rate)); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x5C, i), 0x38); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x0F, i), 0x0); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x65, i), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x69, i), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x21, i), 0x0); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x22, i), 0x0); + } + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_AUTOMODE_THLD), 0x4E20); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_OPTION_SUITE), 0x2e820183); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_LOCAL_TX_LCC_ENABLE), 0x0); + + exynos_ufs_establish_connt(ufs); + + return 0; +} + +static int fsd_ufs_post_link(struct exynos_ufs *ufs) +{ + int i; + struct ufs_hba *hba = ufs->hba; + u32 hw_cap_min_tactivate; + u32 peer_rx_min_actv_time_cap; + u32 max_rx_hibern8_time_cap; + + ufshcd_dme_get(hba, UIC_ARG_MIB_SEL(0x8F, 4), + &hw_cap_min_tactivate); /* HW Capability of MIN_TACTIVATE */ + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_TACTIVATE), + &peer_rx_min_actv_time_cap); /* PA_TActivate */ + ufshcd_dme_get(hba, UIC_ARG_MIB(PA_HIBERN8TIME), + &max_rx_hibern8_time_cap); /* PA_Hibern8Time */ + + if (peer_rx_min_actv_time_cap >= hw_cap_min_tactivate) + ufshcd_dme_peer_set(hba, UIC_ARG_MIB(PA_TACTIVATE), + peer_rx_min_actv_time_cap + 1); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME), max_rx_hibern8_time_cap + 1); + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_MODE), 0x01); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_SAVECONFIGTIME), 0xFA); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_DBG_MODE), 0x00); + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x40); + + for_each_ufs_rx_lane(ufs, i) { + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x35, i), 0x05); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x73, i), 0x01); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x41, i), 0x02); + ufshcd_dme_set(hba, UIC_ARG_MIB_SEL(0x42, i), 0xAC); + } + + ufshcd_dme_set(hba, UIC_ARG_MIB(0x200), 0x0); + + return 0; +} + +static int fsd_ufs_pre_pwr_change(struct exynos_ufs *ufs, + struct ufs_pa_layer_attr *pwr) +{ + struct ufs_hba *hba = ufs->hba; + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), 0x1); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA0), 12000); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA1), 32000); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_PWRMODEUSERDATA2), 16000); + + unipro_writel(ufs, 12000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER0); + unipro_writel(ufs, 32000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER1); + unipro_writel(ufs, 16000, UNIPRO_DME_POWERMODE_REQ_REMOTEL2TIMER2); + + return 0; +} + +static const struct ufs_hba_variant_ops ufs_hba_exynos_ops = { .name = "exynos_ufs", .init = exynos_ufs_init, .hce_enable_notify = exynos_ufs_hce_enable_notify, @@ -1514,9 +1600,14 @@ static int exynos_ufs_probe(struct platform_device *pdev) static int exynos_ufs_remove(struct platform_device *pdev) { struct ufs_hba *hba = platform_get_drvdata(pdev); + struct exynos_ufs *ufs = ufshcd_get_variant(hba); pm_runtime_get_sync(&(pdev)->dev); ufshcd_remove(hba); + + phy_power_off(ufs->phy); + phy_exit(ufs->phy); + return 0; } @@ -1545,7 +1636,7 @@ static struct exynos_ufs_uic_attr exynos7_uic_attr = { .pa_dbg_option_suite = 0x30103, }; -static struct exynos_ufs_drv_data exynosauto_ufs_drvs = { +static const struct exynos_ufs_drv_data exynosauto_ufs_drvs = { .uic_attr = &exynos7_uic_attr, .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR | @@ -1561,7 +1652,7 @@ static struct exynos_ufs_drv_data exynosauto_ufs_drvs = { .post_pwr_change = exynosauto_ufs_post_pwr_change, }; -static struct exynos_ufs_drv_data exynosauto_ufs_vh_drvs = { +static const struct exynos_ufs_drv_data exynosauto_ufs_vh_drvs = { .vops = &ufs_hba_exynosauto_vh_ops, .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR | @@ -1573,7 +1664,7 @@ static struct exynos_ufs_drv_data exynosauto_ufs_vh_drvs = { .opts = EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX, }; -static struct exynos_ufs_drv_data exynos_ufs_drvs = { +static const struct exynos_ufs_drv_data exynos_ufs_drvs = { .uic_attr = &exynos7_uic_attr, .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR | @@ -1595,6 +1686,47 @@ static struct exynos_ufs_drv_data exynos_ufs_drvs = { .post_pwr_change = exynos7_ufs_post_pwr_change, }; +static struct exynos_ufs_uic_attr fsd_uic_attr = { + .tx_trailingclks = 0x10, + .tx_dif_p_nsec = 3000000, /* unit: ns */ + .tx_dif_n_nsec = 1000000, /* unit: ns */ + .tx_high_z_cnt_nsec = 20000, /* unit: ns */ + .tx_base_unit_nsec = 100000, /* unit: ns */ + .tx_gran_unit_nsec = 4000, /* unit: ns */ + .tx_sleep_cnt = 1000, /* unit: ns */ + .tx_min_activatetime = 0xa, + .rx_filler_enable = 0x2, + .rx_dif_p_nsec = 1000000, /* unit: ns */ + .rx_hibern8_wait_nsec = 4000000, /* unit: ns */ + .rx_base_unit_nsec = 100000, /* unit: ns */ + .rx_gran_unit_nsec = 4000, /* unit: ns */ + .rx_sleep_cnt = 1280, /* unit: ns */ + .rx_stall_cnt = 320, /* unit: ns */ + .rx_hs_g1_sync_len_cap = SYNC_LEN_COARSE(0xf), + .rx_hs_g2_sync_len_cap = SYNC_LEN_COARSE(0xf), + .rx_hs_g3_sync_len_cap = SYNC_LEN_COARSE(0xf), + .rx_hs_g1_prep_sync_len_cap = PREP_LEN(0xf), + .rx_hs_g2_prep_sync_len_cap = PREP_LEN(0xf), + .rx_hs_g3_prep_sync_len_cap = PREP_LEN(0xf), + .pa_dbg_option_suite = 0x2E820183, +}; + +struct exynos_ufs_drv_data fsd_ufs_drvs = { + .uic_attr = &fsd_uic_attr, + .quirks = UFSHCD_QUIRK_PRDT_BYTE_GRAN | + UFSHCI_QUIRK_BROKEN_REQ_LIST_CLR | + UFSHCD_QUIRK_BROKEN_OCS_FATAL_ERROR | + UFSHCD_QUIRK_SKIP_DEF_UNIPRO_TIMEOUT_SETTING | + UFSHCI_QUIRK_SKIP_RESET_INTR_AGGR, + .opts = EXYNOS_UFS_OPT_HAS_APB_CLK_CTRL | + EXYNOS_UFS_OPT_BROKEN_AUTO_CLK_CTRL | + EXYNOS_UFS_OPT_SKIP_CONFIG_PHY_ATTR | + EXYNOS_UFS_OPT_BROKEN_RX_SEL_IDX, + .pre_link = fsd_ufs_pre_link, + .post_link = fsd_ufs_post_link, + .pre_pwr_change = fsd_ufs_pre_pwr_change, +}; + static const struct of_device_id exynos_ufs_of_match[] = { { .compatible = "samsung,exynos7-ufs", .data = &exynos_ufs_drvs }, @@ -1602,6 +1734,8 @@ static const struct of_device_id exynos_ufs_of_match[] = { .data = &exynosauto_ufs_drvs }, { .compatible = "samsung,exynosautov9-ufs-vh", .data = &exynosauto_ufs_vh_drvs }, + { .compatible = "tesla,fsd-ufs", + .data = &fsd_ufs_drvs }, {}, }; diff --git a/drivers/ufs/host/ufs-exynos.h b/drivers/ufs/host/ufs-exynos.h index 0b0a3d530ca6..a4bd6646d7f1 100644 --- a/drivers/ufs/host/ufs-exynos.h +++ b/drivers/ufs/host/ufs-exynos.h @@ -22,6 +22,7 @@ #define PA_DBG_RXPHY_CFGUPDT 0x9519 #define PA_DBG_MODE 0x9529 #define PA_DBG_SKIP_RESET_PHY 0x9539 +#define PA_DBG_AUTOMODE_THLD 0x9536 #define PA_DBG_OV_TM 0x9540 #define PA_DBG_SKIP_LINE_RESET 0x9541 #define PA_DBG_LINE_RESET_REQ 0x9543 diff --git a/drivers/ufs/host/ufs-mediatek.c b/drivers/ufs/host/ufs-mediatek.c index beabc3ccd30b..c958279bdd8f 100644 --- a/drivers/ufs/host/ufs-mediatek.c +++ b/drivers/ufs/host/ufs-mediatek.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -30,26 +31,11 @@ #define CREATE_TRACE_POINTS #include "ufs-mediatek-trace.h" -#define ufs_mtk_smc(cmd, val, res) \ - arm_smccc_smc(MTK_SIP_UFS_CONTROL, \ - cmd, val, 0, 0, 0, 0, 0, &(res)) - -#define ufs_mtk_va09_pwr_ctrl(res, on) \ - ufs_mtk_smc(UFS_MTK_SIP_VA09_PWR_CTRL, on, res) - -#define ufs_mtk_crypto_ctrl(res, enable) \ - ufs_mtk_smc(UFS_MTK_SIP_CRYPTO_CTRL, enable, res) - -#define ufs_mtk_ref_clk_notify(on, res) \ - ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, on, res) - -#define ufs_mtk_device_reset_ctrl(high, res) \ - ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, high, res) - static const struct ufs_dev_quirk ufs_mtk_dev_fixups[] = { - { .wmanufacturerid = UFS_VENDOR_MICRON, + { .wmanufacturerid = UFS_ANY_VENDOR, .model = UFS_ANY_MODEL, - .quirk = UFS_DEVICE_QUIRK_DELAY_AFTER_LPM }, + .quirk = UFS_DEVICE_QUIRK_DELAY_AFTER_LPM | + UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM }, { .wmanufacturerid = UFS_VENDOR_SKHYNIX, .model = "H9HQ21AFAMZDAR", .quirk = UFS_DEVICE_QUIRK_SUPPORT_EXTENDED_FEATURES }, @@ -82,6 +68,13 @@ static bool ufs_mtk_is_broken_vcc(struct ufs_hba *hba) return !!(host->caps & UFS_MTK_CAP_BROKEN_VCC); } +static bool ufs_mtk_is_pmc_via_fastauto(struct ufs_hba *hba) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + return (host->caps & UFS_MTK_CAP_PMC_VIA_FASTAUTO); +} + static void ufs_mtk_cfg_unipro_cg(struct ufs_hba *hba, bool enable) { u32 tmp; @@ -191,6 +184,14 @@ static int ufs_mtk_hce_enable_notify(struct ufs_hba *hba, hba->capabilities &= ~MASK_AUTO_HIBERN8_SUPPORT; hba->ahit = 0; } + + /* + * Turn on CLK_CG early to bypass abnormal ERR_CHK signal + * to prevent host hang issue + */ + ufshcd_writel(hba, + ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) | 0x80, + REG_UFS_XOUFS_CTRL); } return 0; @@ -244,8 +245,9 @@ static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on) if (host->ref_clk_enabled == on) return 0; + ufs_mtk_ref_clk_notify(on, PRE_CHANGE, res); + if (on) { - ufs_mtk_ref_clk_notify(on, res); ufshcd_writel(hba, REFCLK_REQUEST, REG_UFS_REFCLK_CTRL); } else { ufshcd_delay_us(host->ref_clk_gating_wait_us, 10); @@ -267,7 +269,7 @@ static int ufs_mtk_setup_ref_clk(struct ufs_hba *hba, bool on) dev_err(hba->dev, "missing ack of refclk req, reg: 0x%x\n", value); - ufs_mtk_ref_clk_notify(host->ref_clk_enabled, res); + ufs_mtk_ref_clk_notify(host->ref_clk_enabled, POST_CHANGE, res); return -ETIMEDOUT; @@ -275,8 +277,8 @@ out: host->ref_clk_enabled = on; if (on) ufshcd_delay_us(host->ref_clk_ungating_wait_us, 10); - else - ufs_mtk_ref_clk_notify(on, res); + + ufs_mtk_ref_clk_notify(on, POST_CHANGE, res); return 0; } @@ -579,20 +581,38 @@ static void ufs_mtk_init_host_caps(struct ufs_hba *hba) if (of_property_read_bool(np, "mediatek,ufs-broken-vcc")) host->caps |= UFS_MTK_CAP_BROKEN_VCC; + if (of_property_read_bool(np, "mediatek,ufs-pmc-via-fastauto")) + host->caps |= UFS_MTK_CAP_PMC_VIA_FASTAUTO; + dev_info(hba->dev, "caps: 0x%x", host->caps); } -static void ufs_mtk_scale_perf(struct ufs_hba *hba, bool up) +static void ufs_mtk_boost_pm_qos(struct ufs_hba *hba, bool boost) { struct ufs_mtk_host *host = ufshcd_get_variant(hba); - ufs_mtk_boost_crypt(hba, up); - ufs_mtk_setup_ref_clk(hba, up); + if (!host || !host->pm_qos_init) + return; - if (up) + cpu_latency_qos_update_request(&host->pm_qos_req, + boost ? 0 : PM_QOS_DEFAULT_VALUE); +} + +static void ufs_mtk_pwr_ctrl(struct ufs_hba *hba, bool on) +{ + struct ufs_mtk_host *host = ufshcd_get_variant(hba); + + if (on) { phy_power_on(host->mphy); - else + ufs_mtk_setup_ref_clk(hba, on); + ufs_mtk_boost_crypt(hba, on); + ufs_mtk_boost_pm_qos(hba, on); + } else { + ufs_mtk_boost_pm_qos(hba, on); + ufs_mtk_boost_crypt(hba, on); + ufs_mtk_setup_ref_clk(hba, on); phy_power_off(host->mphy); + } } /** @@ -637,9 +657,9 @@ static int ufs_mtk_setup_clocks(struct ufs_hba *hba, bool on, } if (clk_pwr_off) - ufs_mtk_scale_perf(hba, false); + ufs_mtk_pwr_ctrl(hba, false); } else if (on && status == POST_CHANGE) { - ufs_mtk_scale_perf(hba, true); + ufs_mtk_pwr_ctrl(hba, true); } return ret; @@ -675,6 +695,73 @@ static u32 ufs_mtk_get_ufs_hci_version(struct ufs_hba *hba) return hba->ufs_version; } +#define MAX_VCC_NAME 30 +static int ufs_mtk_vreg_fix_vcc(struct ufs_hba *hba) +{ + struct ufs_vreg_info *info = &hba->vreg_info; + struct device_node *np = hba->dev->of_node; + struct device *dev = hba->dev; + char vcc_name[MAX_VCC_NAME]; + struct arm_smccc_res res; + int err, ver; + + if (hba->vreg_info.vcc) + return 0; + + if (of_property_read_bool(np, "mediatek,ufs-vcc-by-num")) { + ufs_mtk_get_vcc_num(res); + if (res.a1 > UFS_VCC_NONE && res.a1 < UFS_VCC_MAX) + snprintf(vcc_name, MAX_VCC_NAME, "vcc-opt%lu", res.a1); + else + return -ENODEV; + } else if (of_property_read_bool(np, "mediatek,ufs-vcc-by-ver")) { + ver = (hba->dev_info.wspecversion & 0xF00) >> 8; + snprintf(vcc_name, MAX_VCC_NAME, "vcc-ufs%u", ver); + } else { + return 0; + } + + err = ufshcd_populate_vreg(dev, vcc_name, &info->vcc); + if (err) + return err; + + err = ufshcd_get_vreg(dev, info->vcc); + if (err) + return err; + + err = regulator_enable(info->vcc->reg); + if (!err) { + info->vcc->enabled = true; + dev_info(dev, "%s: %s enabled\n", __func__, vcc_name); + } + + return err; +} + +static void ufs_mtk_vreg_fix_vccqx(struct ufs_hba *hba) +{ + struct ufs_vreg_info *info = &hba->vreg_info; + struct ufs_vreg **vreg_on, **vreg_off; + + if (hba->dev_info.wspecversion >= 0x0300) { + vreg_on = &info->vccq; + vreg_off = &info->vccq2; + } else { + vreg_on = &info->vccq2; + vreg_off = &info->vccq; + } + + if (*vreg_on) + (*vreg_on)->always_on = true; + + if (*vreg_off) { + regulator_disable((*vreg_off)->reg); + devm_kfree(hba->dev, (*vreg_off)->name); + devm_kfree(hba->dev, *vreg_off); + *vreg_off = NULL; + } +} + /** * ufs_mtk_init - find other essential mmio bases * @hba: host controller instance @@ -754,6 +841,26 @@ out: return err; } +static bool ufs_mtk_pmc_via_fastauto(struct ufs_hba *hba, + struct ufs_pa_layer_attr *dev_req_params) +{ + if (!ufs_mtk_is_pmc_via_fastauto(hba)) + return false; + + if (dev_req_params->hs_rate == hba->pwr_info.hs_rate) + return false; + + if (dev_req_params->pwr_tx != FAST_MODE && + dev_req_params->gear_tx < UFS_HS_G4) + return false; + + if (dev_req_params->pwr_rx != FAST_MODE && + dev_req_params->gear_rx < UFS_HS_G4) + return false; + + return true; +} + static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, struct ufs_pa_layer_attr *dev_max_params, struct ufs_pa_layer_attr *dev_req_params) @@ -763,8 +870,8 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, int ret; ufshcd_init_pwr_dev_param(&host_cap); - host_cap.hs_rx_gear = UFS_HS_G4; - host_cap.hs_tx_gear = UFS_HS_G4; + host_cap.hs_rx_gear = UFS_HS_G5; + host_cap.hs_tx_gear = UFS_HS_G5; ret = ufshcd_get_pwr_dev_param(&host_cap, dev_max_params, @@ -774,6 +881,32 @@ static int ufs_mtk_pre_pwr_change(struct ufs_hba *hba, __func__); } + if (ufs_mtk_pmc_via_fastauto(hba, dev_req_params)) { + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXTERMINATION), true); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXGEAR), UFS_HS_G1); + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXTERMINATION), true); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_RXGEAR), UFS_HS_G1); + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVETXDATALANES), + dev_req_params->lane_tx); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_ACTIVERXDATALANES), + dev_req_params->lane_rx); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HSSERIES), + dev_req_params->hs_rate); + + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TXHSADAPTTYPE), + PA_NO_ADAPT); + + ret = ufshcd_uic_change_pwr_mode(hba, + FASTAUTO_MODE << 4 | FASTAUTO_MODE); + + if (ret) { + dev_err(hba->dev, "%s: HSG1B FASTAUTO failed ret=%d\n", + __func__, ret); + } + } + if (host->hw_ver.major >= 3) { ret = ufshcd_dme_configure_adapt(hba, dev_req_params->gear_tx, @@ -963,6 +1096,11 @@ static int ufs_mtk_link_set_lpm(struct ufs_hba *hba) { int err; + /* Disable reset confirm feature by UniPro */ + ufshcd_writel(hba, + (ufshcd_readl(hba, REG_UFS_XOUFS_CTRL) & ~0x100), + REG_UFS_XOUFS_CTRL); + err = ufs_mtk_unipro_set_lpm(hba, true); if (err) { /* Resume UniPro state for following error recovery */ @@ -973,17 +1111,52 @@ static int ufs_mtk_link_set_lpm(struct ufs_hba *hba) return 0; } -static void ufs_mtk_vreg_set_lpm(struct ufs_hba *hba, bool lpm) +static void ufs_mtk_vccqx_set_lpm(struct ufs_hba *hba, bool lpm) { - if (!hba->vreg_info.vccq2 || !hba->vreg_info.vcc) + struct ufs_vreg *vccqx = NULL; + + if (hba->vreg_info.vccq) + vccqx = hba->vreg_info.vccq; + else + vccqx = hba->vreg_info.vccq2; + + regulator_set_mode(vccqx->reg, + lpm ? REGULATOR_MODE_IDLE : REGULATOR_MODE_NORMAL); +} + +static void ufs_mtk_vsx_set_lpm(struct ufs_hba *hba, bool lpm) +{ + struct arm_smccc_res res; + + ufs_mtk_device_pwr_ctrl(!lpm, + (unsigned long)hba->dev_info.wspecversion, + res); +} + +static void ufs_mtk_dev_vreg_set_lpm(struct ufs_hba *hba, bool lpm) +{ + if (!hba->vreg_info.vccq && !hba->vreg_info.vccq2) return; - if (lpm && !hba->vreg_info.vcc->enabled) - regulator_set_mode(hba->vreg_info.vccq2->reg, - REGULATOR_MODE_IDLE); - else if (!lpm) - regulator_set_mode(hba->vreg_info.vccq2->reg, - REGULATOR_MODE_NORMAL); + /* Skip if VCC is assumed always-on */ + if (!hba->vreg_info.vcc) + return; + + /* Bypass LPM when device is still active */ + if (lpm && ufshcd_is_ufs_dev_active(hba)) + return; + + /* Bypass LPM if VCC is enabled */ + if (lpm && hba->vreg_info.vcc->enabled) + return; + + if (lpm) { + ufs_mtk_vccqx_set_lpm(hba, lpm); + ufs_mtk_vsx_set_lpm(hba, lpm); + } else { + ufs_mtk_vsx_set_lpm(hba, lpm); + ufs_mtk_vccqx_set_lpm(hba, lpm); + } } static void ufs_mtk_auto_hibern8_disable(struct ufs_hba *hba) @@ -1026,7 +1199,6 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, * ufshcd_suspend() re-enabling regulators while vreg is still * in low-power mode. */ - ufs_mtk_vreg_set_lpm(hba, true); err = ufs_mtk_mphy_power_on(hba, false); if (err) goto fail; @@ -1035,6 +1207,8 @@ static int ufs_mtk_suspend(struct ufs_hba *hba, enum ufs_pm_op pm_op, if (ufshcd_is_link_off(hba)) ufs_mtk_device_reset_ctrl(0, res); + ufs_mtk_host_pwr_ctrl(HOST_PWR_HCI, false, res); + return 0; fail: /* @@ -1049,13 +1223,17 @@ fail: static int ufs_mtk_resume(struct ufs_hba *hba, enum ufs_pm_op pm_op) { int err; + struct arm_smccc_res res; + + if (hba->ufshcd_state != UFSHCD_STATE_OPERATIONAL) + ufs_mtk_dev_vreg_set_lpm(hba, false); + + ufs_mtk_host_pwr_ctrl(HOST_PWR_HCI, true, res); err = ufs_mtk_mphy_power_on(hba, true); if (err) goto fail; - ufs_mtk_vreg_set_lpm(hba, false); - if (ufshcd_is_link_hibern8(hba)) { err = ufs_mtk_link_set_hpm(hba); if (err) @@ -1087,8 +1265,10 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba) struct ufs_dev_info *dev_info = &hba->dev_info; u16 mid = dev_info->wmanufacturerid; - if (mid == UFS_VENDOR_SAMSUNG) + if (mid == UFS_VENDOR_SAMSUNG) { ufshcd_dme_set(hba, UIC_ARG_MIB(PA_TACTIVATE), 6); + ufshcd_dme_set(hba, UIC_ARG_MIB(PA_HIBERN8TIME), 10); + } /* * Decide waiting time before gating reference clock and @@ -1104,7 +1284,6 @@ static int ufs_mtk_apply_dev_quirks(struct ufs_hba *hba) else ufs_mtk_setup_ref_clk_wait_us(hba, REFCLK_DEFAULT_WAIT_US); - return 0; } @@ -1122,6 +1301,9 @@ static void ufs_mtk_fixup_dev_quirks(struct ufs_hba *hba) hba->dev_quirks &= ~(UFS_DEVICE_QUIRK_DELAY_BEFORE_LPM | UFS_DEVICE_QUIRK_DELAY_AFTER_LPM); } + + ufs_mtk_vreg_fix_vcc(hba); + ufs_mtk_vreg_fix_vccqx(hba); } static void ufs_mtk_event_notify(struct ufs_hba *hba, @@ -1220,9 +1402,59 @@ static int ufs_mtk_remove(struct platform_device *pdev) return 0; } +#ifdef CONFIG_PM_SLEEP +static int ufs_mtk_system_suspend(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + int ret; + + ret = ufshcd_system_suspend(dev); + if (ret) + return ret; + + ufs_mtk_dev_vreg_set_lpm(hba, true); + + return 0; +} + +static int ufs_mtk_system_resume(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + ufs_mtk_dev_vreg_set_lpm(hba, false); + + return ufshcd_system_resume(dev); +} +#endif + +static int ufs_mtk_runtime_suspend(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + int ret = 0; + + ret = ufshcd_runtime_suspend(dev); + if (ret) + return ret; + + ufs_mtk_dev_vreg_set_lpm(hba, true); + + return 0; +} + +static int ufs_mtk_runtime_resume(struct device *dev) +{ + struct ufs_hba *hba = dev_get_drvdata(dev); + + ufs_mtk_dev_vreg_set_lpm(hba, false); + + return ufshcd_runtime_resume(dev); +} + static const struct dev_pm_ops ufs_mtk_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(ufshcd_system_suspend, ufshcd_system_resume) - SET_RUNTIME_PM_OPS(ufshcd_runtime_suspend, ufshcd_runtime_resume, NULL) + SET_SYSTEM_SLEEP_PM_OPS(ufs_mtk_system_suspend, + ufs_mtk_system_resume) + SET_RUNTIME_PM_OPS(ufs_mtk_runtime_suspend, + ufs_mtk_runtime_resume, NULL) .prepare = ufshcd_suspend_prepare, .complete = ufshcd_resume_complete, }; diff --git a/drivers/ufs/host/ufs-mediatek.h b/drivers/ufs/host/ufs-mediatek.h index 414dca86c09f..aa26d415527b 100644 --- a/drivers/ufs/host/ufs-mediatek.h +++ b/drivers/ufs/host/ufs-mediatek.h @@ -7,11 +7,13 @@ #define _UFS_MEDIATEK_H #include +#include #include /* * Vendor specific UFSHCI Registers */ +#define REG_UFS_XOUFS_CTRL 0x140 #define REG_UFS_REFCLK_CTRL 0x144 #define REG_UFS_EXTREG 0x2100 #define REG_UFS_MPHYCTRL 0x2200 @@ -83,6 +85,9 @@ enum { #define UFS_MTK_SIP_DEVICE_RESET BIT(1) #define UFS_MTK_SIP_CRYPTO_CTRL BIT(2) #define UFS_MTK_SIP_REF_CLK_NOTIFICATION BIT(3) +#define UFS_MTK_SIP_HOST_PWR_CTRL BIT(5) +#define UFS_MTK_SIP_GET_VCC_NUM BIT(6) +#define UFS_MTK_SIP_DEVICE_PWR_CTRL BIT(7) /* * VS_DEBUGCLOCKENABLE @@ -108,6 +113,7 @@ enum ufs_mtk_host_caps { UFS_MTK_CAP_VA09_PWR_CTRL = 1 << 1, UFS_MTK_CAP_DISABLE_AH8 = 1 << 2, UFS_MTK_CAP_BROKEN_VCC = 1 << 3, + UFS_MTK_CAP_PMC_VIA_FASTAUTO = 1 << 6, }; struct ufs_mtk_crypt_cfg { @@ -126,6 +132,7 @@ struct ufs_mtk_hw_ver { struct ufs_mtk_host { struct phy *mphy; + struct pm_qos_request pm_qos_req; struct regulator *reg_va09; struct reset_control *hci_reset; struct reset_control *unipro_reset; @@ -135,6 +142,7 @@ struct ufs_mtk_host { struct ufs_mtk_hw_ver hw_ver; enum ufs_mtk_host_caps caps; bool mphy_powered_on; + bool pm_qos_init; bool unipro_lpm; bool ref_clk_enabled; u16 ref_clk_ungating_wait_us; @@ -142,4 +150,70 @@ struct ufs_mtk_host { u32 ip_ver; }; +/* + * Multi-VCC by Numbering + */ +enum ufs_mtk_vcc_num { + UFS_VCC_NONE = 0, + UFS_VCC_1, + UFS_VCC_2, + UFS_VCC_MAX +}; + +/* + * Host Power Control options + */ +enum { + HOST_PWR_HCI = 0, + HOST_PWR_MPHY +}; + +/* + * SMC call wrapper function + */ +struct ufs_mtk_smc_arg { + unsigned long cmd; + struct arm_smccc_res *res; + unsigned long v1; + unsigned long v2; + unsigned long v3; + unsigned long v4; + unsigned long v5; + unsigned long v6; + unsigned long v7; +}; + +static void _ufs_mtk_smc(struct ufs_mtk_smc_arg s) +{ + arm_smccc_smc(MTK_SIP_UFS_CONTROL, + s.cmd, s.v1, s.v2, s.v3, s.v4, s.v5, s.v6, s.res); +} + +#define ufs_mtk_smc(...) \ + _ufs_mtk_smc((struct ufs_mtk_smc_arg) {__VA_ARGS__}) + +/* + * SMC call interface + */ +#define ufs_mtk_va09_pwr_ctrl(res, on) \ + ufs_mtk_smc(UFS_MTK_SIP_VA09_PWR_CTRL, &(res), on) + +#define ufs_mtk_crypto_ctrl(res, enable) \ + ufs_mtk_smc(UFS_MTK_SIP_CRYPTO_CTRL, &(res), enable) + +#define ufs_mtk_ref_clk_notify(on, stage, res) \ + ufs_mtk_smc(UFS_MTK_SIP_REF_CLK_NOTIFICATION, &(res), on, stage) + +#define ufs_mtk_device_reset_ctrl(high, res) \ + ufs_mtk_smc(UFS_MTK_SIP_DEVICE_RESET, &(res), high) + +#define ufs_mtk_host_pwr_ctrl(opt, on, res) \ + ufs_mtk_smc(UFS_MTK_SIP_HOST_PWR_CTRL, &(res), opt, on) + +#define ufs_mtk_get_vcc_num(res) \ + ufs_mtk_smc(UFS_MTK_SIP_GET_VCC_NUM, &(res)) + +#define ufs_mtk_device_pwr_ctrl(on, ufs_ver, res) \ + ufs_mtk_smc(UFS_MTK_SIP_DEVICE_PWR_CTRL, &(res), on, ufs_ver) + #endif /* !_UFS_MEDIATEK_H */ diff --git a/drivers/ufs/host/ufs-qcom.c b/drivers/ufs/host/ufs-qcom.c index f10d4668814c..473fad83701e 100644 --- a/drivers/ufs/host/ufs-qcom.c +++ b/drivers/ufs/host/ufs-qcom.c @@ -58,19 +58,6 @@ static void ufs_qcom_dump_regs_wrapper(struct ufs_hba *hba, int offset, int len, ufshcd_dump_regs(hba, offset, len * 4, prefix); } -static int ufs_qcom_get_connected_tx_lanes(struct ufs_hba *hba, u32 *tx_lanes) -{ - int err = 0; - - err = ufshcd_dme_get(hba, - UIC_ARG_MIB(PA_CONNECTEDTXDATALANES), tx_lanes); - if (err) - dev_err(hba->dev, "%s: couldn't read PA_CONNECTEDTXDATALANES %d\n", - __func__, err); - - return err; -} - static int ufs_qcom_host_clk_get(struct device *dev, const char *name, struct clk **clk_out, bool optional) { @@ -194,13 +181,6 @@ out: return err; } -static int ufs_qcom_link_startup_post_change(struct ufs_hba *hba) -{ - u32 tx_lanes; - - return ufs_qcom_get_connected_tx_lanes(hba, &tx_lanes); -} - static int ufs_qcom_check_hibern8(struct ufs_hba *hba) { int err; @@ -569,9 +549,6 @@ static int ufs_qcom_link_startup_notify(struct ufs_hba *hba, if (ufshcd_get_local_unipro_ver(hba) != UFS_UNIPRO_VER_1_41) err = ufshcd_disable_host_tx_lcc(hba); - break; - case POST_CHANGE: - ufs_qcom_link_startup_post_change(hba); break; default: break; diff --git a/drivers/ufs/host/ufs-renesas.c b/drivers/ufs/host/ufs-renesas.c new file mode 100644 index 000000000000..f8a5e79ed3b4 --- /dev/null +++ b/drivers/ufs/host/ufs-renesas.c @@ -0,0 +1,412 @@ +// SPDX-License-Identifier: GPL-2.0 OR MIT +/* + * Renesas UFS host controller driver + * + * Copyright (C) 2022 Renesas Electronics Corporation + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ufshcd-pltfrm.h" + +struct ufs_renesas_priv { + bool initialized; /* The hardware needs initialization once */ +}; + +enum { + SET_PHY_INDEX_LO = 0, + SET_PHY_INDEX_HI, + TIMER_INDEX, + MAX_INDEX +}; + +enum ufs_renesas_init_param_mode { + MODE_RESTORE, + MODE_SET, + MODE_SAVE, + MODE_POLL, + MODE_WAIT, + MODE_WRITE, +}; + +#define PARAM_RESTORE(_reg, _index) \ + { .mode = MODE_RESTORE, .reg = _reg, .index = _index } +#define PARAM_SET(_index, _set) \ + { .mode = MODE_SET, .index = _index, .u.set = _set } +#define PARAM_SAVE(_reg, _mask, _index) \ + { .mode = MODE_SAVE, .reg = _reg, .mask = (u32)(_mask), \ + .index = _index } +#define PARAM_POLL(_reg, _expected, _mask) \ + { .mode = MODE_POLL, .reg = _reg, .u.expected = _expected, \ + .mask = (u32)(_mask) } +#define PARAM_WAIT(_delay_us) \ + { .mode = MODE_WAIT, .u.delay_us = _delay_us } + +#define PARAM_WRITE(_reg, _val) \ + { .mode = MODE_WRITE, .reg = _reg, .u.val = _val } + +#define PARAM_WRITE_D0_D4(_d0, _d4) \ + PARAM_WRITE(0xd0, _d0), PARAM_WRITE(0xd4, _d4) + +#define PARAM_WRITE_800_80C_POLL(_addr, _data_800) \ + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), \ + PARAM_WRITE_D0_D4(0x00000800, ((_data_800) << 16) | BIT(8) | (_addr)), \ + PARAM_WRITE(0xd0, 0x0000080c), \ + PARAM_POLL(0xd4, BIT(8), BIT(8)) + +#define PARAM_RESTORE_800_80C_POLL(_index) \ + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), \ + PARAM_WRITE(0xd0, 0x00000800), \ + PARAM_RESTORE(0xd4, _index), \ + PARAM_WRITE(0xd0, 0x0000080c), \ + PARAM_POLL(0xd4, BIT(8), BIT(8)) + +#define PARAM_WRITE_804_80C_POLL(_addr, _data_804) \ + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), \ + PARAM_WRITE_D0_D4(0x00000804, ((_data_804) << 16) | BIT(8) | (_addr)), \ + PARAM_WRITE(0xd0, 0x0000080c), \ + PARAM_POLL(0xd4, BIT(8), BIT(8)) + +#define PARAM_WRITE_828_82C_POLL(_data_828) \ + PARAM_WRITE_D0_D4(0x0000082c, 0x0f000000), \ + PARAM_WRITE_D0_D4(0x00000828, _data_828), \ + PARAM_WRITE(0xd0, 0x0000082c), \ + PARAM_POLL(0xd4, _data_828, _data_828) + +#define PARAM_WRITE_PHY(_addr16, _data16) \ + PARAM_WRITE(0xf0, 1), \ + PARAM_WRITE_800_80C_POLL(0x16, (_addr16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x17, ((_addr16) >> 8) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x18, (_data16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x19, ((_data16) >> 8) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x1c, 0x01), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0) + +#define PARAM_SET_PHY(_addr16, _data16) \ + PARAM_WRITE(0xf0, 1), \ + PARAM_WRITE_800_80C_POLL(0x16, (_addr16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x17, ((_addr16) >> 8) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x1c, 0x01), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE_804_80C_POLL(0x1a, 0), \ + PARAM_WRITE(0xd0, 0x00000808), \ + PARAM_SAVE(0xd4, 0xff, SET_PHY_INDEX_LO), \ + PARAM_WRITE_804_80C_POLL(0x1b, 0), \ + PARAM_WRITE(0xd0, 0x00000808), \ + PARAM_SAVE(0xd4, 0xff, SET_PHY_INDEX_HI), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0), \ + PARAM_WRITE(0xf0, 1), \ + PARAM_WRITE_800_80C_POLL(0x16, (_addr16) & 0xff), \ + PARAM_WRITE_800_80C_POLL(0x17, ((_addr16) >> 8) & 0xff), \ + PARAM_SET(SET_PHY_INDEX_LO, ((_data16 & 0xff) << 16) | BIT(8) | 0x18), \ + PARAM_RESTORE_800_80C_POLL(SET_PHY_INDEX_LO), \ + PARAM_SET(SET_PHY_INDEX_HI, (((_data16 >> 8) & 0xff) << 16) | BIT(8) | 0x19), \ + PARAM_RESTORE_800_80C_POLL(SET_PHY_INDEX_HI), \ + PARAM_WRITE_800_80C_POLL(0x1c, 0x01), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0) + +#define PARAM_INDIRECT_WRITE(_gpio, _addr, _data_800) \ + PARAM_WRITE(0xf0, _gpio), \ + PARAM_WRITE_800_80C_POLL(_addr, _data_800), \ + PARAM_WRITE_828_82C_POLL(0x0f000000), \ + PARAM_WRITE(0xf0, 0) + +#define PARAM_INDIRECT_POLL(_gpio, _addr, _expected, _mask) \ + PARAM_WRITE(0xf0, _gpio), \ + PARAM_WRITE_800_80C_POLL(_addr, 0), \ + PARAM_WRITE(0xd0, 0x00000808), \ + PARAM_POLL(0xd4, _expected, _mask), \ + PARAM_WRITE(0xf0, 0) + +struct ufs_renesas_init_param { + enum ufs_renesas_init_param_mode mode; + u32 reg; + union { + u32 expected; + u32 delay_us; + u32 set; + u32 val; + } u; + u32 mask; + u32 index; +}; + +/* This setting is for SERIES B */ +static const struct ufs_renesas_init_param ufs_param[] = { + PARAM_WRITE(0xc0, 0x49425308), + PARAM_WRITE_D0_D4(0x00000104, 0x00000002), + PARAM_WAIT(1), + PARAM_WRITE_D0_D4(0x00000828, 0x00000200), + PARAM_WAIT(1), + PARAM_WRITE_D0_D4(0x00000828, 0x00000000), + PARAM_WRITE_D0_D4(0x00000104, 0x00000001), + PARAM_WRITE_D0_D4(0x00000940, 0x00000001), + PARAM_WAIT(1), + PARAM_WRITE_D0_D4(0x00000940, 0x00000000), + + PARAM_WRITE(0xc0, 0x49425308), + PARAM_WRITE(0xc0, 0x41584901), + + PARAM_WRITE_D0_D4(0x0000080c, 0x00000100), + PARAM_WRITE_D0_D4(0x00000804, 0x00000000), + PARAM_WRITE(0xd0, 0x0000080c), + PARAM_POLL(0xd4, BIT(8), BIT(8)), + + PARAM_WRITE(REG_CONTROLLER_ENABLE, 0x00000001), + + PARAM_WRITE(0xd0, 0x00000804), + PARAM_POLL(0xd4, BIT(8) | BIT(6) | BIT(0), BIT(8) | BIT(6) | BIT(0)), + + PARAM_WRITE(0xd0, 0x00000d00), + PARAM_SAVE(0xd4, 0x0000ffff, TIMER_INDEX), + PARAM_WRITE(0xd4, 0x00000000), + PARAM_WRITE_D0_D4(0x0000082c, 0x0f000000), + PARAM_WRITE_D0_D4(0x00000828, 0x08000000), + PARAM_WRITE(0xd0, 0x0000082c), + PARAM_POLL(0xd4, BIT(27), BIT(27)), + PARAM_WRITE(0xd0, 0x00000d2c), + PARAM_POLL(0xd4, BIT(0), BIT(0)), + + /* phy setup */ + PARAM_INDIRECT_WRITE(1, 0x01, 0x001f), + PARAM_INDIRECT_WRITE(7, 0x5d, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x5e, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x0d, 0x0003), + PARAM_INDIRECT_WRITE(7, 0x0e, 0x0007), + PARAM_INDIRECT_WRITE(7, 0x5f, 0x0003), + PARAM_INDIRECT_WRITE(7, 0x60, 0x0003), + PARAM_INDIRECT_WRITE(7, 0x5b, 0x00a6), + PARAM_INDIRECT_WRITE(7, 0x5c, 0x0003), + + PARAM_INDIRECT_POLL(7, 0x3c, 0, BIT(7)), + PARAM_INDIRECT_POLL(7, 0x4c, 0, BIT(4)), + + PARAM_INDIRECT_WRITE(1, 0x32, 0x0080), + PARAM_INDIRECT_WRITE(1, 0x1f, 0x0001), + PARAM_INDIRECT_WRITE(0, 0x2c, 0x0001), + PARAM_INDIRECT_WRITE(0, 0x32, 0x0087), + + PARAM_INDIRECT_WRITE(1, 0x4d, 0x0061), + PARAM_INDIRECT_WRITE(4, 0x9b, 0x0009), + PARAM_INDIRECT_WRITE(4, 0xa6, 0x0005), + PARAM_INDIRECT_WRITE(4, 0xa5, 0x0058), + PARAM_INDIRECT_WRITE(1, 0x39, 0x0027), + PARAM_INDIRECT_WRITE(1, 0x47, 0x004c), + + PARAM_INDIRECT_WRITE(7, 0x0d, 0x0002), + PARAM_INDIRECT_WRITE(7, 0x0e, 0x0007), + + PARAM_WRITE_PHY(0x0028, 0x0061), + PARAM_WRITE_PHY(0x4014, 0x0061), + PARAM_SET_PHY(0x401c, BIT(2)), + PARAM_WRITE_PHY(0x4000, 0x0000), + PARAM_WRITE_PHY(0x4001, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0000), + PARAM_WRITE_PHY(0x10af, 0x0001), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0000), + PARAM_WRITE_PHY(0x10af, 0x0002), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0080), + PARAM_WRITE_PHY(0x10af, 0x0000), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_WRITE_PHY(0x10ae, 0x0001), + PARAM_WRITE_PHY(0x10ad, 0x0080), + PARAM_WRITE_PHY(0x10af, 0x001a), + PARAM_WRITE_PHY(0x10b6, 0x0001), + PARAM_WRITE_PHY(0x10ae, 0x0000), + + PARAM_INDIRECT_WRITE(7, 0x70, 0x0016), + PARAM_INDIRECT_WRITE(7, 0x71, 0x0016), + PARAM_INDIRECT_WRITE(7, 0x72, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x73, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x74, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x75, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x76, 0x0010), + PARAM_INDIRECT_WRITE(7, 0x77, 0x0010), + PARAM_INDIRECT_WRITE(7, 0x78, 0x00ff), + PARAM_INDIRECT_WRITE(7, 0x79, 0x0000), + + PARAM_INDIRECT_WRITE(7, 0x19, 0x0007), + + PARAM_INDIRECT_WRITE(7, 0x1a, 0x0007), + + PARAM_INDIRECT_WRITE(7, 0x24, 0x000c), + + PARAM_INDIRECT_WRITE(7, 0x25, 0x000c), + + PARAM_INDIRECT_WRITE(7, 0x62, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x63, 0x0000), + PARAM_INDIRECT_WRITE(7, 0x5d, 0x0014), + PARAM_INDIRECT_WRITE(7, 0x5e, 0x0017), + PARAM_INDIRECT_WRITE(7, 0x5d, 0x0004), + PARAM_INDIRECT_WRITE(7, 0x5e, 0x0017), + PARAM_INDIRECT_POLL(7, 0x55, 0, BIT(6)), + PARAM_INDIRECT_POLL(7, 0x41, 0, BIT(7)), + /* end of phy setup */ + + PARAM_WRITE(0xf0, 0), + PARAM_WRITE(0xd0, 0x00000d00), + PARAM_RESTORE(0xd4, TIMER_INDEX), +}; + +static void ufs_renesas_dbg_register_dump(struct ufs_hba *hba) +{ + ufshcd_dump_regs(hba, 0xc0, 0x40, "regs: 0xc0 + "); +} + +static void ufs_renesas_reg_control(struct ufs_hba *hba, + const struct ufs_renesas_init_param *p) +{ + static u32 save[MAX_INDEX]; + int ret; + u32 val; + + WARN_ON(p->index >= MAX_INDEX); + + switch (p->mode) { + case MODE_RESTORE: + ufshcd_writel(hba, save[p->index], p->reg); + break; + case MODE_SET: + save[p->index] |= p->u.set; + break; + case MODE_SAVE: + save[p->index] = ufshcd_readl(hba, p->reg) & p->mask; + break; + case MODE_POLL: + ret = readl_poll_timeout_atomic(hba->mmio_base + p->reg, + val, + (val & p->mask) == p->u.expected, + 10, 1000); + if (ret) + dev_err(hba->dev, "%s: poll failed %d (%08x, %08x, %08x)\n", + __func__, ret, val, p->mask, p->u.expected); + break; + case MODE_WAIT: + if (p->u.delay_us > 1000) + mdelay(DIV_ROUND_UP(p->u.delay_us, 1000)); + else + udelay(p->u.delay_us); + break; + case MODE_WRITE: + ufshcd_writel(hba, p->u.val, p->reg); + break; + default: + break; + } +} + +static void ufs_renesas_pre_init(struct ufs_hba *hba) +{ + const struct ufs_renesas_init_param *p = ufs_param; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(ufs_param); i++) + ufs_renesas_reg_control(hba, &p[i]); +} + +static int ufs_renesas_hce_enable_notify(struct ufs_hba *hba, + enum ufs_notify_change_status status) +{ + struct ufs_renesas_priv *priv = ufshcd_get_variant(hba); + + if (priv->initialized) + return 0; + + if (status == PRE_CHANGE) + ufs_renesas_pre_init(hba); + + priv->initialized = true; + + return 0; +} + +static int ufs_renesas_setup_clocks(struct ufs_hba *hba, bool on, + enum ufs_notify_change_status status) +{ + if (on && status == PRE_CHANGE) + pm_runtime_get_sync(hba->dev); + else if (!on && status == POST_CHANGE) + pm_runtime_put(hba->dev); + + return 0; +} + +static int ufs_renesas_init(struct ufs_hba *hba) +{ + struct ufs_renesas_priv *priv; + + priv = devm_kmalloc(hba->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + ufshcd_set_variant(hba, priv); + + hba->quirks |= UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS | UFSHCD_QUIRK_HIBERN_FASTAUTO; + + return 0; +} + +static const struct ufs_hba_variant_ops ufs_renesas_vops = { + .name = "renesas", + .init = ufs_renesas_init, + .setup_clocks = ufs_renesas_setup_clocks, + .hce_enable_notify = ufs_renesas_hce_enable_notify, + .dbg_register_dump = ufs_renesas_dbg_register_dump, +}; + +static const struct of_device_id __maybe_unused ufs_renesas_of_match[] = { + { .compatible = "renesas,r8a779f0-ufs" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ufs_renesas_of_match); + +static int ufs_renesas_probe(struct platform_device *pdev) +{ + return ufshcd_pltfrm_init(pdev, &ufs_renesas_vops); +} + +static int ufs_renesas_remove(struct platform_device *pdev) +{ + struct ufs_hba *hba = platform_get_drvdata(pdev); + + ufshcd_remove(hba); + + return 0; +} + +static struct platform_driver ufs_renesas_platform = { + .probe = ufs_renesas_probe, + .remove = ufs_renesas_remove, + .driver = { + .name = "ufshcd-renesas", + .of_match_table = of_match_ptr(ufs_renesas_of_match), + }, +}; +module_platform_driver(ufs_renesas_platform); + +MODULE_AUTHOR("Yoshihiro Shimoda "); +MODULE_DESCRIPTION("Renesas UFS host controller driver"); +MODULE_LICENSE("Dual MIT/GPL"); diff --git a/drivers/ufs/host/ufshcd-pci.c b/drivers/ufs/host/ufshcd-pci.c index 04166bda41da..24af1f389bf2 100644 --- a/drivers/ufs/host/ufshcd-pci.c +++ b/drivers/ufs/host/ufshcd-pci.c @@ -426,6 +426,7 @@ static int ufs_intel_adl_init(struct ufs_hba *hba) { hba->nop_out_timeout = 200; hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8; + hba->caps |= UFSHCD_CAP_WB_EN; return ufs_intel_common_init(hba); } diff --git a/drivers/ufs/host/ufshcd-pltfrm.c b/drivers/ufs/host/ufshcd-pltfrm.c index 173aea8e9997..5739ff007828 100644 --- a/drivers/ufs/host/ufshcd-pltfrm.c +++ b/drivers/ufs/host/ufshcd-pltfrm.c @@ -26,7 +26,7 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) int i; struct device *dev = hba->dev; struct device_node *np = dev->of_node; - char *name; + const char *name; u32 *clkfreq = NULL; struct ufs_clk_info *clki; int len = 0; @@ -79,8 +79,8 @@ static int ufshcd_parse_clock_info(struct ufs_hba *hba) } for (i = 0; i < sz; i += 2) { - ret = of_property_read_string_index(np, - "clock-names", i/2, (const char **)&name); + ret = of_property_read_string_index(np, "clock-names", i/2, + &name); if (ret) goto out; @@ -120,8 +120,8 @@ static bool phandle_exists(const struct device_node *np, } #define MAX_PROP_SIZE 32 -static int ufshcd_populate_vreg(struct device *dev, const char *name, - struct ufs_vreg **out_vreg) +int ufshcd_populate_vreg(struct device *dev, const char *name, + struct ufs_vreg **out_vreg) { char prop_name[MAX_PROP_SIZE]; struct ufs_vreg *vreg = NULL; @@ -156,6 +156,7 @@ out: *out_vreg = vreg; return 0; } +EXPORT_SYMBOL_GPL(ufshcd_populate_vreg); /** * ufshcd_parse_regulator_info - get regulator info from device tree @@ -219,8 +220,8 @@ static void ufshcd_init_lanes_per_dir(struct ufs_hba *hba) * * Returns 0 on success, non-zero value on failure */ -int ufshcd_get_pwr_dev_param(struct ufs_dev_params *pltfrm_param, - struct ufs_pa_layer_attr *dev_max, +int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *pltfrm_param, + const struct ufs_pa_layer_attr *dev_max, struct ufs_pa_layer_attr *agreed_pwr) { int min_pltfrm_gear; diff --git a/drivers/ufs/host/ufshcd-pltfrm.h b/drivers/ufs/host/ufshcd-pltfrm.h index 43c2e412bd99..2e4ba2bfbcad 100644 --- a/drivers/ufs/host/ufshcd-pltfrm.h +++ b/drivers/ufs/host/ufshcd-pltfrm.h @@ -25,12 +25,14 @@ struct ufs_dev_params { u32 desired_working_mode; }; -int ufshcd_get_pwr_dev_param(struct ufs_dev_params *dev_param, - struct ufs_pa_layer_attr *dev_max, +int ufshcd_get_pwr_dev_param(const struct ufs_dev_params *dev_param, + const struct ufs_pa_layer_attr *dev_max, struct ufs_pa_layer_attr *agreed_pwr); void ufshcd_init_pwr_dev_param(struct ufs_dev_params *dev_param); int ufshcd_pltfrm_init(struct platform_device *pdev, const struct ufs_hba_variant_ops *vops); void ufshcd_pltfrm_shutdown(struct platform_device *pdev); +int ufshcd_populate_vreg(struct device *dev, const char *name, + struct ufs_vreg **out_vreg); #endif /* UFSHCD_PLTFRM_H_ */ diff --git a/drivers/uio/uio_pruss.c b/drivers/uio/uio_pruss.c index e9096f53b4cc..83966dbd3bbf 100644 --- a/drivers/uio/uio_pruss.c +++ b/drivers/uio/uio_pruss.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Programmable Real-Time Unit Sub System (PRUSS) UIO driver (uio_pruss) * @@ -5,15 +6,6 @@ * and DDR RAM to user space for applications interacting with PRUSS firmware * * Copyright (C) 2010-11 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/drivers/usb/atm/ueagle-atm.c b/drivers/usb/atm/ueagle-atm.c index 786299892c7f..5812f7ea7f90 100644 --- a/drivers/usb/atm/ueagle-atm.c +++ b/drivers/usb/atm/ueagle-atm.c @@ -9,7 +9,7 @@ * HISTORY : some part of the code was base on ueagle 1.3 BSD driver, * Damien Bergamini agree to put his code under a DUAL GPL/BSD license. * - * The rest of the code was was rewritten from scratch. + * The rest of the code was rewritten from scratch. */ #include diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c index 5c15c48952a6..d21b69997e75 100644 --- a/drivers/usb/cdns3/cdns3-gadget.c +++ b/drivers/usb/cdns3/cdns3-gadget.c @@ -220,7 +220,7 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep) if (!priv_ep->trb_pool) { priv_ep->trb_pool = dma_pool_alloc(priv_dev->eps_dma_pool, - GFP_DMA32 | GFP_ATOMIC, + GFP_ATOMIC, &priv_ep->trb_pool_dma); if (!priv_ep->trb_pool) @@ -625,9 +625,9 @@ static void cdns3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep) trace_cdns3_wa2(priv_ep, "removes eldest request"); kfree(priv_req->request.buf); + list_del_init(&priv_req->list); cdns3_gadget_ep_free_request(&priv_ep->endpoint, &priv_req->request); - list_del_init(&priv_req->list); --priv_ep->wa2_counter; if (!chain) @@ -2284,11 +2284,16 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep, int ret = 0; int val; + if (!ep) { + pr_debug("usbss: ep not configured?\n"); + return -EINVAL; + } + priv_ep = ep_to_cdns3_ep(ep); priv_dev = priv_ep->cdns3_dev; comp_desc = priv_ep->endpoint.comp_desc; - if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) { + if (!desc || desc->bDescriptorType != USB_DT_ENDPOINT) { dev_dbg(priv_dev->dev, "usbss: invalid parameters\n"); return -EINVAL; } @@ -2600,7 +2605,7 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request) { struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep); - struct cdns3_device *priv_dev = priv_ep->cdns3_dev; + struct cdns3_device *priv_dev; struct usb_request *req, *req_temp; struct cdns3_request *priv_req; struct cdns3_trb *link_trb; @@ -2611,6 +2616,8 @@ int cdns3_gadget_ep_dequeue(struct usb_ep *ep, if (!ep || !request || !ep->desc) return -EINVAL; + priv_dev = priv_ep->cdns3_dev; + spin_lock_irqsave(&priv_dev->lock, flags); priv_req = to_cdns3_request(request); diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h index 99440baa6458..a4a3be049910 100644 --- a/drivers/usb/chipidea/ci.h +++ b/drivers/usb/chipidea/ci.h @@ -49,6 +49,7 @@ enum ci_hw_regs { OP_USBCMD, OP_USBSTS, OP_USBINTR, + OP_FRINDEX, OP_DEVICEADDR, OP_ENDPTLISTADDR, OP_TTCTRL, diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c index 097142ffb184..9ffcecd3058c 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.c +++ b/drivers/usb/chipidea/ci_hdrc_imx.c @@ -348,25 +348,18 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) data->pinctrl = devm_pinctrl_get(dev); if (PTR_ERR(data->pinctrl) == -ENODEV) data->pinctrl = NULL; - else if (IS_ERR(data->pinctrl)) { - if (PTR_ERR(data->pinctrl) != -EPROBE_DEFER) - dev_err(dev, "pinctrl get failed, err=%ld\n", - PTR_ERR(data->pinctrl)); - return PTR_ERR(data->pinctrl); - } + else if (IS_ERR(data->pinctrl)) + return dev_err_probe(dev, PTR_ERR(data->pinctrl), + "pinctrl get failed\n"); data->hsic_pad_regulator = devm_regulator_get_optional(dev, "hsic"); if (PTR_ERR(data->hsic_pad_regulator) == -ENODEV) { /* no pad regualator is needed */ data->hsic_pad_regulator = NULL; - } else if (IS_ERR(data->hsic_pad_regulator)) { - if (PTR_ERR(data->hsic_pad_regulator) != -EPROBE_DEFER) - dev_err(dev, - "Get HSIC pad regulator error: %ld\n", - PTR_ERR(data->hsic_pad_regulator)); - return PTR_ERR(data->hsic_pad_regulator); - } + } else if (IS_ERR(data->hsic_pad_regulator)) + return dev_err_probe(dev, PTR_ERR(data->hsic_pad_regulator), + "Get HSIC pad regulator error\n"); if (data->hsic_pad_regulator) { ret = regulator_enable(data->hsic_pad_regulator); @@ -458,9 +451,7 @@ static int ci_hdrc_imx_probe(struct platform_device *pdev) &pdata); if (IS_ERR(data->ci_pdev)) { ret = PTR_ERR(data->ci_pdev); - if (ret != -EPROBE_DEFER) - dev_err(dev, "ci_hdrc_add_device failed, err=%d\n", - ret); + dev_err_probe(dev, ret, "ci_hdrc_add_device failed\n"); goto err_clk; } diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h index 999c65390b7f..7daccb9c5006 100644 --- a/drivers/usb/chipidea/ci_hdrc_imx.h +++ b/drivers/usb/chipidea/ci_hdrc_imx.h @@ -21,7 +21,7 @@ struct imx_usbmisc_data { unsigned int pwr_pol:1; /* power polarity */ unsigned int evdo:1; /* set external vbus divider option */ unsigned int ulpi:1; /* connected to an ULPI phy */ - unsigned int hsic:1; /* HSIC controlller */ + unsigned int hsic:1; /* HSIC controller */ unsigned int ext_id:1; /* ID from exteranl event */ unsigned int ext_vbus:1; /* Vbus from exteranl event */ struct usb_phy *usb_phy; diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c index 5359b2a2e4d2..6330fa911792 100644 --- a/drivers/usb/chipidea/core.c +++ b/drivers/usb/chipidea/core.c @@ -53,6 +53,7 @@ static const u8 ci_regs_nolpm[] = { [OP_USBCMD] = 0x00U, [OP_USBSTS] = 0x04U, [OP_USBINTR] = 0x08U, + [OP_FRINDEX] = 0x0CU, [OP_DEVICEADDR] = 0x14U, [OP_ENDPTLISTADDR] = 0x18U, [OP_TTCTRL] = 0x1CU, @@ -78,6 +79,7 @@ static const u8 ci_regs_lpm[] = { [OP_USBCMD] = 0x00U, [OP_USBSTS] = 0x04U, [OP_USBINTR] = 0x08U, + [OP_FRINDEX] = 0x0CU, [OP_DEVICEADDR] = 0x14U, [OP_ENDPTLISTADDR] = 0x18U, [OP_TTCTRL] = 0x1CU, diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c index 6ed4b00dba96..61b157b9c662 100644 --- a/drivers/usb/chipidea/otg_fsm.c +++ b/drivers/usb/chipidea/otg_fsm.c @@ -459,7 +459,7 @@ static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on) struct ci_hdrc *ci = container_of(fsm, struct ci_hdrc, fsm); if (on) { - /* Enable power power */ + /* Enable power */ hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP, PORTSC_PP); if (ci->platdata->reg_vbus) { diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c index 3b8bf6daf7d0..8c3e3a635ac2 100644 --- a/drivers/usb/chipidea/udc.c +++ b/drivers/usb/chipidea/udc.c @@ -1654,6 +1654,19 @@ static const struct usb_ep_ops usb_ep_ops = { /****************************************************************************** * GADGET block *****************************************************************************/ + +static int ci_udc_get_frame(struct usb_gadget *_gadget) +{ + struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget); + unsigned long flags; + int ret; + + spin_lock_irqsave(&ci->lock, flags); + ret = hw_read(ci, OP_FRINDEX, 0x3fff); + spin_unlock_irqrestore(&ci->lock, flags); + return ret >> 3; +} + /* * ci_hdrc_gadget_connect: caller makes sure gadget driver is binded */ @@ -1810,6 +1823,7 @@ static struct usb_ep *ci_udc_match_ep(struct usb_gadget *gadget, * Check "usb_gadget.h" for details */ static const struct usb_gadget_ops usb_gadget_ops = { + .get_frame = ci_udc_get_frame, .vbus_session = ci_udc_vbus_session, .wakeup = ci_udc_wakeup, .set_selfpowered = ci_udc_selfpowered, diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 9b9aea24d58c..483bcb1213f7 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -119,7 +119,7 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value, retval = usb_control_msg(acm->dev, usb_sndctrlpipe(acm->dev, 0), request, USB_RT_ACM, value, acm->control->altsetting[0].desc.bInterfaceNumber, - buf, len, 5000); + buf, len, USB_CTRL_SET_TIMEOUT); dev_dbg(&acm->control->dev, "%s - rq 0x%02x, val %#x, len %#x, result %d\n", @@ -311,7 +311,7 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) dev_dbg(&acm->control->dev, "%s - serial state: 0x%x\n", __func__, newctrl); - if (!acm->clocal && (acm->ctrlin & ~newctrl & ACM_CTRL_DCD)) { + if (!acm->clocal && (acm->ctrlin & ~newctrl & USB_CDC_SERIAL_STATE_DCD)) { dev_dbg(&acm->control->dev, "%s - calling hangup\n", __func__); tty_port_tty_hangup(&acm->port, false); @@ -322,25 +322,25 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf) acm->ctrlin = newctrl; acm->oldcount = acm->iocount; - if (difference & ACM_CTRL_DSR) + if (difference & USB_CDC_SERIAL_STATE_DSR) acm->iocount.dsr++; - if (difference & ACM_CTRL_DCD) + if (difference & USB_CDC_SERIAL_STATE_DCD) acm->iocount.dcd++; - if (newctrl & ACM_CTRL_BRK) { + if (newctrl & USB_CDC_SERIAL_STATE_BREAK) { acm->iocount.brk++; tty_insert_flip_char(&acm->port, 0, TTY_BREAK); } - if (newctrl & ACM_CTRL_RI) + if (newctrl & USB_CDC_SERIAL_STATE_RING_SIGNAL) acm->iocount.rng++; - if (newctrl & ACM_CTRL_FRAMING) + if (newctrl & USB_CDC_SERIAL_STATE_FRAMING) acm->iocount.frame++; - if (newctrl & ACM_CTRL_PARITY) + if (newctrl & USB_CDC_SERIAL_STATE_PARITY) acm->iocount.parity++; - if (newctrl & ACM_CTRL_OVERRUN) + if (newctrl & USB_CDC_SERIAL_STATE_OVERRUN) acm->iocount.overrun++; spin_unlock_irqrestore(&acm->read_lock, flags); - if (newctrl & ACM_CTRL_BRK) + if (newctrl & USB_CDC_SERIAL_STATE_BREAK) tty_flip_buffer_push(&acm->port); if (difference) @@ -658,7 +658,7 @@ static void acm_port_dtr_rts(struct tty_port *port, int raise) int res; if (raise) - val = ACM_CTRL_DTR | ACM_CTRL_RTS; + val = USB_CDC_CTRL_DTR | USB_CDC_CTRL_RTS; else val = 0; @@ -903,11 +903,11 @@ static int acm_tty_tiocmget(struct tty_struct *tty) { struct acm *acm = tty->driver_data; - return (acm->ctrlout & ACM_CTRL_DTR ? TIOCM_DTR : 0) | - (acm->ctrlout & ACM_CTRL_RTS ? TIOCM_RTS : 0) | - (acm->ctrlin & ACM_CTRL_DSR ? TIOCM_DSR : 0) | - (acm->ctrlin & ACM_CTRL_RI ? TIOCM_RI : 0) | - (acm->ctrlin & ACM_CTRL_DCD ? TIOCM_CD : 0) | + return (acm->ctrlout & USB_CDC_CTRL_DTR ? TIOCM_DTR : 0) | + (acm->ctrlout & USB_CDC_CTRL_RTS ? TIOCM_RTS : 0) | + (acm->ctrlin & USB_CDC_SERIAL_STATE_DSR ? TIOCM_DSR : 0) | + (acm->ctrlin & USB_CDC_SERIAL_STATE_RING_SIGNAL ? TIOCM_RI : 0) | + (acm->ctrlin & USB_CDC_SERIAL_STATE_DCD ? TIOCM_CD : 0) | TIOCM_CTS; } @@ -918,10 +918,10 @@ static int acm_tty_tiocmset(struct tty_struct *tty, unsigned int newctrl; newctrl = acm->ctrlout; - set = (set & TIOCM_DTR ? ACM_CTRL_DTR : 0) | - (set & TIOCM_RTS ? ACM_CTRL_RTS : 0); - clear = (clear & TIOCM_DTR ? ACM_CTRL_DTR : 0) | - (clear & TIOCM_RTS ? ACM_CTRL_RTS : 0); + set = (set & TIOCM_DTR ? USB_CDC_CTRL_DTR : 0) | + (set & TIOCM_RTS ? USB_CDC_CTRL_RTS : 0); + clear = (clear & TIOCM_DTR ? USB_CDC_CTRL_DTR : 0) | + (clear & TIOCM_RTS ? USB_CDC_CTRL_RTS : 0); newctrl = (newctrl & ~clear) | set; @@ -1068,9 +1068,9 @@ static void acm_tty_set_termios(struct tty_struct *tty, if (C_BAUD(tty) == B0) { newline.dwDTERate = acm->line.dwDTERate; - newctrl &= ~ACM_CTRL_DTR; + newctrl &= ~USB_CDC_CTRL_DTR; } else if (termios_old && (termios_old->c_cflag & CBAUD) == B0) { - newctrl |= ACM_CTRL_DTR; + newctrl |= USB_CDC_CTRL_DTR; } if (newctrl != acm->ctrlout) diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index d26ecd15be60..759ac15631d3 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h @@ -22,26 +22,6 @@ #define USB_RT_ACM (USB_TYPE_CLASS | USB_RECIP_INTERFACE) -/* - * Output control lines. - */ - -#define ACM_CTRL_DTR 0x01 -#define ACM_CTRL_RTS 0x02 - -/* - * Input control lines and line errors. - */ - -#define ACM_CTRL_DCD 0x01 -#define ACM_CTRL_DSR 0x02 -#define ACM_CTRL_BRK 0x04 -#define ACM_CTRL_RI 0x08 - -#define ACM_CTRL_FRAMING 0x10 -#define ACM_CTRL_PARITY 0x20 -#define ACM_CTRL_OVERRUN 0x40 - /* * Internal driver structures. */ diff --git a/drivers/usb/common/usb-conn-gpio.c b/drivers/usb/common/usb-conn-gpio.c index 395f9bbe3056..b39c9f1c375d 100644 --- a/drivers/usb/common/usb-conn-gpio.c +++ b/drivers/usb/common/usb-conn-gpio.c @@ -257,6 +257,7 @@ static int usb_conn_probe(struct platform_device *pdev) } platform_set_drvdata(pdev, info); + device_set_wakeup_capable(&pdev->dev, true); /* Perform initial detection */ usb_conn_queue_dwork(info, 0); @@ -286,6 +287,14 @@ static int __maybe_unused usb_conn_suspend(struct device *dev) { struct usb_conn_info *info = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) { + if (info->id_gpiod) + enable_irq_wake(info->id_irq); + if (info->vbus_gpiod) + enable_irq_wake(info->vbus_irq); + return 0; + } + if (info->id_gpiod) disable_irq(info->id_irq); if (info->vbus_gpiod) @@ -300,6 +309,14 @@ static int __maybe_unused usb_conn_resume(struct device *dev) { struct usb_conn_info *info = dev_get_drvdata(dev); + if (device_may_wakeup(dev)) { + if (info->id_gpiod) + disable_irq_wake(info->id_irq); + if (info->vbus_gpiod) + disable_irq_wake(info->vbus_irq); + return 0; + } + pinctrl_pm_select_default_state(dev); if (info->id_gpiod) diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile index 18e874b0441e..7d338e9c0657 100644 --- a/drivers/usb/core/Makefile +++ b/drivers/usb/core/Makefile @@ -12,6 +12,10 @@ usbcore-$(CONFIG_OF) += of.o usbcore-$(CONFIG_USB_PCI) += hcd-pci.o usbcore-$(CONFIG_ACPI) += usb-acpi.o +ifdef CONFIG_USB_ONBOARD_HUB +usbcore-y += ../misc/onboard_usb_hub_pdevs.o +endif + obj-$(CONFIG_USB) += usbcore.o obj-$(CONFIG_USB_LEDS_TRIGGER_USBPORT) += ledtrig-usbport.o diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index b87452e22835..7e7e119c253f 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -1482,7 +1482,7 @@ static int usb_suspend_both(struct usb_device *udev, pm_message_t msg) * @msg: Power Management message describing this state transition * * This is the central routine for resuming USB devices. It calls the - * the resume method for @udev and then calls the resume methods for all + * resume method for @udev and then calls the resume methods for all * the interface drivers in @udev. * * Autoresume requests originating from a child device or an interface diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 06eea8848ccc..a6a87c5d1b05 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -1691,7 +1691,6 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) spin_lock_irq(&bh->lock); bh->running = true; - restart: list_replace_init(&bh->head, &local_list); spin_unlock_irq(&bh->lock); @@ -1705,10 +1704,17 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) bh->completing_ep = NULL; } - /* check if there are new URBs to giveback */ + /* + * giveback new URBs next time to prevent this function + * from not exiting for a long time. + */ spin_lock_irq(&bh->lock); - if (!list_empty(&bh->head)) - goto restart; + if (!list_empty(&bh->head)) { + if (bh->high_prio) + tasklet_hi_schedule(&bh->bh); + else + tasklet_schedule(&bh->bh); + } bh->running = false; spin_unlock_irq(&bh->lock); } @@ -1737,7 +1743,7 @@ static void usb_giveback_urb_bh(struct tasklet_struct *t) void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) { struct giveback_urb_bh *bh; - bool running, high_prio_bh; + bool running; /* pass status to tasklet via unlinked */ if (likely(!urb->unlinked)) @@ -1748,13 +1754,10 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) return; } - if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) { + if (usb_pipeisoc(urb->pipe) || usb_pipeint(urb->pipe)) bh = &hcd->high_prio_bh; - high_prio_bh = true; - } else { + else bh = &hcd->low_prio_bh; - high_prio_bh = false; - } spin_lock(&bh->lock); list_add_tail(&urb->urb_list, &bh->head); @@ -1763,7 +1766,7 @@ void usb_hcd_giveback_urb(struct usb_hcd *hcd, struct urb *urb, int status) if (running) ; - else if (high_prio_bh) + else if (bh->high_prio) tasklet_hi_schedule(&bh->bh); else tasklet_schedule(&bh->bh); @@ -2959,6 +2962,7 @@ int usb_add_hcd(struct usb_hcd *hcd, /* initialize tasklets */ init_giveback_urb_bh(&hcd->high_prio_bh); + hcd->high_prio_bh.high_prio = true; init_giveback_urb_bh(&hcd->low_prio_bh); /* enable irqs just before we start the controller, @@ -3033,9 +3037,15 @@ EXPORT_SYMBOL_GPL(usb_add_hcd); */ void usb_remove_hcd(struct usb_hcd *hcd) { - struct usb_device *rhdev = hcd->self.root_hub; + struct usb_device *rhdev; bool rh_registered; + if (!hcd) { + pr_debug("%s: hcd is NULL\n", __func__); + return; + } + rhdev = hcd->self.root_hub; + dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); usb_get_dev(rhdev); diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 68e9121c1878..2633acde7ac1 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -613,7 +614,7 @@ static int hub_ext_port_status(struct usb_hub *hub, int port1, int type, return ret; } -static int hub_port_status(struct usb_hub *hub, int port1, +int usb_hub_port_status(struct usb_hub *hub, int port1, u16 *status, u16 *change) { return hub_ext_port_status(hub, port1, HUB_PORT_STATUS, @@ -1126,7 +1127,7 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type) u16 portstatus, portchange; portstatus = portchange = 0; - status = hub_port_status(hub, port1, &portstatus, &portchange); + status = usb_hub_port_status(hub, port1, &portstatus, &portchange); if (status) goto abort; @@ -1752,6 +1753,8 @@ static void hub_disconnect(struct usb_interface *intf) if (hub->quirk_disable_autosuspend) usb_autopm_put_interface(intf); + onboard_hub_destroy_pdevs(&hub->onboard_hub_devs); + kref_put(&hub->kref, hub_release); } @@ -1869,6 +1872,7 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) INIT_DELAYED_WORK(&hub->leds, led_work); INIT_DELAYED_WORK(&hub->init_work, NULL); INIT_WORK(&hub->events, hub_event); + INIT_LIST_HEAD(&hub->onboard_hub_devs); spin_lock_init(&hub->irq_urb_lock); timer_setup(&hub->irq_urb_retry, hub_retry_irq_urb, 0); usb_get_intf(intf); @@ -1889,8 +1893,11 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id) usb_autopm_get_interface_no_resume(intf); } - if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) + if (hub_configure(hub, &desc->endpoint[0].desc) >= 0) { + onboard_hub_create_pdevs(hdev, &hub->onboard_hub_devs); + return 0; + } hub_disconnect(intf); return -ENODEV; @@ -2855,7 +2862,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1, &portstatus, &portchange, &ext_portstatus); else - ret = hub_port_status(hub, port1, &portstatus, + ret = usb_hub_port_status(hub, port1, &portstatus, &portchange); if (ret < 0) return ret; @@ -2956,7 +2963,8 @@ static int hub_port_reset(struct usb_hub *hub, int port1, * If the caller hasn't explicitly requested a warm reset, * double check and see if one is needed. */ - if (hub_port_status(hub, port1, &portstatus, &portchange) == 0) + if (usb_hub_port_status(hub, port1, &portstatus, + &portchange) == 0) if (hub_port_warm_reset_required(hub, port1, portstatus)) warm = true; @@ -3008,7 +3016,7 @@ static int hub_port_reset(struct usb_hub *hub, int port1, * If a USB 3.0 device migrates from reset to an error * state, re-issue the warm reset. */ - if (hub_port_status(hub, port1, + if (usb_hub_port_status(hub, port1, &portstatus, &portchange) < 0) goto done; @@ -3074,7 +3082,7 @@ done: } /* Check if a port is power on */ -static int port_is_power_on(struct usb_hub *hub, unsigned portstatus) +int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus) { int ret = 0; @@ -3140,13 +3148,13 @@ static int check_port_resume_type(struct usb_device *udev, } /* Is the device still present? */ else if (status || port_is_suspended(hub, portstatus) || - !port_is_power_on(hub, portstatus)) { + !usb_port_is_power_on(hub, portstatus)) { if (status >= 0) status = -ENODEV; } else if (!(portstatus & USB_PORT_STAT_CONNECTION)) { if (retries--) { usleep_range(200, 300); - status = hub_port_status(hub, port1, &portstatus, + status = usb_hub_port_status(hub, port1, &portstatus, &portchange); goto retry; } @@ -3409,7 +3417,7 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg) u16 portstatus, portchange; portstatus = portchange = 0; - ret = hub_port_status(hub, port1, &portstatus, + ret = usb_hub_port_status(hub, port1, &portstatus, &portchange); dev_dbg(&port_dev->dev, @@ -3587,13 +3595,13 @@ static int wait_for_connected(struct usb_device *udev, while (delay_ms < 2000) { if (status || *portstatus & USB_PORT_STAT_CONNECTION) break; - if (!port_is_power_on(hub, *portstatus)) { + if (!usb_port_is_power_on(hub, *portstatus)) { status = -ENODEV; break; } msleep(20); delay_ms += 20; - status = hub_port_status(hub, port1, portstatus, portchange); + status = usb_hub_port_status(hub, port1, portstatus, portchange); } dev_dbg(&udev->dev, "Waited %dms for CONNECT\n", delay_ms); return status; @@ -3653,7 +3661,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) usb_lock_port(port_dev); /* Skip the initial Clear-Suspend step for a remote wakeup */ - status = hub_port_status(hub, port1, &portstatus, &portchange); + status = usb_hub_port_status(hub, port1, &portstatus, &portchange); if (status == 0 && !port_is_suspended(hub, portstatus)) { if (portchange & USB_PORT_STAT_C_SUSPEND) pm_wakeup_event(&udev->dev, 0); @@ -3678,7 +3686,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg) * stop resume signaling. Then finish the resume * sequence. */ - status = hub_port_status(hub, port1, &portstatus, &portchange); + status = usb_hub_port_status(hub, port1, &portstatus, &portchange); } SuspendCleared: @@ -3791,7 +3799,7 @@ static int check_ports_changed(struct usb_hub *hub) u16 portstatus, portchange; int status; - status = hub_port_status(hub, port1, &portstatus, &portchange); + status = usb_hub_port_status(hub, port1, &portstatus, &portchange); if (!status && portchange) return 1; } @@ -3946,7 +3954,7 @@ static const char * const usb3_lpm_names[] = { * This function will fail if the SEL or PEL values for udev are greater than * the maximum allowed values for the link state to be enabled. */ -static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) +static int usb_req_set_sel(struct usb_device *udev) { struct usb_set_sel_req *sel_values; unsigned long long u1_sel; @@ -3955,7 +3963,7 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) unsigned long long u2_pel; int ret; - if (udev->state != USB_STATE_CONFIGURED) + if (!udev->parent || udev->speed < USB_SPEED_SUPER || !udev->lpm_capable) return 0; /* Convert SEL and PEL stored in ns to us */ @@ -3972,34 +3980,14 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) * latency for the link state, and could start a device-initiated * U1/U2 when the exit latencies are too high. */ - if ((state == USB3_LPM_U1 && - (u1_sel > USB3_LPM_MAX_U1_SEL_PEL || - u1_pel > USB3_LPM_MAX_U1_SEL_PEL)) || - (state == USB3_LPM_U2 && - (u2_sel > USB3_LPM_MAX_U2_SEL_PEL || - u2_pel > USB3_LPM_MAX_U2_SEL_PEL))) { - dev_dbg(&udev->dev, "Device-initiated %s disabled due to long SEL %llu us or PEL %llu us\n", - usb3_lpm_names[state], u1_sel, u1_pel); + if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL || + u1_pel > USB3_LPM_MAX_U1_SEL_PEL || + u2_sel > USB3_LPM_MAX_U2_SEL_PEL || + u2_pel > USB3_LPM_MAX_U2_SEL_PEL) { + dev_dbg(&udev->dev, "Device-initiated U1/U2 disabled due to long SEL or PEL\n"); return -EINVAL; } - /* - * If we're enabling device-initiated LPM for one link state, - * but the other link state has a too high SEL or PEL value, - * just set those values to the max in the Set SEL request. - */ - if (u1_sel > USB3_LPM_MAX_U1_SEL_PEL) - u1_sel = USB3_LPM_MAX_U1_SEL_PEL; - - if (u1_pel > USB3_LPM_MAX_U1_SEL_PEL) - u1_pel = USB3_LPM_MAX_U1_SEL_PEL; - - if (u2_sel > USB3_LPM_MAX_U2_SEL_PEL) - u2_sel = USB3_LPM_MAX_U2_SEL_PEL; - - if (u2_pel > USB3_LPM_MAX_U2_SEL_PEL) - u2_pel = USB3_LPM_MAX_U2_SEL_PEL; - /* * usb_enable_lpm() can be called as part of a failed device reset, * which may be initiated by an error path of a mass storage driver. @@ -4021,6 +4009,10 @@ static int usb_req_set_sel(struct usb_device *udev, enum usb3_link_state state) sel_values, sizeof *(sel_values), USB_CTRL_SET_TIMEOUT); kfree(sel_values); + + if (ret > 0) + udev->lpm_devinit_allow = 1; + return ret; } @@ -4136,6 +4128,9 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev, unsigned int sel; /* us */ int i, j; + if (!udev->lpm_devinit_allow) + return false; + if (state == USB3_LPM_U1) sel = DIV_ROUND_UP(udev->u1_params.sel, 1000); else if (state == USB3_LPM_U2) @@ -4184,7 +4179,7 @@ static bool usb_device_may_initiate_lpm(struct usb_device *udev, static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, enum usb3_link_state state) { - int timeout, ret; + int timeout; __u8 u1_mel = udev->bos->ss_cap->bU1devExitLat; __le16 u2_mel = udev->bos->ss_cap->bU2DevExitLat; @@ -4196,17 +4191,6 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, (state == USB3_LPM_U2 && u2_mel == 0)) return; - /* - * First, let the device know about the exit latencies - * associated with the link state we're about to enable. - */ - ret = usb_req_set_sel(udev, state); - if (ret < 0) { - dev_warn(&udev->dev, "Set SEL for device-initiated %s failed.\n", - usb3_lpm_names[state]); - return; - } - /* We allow the host controller to set the U1/U2 timeout internally * first, so that it can change its schedule to account for the * additional latency to send data to a device in a lower power @@ -4486,6 +4470,11 @@ static int hub_handle_remote_wakeup(struct usb_hub *hub, unsigned int port, return 0; } +static int usb_req_set_sel(struct usb_device *udev) +{ + return 0; +} + #endif /* CONFIG_PM */ /* @@ -4554,7 +4543,7 @@ int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected) struct usb_port *port_dev = hub->ports[port1 - 1]; for (total_time = 0; ; total_time += HUB_DEBOUNCE_STEP) { - ret = hub_port_status(hub, port1, &portstatus, &portchange); + ret = usb_hub_port_status(hub, port1, &portstatus, &portchange); if (ret < 0) return ret; @@ -5011,6 +5000,7 @@ hub_port_init(struct usb_hub *hub, struct usb_device *udev, int port1, udev->lpm_capable = usb_device_supports_lpm(udev); udev->lpm_disable_count = 1; usb_set_lpm_parameters(udev); + usb_req_set_sel(udev); } } @@ -5240,7 +5230,7 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus, * but only if the port isn't owned by someone else. */ if (hub_is_port_power_switchable(hub) - && !port_is_power_on(hub, portstatus) + && !usb_port_is_power_on(hub, portstatus) && !port_dev->port_owner) set_port_feature(hdev, port1, USB_PORT_FEAT_POWER); @@ -5557,7 +5547,7 @@ static void port_event(struct usb_hub *hub, int port1) clear_bit(port1, hub->event_bits); clear_bit(port1, hub->wakeup_bits); - if (hub_port_status(hub, port1, &portstatus, &portchange) < 0) + if (usb_hub_port_status(hub, port1, &portstatus, &portchange) < 0) return; if (portchange & USB_PORT_STAT_C_CONNECTION) { @@ -5594,7 +5584,7 @@ static void port_event(struct usb_hub *hub, int port1) USB_PORT_FEAT_C_OVER_CURRENT); msleep(100); /* Cool down */ hub_power_on(hub, true); - hub_port_status(hub, port1, &status, &unused); + usb_hub_port_status(hub, port1, &status, &unused); if (status & USB_PORT_STAT_OVERCURRENT) dev_err(&port_dev->dev, "over-current condition\n"); } @@ -5638,7 +5628,7 @@ static void port_event(struct usb_hub *hub, int port1) u16 unused; msleep(20); - hub_port_status(hub, port1, &portstatus, &unused); + usb_hub_port_status(hub, port1, &portstatus, &unused); dev_dbg(&port_dev->dev, "Wait for inactive link disconnect detect\n"); continue; } else if (!udev || !(portstatus & USB_PORT_STAT_CONNECTION) diff --git a/drivers/usb/core/hub.h b/drivers/usb/core/hub.h index 22ea1f4f2d66..b2925856b4cb 100644 --- a/drivers/usb/core/hub.h +++ b/drivers/usb/core/hub.h @@ -73,6 +73,7 @@ struct usb_hub { spinlock_t irq_urb_lock; struct timer_list irq_urb_retry; struct usb_port **ports; + struct list_head onboard_hub_devs; }; /** @@ -121,6 +122,9 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1, bool must_be_connected); extern int usb_clear_port_feature(struct usb_device *hdev, int port1, int feature); +extern int usb_hub_port_status(struct usb_hub *hub, int port1, + u16 *status, u16 *change); +extern int usb_port_is_power_on(struct usb_hub *hub, unsigned int portstatus); static inline bool hub_is_port_power_switchable(struct usb_hub *hub) { diff --git a/drivers/usb/core/port.c b/drivers/usb/core/port.c index d5bc36ca5b1f..38c1a4f4fdea 100644 --- a/drivers/usb/core/port.c +++ b/drivers/usb/core/port.c @@ -17,6 +17,88 @@ static int usb_port_block_power_off; static const struct attribute_group *port_dev_group[]; +static ssize_t disable_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct usb_port *port_dev = to_usb_port(dev); + struct usb_device *hdev = to_usb_device(dev->parent->parent); + struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + struct usb_interface *intf = to_usb_interface(hub->intfdev); + int port1 = port_dev->portnum; + u16 portstatus, unused; + bool disabled; + int rc; + + rc = usb_autopm_get_interface(intf); + if (rc < 0) + return rc; + + usb_lock_device(hdev); + if (hub->disconnected) { + rc = -ENODEV; + goto out_hdev_lock; + } + + usb_hub_port_status(hub, port1, &portstatus, &unused); + disabled = !usb_port_is_power_on(hub, portstatus); + +out_hdev_lock: + usb_unlock_device(hdev); + usb_autopm_put_interface(intf); + + if (rc) + return rc; + + return sysfs_emit(buf, "%s\n", disabled ? "1" : "0"); +} + +static ssize_t disable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct usb_port *port_dev = to_usb_port(dev); + struct usb_device *hdev = to_usb_device(dev->parent->parent); + struct usb_hub *hub = usb_hub_to_struct_hub(hdev); + struct usb_interface *intf = to_usb_interface(hub->intfdev); + int port1 = port_dev->portnum; + bool disabled; + int rc; + + rc = strtobool(buf, &disabled); + if (rc) + return rc; + + rc = usb_autopm_get_interface(intf); + if (rc < 0) + return rc; + + usb_lock_device(hdev); + if (hub->disconnected) { + rc = -ENODEV; + goto out_hdev_lock; + } + + if (disabled && port_dev->child) + usb_disconnect(&port_dev->child); + + rc = usb_hub_set_port_power(hdev, hub, port1, !disabled); + + if (disabled) { + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_CONNECTION); + if (!port_dev->is_superspeed) + usb_clear_port_feature(hdev, port1, USB_PORT_FEAT_C_ENABLE); + } + + if (!rc) + rc = count; + +out_hdev_lock: + usb_unlock_device(hdev); + usb_autopm_put_interface(intf); + + return rc; +} +static DEVICE_ATTR_RW(disable); + static ssize_t location_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -153,6 +235,7 @@ static struct attribute *port_dev_attrs[] = { &dev_attr_location.attr, &dev_attr_quirks.attr, &dev_attr_over_current_count.attr, + &dev_attr_disable.attr, NULL, }; diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index fa2e49d432ff..631574718d8a 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -35,7 +35,7 @@ static ssize_t field##_show(struct device *dev, \ return -EINTR; \ actconfig = udev->actconfig; \ if (actconfig) \ - rc = sprintf(buf, format_string, \ + rc = sysfs_emit(buf, format_string, \ actconfig->desc.field); \ usb_unlock_device(udev); \ return rc; \ @@ -61,7 +61,7 @@ static ssize_t bMaxPower_show(struct device *dev, return -EINTR; actconfig = udev->actconfig; if (actconfig) - rc = sprintf(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); + rc = sysfs_emit(buf, "%dmA\n", usb_get_max_power(udev, actconfig)); usb_unlock_device(udev); return rc; } @@ -80,7 +80,7 @@ static ssize_t configuration_show(struct device *dev, return -EINTR; actconfig = udev->actconfig; if (actconfig && actconfig->string) - rc = sprintf(buf, "%s\n", actconfig->string); + rc = sysfs_emit(buf, "%s\n", actconfig->string); usb_unlock_device(udev); return rc; } @@ -114,7 +114,7 @@ static ssize_t devspec_show(struct device *dev, struct device_attribute *attr, { struct device_node *of_node = dev->of_node; - return sprintf(buf, "%pOF\n", of_node); + return sysfs_emit(buf, "%pOF\n", of_node); } static DEVICE_ATTR_RO(devspec); #endif @@ -131,7 +131,7 @@ static ssize_t name##_show(struct device *dev, \ retval = usb_lock_device_interruptible(udev); \ if (retval < 0) \ return -EINTR; \ - retval = sprintf(buf, "%s\n", udev->name); \ + retval = sysfs_emit(buf, "%s\n", udev->name); \ usb_unlock_device(udev); \ return retval; \ } \ @@ -175,7 +175,7 @@ static ssize_t speed_show(struct device *dev, struct device_attribute *attr, default: speed = "unknown"; } - return sprintf(buf, "%s\n", speed); + return sysfs_emit(buf, "%s\n", speed); } static DEVICE_ATTR_RO(speed); @@ -185,7 +185,7 @@ static ssize_t rx_lanes_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->rx_lanes); + return sysfs_emit(buf, "%d\n", udev->rx_lanes); } static DEVICE_ATTR_RO(rx_lanes); @@ -195,7 +195,7 @@ static ssize_t tx_lanes_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->tx_lanes); + return sysfs_emit(buf, "%d\n", udev->tx_lanes); } static DEVICE_ATTR_RO(tx_lanes); @@ -205,7 +205,7 @@ static ssize_t busnum_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->bus->busnum); + return sysfs_emit(buf, "%d\n", udev->bus->busnum); } static DEVICE_ATTR_RO(busnum); @@ -215,7 +215,7 @@ static ssize_t devnum_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->devnum); + return sysfs_emit(buf, "%d\n", udev->devnum); } static DEVICE_ATTR_RO(devnum); @@ -225,7 +225,7 @@ static ssize_t devpath_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%s\n", udev->devpath); + return sysfs_emit(buf, "%s\n", udev->devpath); } static DEVICE_ATTR_RO(devpath); @@ -237,7 +237,7 @@ static ssize_t version_show(struct device *dev, struct device_attribute *attr, udev = to_usb_device(dev); bcdUSB = le16_to_cpu(udev->descriptor.bcdUSB); - return sprintf(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff); + return sysfs_emit(buf, "%2x.%02x\n", bcdUSB >> 8, bcdUSB & 0xff); } static DEVICE_ATTR_RO(version); @@ -247,7 +247,7 @@ static ssize_t maxchild_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->maxchild); + return sysfs_emit(buf, "%d\n", udev->maxchild); } static DEVICE_ATTR_RO(maxchild); @@ -257,7 +257,7 @@ static ssize_t quirks_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "0x%x\n", udev->quirks); + return sysfs_emit(buf, "0x%x\n", udev->quirks); } static DEVICE_ATTR_RO(quirks); @@ -267,7 +267,7 @@ static ssize_t avoid_reset_quirk_show(struct device *dev, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET)); + return sysfs_emit(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET)); } static ssize_t avoid_reset_quirk_store(struct device *dev, @@ -297,7 +297,7 @@ static ssize_t urbnum_show(struct device *dev, struct device_attribute *attr, struct usb_device *udev; udev = to_usb_device(dev); - return sprintf(buf, "%d\n", atomic_read(&udev->urbnum)); + return sysfs_emit(buf, "%d\n", atomic_read(&udev->urbnum)); } static DEVICE_ATTR_RO(urbnum); @@ -305,8 +305,8 @@ static ssize_t ltm_capable_show(struct device *dev, struct device_attribute *attr, char *buf) { if (usb_device_supports_ltm(to_usb_device(dev))) - return sprintf(buf, "%s\n", "yes"); - return sprintf(buf, "%s\n", "no"); + return sysfs_emit(buf, "%s\n", "yes"); + return sysfs_emit(buf, "%s\n", "no"); } static DEVICE_ATTR_RO(ltm_capable); @@ -317,7 +317,7 @@ static ssize_t persist_show(struct device *dev, struct device_attribute *attr, { struct usb_device *udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->persist_enabled); + return sysfs_emit(buf, "%d\n", udev->persist_enabled); } static ssize_t persist_store(struct device *dev, struct device_attribute *attr, @@ -372,7 +372,7 @@ static ssize_t connected_duration_show(struct device *dev, { struct usb_device *udev = to_usb_device(dev); - return sprintf(buf, "%u\n", + return sysfs_emit(buf, "%u\n", jiffies_to_msecs(jiffies - udev->connect_time)); } static DEVICE_ATTR_RO(connected_duration); @@ -394,14 +394,14 @@ static ssize_t active_duration_show(struct device *dev, duration = jiffies_to_msecs(jiffies + udev->active_duration); else duration = jiffies_to_msecs(udev->active_duration); - return sprintf(buf, "%u\n", duration); + return sysfs_emit(buf, "%u\n", duration); } static DEVICE_ATTR_RO(active_duration); static ssize_t autosuspend_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%d\n", dev->power.autosuspend_delay / 1000); + return sysfs_emit(buf, "%d\n", dev->power.autosuspend_delay / 1000); } static ssize_t autosuspend_store(struct device *dev, @@ -442,7 +442,7 @@ static ssize_t level_show(struct device *dev, struct device_attribute *attr, warn_level(); if (udev->state != USB_STATE_SUSPENDED && !udev->dev.power.runtime_auto) p = on_string; - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static ssize_t level_store(struct device *dev, struct device_attribute *attr, @@ -490,7 +490,7 @@ static ssize_t usb2_hardware_lpm_show(struct device *dev, else p = "disabled"; - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static ssize_t usb2_hardware_lpm_store(struct device *dev, @@ -529,7 +529,7 @@ static ssize_t usb2_lpm_l1_timeout_show(struct device *dev, char *buf) { struct usb_device *udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->l1_params.timeout); + return sysfs_emit(buf, "%d\n", udev->l1_params.timeout); } static ssize_t usb2_lpm_l1_timeout_store(struct device *dev, @@ -552,7 +552,7 @@ static ssize_t usb2_lpm_besl_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *udev = to_usb_device(dev); - return sprintf(buf, "%d\n", udev->l1_params.besl); + return sysfs_emit(buf, "%d\n", udev->l1_params.besl); } static ssize_t usb2_lpm_besl_store(struct device *dev, @@ -589,7 +589,7 @@ static ssize_t usb3_hardware_lpm_u1_show(struct device *dev, usb_unlock_device(udev); - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static DEVICE_ATTR_RO(usb3_hardware_lpm_u1); @@ -611,7 +611,7 @@ static ssize_t usb3_hardware_lpm_u2_show(struct device *dev, usb_unlock_device(udev); - return sprintf(buf, "%s\n", p); + return sysfs_emit(buf, "%s\n", p); } static DEVICE_ATTR_RO(usb3_hardware_lpm_u2); @@ -694,7 +694,7 @@ field##_show(struct device *dev, struct device_attribute *attr, \ struct usb_device *udev; \ \ udev = to_usb_device(dev); \ - return sprintf(buf, format_string, \ + return sysfs_emit(buf, format_string, \ le16_to_cpu(udev->descriptor.field)); \ } \ static DEVICE_ATTR_RO(field) @@ -711,7 +711,7 @@ field##_show(struct device *dev, struct device_attribute *attr, \ struct usb_device *udev; \ \ udev = to_usb_device(dev); \ - return sprintf(buf, format_string, udev->descriptor.field); \ + return sysfs_emit(buf, format_string, udev->descriptor.field); \ } \ static DEVICE_ATTR_RO(field) @@ -727,7 +727,7 @@ static ssize_t authorized_show(struct device *dev, struct device_attribute *attr, char *buf) { struct usb_device *usb_dev = to_usb_device(dev); - return snprintf(buf, PAGE_SIZE, "%u\n", usb_dev->authorized); + return sysfs_emit(buf, "%u\n", usb_dev->authorized); } /* @@ -918,7 +918,7 @@ static ssize_t authorized_default_show(struct device *dev, struct usb_hcd *hcd; hcd = bus_to_hcd(usb_bus); - return snprintf(buf, PAGE_SIZE, "%u\n", hcd->dev_policy); + return sysfs_emit(buf, "%u\n", hcd->dev_policy); } static ssize_t authorized_default_store(struct device *dev, @@ -957,7 +957,7 @@ static ssize_t interface_authorized_default_show(struct device *dev, struct usb_device *usb_dev = to_usb_device(dev); struct usb_hcd *hcd = bus_to_hcd(usb_dev->bus); - return sprintf(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd)); + return sysfs_emit(buf, "%u\n", !!HCD_INTF_AUTHORIZED(hcd)); } /* @@ -1066,7 +1066,7 @@ iad_##field##_show(struct device *dev, struct device_attribute *attr, \ { \ struct usb_interface *intf = to_usb_interface(dev); \ \ - return sprintf(buf, format_string, \ + return sysfs_emit(buf, format_string, \ intf->intf_assoc->field); \ } \ static DEVICE_ATTR_RO(iad_##field) @@ -1085,7 +1085,7 @@ field##_show(struct device *dev, struct device_attribute *attr, \ { \ struct usb_interface *intf = to_usb_interface(dev); \ \ - return sprintf(buf, format_string, \ + return sysfs_emit(buf, format_string, \ intf->cur_altsetting->desc.field); \ } \ static DEVICE_ATTR_RO(field) @@ -1107,7 +1107,7 @@ static ssize_t interface_show(struct device *dev, struct device_attribute *attr, string = READ_ONCE(intf->cur_altsetting->string); if (!string) return 0; - return sprintf(buf, "%s\n", string); + return sysfs_emit(buf, "%s\n", string); } static DEVICE_ATTR_RO(interface); @@ -1122,7 +1122,8 @@ static ssize_t modalias_show(struct device *dev, struct device_attribute *attr, udev = interface_to_usbdev(intf); alt = READ_ONCE(intf->cur_altsetting); - return sprintf(buf, "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" + return sysfs_emit(buf, + "usb:v%04Xp%04Xd%04Xdc%02Xdsc%02Xdp%02X" "ic%02Xisc%02Xip%02Xin%02X\n", le16_to_cpu(udev->descriptor.idVendor), le16_to_cpu(udev->descriptor.idProduct), @@ -1150,7 +1151,7 @@ static ssize_t supports_autosuspend_show(struct device *dev, s = (!dev->driver || to_usb_driver(dev->driver)->supports_autosuspend); device_unlock(dev); - return sprintf(buf, "%u\n", s); + return sysfs_emit(buf, "%u\n", s); } static DEVICE_ATTR_RO(supports_autosuspend); @@ -1163,7 +1164,7 @@ static ssize_t interface_authorized_show(struct device *dev, { struct usb_interface *intf = to_usb_interface(dev); - return sprintf(buf, "%u\n", intf->authorized); + return sysfs_emit(buf, "%u\n", intf->authorized); } /* diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 2f71636af6e1..11b15d7b357a 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -801,7 +801,7 @@ EXPORT_SYMBOL_GPL(usb_intf_get_dma_device); * is simple: * * When locking both a device and its parent, always lock the - * the parent first. + * parent first. */ /** diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index fe2a58c75861..8b15742d9e8a 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -3594,7 +3594,8 @@ void dwc2_hsotg_core_disconnect(struct dwc2_hsotg *hsotg) void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) { /* remove the soft-disconnect and let's go */ - dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); + if (!hsotg->role_sw || (dwc2_readl(hsotg, GOTGCTL) & GOTGCTL_BSESVLD)) + dwc2_clear_bit(hsotg, DCTL, DCTL_SFTDISCON); } /** diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 3f107a06817d..aaf7b9fc4d34 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -52,6 +52,7 @@ #include #include +#include #include "core.h" #include "hcd.h" @@ -999,7 +1000,7 @@ static void dwc2_hc_set_even_odd_frame(struct dwc2_hsotg *hsotg, /* * Try to figure out if we're an even or odd frame. If we set - * even and the current frame number is even the the transfer + * even and the current frame number is even the transfer * will happen immediately. Similar if both are odd. If one is * even and the other is odd then the transfer will happen when * the frame number ticks. @@ -5339,6 +5340,8 @@ int dwc2_hcd_init(struct dwc2_hsotg *hsotg) /* Don't support SG list at this point */ hcd->self.sg_tablesize = 0; + hcd->tpl_support = of_usb_host_tpl_support(hsotg->dev->of_node); + if (!IS_ERR_OR_NULL(hsotg->uphy)) otg_set_host(hsotg->uphy->otg, &hcd->self); diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig index cd9a734522a7..03ededa86da1 100644 --- a/drivers/usb/dwc3/Kconfig +++ b/drivers/usb/dwc3/Kconfig @@ -9,7 +9,7 @@ config USB_DWC3 Say Y or M here if your system has a Dual Role SuperSpeed USB controller based on the DesignWare USB3 IP Core. - If you choose to build this driver is a dynamically linked + If you choose to build this driver as a dynamically linked module, the module will be called dwc3.ko. if USB_DWC3 @@ -165,7 +165,7 @@ config USB_DWC3_AM62 default USB_DWC3 help Support TI's AM62 platforms with DesignWare Core USB3 IP. - The Designware Core USB3 IP is progammed to operate in + The Designware Core USB3 IP is programmed to operate in in USB 2.0 mode only. Say 'Y' or 'M' here if you have one such device endif diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 573421984948..c5c238ab3083 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -158,8 +158,13 @@ static void __dwc3_set_mode(struct work_struct *work) break; } - /* For DRD host or device mode only */ - if (dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG) { + /* + * When current_dr_role is not set, there's no role switching. + * Only perform GCTL.CoreSoftReset when there's DRD role switching. + */ + if (dwc->current_dr_role && ((DWC3_IP_IS(DWC3) || + DWC3_VER_IS_PRIOR(DWC31, 190A)) && + dwc->desired_dr_role != DWC3_GCTL_PRTCAP_OTG)) { reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg |= DWC3_GCTL_CORESOFTRESET; dwc3_writel(dwc->regs, DWC3_GCTL, reg); @@ -426,7 +431,7 @@ static void dwc3_free_one_event_buffer(struct dwc3 *dwc, * otherwise ERR_PTR(errno). */ static struct dwc3_event_buffer *dwc3_alloc_one_event_buffer(struct dwc3 *dwc, - unsigned length) + unsigned int length) { struct dwc3_event_buffer *evt; @@ -469,7 +474,7 @@ static void dwc3_free_event_buffers(struct dwc3 *dwc) * Returns 0 on success otherwise negative errno. In the error case, dwc * may contain some buffers allocated but not all which were requested. */ -static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned length) +static int dwc3_alloc_event_buffers(struct dwc3 *dwc, unsigned int length) { struct dwc3_event_buffer *evt; @@ -1029,6 +1034,37 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); } +static void dwc3_set_power_down_clk_scale(struct dwc3 *dwc) +{ + u32 scale; + u32 reg; + + if (!dwc->susp_clk) + return; + + /* + * The power down scale field specifies how many suspend_clk + * periods fit into a 16KHz clock period. When performing + * the division, round up the remainder. + * + * The power down scale value is calculated using the fastest + * frequency of the suspend_clk. If it isn't fixed (but within + * the accuracy requirement), the driver may not know the max + * rate of the suspend_clk, so only update the power down scale + * if the default is less than the calculated value from + * clk_get_rate() or if the default is questionably high + * (3x or more) to be within the requirement. + */ + scale = DIV_ROUND_UP(clk_get_rate(dwc->susp_clk), 16000); + reg = dwc3_readl(dwc->regs, DWC3_GCTL); + if ((reg & DWC3_GCTL_PWRDNSCALE_MASK) < DWC3_GCTL_PWRDNSCALE(scale) || + (reg & DWC3_GCTL_PWRDNSCALE_MASK) > DWC3_GCTL_PWRDNSCALE(scale*3)) { + reg &= ~(DWC3_GCTL_PWRDNSCALE_MASK); + reg |= DWC3_GCTL_PWRDNSCALE(scale); + dwc3_writel(dwc->regs, DWC3_GCTL, reg); + } +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -1105,6 +1141,9 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err1; + /* Set power down scale of suspend_clk */ + dwc3_set_power_down_clk_scale(dwc); + /* Adjust Frame Length */ dwc3_frame_length_adjustment(dwc); @@ -1782,6 +1821,7 @@ static int dwc3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); + device_init_wakeup(&pdev->dev, of_property_read_bool(dev->of_node, "wakeup-source")); spin_lock_init(&dwc->lock); mutex_init(&dwc->mutex); @@ -1943,7 +1983,7 @@ static int dwc3_suspend_common(struct dwc3 *dwc, pm_message_t msg) dwc3_core_exit(dwc); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg)) { + if (!PMSG_IS_AUTO(msg) && !device_can_wakeup(dwc->dev)) { dwc3_core_exit(dwc); break; } @@ -2004,7 +2044,7 @@ static int dwc3_resume_common(struct dwc3 *dwc, pm_message_t msg) spin_unlock_irqrestore(&dwc->lock, flags); break; case DWC3_GCTL_PRTCAP_HOST: - if (!PMSG_IS_AUTO(msg)) { + if (!PMSG_IS_AUTO(msg) && !device_can_wakeup(dwc->dev)) { ret = dwc3_core_init_for_resume(dwc); if (ret) return ret; @@ -2081,8 +2121,6 @@ static int dwc3_runtime_suspend(struct device *dev) if (ret) return ret; - device_init_wakeup(dev, true); - return 0; } @@ -2091,8 +2129,6 @@ static int dwc3_runtime_resume(struct device *dev) struct dwc3 *dwc = dev_get_drvdata(dev); int ret; - device_init_wakeup(dev, false); - ret = dwc3_resume_common(dwc, PMSG_AUTO_RESUME); if (ret) return ret; diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 81c486b3941c..4fe4287dc934 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -231,6 +231,7 @@ /* Global Configuration Register */ #define DWC3_GCTL_PWRDNSCALE(n) ((n) << 19) +#define DWC3_GCTL_PWRDNSCALE_MASK GENMASK(31, 19) #define DWC3_GCTL_U2RSTECN BIT(16) #define DWC3_GCTL_RAMCLKSEL(x) (((x) & DWC3_GCTL_CLK_MASK) << 6) #define DWC3_GCTL_CLK_BUS (0) @@ -1086,6 +1087,8 @@ struct dwc3_scratchpad_array { * @dis_u1_entry_quirk: set if link entering into U1 state needs to be disabled. * @dis_u2_entry_quirk: set if link entering into U2 state needs to be disabled. * @dis_rxdet_inp3_quirk: set if we disable Rx.Detect in P3 + * @async_callbacks: if set, indicate that async callbacks will be used. + * * @dis_u2_freeclk_exists_quirk : set if we clear u2_freeclk_exists * in GUSB2PHYCFG, specify that USB2 PHY doesn't * provide a free-running PHY clock. diff --git a/drivers/usb/dwc3/dwc3-qcom.c b/drivers/usb/dwc3/dwc3-qcom.c index 6cba990da32e..c5e482f53e9d 100644 --- a/drivers/usb/dwc3/dwc3-qcom.c +++ b/drivers/usb/dwc3/dwc3-qcom.c @@ -17,10 +17,12 @@ #include #include #include +#include #include #include #include - +#include +#include #include "core.h" /* USB QSCRATCH Hardware registers */ @@ -76,6 +78,7 @@ struct dwc3_qcom { int dp_hs_phy_irq; int dm_hs_phy_irq; int ss_phy_irq; + enum usb_device_speed usb2_speed; struct extcon_dev *edev; struct extcon_dev *host_edev; @@ -296,50 +299,92 @@ static void dwc3_qcom_interconnect_exit(struct dwc3_qcom *qcom) icc_put(qcom->icc_path_apps); } +static enum usb_device_speed dwc3_qcom_read_usb2_speed(struct dwc3_qcom *qcom) +{ + struct dwc3 *dwc = platform_get_drvdata(qcom->dwc3); + struct usb_hcd *hcd = platform_get_drvdata(dwc->xhci); + struct usb_device *udev; + + /* + * It is possible to query the speed of all children of + * USB2.0 root hub via usb_hub_for_each_child(). DWC3 code + * currently supports only 1 port per controller. So + * this is sufficient. + */ + udev = usb_hub_find_child(hcd->self.root_hub, 1); + + if (!udev) + return USB_SPEED_UNKNOWN; + + return udev->speed; +} + +static void dwc3_qcom_enable_wakeup_irq(int irq, unsigned int polarity) +{ + if (!irq) + return; + + if (polarity) + irq_set_irq_type(irq, polarity); + + enable_irq(irq); + enable_irq_wake(irq); +} + +static void dwc3_qcom_disable_wakeup_irq(int irq) +{ + if (!irq) + return; + + disable_irq_wake(irq); + disable_irq_nosync(irq); +} + static void dwc3_qcom_disable_interrupts(struct dwc3_qcom *qcom) { - if (qcom->hs_phy_irq) { - disable_irq_wake(qcom->hs_phy_irq); - disable_irq_nosync(qcom->hs_phy_irq); + dwc3_qcom_disable_wakeup_irq(qcom->hs_phy_irq); + + if (qcom->usb2_speed == USB_SPEED_LOW) { + dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq); + } else if ((qcom->usb2_speed == USB_SPEED_HIGH) || + (qcom->usb2_speed == USB_SPEED_FULL)) { + dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq); + } else { + dwc3_qcom_disable_wakeup_irq(qcom->dp_hs_phy_irq); + dwc3_qcom_disable_wakeup_irq(qcom->dm_hs_phy_irq); } - if (qcom->dp_hs_phy_irq) { - disable_irq_wake(qcom->dp_hs_phy_irq); - disable_irq_nosync(qcom->dp_hs_phy_irq); - } - - if (qcom->dm_hs_phy_irq) { - disable_irq_wake(qcom->dm_hs_phy_irq); - disable_irq_nosync(qcom->dm_hs_phy_irq); - } - - if (qcom->ss_phy_irq) { - disable_irq_wake(qcom->ss_phy_irq); - disable_irq_nosync(qcom->ss_phy_irq); - } + dwc3_qcom_disable_wakeup_irq(qcom->ss_phy_irq); } static void dwc3_qcom_enable_interrupts(struct dwc3_qcom *qcom) { - if (qcom->hs_phy_irq) { - enable_irq(qcom->hs_phy_irq); - enable_irq_wake(qcom->hs_phy_irq); + dwc3_qcom_enable_wakeup_irq(qcom->hs_phy_irq, 0); + + /* + * Configure DP/DM line interrupts based on the USB2 device attached to + * the root hub port. When HS/FS device is connected, configure the DP line + * as falling edge to detect both disconnect and remote wakeup scenarios. When + * LS device is connected, configure DM line as falling edge to detect both + * disconnect and remote wakeup. When no device is connected, configure both + * DP and DM lines as rising edge to detect HS/HS/LS device connect scenario. + */ + + if (qcom->usb2_speed == USB_SPEED_LOW) { + dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq, + IRQ_TYPE_EDGE_FALLING); + } else if ((qcom->usb2_speed == USB_SPEED_HIGH) || + (qcom->usb2_speed == USB_SPEED_FULL)) { + dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq, + IRQ_TYPE_EDGE_FALLING); + } else { + dwc3_qcom_enable_wakeup_irq(qcom->dp_hs_phy_irq, + IRQ_TYPE_EDGE_RISING); + dwc3_qcom_enable_wakeup_irq(qcom->dm_hs_phy_irq, + IRQ_TYPE_EDGE_RISING); } - if (qcom->dp_hs_phy_irq) { - enable_irq(qcom->dp_hs_phy_irq); - enable_irq_wake(qcom->dp_hs_phy_irq); - } - - if (qcom->dm_hs_phy_irq) { - enable_irq(qcom->dm_hs_phy_irq); - enable_irq_wake(qcom->dm_hs_phy_irq); - } - - if (qcom->ss_phy_irq) { - enable_irq(qcom->ss_phy_irq); - enable_irq_wake(qcom->ss_phy_irq); - } + dwc3_qcom_enable_wakeup_irq(qcom->ss_phy_irq, 0); } static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) @@ -361,8 +406,10 @@ static int dwc3_qcom_suspend(struct dwc3_qcom *qcom) if (ret) dev_warn(qcom->dev, "failed to disable interconnect: %d\n", ret); - if (device_may_wakeup(qcom->dev)) + if (device_may_wakeup(qcom->dev)) { + qcom->usb2_speed = dwc3_qcom_read_usb2_speed(qcom); dwc3_qcom_enable_interrupts(qcom); + } qcom->is_suspended = true; @@ -443,9 +490,9 @@ static int dwc3_qcom_get_irq(struct platform_device *pdev, int ret; if (np) - ret = platform_get_irq_byname(pdev_irq, name); + ret = platform_get_irq_byname_optional(pdev_irq, name); else - ret = platform_get_irq(pdev_irq, num); + ret = platform_get_irq_optional(pdev_irq, num); return ret; } @@ -710,12 +757,13 @@ dwc3_qcom_create_urs_usb_platdev(struct device *dev) static int dwc3_qcom_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; - struct device *dev = &pdev->dev; - struct dwc3_qcom *qcom; - struct resource *res, *parent_res = NULL; - int ret, i; - bool ignore_pipe_clk; + struct device_node *np = pdev->dev.of_node; + struct device *dev = &pdev->dev; + struct dwc3_qcom *qcom; + struct resource *res, *parent_res = NULL; + int ret, i; + bool ignore_pipe_clk; + struct generic_pm_domain *genpd; qcom = devm_kzalloc(&pdev->dev, sizeof(*qcom), GFP_KERNEL); if (!qcom) @@ -724,6 +772,8 @@ static int dwc3_qcom_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qcom); qcom->dev = &pdev->dev; + genpd = pd_to_genpd(qcom->dev->pm_domain); + if (has_acpi_companion(dev)) { qcom->acpi_pdata = acpi_device_get_match_data(dev); if (!qcom->acpi_pdata) { @@ -831,7 +881,17 @@ static int dwc3_qcom_probe(struct platform_device *pdev) if (ret) goto interconnect_exit; - device_init_wakeup(&pdev->dev, 1); + if (device_can_wakeup(&qcom->dwc3->dev)) { + /* + * Setting GENPD_FLAG_ALWAYS_ON flag takes care of keeping + * genpd on in both runtime suspend and system suspend cases. + */ + genpd->flags |= GENPD_FLAG_ALWAYS_ON; + device_init_wakeup(&pdev->dev, true); + } else { + genpd->flags |= GENPD_FLAG_RPM_ALWAYS_ON; + } + qcom->is_suspended = false; pm_runtime_set_active(dev); pm_runtime_enable(dev); diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 5d642660fd15..197af63f8d05 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -239,6 +239,8 @@ void dwc3_ep0_stall_and_restart(struct dwc3 *dwc) dwc3_gadget_giveback(dep, req, -ECONNRESET); } + dwc->eps[0]->trb_enqueue = 0; + dwc->eps[1]->trb_enqueue = 0; dwc->ep0state = EP0_SETUP_PHASE; dwc3_ep0_out_start(dwc); } @@ -473,7 +475,7 @@ static int dwc3_ep0_handle_device(struct dwc3 *dwc, case USB_DEVICE_REMOTE_WAKEUP: break; /* - * 9.4.1 says only only for SS, in AddressState only for + * 9.4.1 says only for SS, in AddressState only for * default control pipe */ case USB_DEVICE_U1_ENABLE: @@ -1140,6 +1142,11 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, if (dwc->ep0_next_event != DWC3_EP0_NRDY_STATUS) return; + if (dwc->setup_packet_pending) { + dwc3_ep0_stall_and_restart(dwc); + return; + } + dwc->ep0state = EP0_STATUS_PHASE; if (dwc->delayed_status) { diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0d89dfa6eef5..aeeec751c53c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -657,6 +657,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3_ep *dep, unsigned int action) /** * dwc3_gadget_calc_tx_fifo_size - calculates the txfifo size value * @dwc: pointer to the DWC3 context + * @mult: multiplier to be used when calculating the fifo_size * * Calculates the size value based on the equation below: * @@ -1182,17 +1183,49 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) return trbs_left; } -static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, - dma_addr_t dma, unsigned int length, unsigned int chain, - unsigned int node, unsigned int stream_id, - unsigned int short_not_ok, unsigned int no_interrupt, - unsigned int is_last, bool must_interrupt) +/** + * dwc3_prepare_one_trb - setup one TRB from one request + * @dep: endpoint for which this request is prepared + * @req: dwc3_request pointer + * @trb_length: buffer size of the TRB + * @chain: should this TRB be chained to the next? + * @node: only for isochronous endpoints. First TRB needs different type. + * @use_bounce_buffer: set to use bounce buffer + * @must_interrupt: set to interrupt on TRB completion + */ +static void dwc3_prepare_one_trb(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int trb_length, + unsigned int chain, unsigned int node, bool use_bounce_buffer, + bool must_interrupt) { + struct dwc3_trb *trb; + dma_addr_t dma; + unsigned int stream_id = req->request.stream_id; + unsigned int short_not_ok = req->request.short_not_ok; + unsigned int no_interrupt = req->request.no_interrupt; + unsigned int is_last = req->request.is_last; struct dwc3 *dwc = dep->dwc; struct usb_gadget *gadget = dwc->gadget; enum usb_device_speed speed = gadget->speed; - trb->size = DWC3_TRB_SIZE_LENGTH(length); + if (use_bounce_buffer) + dma = dep->dwc->bounce_addr; + else if (req->request.num_sgs > 0) + dma = sg_dma_address(req->start_sg); + else + dma = req->request.dma; + + trb = &dep->trb_pool[dep->trb_enqueue]; + + if (!req->trb) { + dwc3_gadget_move_started_request(req); + req->trb = trb; + req->trb_dma = dwc3_trb_dma_offset(dep, trb); + } + + req->num_trbs++; + + trb->size = DWC3_TRB_SIZE_LENGTH(trb_length); trb->bpl = lower_32_bits(dma); trb->bph = upper_32_bits(dma); @@ -1232,10 +1265,10 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, unsigned int mult = 2; unsigned int maxp = usb_endpoint_maxp(ep->desc); - if (length <= (2 * maxp)) + if (req->request.length <= (2 * maxp)) mult--; - if (length <= maxp) + if (req->request.length <= maxp) mult--; trb->size |= DWC3_TRB_SIZE_PCM1(mult); @@ -1309,50 +1342,6 @@ static void __dwc3_prepare_one_trb(struct dwc3_ep *dep, struct dwc3_trb *trb, trace_dwc3_prepare_trb(dep, trb); } -/** - * dwc3_prepare_one_trb - setup one TRB from one request - * @dep: endpoint for which this request is prepared - * @req: dwc3_request pointer - * @trb_length: buffer size of the TRB - * @chain: should this TRB be chained to the next? - * @node: only for isochronous endpoints. First TRB needs different type. - * @use_bounce_buffer: set to use bounce buffer - * @must_interrupt: set to interrupt on TRB completion - */ -static void dwc3_prepare_one_trb(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned int trb_length, - unsigned int chain, unsigned int node, bool use_bounce_buffer, - bool must_interrupt) -{ - struct dwc3_trb *trb; - dma_addr_t dma; - unsigned int stream_id = req->request.stream_id; - unsigned int short_not_ok = req->request.short_not_ok; - unsigned int no_interrupt = req->request.no_interrupt; - unsigned int is_last = req->request.is_last; - - if (use_bounce_buffer) - dma = dep->dwc->bounce_addr; - else if (req->request.num_sgs > 0) - dma = sg_dma_address(req->start_sg); - else - dma = req->request.dma; - - trb = &dep->trb_pool[dep->trb_enqueue]; - - if (!req->trb) { - dwc3_gadget_move_started_request(req); - req->trb = trb; - req->trb_dma = dwc3_trb_dma_offset(dep, trb); - } - - req->num_trbs++; - - __dwc3_prepare_one_trb(dep, trb, dma, trb_length, chain, node, - stream_id, short_not_ok, no_interrupt, is_last, - must_interrupt); -} - static bool dwc3_needs_extra_trb(struct dwc3_ep *dep, struct dwc3_request *req) { unsigned int maxp = usb_endpoint_maxp(dep->endpoint.desc); diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 411eb489e0ff..cb523f118f04 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -57,18 +57,8 @@ struct f_acm { /* SetControlLineState request -- CDC 1.1 section 6.2.14 (INPUT) */ u16 port_handshake_bits; -#define ACM_CTRL_RTS (1 << 1) /* unused with full duplex */ -#define ACM_CTRL_DTR (1 << 0) /* host is ready for data r/w */ - /* SerialState notification -- CDC 1.1 section 6.3.5 (OUTPUT) */ u16 serial_state; -#define ACM_CTRL_OVERRUN (1 << 6) -#define ACM_CTRL_PARITY (1 << 5) -#define ACM_CTRL_FRAMING (1 << 4) -#define ACM_CTRL_RI (1 << 3) -#define ACM_CTRL_BRK (1 << 2) -#define ACM_CTRL_DSR (1 << 1) -#define ACM_CTRL_DCD (1 << 0) }; static inline struct f_acm *func_to_acm(struct usb_function *f) @@ -387,7 +377,7 @@ static int acm_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) value = 0; /* FIXME we should not allow data to flow until the - * host sets the ACM_CTRL_DTR bit; and when it clears + * host sets the USB_CDC_CTRL_DTR bit; and when it clears * that bit, we should return to that no-flow state. */ acm->port_handshake_bits = w_value; @@ -585,7 +575,7 @@ static void acm_connect(struct gserial *port) { struct f_acm *acm = port_to_acm(port); - acm->serial_state |= ACM_CTRL_DSR | ACM_CTRL_DCD; + acm->serial_state |= USB_CDC_SERIAL_STATE_DSR | USB_CDC_SERIAL_STATE_DCD; acm_notify_serial_state(acm); } @@ -593,7 +583,7 @@ static void acm_disconnect(struct gserial *port) { struct f_acm *acm = port_to_acm(port); - acm->serial_state &= ~(ACM_CTRL_DSR | ACM_CTRL_DCD); + acm->serial_state &= ~(USB_CDC_SERIAL_STATE_DSR | USB_CDC_SERIAL_STATE_DCD); acm_notify_serial_state(acm); } @@ -603,9 +593,9 @@ static int acm_send_break(struct gserial *port, int duration) u16 state; state = acm->serial_state; - state &= ~ACM_CTRL_BRK; + state &= ~USB_CDC_SERIAL_STATE_BREAK; if (duration) - state |= ACM_CTRL_BRK; + state |= USB_CDC_SERIAL_STATE_BREAK; acm->serial_state = state; return acm_notify_serial_state(acm); diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 3a77bca0ebe1..925e99f9775c 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -1192,13 +1192,14 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) u8 format; int i, len; + format = common->cmnd[2] & 0xf; + if ((common->cmnd[1] & ~0x02) != 0 || /* Mask away MSF */ - start_track > 1) { + (start_track > 1 && format != 0x1)) { curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } - format = common->cmnd[2] & 0xf; /* * Check if CDB is old style SFF-8020i * i.e. format is in 2 MSBs of byte 9 @@ -1208,8 +1209,8 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) format = (common->cmnd[9] >> 6) & 0x3; switch (format) { - case 0: - /* Formatted TOC */ + case 0: /* Formatted TOC */ + case 1: /* Multi-session info */ len = 4 + 2*8; /* 4 byte header + 2 descriptors */ memset(buf, 0, len); buf[1] = len - 2; /* TOC Length excludes length field */ @@ -1250,7 +1251,7 @@ static int do_read_toc(struct fsg_common *common, struct fsg_buffhd *bh) return len; default: - /* Multi-session, PMA, ATIP, CD-TEXT not supported/required */ + /* PMA, ATIP, CD-TEXT not supported/required */ curlun->sense_data = SS_INVALID_FIELD_IN_CDB; return -EINVAL; } @@ -2650,10 +2651,21 @@ static ssize_t file_store(struct device *dev, struct device_attribute *attr, return fsg_store_file(curlun, filesem, buf, count); } +static ssize_t forced_eject_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fsg_lun *curlun = fsg_lun_from_dev(dev); + struct rw_semaphore *filesem = dev_get_drvdata(dev); + + return fsg_store_forced_eject(curlun, filesem, buf, count); +} + static DEVICE_ATTR_RW(nofua); /* mode wil be set in fsg_lun_attr_is_visible() */ static DEVICE_ATTR(ro, 0, ro_show, ro_store); static DEVICE_ATTR(file, 0, file_show, file_store); +static DEVICE_ATTR_WO(forced_eject); /****************************** FSG COMMON ******************************/ @@ -2807,6 +2819,7 @@ static struct attribute *fsg_lun_dev_attrs[] = { &dev_attr_ro.attr, &dev_attr_file.attr, &dev_attr_nofua.attr, + &dev_attr_forced_eject.attr, NULL }; @@ -3220,6 +3233,18 @@ static ssize_t fsg_lun_opts_inquiry_string_store(struct config_item *item, CONFIGFS_ATTR(fsg_lun_opts_, inquiry_string); +static ssize_t fsg_lun_opts_forced_eject_store(struct config_item *item, + const char *page, size_t len) +{ + struct fsg_lun_opts *opts = to_fsg_lun_opts(item); + struct fsg_opts *fsg_opts = to_fsg_opts(opts->group.cg_item.ci_parent); + + return fsg_store_forced_eject(opts->lun, &fsg_opts->common->filesem, + page, len); +} + +CONFIGFS_ATTR_WO(fsg_lun_opts_, forced_eject); + static struct configfs_attribute *fsg_lun_attrs[] = { &fsg_lun_opts_attr_file, &fsg_lun_opts_attr_ro, @@ -3227,6 +3252,7 @@ static struct configfs_attribute *fsg_lun_attrs[] = { &fsg_lun_opts_attr_cdrom, &fsg_lun_opts_attr_nofua, &fsg_lun_opts_attr_inquiry_string, + &fsg_lun_opts_attr_forced_eject, NULL, }; diff --git a/drivers/usb/gadget/function/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c index d3feeeb50841..71669e0e4d00 100644 --- a/drivers/usb/gadget/function/f_uvc.c +++ b/drivers/usb/gadget/function/f_uvc.c @@ -141,7 +141,8 @@ static struct usb_endpoint_descriptor uvc_fs_streaming_ep = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC, - /* The wMaxPacketSize and bInterval values will be initialized from + /* + * The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ }; @@ -152,7 +153,8 @@ static struct usb_endpoint_descriptor uvc_hs_streaming_ep = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC, - /* The wMaxPacketSize and bInterval values will be initialized from + /* + * The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ }; @@ -164,7 +166,8 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep = { .bEndpointAddress = USB_DIR_IN, .bmAttributes = USB_ENDPOINT_SYNC_ASYNC | USB_ENDPOINT_XFER_ISOC, - /* The wMaxPacketSize and bInterval values will be initialized from + /* + * The wMaxPacketSize and bInterval values will be initialized from * module parameters. */ }; @@ -172,7 +175,8 @@ static struct usb_endpoint_descriptor uvc_ss_streaming_ep = { static struct usb_ss_ep_comp_descriptor uvc_ss_streaming_comp = { .bLength = sizeof(uvc_ss_streaming_comp), .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, - /* The bMaxBurst, bmAttributes and wBytesPerInterval values will be + /* + * The bMaxBurst, bmAttributes and wBytesPerInterval values will be * initialized from module parameters. */ }; @@ -234,7 +238,8 @@ uvc_function_setup(struct usb_function *f, const struct usb_ctrlrequest *ctrl) if (le16_to_cpu(ctrl->wLength) > UVC_MAX_REQUEST_SIZE) return -EINVAL; - /* Tell the complete callback to generate an event for the next request + /* + * Tell the complete callback to generate an event for the next request * that will be enqueued by UVCIOC_SEND_RESPONSE. */ uvc->event_setup_out = !(ctrl->bRequestType & USB_DIR_IN); @@ -500,7 +505,8 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed) if (!uvc_control_desc || !uvc_streaming_cls) return ERR_PTR(-ENODEV); - /* Descriptors layout + /* + * Descriptors layout * * uvc_iad * uvc_control_intf @@ -597,8 +603,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) uvcg_info(f, "%s()\n", __func__); opts = fi_to_f_uvc_opts(f->fi); - /* Sanity check the streaming endpoint module parameters. - */ + /* Sanity check the streaming endpoint module parameters. */ opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U); opts->streaming_maxpacket = clamp(opts->streaming_maxpacket, 1U, 3072U); opts->streaming_maxburst = min(opts->streaming_maxburst, 15U); @@ -611,7 +616,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f) opts->streaming_maxpacket); } - /* Fill in the FS/HS/SS Video Streaming specific descriptors from the + /* + * Fill in the FS/HS/SS Video Streaming specific descriptors from the * module parameters. * * NOTE: We assume that the user knows what they are doing and won't @@ -895,7 +901,8 @@ static void uvc_function_unbind(struct usb_configuration *c, uvcg_info(f, "%s()\n", __func__); - /* If we know we're connected via v4l2, then there should be a cleanup + /* + * If we know we're connected via v4l2, then there should be a cleanup * of the device from userspace either via UVC_EVENT_DISCONNECT or * though the video device removal uevent. Allow some time for the * application to close out before things get deleted. @@ -912,7 +919,8 @@ static void uvc_function_unbind(struct usb_configuration *c, v4l2_device_unregister(&uvc->v4l2_dev); if (uvc->func_connected) { - /* Wait for the release to occur to ensure there are no longer any + /* + * Wait for the release to occur to ensure there are no longer any * pending operations that may cause panics when resources are cleaned * up. */ diff --git a/drivers/usb/gadget/function/storage_common.c b/drivers/usb/gadget/function/storage_common.c index b859a158a414..03035dbbe97b 100644 --- a/drivers/usb/gadget/function/storage_common.c +++ b/drivers/usb/gadget/function/storage_common.c @@ -519,4 +519,19 @@ ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, } EXPORT_SYMBOL_GPL(fsg_store_inquiry_string); +ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count) +{ + int ret; + + /* + * Forcibly detach the backing file from the LUN + * regardless of whether the host has allowed it. + */ + curlun->prevent_medium_removal = 0; + ret = fsg_store_file(curlun, filesem, "", 0); + return ret < 0 ? ret : count; +} +EXPORT_SYMBOL_GPL(fsg_store_forced_eject); + MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/function/storage_common.h b/drivers/usb/gadget/function/storage_common.h index bdeb1e233fc9..0a544a82cbf8 100644 --- a/drivers/usb/gadget/function/storage_common.h +++ b/drivers/usb/gadget/function/storage_common.h @@ -219,5 +219,7 @@ ssize_t fsg_store_removable(struct fsg_lun *curlun, const char *buf, size_t count); ssize_t fsg_store_inquiry_string(struct fsg_lun *curlun, const char *buf, size_t count); +ssize_t fsg_store_forced_eject(struct fsg_lun *curlun, struct rw_semaphore *filesem, + const char *buf, size_t count); #endif /* USB_STORAGE_COMMON_H */ diff --git a/drivers/usb/gadget/function/u_ether.c b/drivers/usb/gadget/function/u_ether.c index f51694f29de9..7887def05dc2 100644 --- a/drivers/usb/gadget/function/u_ether.c +++ b/drivers/usb/gadget/function/u_ether.c @@ -17,7 +17,6 @@ #include #include #include -#include #include "u_ether.h" diff --git a/drivers/usb/gadget/function/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c index d25edc3d2174..ec500ee499ee 100644 --- a/drivers/usb/gadget/function/uvc_queue.c +++ b/drivers/usb/gadget/function/uvc_queue.c @@ -44,7 +44,8 @@ static int uvc_queue_setup(struct vb2_queue *vq, { struct uvc_video_queue *queue = vb2_get_drv_priv(vq); struct uvc_video *video = container_of(queue, struct uvc_video, queue); - struct usb_composite_dev *cdev = video->uvc->func.config->cdev; + unsigned int req_size; + unsigned int nreq; if (*nbuffers > UVC_MAX_VIDEO_BUFFERS) *nbuffers = UVC_MAX_VIDEO_BUFFERS; @@ -53,10 +54,16 @@ static int uvc_queue_setup(struct vb2_queue *vq, sizes[0] = video->imagesize; - if (cdev->gadget->speed < USB_SPEED_SUPER) - video->uvc_num_requests = 4; - else - video->uvc_num_requests = 64; + req_size = video->ep->maxpacket + * max_t(unsigned int, video->ep->maxburst, 1) + * (video->ep->mult); + + /* We divide by two, to increase the chance to run + * into fewer requests for smaller framesizes. + */ + nreq = DIV_ROUND_UP(DIV_ROUND_UP(sizes[0], 2), req_size); + nreq = clamp(nreq, 4U, 64U); + video->uvc_num_requests = nreq; return 0; } @@ -104,7 +111,8 @@ static void uvc_buffer_queue(struct vb2_buffer *vb) if (likely(!(queue->flags & UVC_QUEUE_DISCONNECTED))) { list_add_tail(&buf->queue, &queue->irqqueue); } else { - /* If the device is disconnected return the buffer to userspace + /* + * If the device is disconnected return the buffer to userspace * directly. The next QBUF call will fail with -ENODEV. */ buf->state = UVC_BUF_STATE_ERROR; @@ -255,7 +263,8 @@ void uvcg_queue_cancel(struct uvc_video_queue *queue, int disconnect) } queue->buf_used = 0; - /* This must be protected by the irqlock spinlock to avoid race + /* + * This must be protected by the irqlock spinlock to avoid race * conditions between uvc_queue_buffer and the disconnection event that * could result in an interruptible wait in uvc_dequeue_buffer. Do not * blindly replace this logic by checking for the UVC_DEV_DISCONNECTED diff --git a/drivers/usb/gadget/function/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c index d42bb3346745..c00ce0e91f5d 100644 --- a/drivers/usb/gadget/function/uvc_video.c +++ b/drivers/usb/gadget/function/uvc_video.c @@ -261,7 +261,7 @@ uvc_video_complete(struct usb_ep *ep, struct usb_request *req) break; default: - uvcg_info(&video->uvc->func, + uvcg_warn(&video->uvc->func, "VS request completed with status %d.\n", req->status); uvcg_queue_cancel(queue, 0); @@ -378,7 +378,8 @@ static void uvcg_video_pump(struct work_struct *work) int ret; while (video->ep->enabled) { - /* Retrieve the first available USB request, protected by the + /* + * Retrieve the first available USB request, protected by the * request lock. */ spin_lock_irqsave(&video->req_lock, flags); @@ -391,7 +392,8 @@ static void uvcg_video_pump(struct work_struct *work) list_del(&req->list); spin_unlock_irqrestore(&video->req_lock, flags); - /* Retrieve the first available video buffer and fill the + /* + * Retrieve the first available video buffer and fill the * request, protected by the video queue irqlock. */ spin_lock_irqsave(&queue->irqlock, flags); @@ -403,9 +405,11 @@ static void uvcg_video_pump(struct work_struct *work) video->encode(req, video, buf); - /* With usb3 we have more requests. This will decrease the + /* + * With usb3 we have more requests. This will decrease the * interrupt load to a quarter but also catches the corner - * cases, which needs to be handled */ + * cases, which needs to be handled. + */ if (list_empty(&video->req_free) || buf->state == UVC_BUF_STATE_DONE || !(video->req_int_count % diff --git a/drivers/usb/gadget/legacy/inode.c b/drivers/usb/gadget/legacy/inode.c index 79990597c39f..01c3ead7d1b4 100644 --- a/drivers/usb/gadget/legacy/inode.c +++ b/drivers/usb/gadget/legacy/inode.c @@ -362,6 +362,7 @@ ep_io (struct ep_data *epdata, void *buf, unsigned len) spin_unlock_irq (&epdata->dev->lock); DBG (epdata->dev, "endpoint gone\n"); + wait_for_completion(&done); epdata->status = -ENODEV; } } diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 69394dc1cdfb..5756acb07b8d 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -59,8 +59,8 @@ config USB_ATMEL_USBA tristate "Atmel USBA" depends on ARCH_AT91 help - USBA is the integrated high-speed USB Device controller on - the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel. + USBA is the integrated high-speed USB Device controller on some + AT91SAM9 and AT91CAP9 processors from Atmel. The fifo_mode parameter is used to select endpoint allocation mode. fifo_mode = 0 is used to let the driver autoconfigure the endpoints. @@ -311,7 +311,7 @@ source "drivers/usb/gadget/udc/bdc/Kconfig" config USB_AMD5536UDC tristate "AMD5536 UDC" - depends on USB_PCI + depends on USB_PCI && HAS_DMA select USB_SNP_CORE help The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge. @@ -463,6 +463,19 @@ config USB_TEGRA_XUDC dynamically linked module called "tegra_xudc" and force all gadget drivers to also be dynamically linked. +config USB_ASPEED_UDC + tristate "Aspeed UDC driver support" + depends on ARCH_ASPEED || COMPILE_TEST + depends on USB_LIBCOMPOSITE + help + Enables Aspeed USB2.0 Device Controller driver for AST260x + family SoCs. The controller supports 1 control endpoint and + 4 programmable endpoints. + + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "aspeed_udc" and force all + gadget drivers to also be dynamically linked. + source "drivers/usb/gadget/udc/aspeed-vhub/Kconfig" # diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index a21f2224e7eb..12f9e4c9eb0c 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -40,5 +40,6 @@ obj-$(CONFIG_USB_GR_UDC) += gr_udc.o obj-$(CONFIG_USB_GADGET_XILINX) += udc-xilinx.o obj-$(CONFIG_USB_SNP_UDC_PLAT) += snps_udc_plat.o obj-$(CONFIG_USB_ASPEED_VHUB) += aspeed-vhub/ +obj-$(CONFIG_USB_ASPEED_UDC) += aspeed_udc.o obj-$(CONFIG_USB_BDC_UDC) += bdc/ obj-$(CONFIG_USB_MAX3420_UDC) += max3420_udc.o diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c index 65cd4e46f031..e2207d014620 100644 --- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c +++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c @@ -1059,8 +1059,10 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub) /* Initialize vhub String Descriptors. */ INIT_LIST_HEAD(&vhub->vhub_str_desc); desc_np = of_get_child_by_name(vhub_np, "vhub-strings"); - if (desc_np) + if (desc_np) { ret = ast_vhub_of_parse_str_desc(vhub, desc_np); + of_node_put(desc_np); + } else ret = ast_vhub_str_alloc_add(vhub, &ast_vhub_strings); diff --git a/drivers/usb/gadget/udc/aspeed_udc.c b/drivers/usb/gadget/udc/aspeed_udc.c new file mode 100644 index 000000000000..01968e2167f9 --- /dev/null +++ b/drivers/usb/gadget/udc/aspeed_udc.c @@ -0,0 +1,1597 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2021 Aspeed Technology Inc. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AST_UDC_NUM_ENDPOINTS (1 + 4) +#define AST_UDC_EP0_MAX_PACKET 64 /* EP0's max packet size */ +#define AST_UDC_EPn_MAX_PACKET 1024 /* Generic EPs max packet size */ +#define AST_UDC_DESCS_COUNT 256 /* Use 256 stages descriptor mode (32/256) */ +#define AST_UDC_DESC_MODE 1 /* Single/Multiple Stage(s) Descriptor Mode */ + +#define AST_UDC_EP_DMA_SIZE (AST_UDC_EPn_MAX_PACKET + 8 * AST_UDC_DESCS_COUNT) + +/***************************** + * * + * UDC register definitions * + * * + *****************************/ + +#define AST_UDC_FUNC_CTRL 0x00 /* Root Function Control & Status Register */ +#define AST_UDC_CONFIG 0x04 /* Root Configuration Setting Register */ +#define AST_UDC_IER 0x08 /* Interrupt Control Register */ +#define AST_UDC_ISR 0x0C /* Interrupt Status Register */ +#define AST_UDC_EP_ACK_IER 0x10 /* Programmable ep Pool ACK Interrupt Enable Reg */ +#define AST_UDC_EP_NAK_IER 0x14 /* Programmable ep Pool NAK Interrupt Enable Reg */ +#define AST_UDC_EP_ACK_ISR 0x18 /* Programmable ep Pool ACK Interrupt Status Reg */ +#define AST_UDC_EP_NAK_ISR 0x1C /* Programmable ep Pool NAK Interrupt Status Reg */ +#define AST_UDC_DEV_RESET 0x20 /* Device Controller Soft Reset Enable Register */ +#define AST_UDC_STS 0x24 /* USB Status Register */ +#define AST_VHUB_EP_DATA 0x28 /* Programmable ep Pool Data Toggle Value Set */ +#define AST_VHUB_ISO_TX_FAIL 0x2C /* Isochronous Transaction Fail Accumulator */ +#define AST_UDC_EP0_CTRL 0x30 /* Endpoint 0 Control/Status Register */ +#define AST_UDC_EP0_DATA_BUFF 0x34 /* Base Address of ep0 IN/OUT Data Buffer Reg */ +#define AST_UDC_SETUP0 0x80 /* Root Device Setup Data Buffer0 */ +#define AST_UDC_SETUP1 0x84 /* Root Device Setup Data Buffer1 */ + + +/* Main control reg */ +#define USB_PHY_CLK_EN BIT(31) +#define USB_FIFO_DYN_PWRD_EN BIT(19) +#define USB_EP_LONG_DESC BIT(18) +#define USB_BIST_TEST_PASS BIT(13) +#define USB_BIST_TURN_ON BIT(12) +#define USB_PHY_RESET_DIS BIT(11) +#define USB_TEST_MODE(x) ((x) << 8) +#define USB_FORCE_TIMER_HS BIT(7) +#define USB_FORCE_HS BIT(6) +#define USB_REMOTE_WAKEUP_12MS BIT(5) +#define USB_REMOTE_WAKEUP_EN BIT(4) +#define USB_AUTO_REMOTE_WAKEUP_EN BIT(3) +#define USB_STOP_CLK_IN_SUPEND BIT(2) +#define USB_UPSTREAM_FS BIT(1) +#define USB_UPSTREAM_EN BIT(0) + +/* Main config reg */ +#define UDC_CFG_SET_ADDR(x) ((x) & 0x3f) +#define UDC_CFG_ADDR_MASK (0x3f) + +/* Interrupt ctrl & status reg */ +#define UDC_IRQ_EP_POOL_NAK BIT(17) +#define UDC_IRQ_EP_POOL_ACK_STALL BIT(16) +#define UDC_IRQ_BUS_RESUME BIT(8) +#define UDC_IRQ_BUS_SUSPEND BIT(7) +#define UDC_IRQ_BUS_RESET BIT(6) +#define UDC_IRQ_EP0_IN_DATA_NAK BIT(4) +#define UDC_IRQ_EP0_IN_ACK_STALL BIT(3) +#define UDC_IRQ_EP0_OUT_NAK BIT(2) +#define UDC_IRQ_EP0_OUT_ACK_STALL BIT(1) +#define UDC_IRQ_EP0_SETUP BIT(0) +#define UDC_IRQ_ACK_ALL (0x1ff) + +/* EP isr reg */ +#define USB_EP3_ISR BIT(3) +#define USB_EP2_ISR BIT(2) +#define USB_EP1_ISR BIT(1) +#define USB_EP0_ISR BIT(0) +#define UDC_IRQ_EP_ACK_ALL (0xf) + +/*Soft reset reg */ +#define ROOT_UDC_SOFT_RESET BIT(0) + +/* USB status reg */ +#define UDC_STS_HIGHSPEED BIT(27) + +/* Programmable EP data toggle */ +#define EP_TOGGLE_SET_EPNUM(x) ((x) & 0x3) + +/* EP0 ctrl reg */ +#define EP0_GET_RX_LEN(x) ((x >> 16) & 0x7f) +#define EP0_TX_LEN(x) ((x & 0x7f) << 8) +#define EP0_RX_BUFF_RDY BIT(2) +#define EP0_TX_BUFF_RDY BIT(1) +#define EP0_STALL BIT(0) + +/************************************* + * * + * per-endpoint register definitions * + * * + *************************************/ + +#define AST_UDC_EP_CONFIG 0x00 /* Endpoint Configuration Register */ +#define AST_UDC_EP_DMA_CTRL 0x04 /* DMA Descriptor List Control/Status Register */ +#define AST_UDC_EP_DMA_BUFF 0x08 /* DMA Descriptor/Buffer Base Address */ +#define AST_UDC_EP_DMA_STS 0x0C /* DMA Descriptor List R/W Pointer and Status */ + +#define AST_UDC_EP_BASE 0x200 +#define AST_UDC_EP_OFFSET 0x10 + +/* EP config reg */ +#define EP_SET_MAX_PKT(x) ((x & 0x3ff) << 16) +#define EP_DATA_FETCH_CTRL(x) ((x & 0x3) << 14) +#define EP_AUTO_DATA_DISABLE (0x1 << 13) +#define EP_SET_EP_STALL (0x1 << 12) +#define EP_SET_EP_NUM(x) ((x & 0xf) << 8) +#define EP_SET_TYPE_MASK(x) ((x) << 5) +#define EP_TYPE_BULK (0x1) +#define EP_TYPE_INT (0x2) +#define EP_TYPE_ISO (0x3) +#define EP_DIR_OUT (0x1 << 4) +#define EP_ALLOCATED_MASK (0x7 << 1) +#define EP_ENABLE BIT(0) + +/* EP DMA ctrl reg */ +#define EP_DMA_CTRL_GET_PROC_STS(x) ((x >> 4) & 0xf) +#define EP_DMA_CTRL_STS_RX_IDLE 0x0 +#define EP_DMA_CTRL_STS_TX_IDLE 0x8 +#define EP_DMA_CTRL_IN_LONG_MODE (0x1 << 3) +#define EP_DMA_CTRL_RESET (0x1 << 2) +#define EP_DMA_SINGLE_STAGE (0x1 << 1) +#define EP_DMA_DESC_MODE (0x1 << 0) + +/* EP DMA status reg */ +#define EP_DMA_SET_TX_SIZE(x) ((x & 0x7ff) << 16) +#define EP_DMA_GET_TX_SIZE(x) (((x) >> 16) & 0x7ff) +#define EP_DMA_GET_RPTR(x) (((x) >> 8) & 0xff) +#define EP_DMA_GET_WPTR(x) ((x) & 0xff) +#define EP_DMA_SINGLE_KICK (1 << 0) /* WPTR = 1 for single mode */ + +/* EP desc reg */ +#define AST_EP_DMA_DESC_INTR_ENABLE BIT(31) +#define AST_EP_DMA_DESC_PID_DATA0 (0 << 14) +#define AST_EP_DMA_DESC_PID_DATA2 BIT(14) +#define AST_EP_DMA_DESC_PID_DATA1 (2 << 14) +#define AST_EP_DMA_DESC_PID_MDATA (3 << 14) +#define EP_DESC1_IN_LEN(x) ((x) & 0x1fff) +#define AST_EP_DMA_DESC_MAX_LEN (7680) /* Max packet length for trasmit in 1 desc */ + +struct ast_udc_request { + struct usb_request req; + struct list_head queue; + unsigned mapped:1; + unsigned int actual_dma_length; + u32 saved_dma_wptr; +}; + +#define to_ast_req(__req) container_of(__req, struct ast_udc_request, req) + +struct ast_dma_desc { + u32 des_0; + u32 des_1; +}; + +struct ast_udc_ep { + struct usb_ep ep; + + /* Request queue */ + struct list_head queue; + + struct ast_udc_dev *udc; + void __iomem *ep_reg; + void *epn_buf; + dma_addr_t epn_buf_dma; + const struct usb_endpoint_descriptor *desc; + + /* DMA Descriptors */ + struct ast_dma_desc *descs; + dma_addr_t descs_dma; + u32 descs_wptr; + u32 chunk_max; + + bool dir_in:1; + unsigned stopped:1; + bool desc_mode:1; +}; + +#define to_ast_ep(__ep) container_of(__ep, struct ast_udc_ep, ep) + +struct ast_udc_dev { + struct platform_device *pdev; + void __iomem *reg; + int irq; + spinlock_t lock; + struct clk *clk; + struct work_struct wake_work; + + /* EP0 DMA buffers allocated in one chunk */ + void *ep0_buf; + dma_addr_t ep0_buf_dma; + struct ast_udc_ep ep[AST_UDC_NUM_ENDPOINTS]; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + void __iomem *creq; + enum usb_device_state suspended_from; + int desc_mode; + + /* Force full speed only */ + bool force_usb1:1; + unsigned is_control_tx:1; + bool wakeup_en:1; +}; + +#define to_ast_dev(__g) container_of(__g, struct ast_udc_dev, gadget) + +static const char * const ast_ep_name[] = { + "ep0", "ep1", "ep2", "ep3", "ep4" +}; + +#ifdef AST_UDC_DEBUG_ALL +#define AST_UDC_DEBUG +#define AST_SETUP_DEBUG +#define AST_EP_DEBUG +#define AST_ISR_DEBUG +#endif + +#ifdef AST_SETUP_DEBUG +#define SETUP_DBG(u, fmt, ...) \ + dev_dbg(&(u)->pdev->dev, "%s() " fmt, __func__, ##__VA_ARGS__) +#else +#define SETUP_DBG(u, fmt, ...) +#endif + +#ifdef AST_EP_DEBUG +#define EP_DBG(e, fmt, ...) \ + dev_dbg(&(e)->udc->pdev->dev, "%s():%s " fmt, __func__, \ + (e)->ep.name, ##__VA_ARGS__) +#else +#define EP_DBG(ep, fmt, ...) ((void)(ep)) +#endif + +#ifdef AST_UDC_DEBUG +#define UDC_DBG(u, fmt, ...) \ + dev_dbg(&(u)->pdev->dev, "%s() " fmt, __func__, ##__VA_ARGS__) +#else +#define UDC_DBG(u, fmt, ...) +#endif + +#ifdef AST_ISR_DEBUG +#define ISR_DBG(u, fmt, ...) \ + dev_dbg(&(u)->pdev->dev, "%s() " fmt, __func__, ##__VA_ARGS__) +#else +#define ISR_DBG(u, fmt, ...) +#endif + +/*-------------------------------------------------------------------------*/ +#define ast_udc_read(udc, offset) \ + readl((udc)->reg + (offset)) +#define ast_udc_write(udc, val, offset) \ + writel((val), (udc)->reg + (offset)) + +#define ast_ep_read(ep, reg) \ + readl((ep)->ep_reg + (reg)) +#define ast_ep_write(ep, val, reg) \ + writel((val), (ep)->ep_reg + (reg)) + +/*-------------------------------------------------------------------------*/ + +static void ast_udc_done(struct ast_udc_ep *ep, struct ast_udc_request *req, + int status) +{ + struct ast_udc_dev *udc = ep->udc; + + EP_DBG(ep, "req @%p, len (%d/%d), buf:0x%x, dir:0x%x\n", + req, req->req.actual, req->req.length, + (u32)req->req.buf, ep->dir_in); + + list_del(&req->queue); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + EP_DBG(ep, "done req:%p, status:%d\n", req, status); + + spin_unlock(&udc->lock); + usb_gadget_giveback_request(&ep->ep, &req->req); + spin_lock(&udc->lock); +} + +static void ast_udc_nuke(struct ast_udc_ep *ep, int status) +{ + int count = 0; + + while (!list_empty(&ep->queue)) { + struct ast_udc_request *req; + + req = list_entry(ep->queue.next, struct ast_udc_request, + queue); + ast_udc_done(ep, req, status); + count++; + } + + if (count) + EP_DBG(ep, "Nuked %d request(s)\n", count); +} + +/* + * Stop activity on all endpoints. + * Device controller for which EP activity is to be stopped. + * + * All the endpoints are stopped and any pending transfer requests if any on + * the endpoint are terminated. + */ +static void ast_udc_stop_activity(struct ast_udc_dev *udc) +{ + struct ast_udc_ep *ep; + int i; + + for (i = 0; i < AST_UDC_NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + ep->stopped = 1; + ast_udc_nuke(ep, -ESHUTDOWN); + } +} + +static int ast_udc_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + u16 maxpacket = usb_endpoint_maxp(desc); + struct ast_udc_ep *ep = to_ast_ep(_ep); + struct ast_udc_dev *udc = ep->udc; + u8 epnum = usb_endpoint_num(desc); + unsigned long flags; + u32 ep_conf = 0; + u8 dir_in; + u8 type; + + if (!_ep || !ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT || + maxpacket == 0 || maxpacket > ep->ep.maxpacket) { + EP_DBG(ep, "Failed, invalid EP enable param\n"); + return -EINVAL; + } + + if (!udc->driver) { + EP_DBG(ep, "bogus device state\n"); + return -ESHUTDOWN; + } + + EP_DBG(ep, "maxpacket:0x%x\n", maxpacket); + + spin_lock_irqsave(&udc->lock, flags); + + ep->desc = desc; + ep->stopped = 0; + ep->ep.maxpacket = maxpacket; + ep->chunk_max = AST_EP_DMA_DESC_MAX_LEN; + + if (maxpacket < AST_UDC_EPn_MAX_PACKET) + ep_conf = EP_SET_MAX_PKT(maxpacket); + + ep_conf |= EP_SET_EP_NUM(epnum); + + type = usb_endpoint_type(desc); + dir_in = usb_endpoint_dir_in(desc); + ep->dir_in = dir_in; + if (!ep->dir_in) + ep_conf |= EP_DIR_OUT; + + EP_DBG(ep, "type %d, dir_in %d\n", type, dir_in); + switch (type) { + case USB_ENDPOINT_XFER_ISOC: + ep_conf |= EP_SET_TYPE_MASK(EP_TYPE_ISO); + break; + + case USB_ENDPOINT_XFER_BULK: + ep_conf |= EP_SET_TYPE_MASK(EP_TYPE_BULK); + break; + + case USB_ENDPOINT_XFER_INT: + ep_conf |= EP_SET_TYPE_MASK(EP_TYPE_INT); + break; + } + + ep->desc_mode = udc->desc_mode && ep->descs_dma && ep->dir_in; + if (ep->desc_mode) { + ast_ep_write(ep, EP_DMA_CTRL_RESET, AST_UDC_EP_DMA_CTRL); + ast_ep_write(ep, 0, AST_UDC_EP_DMA_STS); + ast_ep_write(ep, ep->descs_dma, AST_UDC_EP_DMA_BUFF); + + /* Enable Long Descriptor Mode */ + ast_ep_write(ep, EP_DMA_CTRL_IN_LONG_MODE | EP_DMA_DESC_MODE, + AST_UDC_EP_DMA_CTRL); + + ep->descs_wptr = 0; + + } else { + ast_ep_write(ep, EP_DMA_CTRL_RESET, AST_UDC_EP_DMA_CTRL); + ast_ep_write(ep, EP_DMA_SINGLE_STAGE, AST_UDC_EP_DMA_CTRL); + ast_ep_write(ep, 0, AST_UDC_EP_DMA_STS); + } + + /* Cleanup data toggle just in case */ + ast_udc_write(udc, EP_TOGGLE_SET_EPNUM(epnum), AST_VHUB_EP_DATA); + + /* Enable EP */ + ast_ep_write(ep, ep_conf | EP_ENABLE, AST_UDC_EP_CONFIG); + + EP_DBG(ep, "ep_config: 0x%x\n", ast_ep_read(ep, AST_UDC_EP_CONFIG)); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int ast_udc_ep_disable(struct usb_ep *_ep) +{ + struct ast_udc_ep *ep = to_ast_ep(_ep); + struct ast_udc_dev *udc = ep->udc; + unsigned long flags; + + spin_lock_irqsave(&udc->lock, flags); + + ep->ep.desc = NULL; + ep->stopped = 1; + + ast_udc_nuke(ep, -ESHUTDOWN); + ast_ep_write(ep, 0, AST_UDC_EP_CONFIG); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static struct usb_request *ast_udc_ep_alloc_request(struct usb_ep *_ep, + gfp_t gfp_flags) +{ + struct ast_udc_ep *ep = to_ast_ep(_ep); + struct ast_udc_request *req; + + req = kzalloc(sizeof(struct ast_udc_request), gfp_flags); + if (!req) { + EP_DBG(ep, "request allocation failed\n"); + return NULL; + } + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void ast_udc_ep_free_request(struct usb_ep *_ep, + struct usb_request *_req) +{ + struct ast_udc_request *req = to_ast_req(_req); + + kfree(req); +} + +static int ast_dma_descriptor_setup(struct ast_udc_ep *ep, u32 dma_buf, + u16 tx_len, struct ast_udc_request *req) +{ + struct ast_udc_dev *udc = ep->udc; + struct device *dev = &udc->pdev->dev; + bool last = false; + int chunk, count; + u32 offset; + + if (!ep->descs) { + dev_warn(dev, "%s: Empty DMA descs list failure\n", + ep->ep.name); + return -EINVAL; + } + + chunk = tx_len; + offset = count = 0; + + EP_DBG(ep, "req @%p, %s:%d, %s:0x%x, %s:0x%x\n", req, + "wptr", ep->descs_wptr, "dma_buf", dma_buf, + "tx_len", tx_len); + + /* Create Descriptor Lists */ + while (chunk >= 0 && !last && count < AST_UDC_DESCS_COUNT) { + + ep->descs[ep->descs_wptr].des_0 = dma_buf + offset; + + if (chunk > ep->chunk_max) { + ep->descs[ep->descs_wptr].des_1 = ep->chunk_max; + } else { + ep->descs[ep->descs_wptr].des_1 = chunk; + last = true; + } + + chunk -= ep->chunk_max; + + EP_DBG(ep, "descs[%d]: 0x%x 0x%x\n", + ep->descs_wptr, + ep->descs[ep->descs_wptr].des_0, + ep->descs[ep->descs_wptr].des_1); + + if (count == 0) + req->saved_dma_wptr = ep->descs_wptr; + + ep->descs_wptr++; + count++; + + if (ep->descs_wptr >= AST_UDC_DESCS_COUNT) + ep->descs_wptr = 0; + + offset = ep->chunk_max * count; + } + + return 0; +} + +static void ast_udc_epn_kick(struct ast_udc_ep *ep, struct ast_udc_request *req) +{ + u32 tx_len; + u32 last; + + last = req->req.length - req->req.actual; + tx_len = last > ep->ep.maxpacket ? ep->ep.maxpacket : last; + + EP_DBG(ep, "kick req @%p, len:%d, dir:%d\n", + req, tx_len, ep->dir_in); + + ast_ep_write(ep, req->req.dma + req->req.actual, AST_UDC_EP_DMA_BUFF); + + /* Start DMA */ + ast_ep_write(ep, EP_DMA_SET_TX_SIZE(tx_len), AST_UDC_EP_DMA_STS); + ast_ep_write(ep, EP_DMA_SET_TX_SIZE(tx_len) | EP_DMA_SINGLE_KICK, + AST_UDC_EP_DMA_STS); +} + +static void ast_udc_epn_kick_desc(struct ast_udc_ep *ep, + struct ast_udc_request *req) +{ + u32 descs_max_size; + u32 tx_len; + u32 last; + + descs_max_size = AST_EP_DMA_DESC_MAX_LEN * AST_UDC_DESCS_COUNT; + + last = req->req.length - req->req.actual; + tx_len = last > descs_max_size ? descs_max_size : last; + + EP_DBG(ep, "kick req @%p, %s:%d, %s:0x%x, %s:0x%x (%d/%d), %s:0x%x\n", + req, "tx_len", tx_len, "dir_in", ep->dir_in, + "dma", req->req.dma + req->req.actual, + req->req.actual, req->req.length, + "descs_max_size", descs_max_size); + + if (!ast_dma_descriptor_setup(ep, req->req.dma + req->req.actual, + tx_len, req)) + req->actual_dma_length += tx_len; + + /* make sure CPU done everything before triggering DMA */ + mb(); + + ast_ep_write(ep, ep->descs_wptr, AST_UDC_EP_DMA_STS); + + EP_DBG(ep, "descs_wptr:%d, dstat:0x%x, dctrl:0x%x\n", + ep->descs_wptr, + ast_ep_read(ep, AST_UDC_EP_DMA_STS), + ast_ep_read(ep, AST_UDC_EP_DMA_CTRL)); +} + +static void ast_udc_ep0_queue(struct ast_udc_ep *ep, + struct ast_udc_request *req) +{ + struct ast_udc_dev *udc = ep->udc; + u32 tx_len; + u32 last; + + last = req->req.length - req->req.actual; + tx_len = last > ep->ep.maxpacket ? ep->ep.maxpacket : last; + + ast_udc_write(udc, req->req.dma + req->req.actual, + AST_UDC_EP0_DATA_BUFF); + + if (ep->dir_in) { + /* IN requests, send data */ + SETUP_DBG(udc, "IN: %s:0x%x, %s:0x%x, %s:%d (%d/%d), %s:%d\n", + "buf", (u32)req->req.buf, + "dma", req->req.dma + req->req.actual, + "tx_len", tx_len, + req->req.actual, req->req.length, + "dir_in", ep->dir_in); + + req->req.actual += tx_len; + ast_udc_write(udc, EP0_TX_LEN(tx_len), AST_UDC_EP0_CTRL); + ast_udc_write(udc, EP0_TX_LEN(tx_len) | EP0_TX_BUFF_RDY, + AST_UDC_EP0_CTRL); + + } else { + /* OUT requests, receive data */ + SETUP_DBG(udc, "OUT: %s:%x, %s:%x, %s:(%d/%d), %s:%d\n", + "buf", (u32)req->req.buf, + "dma", req->req.dma + req->req.actual, + "len", req->req.actual, req->req.length, + "dir_in", ep->dir_in); + + if (!req->req.length) { + /* 0 len request, send tx as completion */ + ast_udc_write(udc, EP0_TX_BUFF_RDY, AST_UDC_EP0_CTRL); + ep->dir_in = 0x1; + } else + ast_udc_write(udc, EP0_RX_BUFF_RDY, AST_UDC_EP0_CTRL); + } +} + +static int ast_udc_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct ast_udc_request *req = to_ast_req(_req); + struct ast_udc_ep *ep = to_ast_ep(_ep); + struct ast_udc_dev *udc = ep->udc; + struct device *dev = &udc->pdev->dev; + unsigned long flags; + int rc; + + if (unlikely(!_req || !_req->complete || !_req->buf || !_ep)) { + dev_warn(dev, "Invalid EP request !\n"); + return -EINVAL; + } + + if (ep->stopped) { + dev_warn(dev, "%s is already stopped !\n", _ep->name); + return -ESHUTDOWN; + } + + spin_lock_irqsave(&udc->lock, flags); + + list_add_tail(&req->queue, &ep->queue); + + req->req.actual = 0; + req->req.status = -EINPROGRESS; + req->actual_dma_length = 0; + + rc = usb_gadget_map_request(&udc->gadget, &req->req, ep->dir_in); + if (rc) { + EP_DBG(ep, "Request mapping failure %d\n", rc); + dev_warn(dev, "Request mapping failure %d\n", rc); + goto end; + } + + EP_DBG(ep, "enqueue req @%p\n", req); + EP_DBG(ep, "l=%d, dma:0x%x, zero:%d, is_in:%d\n", + _req->length, _req->dma, _req->zero, ep->dir_in); + + /* EP0 request enqueue */ + if (ep->ep.desc == NULL) { + if ((req->req.dma % 4) != 0) { + dev_warn(dev, "EP0 req dma alignment error\n"); + rc = -ESHUTDOWN; + goto end; + } + + ast_udc_ep0_queue(ep, req); + goto end; + } + + /* EPn request enqueue */ + if (list_is_singular(&ep->queue)) { + if (ep->desc_mode) + ast_udc_epn_kick_desc(ep, req); + else + ast_udc_epn_kick(ep, req); + } + +end: + spin_unlock_irqrestore(&udc->lock, flags); + + return rc; +} + +static int ast_udc_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct ast_udc_ep *ep = to_ast_ep(_ep); + struct ast_udc_dev *udc = ep->udc; + struct ast_udc_request *req; + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&udc->lock, flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &ep->queue, queue) { + if (&req->req == _req) { + list_del_init(&req->queue); + ast_udc_done(ep, req, -ESHUTDOWN); + _req->status = -ECONNRESET; + break; + } + } + + /* dequeue request not found */ + if (&req->req != _req) + rc = -EINVAL; + + spin_unlock_irqrestore(&udc->lock, flags); + + return rc; +} + +static int ast_udc_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct ast_udc_ep *ep = to_ast_ep(_ep); + struct ast_udc_dev *udc = ep->udc; + unsigned long flags; + int epnum; + u32 ctrl; + + EP_DBG(ep, "val:%d\n", value); + + spin_lock_irqsave(&udc->lock, flags); + + epnum = usb_endpoint_num(ep->desc); + + /* EP0 */ + if (epnum == 0) { + ctrl = ast_udc_read(udc, AST_UDC_EP0_CTRL); + if (value) + ctrl |= EP0_STALL; + else + ctrl &= ~EP0_STALL; + + ast_udc_write(udc, ctrl, AST_UDC_EP0_CTRL); + + } else { + /* EPn */ + ctrl = ast_udc_read(udc, AST_UDC_EP_CONFIG); + if (value) + ctrl |= EP_SET_EP_STALL; + else + ctrl &= ~EP_SET_EP_STALL; + + ast_ep_write(ep, ctrl, AST_UDC_EP_CONFIG); + + /* only epn is stopped and waits for clear */ + ep->stopped = value ? 1 : 0; + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_ep_ops ast_udc_ep_ops = { + .enable = ast_udc_ep_enable, + .disable = ast_udc_ep_disable, + .alloc_request = ast_udc_ep_alloc_request, + .free_request = ast_udc_ep_free_request, + .queue = ast_udc_ep_queue, + .dequeue = ast_udc_ep_dequeue, + .set_halt = ast_udc_ep_set_halt, + /* there's only imprecise fifo status reporting */ +}; + +static void ast_udc_ep0_rx(struct ast_udc_dev *udc) +{ + ast_udc_write(udc, udc->ep0_buf_dma, AST_UDC_EP0_DATA_BUFF); + ast_udc_write(udc, EP0_RX_BUFF_RDY, AST_UDC_EP0_CTRL); +} + +static void ast_udc_ep0_tx(struct ast_udc_dev *udc) +{ + ast_udc_write(udc, udc->ep0_buf_dma, AST_UDC_EP0_DATA_BUFF); + ast_udc_write(udc, EP0_TX_BUFF_RDY, AST_UDC_EP0_CTRL); +} + +static void ast_udc_ep0_out(struct ast_udc_dev *udc) +{ + struct device *dev = &udc->pdev->dev; + struct ast_udc_ep *ep = &udc->ep[0]; + struct ast_udc_request *req; + u16 rx_len; + + if (list_empty(&ep->queue)) + return; + + req = list_entry(ep->queue.next, struct ast_udc_request, queue); + + rx_len = EP0_GET_RX_LEN(ast_udc_read(udc, AST_UDC_EP0_CTRL)); + req->req.actual += rx_len; + + SETUP_DBG(udc, "req %p (%d/%d)\n", req, + req->req.actual, req->req.length); + + if ((rx_len < ep->ep.maxpacket) || + (req->req.actual == req->req.length)) { + ast_udc_ep0_tx(udc); + if (!ep->dir_in) + ast_udc_done(ep, req, 0); + + } else { + if (rx_len > req->req.length) { + // Issue Fix + dev_warn(dev, "Something wrong (%d/%d)\n", + req->req.actual, req->req.length); + ast_udc_ep0_tx(udc); + ast_udc_done(ep, req, 0); + return; + } + + ep->dir_in = 0; + + /* More works */ + ast_udc_ep0_queue(ep, req); + } +} + +static void ast_udc_ep0_in(struct ast_udc_dev *udc) +{ + struct ast_udc_ep *ep = &udc->ep[0]; + struct ast_udc_request *req; + + if (list_empty(&ep->queue)) { + if (udc->is_control_tx) { + ast_udc_ep0_rx(udc); + udc->is_control_tx = 0; + } + + return; + } + + req = list_entry(ep->queue.next, struct ast_udc_request, queue); + + SETUP_DBG(udc, "req %p (%d/%d)\n", req, + req->req.actual, req->req.length); + + if (req->req.length == req->req.actual) { + if (req->req.length) + ast_udc_ep0_rx(udc); + + if (ep->dir_in) + ast_udc_done(ep, req, 0); + + } else { + /* More works */ + ast_udc_ep0_queue(ep, req); + } +} + +static void ast_udc_epn_handle(struct ast_udc_dev *udc, u16 ep_num) +{ + struct ast_udc_ep *ep = &udc->ep[ep_num]; + struct ast_udc_request *req; + u16 len = 0; + + if (list_empty(&ep->queue)) + return; + + req = list_first_entry(&ep->queue, struct ast_udc_request, queue); + + len = EP_DMA_GET_TX_SIZE(ast_ep_read(ep, AST_UDC_EP_DMA_STS)); + req->req.actual += len; + + EP_DBG(ep, "req @%p, length:(%d/%d), %s:0x%x\n", req, + req->req.actual, req->req.length, "len", len); + + /* Done this request */ + if (req->req.length == req->req.actual) { + ast_udc_done(ep, req, 0); + req = list_first_entry_or_null(&ep->queue, + struct ast_udc_request, + queue); + + } else { + /* Check for short packet */ + if (len < ep->ep.maxpacket) { + ast_udc_done(ep, req, 0); + req = list_first_entry_or_null(&ep->queue, + struct ast_udc_request, + queue); + } + } + + /* More requests */ + if (req) + ast_udc_epn_kick(ep, req); +} + +static void ast_udc_epn_handle_desc(struct ast_udc_dev *udc, u16 ep_num) +{ + struct ast_udc_ep *ep = &udc->ep[ep_num]; + struct device *dev = &udc->pdev->dev; + struct ast_udc_request *req; + u32 proc_sts, wr_ptr, rd_ptr; + u32 len_in_desc, ctrl; + u16 total_len = 0; + int i; + + if (list_empty(&ep->queue)) { + dev_warn(dev, "%s request queue empty!\n", ep->ep.name); + return; + } + + req = list_first_entry(&ep->queue, struct ast_udc_request, queue); + + ctrl = ast_ep_read(ep, AST_UDC_EP_DMA_CTRL); + proc_sts = EP_DMA_CTRL_GET_PROC_STS(ctrl); + + /* Check processing status is idle */ + if (proc_sts != EP_DMA_CTRL_STS_RX_IDLE && + proc_sts != EP_DMA_CTRL_STS_TX_IDLE) { + dev_warn(dev, "EP DMA CTRL: 0x%x, PS:0x%x\n", + ast_ep_read(ep, AST_UDC_EP_DMA_CTRL), + proc_sts); + return; + } + + ctrl = ast_ep_read(ep, AST_UDC_EP_DMA_STS); + rd_ptr = EP_DMA_GET_RPTR(ctrl); + wr_ptr = EP_DMA_GET_WPTR(ctrl); + + if (rd_ptr != wr_ptr) { + dev_warn(dev, "desc list is not empty ! %s:%d, %s:%d\n", + "rptr", rd_ptr, "wptr", wr_ptr); + return; + } + + EP_DBG(ep, "rd_ptr:%d, wr_ptr:%d\n", rd_ptr, wr_ptr); + i = req->saved_dma_wptr; + + do { + len_in_desc = EP_DESC1_IN_LEN(ep->descs[i].des_1); + EP_DBG(ep, "desc[%d] len: %d\n", i, len_in_desc); + total_len += len_in_desc; + i++; + if (i >= AST_UDC_DESCS_COUNT) + i = 0; + + } while (i != wr_ptr); + + req->req.actual += total_len; + + EP_DBG(ep, "req @%p, length:(%d/%d), %s:0x%x\n", req, + req->req.actual, req->req.length, "len", total_len); + + /* Done this request */ + if (req->req.length == req->req.actual) { + ast_udc_done(ep, req, 0); + req = list_first_entry_or_null(&ep->queue, + struct ast_udc_request, + queue); + + } else { + /* Check for short packet */ + if (total_len < ep->ep.maxpacket) { + ast_udc_done(ep, req, 0); + req = list_first_entry_or_null(&ep->queue, + struct ast_udc_request, + queue); + } + } + + /* More requests & dma descs not setup yet */ + if (req && (req->actual_dma_length == req->req.actual)) { + EP_DBG(ep, "More requests\n"); + ast_udc_epn_kick_desc(ep, req); + } +} + +static void ast_udc_ep0_data_tx(struct ast_udc_dev *udc, u8 *tx_data, u32 len) +{ + if (len) { + memcpy(udc->ep0_buf, tx_data, len); + + ast_udc_write(udc, udc->ep0_buf_dma, AST_UDC_EP0_DATA_BUFF); + ast_udc_write(udc, EP0_TX_LEN(len), AST_UDC_EP0_CTRL); + ast_udc_write(udc, EP0_TX_LEN(len) | EP0_TX_BUFF_RDY, + AST_UDC_EP0_CTRL); + udc->is_control_tx = 1; + + } else + ast_udc_write(udc, EP0_TX_BUFF_RDY, AST_UDC_EP0_CTRL); +} + +static void ast_udc_getstatus(struct ast_udc_dev *udc) +{ + struct usb_ctrlrequest crq; + struct ast_udc_ep *ep; + u16 status = 0; + u16 epnum = 0; + + memcpy_fromio(&crq, udc->creq, sizeof(crq)); + + switch (crq.bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + /* Get device status */ + status = 1 << USB_DEVICE_SELF_POWERED; + break; + case USB_RECIP_INTERFACE: + break; + case USB_RECIP_ENDPOINT: + epnum = crq.wIndex & USB_ENDPOINT_NUMBER_MASK; + status = udc->ep[epnum].stopped; + break; + default: + goto stall; + } + + ep = &udc->ep[epnum]; + EP_DBG(ep, "status: 0x%x\n", status); + ast_udc_ep0_data_tx(udc, (u8 *)&status, sizeof(status)); + + return; + +stall: + EP_DBG(ep, "Can't respond request\n"); + ast_udc_write(udc, ast_udc_read(udc, AST_UDC_EP0_CTRL) | EP0_STALL, + AST_UDC_EP0_CTRL); +} + +static void ast_udc_ep0_handle_setup(struct ast_udc_dev *udc) +{ + struct ast_udc_ep *ep = &udc->ep[0]; + struct ast_udc_request *req; + struct usb_ctrlrequest crq; + int req_num = 0; + int rc = 0; + u32 reg; + + memcpy_fromio(&crq, udc->creq, sizeof(crq)); + + SETUP_DBG(udc, "SETUP packet: %02x/%02x/%04x/%04x/%04x\n", + crq.bRequestType, crq.bRequest, le16_to_cpu(crq.wValue), + le16_to_cpu(crq.wIndex), le16_to_cpu(crq.wLength)); + + /* + * Cleanup ep0 request(s) in queue because + * there is a new control setup comes. + */ + list_for_each_entry(req, &udc->ep[0].queue, queue) { + req_num++; + EP_DBG(ep, "there is req %p in ep0 queue !\n", req); + } + + if (req_num) + ast_udc_nuke(&udc->ep[0], -ETIMEDOUT); + + udc->ep[0].dir_in = crq.bRequestType & USB_DIR_IN; + + if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (crq.bRequest) { + case USB_REQ_SET_ADDRESS: + if (ast_udc_read(udc, AST_UDC_STS) & UDC_STS_HIGHSPEED) + udc->gadget.speed = USB_SPEED_HIGH; + else + udc->gadget.speed = USB_SPEED_FULL; + + SETUP_DBG(udc, "set addr: 0x%x\n", crq.wValue); + reg = ast_udc_read(udc, AST_UDC_CONFIG); + reg &= ~UDC_CFG_ADDR_MASK; + reg |= UDC_CFG_SET_ADDR(crq.wValue); + ast_udc_write(udc, reg, AST_UDC_CONFIG); + goto req_complete; + + case USB_REQ_CLEAR_FEATURE: + SETUP_DBG(udc, "ep0: CLEAR FEATURE\n"); + goto req_driver; + + case USB_REQ_SET_FEATURE: + SETUP_DBG(udc, "ep0: SET FEATURE\n"); + goto req_driver; + + case USB_REQ_GET_STATUS: + ast_udc_getstatus(udc); + return; + + default: + goto req_driver; + } + + } + +req_driver: + if (udc->driver) { + SETUP_DBG(udc, "Forwarding %s to gadget...\n", + udc->gadget.name); + + spin_unlock(&udc->lock); + rc = udc->driver->setup(&udc->gadget, &crq); + spin_lock(&udc->lock); + + } else { + SETUP_DBG(udc, "No gadget for request !\n"); + } + + if (rc >= 0) + return; + + /* Stall if gadget failed */ + SETUP_DBG(udc, "Stalling, rc:0x%x\n", rc); + ast_udc_write(udc, ast_udc_read(udc, AST_UDC_EP0_CTRL) | EP0_STALL, + AST_UDC_EP0_CTRL); + return; + +req_complete: + SETUP_DBG(udc, "ep0: Sending IN status without data\n"); + ast_udc_write(udc, EP0_TX_BUFF_RDY, AST_UDC_EP0_CTRL); +} + +static irqreturn_t ast_udc_isr(int irq, void *data) +{ + struct ast_udc_dev *udc = (struct ast_udc_dev *)data; + struct ast_udc_ep *ep; + u32 isr, ep_isr; + int i; + + spin_lock(&udc->lock); + + isr = ast_udc_read(udc, AST_UDC_ISR); + if (!isr) + goto done; + + /* Ack interrupts */ + ast_udc_write(udc, isr, AST_UDC_ISR); + + if (isr & UDC_IRQ_BUS_RESET) { + ISR_DBG(udc, "UDC_IRQ_BUS_RESET\n"); + udc->gadget.speed = USB_SPEED_UNKNOWN; + + ep = &udc->ep[1]; + EP_DBG(ep, "dctrl:0x%x\n", + ast_ep_read(ep, AST_UDC_EP_DMA_CTRL)); + + if (udc->driver && udc->driver->reset) { + spin_unlock(&udc->lock); + udc->driver->reset(&udc->gadget); + spin_lock(&udc->lock); + } + } + + if (isr & UDC_IRQ_BUS_SUSPEND) { + ISR_DBG(udc, "UDC_IRQ_BUS_SUSPEND\n"); + udc->suspended_from = udc->gadget.state; + usb_gadget_set_state(&udc->gadget, USB_STATE_SUSPENDED); + + if (udc->driver && udc->driver->suspend) { + spin_unlock(&udc->lock); + udc->driver->suspend(&udc->gadget); + spin_lock(&udc->lock); + } + } + + if (isr & UDC_IRQ_BUS_RESUME) { + ISR_DBG(udc, "UDC_IRQ_BUS_RESUME\n"); + usb_gadget_set_state(&udc->gadget, udc->suspended_from); + + if (udc->driver && udc->driver->resume) { + spin_unlock(&udc->lock); + udc->driver->resume(&udc->gadget); + spin_lock(&udc->lock); + } + } + + if (isr & UDC_IRQ_EP0_IN_ACK_STALL) { + ISR_DBG(udc, "UDC_IRQ_EP0_IN_ACK_STALL\n"); + ast_udc_ep0_in(udc); + } + + if (isr & UDC_IRQ_EP0_OUT_ACK_STALL) { + ISR_DBG(udc, "UDC_IRQ_EP0_OUT_ACK_STALL\n"); + ast_udc_ep0_out(udc); + } + + if (isr & UDC_IRQ_EP0_SETUP) { + ISR_DBG(udc, "UDC_IRQ_EP0_SETUP\n"); + ast_udc_ep0_handle_setup(udc); + } + + if (isr & UDC_IRQ_EP_POOL_ACK_STALL) { + ISR_DBG(udc, "UDC_IRQ_EP_POOL_ACK_STALL\n"); + ep_isr = ast_udc_read(udc, AST_UDC_EP_ACK_ISR); + + /* Ack EP interrupts */ + ast_udc_write(udc, ep_isr, AST_UDC_EP_ACK_ISR); + + /* Handle each EP */ + for (i = 0; i < AST_UDC_NUM_ENDPOINTS - 1; i++) { + if (ep_isr & (0x1 << i)) { + ep = &udc->ep[i + 1]; + if (ep->desc_mode) + ast_udc_epn_handle_desc(udc, i + 1); + else + ast_udc_epn_handle(udc, i + 1); + } + } + } + +done: + spin_unlock(&udc->lock); + return IRQ_HANDLED; +} + +static int ast_udc_gadget_getframe(struct usb_gadget *gadget) +{ + struct ast_udc_dev *udc = to_ast_dev(gadget); + + return (ast_udc_read(udc, AST_UDC_STS) >> 16) & 0x7ff; +} + +static void ast_udc_wake_work(struct work_struct *work) +{ + struct ast_udc_dev *udc = container_of(work, struct ast_udc_dev, + wake_work); + unsigned long flags; + u32 ctrl; + + spin_lock_irqsave(&udc->lock, flags); + + UDC_DBG(udc, "Wakeup Host !\n"); + ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL); + ast_udc_write(udc, ctrl | USB_REMOTE_WAKEUP_EN, AST_UDC_FUNC_CTRL); + + spin_unlock_irqrestore(&udc->lock, flags); +} + +static void ast_udc_wakeup_all(struct ast_udc_dev *udc) +{ + /* + * A device is trying to wake the world, because this + * can recurse into the device, we break the call chain + * using a work queue + */ + schedule_work(&udc->wake_work); +} + +static int ast_udc_wakeup(struct usb_gadget *gadget) +{ + struct ast_udc_dev *udc = to_ast_dev(gadget); + unsigned long flags; + int rc = 0; + + spin_lock_irqsave(&udc->lock, flags); + + if (!udc->wakeup_en) { + UDC_DBG(udc, "Remote Wakeup is disabled\n"); + rc = -EINVAL; + goto err; + } + + UDC_DBG(udc, "Device initiated wakeup\n"); + ast_udc_wakeup_all(udc); + +err: + spin_unlock_irqrestore(&udc->lock, flags); + return rc; +} + +/* + * Activate/Deactivate link with host + */ +static int ast_udc_pullup(struct usb_gadget *gadget, int is_on) +{ + struct ast_udc_dev *udc = to_ast_dev(gadget); + unsigned long flags; + u32 ctrl; + + spin_lock_irqsave(&udc->lock, flags); + + UDC_DBG(udc, "is_on: %d\n", is_on); + if (is_on) + ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL) | USB_UPSTREAM_EN; + else + ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL) & ~USB_UPSTREAM_EN; + + ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int ast_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct ast_udc_dev *udc = to_ast_dev(gadget); + struct ast_udc_ep *ep; + unsigned long flags; + int i; + + spin_lock_irqsave(&udc->lock, flags); + + UDC_DBG(udc, "\n"); + udc->driver = driver; + udc->gadget.dev.of_node = udc->pdev->dev.of_node; + + for (i = 0; i < AST_UDC_NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + ep->stopped = 0; + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int ast_udc_stop(struct usb_gadget *gadget) +{ + struct ast_udc_dev *udc = to_ast_dev(gadget); + unsigned long flags; + u32 ctrl; + + spin_lock_irqsave(&udc->lock, flags); + + UDC_DBG(udc, "\n"); + ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL) & ~USB_UPSTREAM_EN; + ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->driver = NULL; + + ast_udc_stop_activity(udc); + usb_gadget_set_state(&udc->gadget, USB_STATE_NOTATTACHED); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops ast_udc_ops = { + .get_frame = ast_udc_gadget_getframe, + .wakeup = ast_udc_wakeup, + .pullup = ast_udc_pullup, + .udc_start = ast_udc_start, + .udc_stop = ast_udc_stop, +}; + +/* + * Support 1 Control Endpoint. + * Support multiple programmable endpoints that can be configured to + * Bulk IN/OUT, Interrupt IN/OUT, and Isochronous IN/OUT type endpoint. + */ +static void ast_udc_init_ep(struct ast_udc_dev *udc) +{ + struct ast_udc_ep *ep; + int i; + + for (i = 0; i < AST_UDC_NUM_ENDPOINTS; i++) { + ep = &udc->ep[i]; + ep->ep.name = ast_ep_name[i]; + if (i == 0) { + ep->ep.caps.type_control = true; + } else { + ep->ep.caps.type_iso = true; + ep->ep.caps.type_bulk = true; + ep->ep.caps.type_int = true; + } + ep->ep.caps.dir_in = true; + ep->ep.caps.dir_out = true; + + ep->ep.ops = &ast_udc_ep_ops; + ep->udc = udc; + + INIT_LIST_HEAD(&ep->queue); + + if (i == 0) { + usb_ep_set_maxpacket_limit(&ep->ep, + AST_UDC_EP0_MAX_PACKET); + continue; + } + + ep->ep_reg = udc->reg + AST_UDC_EP_BASE + + (AST_UDC_EP_OFFSET * (i - 1)); + + ep->epn_buf = udc->ep0_buf + (i * AST_UDC_EP_DMA_SIZE); + ep->epn_buf_dma = udc->ep0_buf_dma + (i * AST_UDC_EP_DMA_SIZE); + usb_ep_set_maxpacket_limit(&ep->ep, AST_UDC_EPn_MAX_PACKET); + + ep->descs = ep->epn_buf + AST_UDC_EPn_MAX_PACKET; + ep->descs_dma = ep->epn_buf_dma + AST_UDC_EPn_MAX_PACKET; + ep->descs_wptr = 0; + + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + } +} + +static void ast_udc_init_dev(struct ast_udc_dev *udc) +{ + INIT_WORK(&udc->wake_work, ast_udc_wake_work); +} + +static void ast_udc_init_hw(struct ast_udc_dev *udc) +{ + u32 ctrl; + + /* Enable PHY */ + ctrl = USB_PHY_CLK_EN | USB_PHY_RESET_DIS; + ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL); + + udelay(1); + ast_udc_write(udc, 0, AST_UDC_DEV_RESET); + + /* Set descriptor ring size */ + if (AST_UDC_DESCS_COUNT == 256) { + ctrl |= USB_EP_LONG_DESC; + ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL); + } + + /* Mask & ack all interrupts before installing the handler */ + ast_udc_write(udc, 0, AST_UDC_IER); + ast_udc_write(udc, UDC_IRQ_ACK_ALL, AST_UDC_ISR); + + /* Enable some interrupts */ + ctrl = UDC_IRQ_EP_POOL_ACK_STALL | UDC_IRQ_BUS_RESUME | + UDC_IRQ_BUS_SUSPEND | UDC_IRQ_BUS_RESET | + UDC_IRQ_EP0_IN_ACK_STALL | UDC_IRQ_EP0_OUT_ACK_STALL | + UDC_IRQ_EP0_SETUP; + ast_udc_write(udc, ctrl, AST_UDC_IER); + + /* Cleanup and enable ep ACK interrupts */ + ast_udc_write(udc, UDC_IRQ_EP_ACK_ALL, AST_UDC_EP_ACK_IER); + ast_udc_write(udc, UDC_IRQ_EP_ACK_ALL, AST_UDC_EP_ACK_ISR); + + ast_udc_write(udc, 0, AST_UDC_EP0_CTRL); +} + +static int ast_udc_remove(struct platform_device *pdev) +{ + struct ast_udc_dev *udc = platform_get_drvdata(pdev); + unsigned long flags; + u32 ctrl; + + usb_del_gadget_udc(&udc->gadget); + if (udc->driver) + return -EBUSY; + + spin_lock_irqsave(&udc->lock, flags); + + /* Disable upstream port connection */ + ctrl = ast_udc_read(udc, AST_UDC_FUNC_CTRL) & ~USB_UPSTREAM_EN; + ast_udc_write(udc, ctrl, AST_UDC_FUNC_CTRL); + + clk_disable_unprepare(udc->clk); + + spin_unlock_irqrestore(&udc->lock, flags); + + if (udc->ep0_buf) + dma_free_coherent(&pdev->dev, + AST_UDC_EP_DMA_SIZE * AST_UDC_NUM_ENDPOINTS, + udc->ep0_buf, + udc->ep0_buf_dma); + + udc->ep0_buf = NULL; + + return 0; +} + +static int ast_udc_probe(struct platform_device *pdev) +{ + enum usb_device_speed max_speed; + struct device *dev = &pdev->dev; + struct ast_udc_dev *udc; + struct resource *res; + int rc; + + udc = devm_kzalloc(&pdev->dev, sizeof(struct ast_udc_dev), GFP_KERNEL); + if (!udc) + return -ENOMEM; + + udc->gadget.dev.parent = dev; + udc->pdev = pdev; + spin_lock_init(&udc->lock); + + udc->gadget.ops = &ast_udc_ops; + udc->gadget.ep0 = &udc->ep[0].ep; + udc->gadget.name = "aspeed-udc"; + udc->gadget.dev.init_name = "gadget"; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + udc->reg = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(udc->reg)) { + dev_err(&pdev->dev, "Failed to map resources\n"); + return PTR_ERR(udc->reg); + } + + platform_set_drvdata(pdev, udc); + + udc->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(udc->clk)) { + rc = PTR_ERR(udc->clk); + goto err; + } + rc = clk_prepare_enable(udc->clk); + if (rc) { + dev_err(&pdev->dev, "Failed to enable clock (0x%x)\n", rc); + goto err; + } + + /* Check if we need to limit the HW to USB1 */ + max_speed = usb_get_maximum_speed(&pdev->dev); + if (max_speed != USB_SPEED_UNKNOWN && max_speed < USB_SPEED_HIGH) + udc->force_usb1 = true; + + /* + * Allocate DMA buffers for all EPs in one chunk + */ + udc->ep0_buf = dma_alloc_coherent(&pdev->dev, + AST_UDC_EP_DMA_SIZE * + AST_UDC_NUM_ENDPOINTS, + &udc->ep0_buf_dma, GFP_KERNEL); + + udc->gadget.speed = USB_SPEED_UNKNOWN; + udc->gadget.max_speed = USB_SPEED_HIGH; + udc->creq = udc->reg + AST_UDC_SETUP0; + + /* + * Support single stage mode or 32/256 stages descriptor mode. + * Set default as Descriptor Mode. + */ + udc->desc_mode = AST_UDC_DESC_MODE; + + dev_info(&pdev->dev, "DMA %s\n", udc->desc_mode ? + "descriptor mode" : "single mode"); + + INIT_LIST_HEAD(&udc->gadget.ep_list); + INIT_LIST_HEAD(&udc->gadget.ep0->ep_list); + + /* Initialized udc ep */ + ast_udc_init_ep(udc); + + /* Initialized udc device */ + ast_udc_init_dev(udc); + + /* Initialized udc hardware */ + ast_udc_init_hw(udc); + + /* Find interrupt and install handler */ + udc->irq = platform_get_irq(pdev, 0); + if (udc->irq < 0) { + rc = udc->irq; + goto err; + } + + rc = devm_request_irq(&pdev->dev, udc->irq, ast_udc_isr, 0, + KBUILD_MODNAME, udc); + if (rc) { + dev_err(&pdev->dev, "Failed to request interrupt\n"); + goto err; + } + + rc = usb_add_gadget_udc(&pdev->dev, &udc->gadget); + if (rc) { + dev_err(&pdev->dev, "Failed to add gadget udc\n"); + goto err; + } + + dev_info(&pdev->dev, "Initialized udc in USB%s mode\n", + udc->force_usb1 ? "1" : "2"); + + return 0; + +err: + dev_err(&pdev->dev, "Failed to udc probe, rc:0x%x\n", rc); + ast_udc_remove(pdev); + + return rc; +} + +static const struct of_device_id ast_udc_of_dt_ids[] = { + { .compatible = "aspeed,ast2600-udc", }, + {} +}; + +MODULE_DEVICE_TABLE(of, ast_udc_of_dt_ids); + +static struct platform_driver ast_udc_driver = { + .probe = ast_udc_probe, + .remove = ast_udc_remove, + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = ast_udc_of_dt_ids, + }, +}; + +module_platform_driver(ast_udc_driver); + +MODULE_DESCRIPTION("ASPEED UDC driver"); +MODULE_AUTHOR("Neal Liu "); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index ae2bfbac603e..53ca38c4b3ec 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -2060,7 +2060,7 @@ static const struct usba_udc_errata at91sam9g45_errata = { .pulse_bias = at91sam9g45_pulse_bias, }; -static const struct usba_ep_config ep_config_sam9[] __initconst = { +static const struct usba_ep_config ep_config_sam9[] = { { .nr_banks = 1 }, /* ep 0 */ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */ { .nr_banks = 2, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */ @@ -2070,7 +2070,7 @@ static const struct usba_ep_config ep_config_sam9[] __initconst = { { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 6 */ }; -static const struct usba_ep_config ep_config_sama5[] __initconst = { +static const struct usba_ep_config ep_config_sama5[] = { { .nr_banks = 1 }, /* ep 0 */ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 1 */ { .nr_banks = 3, .can_dma = 1, .can_isoc = 1 }, /* ep 2 */ @@ -2165,6 +2165,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, udc->vbus_pin = devm_gpiod_get_optional(&pdev->dev, "atmel,vbus", GPIOD_IN); + if (IS_ERR(udc->vbus_pin)) + return ERR_CAST(udc->vbus_pin); if (fifo_mode == 0) { udc->num_ep = udc_config->num_ep; @@ -2447,6 +2449,7 @@ static int usba_udc_resume(struct device *dev) static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume); static struct platform_driver udc_driver = { + .probe = usba_udc_probe, .remove = usba_udc_remove, .driver = { .name = "atmel_usba_udc", @@ -2454,8 +2457,7 @@ static struct platform_driver udc_driver = { .of_match_table = atmel_udc_dt_ids, }, }; - -module_platform_driver_probe(udc_driver, usba_udc_probe); +module_platform_driver(udc_driver); MODULE_DESCRIPTION("Atmel USBA UDC driver"); MODULE_AUTHOR("Haavard Skinnemoen (Atmel)"); diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c index 67887316a1a6..1848ced073f8 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_cmd.c +++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c @@ -307,7 +307,7 @@ int bdc_ep_clear_stall(struct bdc *bdc, int epnum) * his will reset the seq number for non EP0. */ if (epnum != 1) { - /* if the endpoint it not stallled */ + /* if the endpoint it not stalled */ if (!(ep->flags & BDC_EP_STALL)) { ret = bdc_ep_set_stall(bdc, epnum); if (ret) diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 7886497253cc..cafcf260394c 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1728,13 +1728,14 @@ static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) return ret; } - if (udc->driver) { + mutex_lock(&udc_lock); + if (udc->driver) ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", udc->driver->function); - if (ret) { - dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); - return ret; - } + mutex_unlock(&udc_lock); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); + return ret; } return 0; diff --git a/drivers/usb/gadget/udc/tegra-xudc.c b/drivers/usb/gadget/udc/tegra-xudc.c index 6d31ccf6aee5..3c37effdfa64 100644 --- a/drivers/usb/gadget/udc/tegra-xudc.c +++ b/drivers/usb/gadget/udc/tegra-xudc.c @@ -3691,15 +3691,15 @@ static int tegra_xudc_powerdomain_init(struct tegra_xudc *xudc) int err; xudc->genpd_dev_device = dev_pm_domain_attach_by_name(dev, "dev"); - if (IS_ERR(xudc->genpd_dev_device)) { - err = PTR_ERR(xudc->genpd_dev_device); + if (IS_ERR_OR_NULL(xudc->genpd_dev_device)) { + err = PTR_ERR(xudc->genpd_dev_device) ? : -ENODATA; dev_err(dev, "failed to get device power domain: %d\n", err); return err; } xudc->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "ss"); - if (IS_ERR(xudc->genpd_dev_ss)) { - err = PTR_ERR(xudc->genpd_dev_ss); + if (IS_ERR_OR_NULL(xudc->genpd_dev_ss)) { + err = PTR_ERR(xudc->genpd_dev_ss) ? : -ENODATA; dev_err(dev, "failed to get SuperSpeed power domain: %d\n", err); return err; } diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h index 98584f6b6c66..abdbcb1bacb0 100644 --- a/drivers/usb/gadget/udc/trace.h +++ b/drivers/usb/gadget/udc/trace.h @@ -140,7 +140,7 @@ DECLARE_EVENT_CLASS(udc_log_ep, TP_PROTO(struct usb_ep *ep, int ret), TP_ARGS(ep, ret), TP_STRUCT__entry( - __dynamic_array(char, name, UDC_TRACE_STR_MAX) + __string(name, ep->name) __field(unsigned, maxpacket) __field(unsigned, maxpacket_limit) __field(unsigned, max_streams) @@ -152,7 +152,7 @@ DECLARE_EVENT_CLASS(udc_log_ep, __field(int, ret) ), TP_fast_assign( - snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name); + __assign_str(name, ep->name); __entry->maxpacket = ep->maxpacket; __entry->maxpacket_limit = ep->maxpacket_limit; __entry->max_streams = ep->max_streams; @@ -214,7 +214,7 @@ DECLARE_EVENT_CLASS(udc_log_req, TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), TP_ARGS(ep, req, ret), TP_STRUCT__entry( - __dynamic_array(char, name, UDC_TRACE_STR_MAX) + __string(name, ep->name) __field(unsigned, length) __field(unsigned, actual) __field(unsigned, num_sgs) @@ -228,7 +228,7 @@ DECLARE_EVENT_CLASS(udc_log_req, __field(struct usb_request *, req) ), TP_fast_assign( - snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name); + __assign_str(name, ep->name); __entry->length = req->length; __entry->actual = req->actual; __entry->num_sgs = req->num_sgs; diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 682b3d2da623..fd9264cf6c87 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -306,6 +306,16 @@ config USB_EHCI_MV Dova, Armada 370 and Armada XP. See "Support for Marvell EBU on-chip EHCI USB controller" for those. +config USB_OCTEON_HCD + tristate "Cavium Networks Octeon USB support" + depends on CAVIUM_OCTEON_SOC && USB + help + This driver supports USB host controller on some Cavium + Networks' products in the Octeon family. + + To compile this driver as a module, choose M here. The module + will be called octeon-hcd. + config USB_CNS3XXX_EHCI bool "Cavium CNS3XXX EHCI Module (DEPRECATED)" depends on ARCH_CNS3XXX || COMPILE_TEST diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 2948983618fb..2c8a61be7e46 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -63,6 +63,7 @@ obj-$(CONFIG_USB_OHCI_HCD_S3C2410) += ohci-s3c2410.o obj-$(CONFIG_USB_OHCI_HCD_LPC32XX) += ohci-nxp.o obj-$(CONFIG_USB_OHCI_HCD_PXA27X) += ohci-pxa27x.o obj-$(CONFIG_USB_OHCI_HCD_DAVINCI) += ohci-da8xx.o +obj-$(CONFIG_USB_OCTEON_HCD) += octeon-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o obj-$(CONFIG_USB_FHCI_HCD) += fhci.o diff --git a/drivers/usb/host/ehci-platform.c b/drivers/usb/host/ehci-platform.c index f343967443e2..6924f0316e9a 100644 --- a/drivers/usb/host/ehci-platform.c +++ b/drivers/usb/host/ehci-platform.c @@ -370,6 +370,8 @@ static int ehci_platform_probe(struct platform_device *dev) hcd->rsrc_start = res_mem->start; hcd->rsrc_len = resource_size(res_mem); + hcd->tpl_support = of_usb_host_tpl_support(dev->dev.of_node); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) goto err_power; diff --git a/drivers/usb/host/ehci-ppc-of.c b/drivers/usb/host/ehci-ppc-of.c index 6bbaee74f7e7..28a19693c19f 100644 --- a/drivers/usb/host/ehci-ppc-of.c +++ b/drivers/usb/host/ehci-ppc-of.c @@ -148,6 +148,7 @@ static int ehci_hcd_ppc_of_probe(struct platform_device *op) } else { ehci->has_amcc_usb23 = 1; } + of_node_put(np); } if (of_get_property(dn, "big-endian", NULL)) { diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 1163af6fad77..807e64991e3e 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -1162,7 +1162,7 @@ submit_async ( * This is done in two parts: first SETUP req for GetDesc is sent then * 15 seconds later, the IN stage for GetDesc starts to req data from dev * - * is_setup : i/p arguement decides which of the two stage needs to be + * is_setup : i/p argument decides which of the two stage needs to be * performed; TRUE - SETUP and FALSE - IN+STATUS * Returns 0 if success */ diff --git a/drivers/usb/host/max3421-hcd.c b/drivers/usb/host/max3421-hcd.c index 502a3ac5e35b..352e3ac2b377 100644 --- a/drivers/usb/host/max3421-hcd.c +++ b/drivers/usb/host/max3421-hcd.c @@ -312,7 +312,7 @@ static const int hrsl_to_error[] = { /* * See https://www.beyondlogic.org/usbnutshell/usb4.shtml#Control for a - * reasonable overview of how control transfers use the the IN/OUT + * reasonable overview of how control transfers use the IN/OUT * tokens. */ #define MAX3421_HXFR_BULK_IN(ep) (0x00 | (ep)) /* bulk or interrupt */ diff --git a/drivers/staging/octeon-usb/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c similarity index 100% rename from drivers/staging/octeon-usb/octeon-hcd.c rename to drivers/usb/host/octeon-hcd.c diff --git a/drivers/staging/octeon-usb/octeon-hcd.h b/drivers/usb/host/octeon-hcd.h similarity index 100% rename from drivers/staging/octeon-usb/octeon-hcd.h rename to drivers/usb/host/octeon-hcd.h diff --git a/drivers/usb/host/ohci-at91.c b/drivers/usb/host/ohci-at91.c index a24aea3d2759..98326465e2dc 100644 --- a/drivers/usb/host/ohci-at91.c +++ b/drivers/usb/host/ohci-at91.c @@ -13,6 +13,7 @@ * This file is licenced under the GPL. */ +#include #include #include #include @@ -55,6 +56,7 @@ struct ohci_at91_priv { bool clocked; bool wakeup; /* Saved wake-up state for resume */ struct regmap *sfr_regmap; + u32 suspend_smc_id; }; /* interface and function clocks; sometimes also an AHB clock */ @@ -135,6 +137,19 @@ static void at91_stop_hc(struct platform_device *pdev) static void usb_hcd_at91_remove (struct usb_hcd *, struct platform_device *); +static u32 at91_dt_suspend_smc(struct device *dev) +{ + u32 suspend_smc_id; + + if (!dev->of_node) + return 0; + + if (of_property_read_u32(dev->of_node, "microchip,suspend-smc-id", &suspend_smc_id)) + return 0; + + return suspend_smc_id; +} + static struct regmap *at91_dt_syscon_sfr(void) { struct regmap *regmap; @@ -215,9 +230,13 @@ static int usb_hcd_at91_probe(const struct hc_driver *driver, goto err; } - ohci_at91->sfr_regmap = at91_dt_syscon_sfr(); - if (!ohci_at91->sfr_regmap) - dev_dbg(dev, "failed to find sfr node\n"); + ohci_at91->suspend_smc_id = at91_dt_suspend_smc(dev); + if (!ohci_at91->suspend_smc_id) { + dev_dbg(dev, "failed to find sfr suspend smc id, using regmap\n"); + ohci_at91->sfr_regmap = at91_dt_syscon_sfr(); + if (!ohci_at91->sfr_regmap) + dev_dbg(dev, "failed to find sfr node\n"); + } board = hcd->self.controller->platform_data; ohci = hcd_to_ohci(hcd); @@ -303,24 +322,30 @@ static int ohci_at91_hub_status_data(struct usb_hcd *hcd, char *buf) return length; } -static int ohci_at91_port_suspend(struct regmap *regmap, u8 set) +static int ohci_at91_port_suspend(struct ohci_at91_priv *ohci_at91, u8 set) { + struct regmap *regmap = ohci_at91->sfr_regmap; u32 regval; int ret; - if (!regmap) - return 0; + if (ohci_at91->suspend_smc_id) { + struct arm_smccc_res res; - ret = regmap_read(regmap, AT91_SFR_OHCIICR, ®val); - if (ret) - return ret; + arm_smccc_smc(ohci_at91->suspend_smc_id, set, 0, 0, 0, 0, 0, 0, &res); + if (res.a0) + return -EINVAL; + } else if (regmap) { + ret = regmap_read(regmap, AT91_SFR_OHCIICR, ®val); + if (ret) + return ret; - if (set) - regval |= AT91_OHCIICR_USB_SUSPEND; - else - regval &= ~AT91_OHCIICR_USB_SUSPEND; + if (set) + regval |= AT91_OHCIICR_USB_SUSPEND; + else + regval &= ~AT91_OHCIICR_USB_SUSPEND; - regmap_write(regmap, AT91_SFR_OHCIICR, regval); + regmap_write(regmap, AT91_SFR_OHCIICR, regval); + } return 0; } @@ -357,9 +382,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_SUSPEND: dev_dbg(hcd->self.controller, "SetPortFeat: SUSPEND\n"); - if (valid_port(wIndex) && ohci_at91->sfr_regmap) { - ohci_at91_port_suspend(ohci_at91->sfr_regmap, - 1); + if (valid_port(wIndex)) { + ohci_at91_port_suspend(ohci_at91, 1); return 0; } break; @@ -400,9 +424,8 @@ static int ohci_at91_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, case USB_PORT_FEAT_SUSPEND: dev_dbg(hcd->self.controller, "ClearPortFeature: SUSPEND\n"); - if (valid_port(wIndex) && ohci_at91->sfr_regmap) { - ohci_at91_port_suspend(ohci_at91->sfr_regmap, - 0); + if (valid_port(wIndex)) { + ohci_at91_port_suspend(ohci_at91, 0); return 0; } break; @@ -630,10 +653,10 @@ ohci_hcd_at91_drv_suspend(struct device *dev) /* flush the writes */ (void) ohci_readl (ohci, &ohci->regs->control); msleep(1); - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1); + ohci_at91_port_suspend(ohci_at91, 1); at91_stop_clock(ohci_at91); } else { - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 1); + ohci_at91_port_suspend(ohci_at91, 1); } return ret; @@ -645,7 +668,7 @@ ohci_hcd_at91_drv_resume(struct device *dev) struct usb_hcd *hcd = dev_get_drvdata(dev); struct ohci_at91_priv *ohci_at91 = hcd_to_ohci_at91_priv(hcd); - ohci_at91_port_suspend(ohci_at91->sfr_regmap, 0); + ohci_at91_port_suspend(ohci_at91, 0); if (ohci_at91->wakeup) disable_irq_wake(hcd->irq); diff --git a/drivers/usb/host/ohci-nxp.c b/drivers/usb/host/ohci-nxp.c index 85878e8ad331..106a6bcefb08 100644 --- a/drivers/usb/host/ohci-nxp.c +++ b/drivers/usb/host/ohci-nxp.c @@ -164,6 +164,7 @@ static int ohci_hcd_nxp_probe(struct platform_device *pdev) } isp1301_i2c_client = isp1301_get_client(isp1301_node); + of_node_put(isp1301_node); if (!isp1301_i2c_client) return -EPROBE_DEFER; diff --git a/drivers/usb/host/ohci-platform.c b/drivers/usb/host/ohci-platform.c index 47dfbfe9e519..0adae6265127 100644 --- a/drivers/usb/host/ohci-platform.c +++ b/drivers/usb/host/ohci-platform.c @@ -28,6 +28,7 @@ #include #include #include +#include #include "ohci.h" @@ -210,6 +211,8 @@ static int ohci_platform_probe(struct platform_device *dev) hcd->rsrc_start = res_mem->start; hcd->rsrc_len = resource_size(res_mem); + hcd->tpl_support = of_usb_host_tpl_support(dev->dev.of_node); + err = usb_add_hcd(hcd, irq, IRQF_SHARED); if (err) goto err_power; diff --git a/drivers/usb/host/ohci-ppc-of.c b/drivers/usb/host/ohci-ppc-of.c index 1960b8dfdba5..591f675cc930 100644 --- a/drivers/usb/host/ohci-ppc-of.c +++ b/drivers/usb/host/ohci-ppc-of.c @@ -166,6 +166,7 @@ static int ohci_hcd_ppc_of_probe(struct platform_device *op) release_mem_region(res.start, 0x4); } else pr_debug("%s: cannot get ehci offset from fdt\n", __FILE__); + of_node_put(np); } irq_dispose_mapping(irq); diff --git a/drivers/usb/host/ohci-sm501.c b/drivers/usb/host/ohci-sm501.c index b91d50da6127..f5de586454e3 100644 --- a/drivers/usb/host/ohci-sm501.c +++ b/drivers/usb/host/ohci-sm501.c @@ -153,7 +153,7 @@ static int ohci_hcd_sm501_drv_probe(struct platform_device *pdev) * fine. This is however not always the case - buffers may be allocated * using kmalloc() - so the usb core needs to be told that it must copy * data into our local memory if the buffers happen to be placed in - * regular memory. A non-null hcd->localmem_pool initialized by the + * regular memory. A non-null hcd->localmem_pool initialized by * the call to usb_hcd_setup_local_mem() below does just that. */ diff --git a/drivers/usb/host/uhci-grlib.c b/drivers/usb/host/uhci-grlib.c index 0a201a73b196..3ef6d52839e5 100644 --- a/drivers/usb/host/uhci-grlib.c +++ b/drivers/usb/host/uhci-grlib.c @@ -43,7 +43,7 @@ static int uhci_grlib_init(struct usb_hcd *hcd) uhci->rh_numports = uhci_count_ports(hcd); - /* Set up pointers to to generic functions */ + /* Set up pointers to generic functions */ uhci->reset_hc = uhci_generic_reset_hc; uhci->check_and_reset_hc = uhci_generic_check_and_reset_hc; /* No special actions need to be taken for the functions below */ diff --git a/drivers/usb/host/uhci-hcd.h b/drivers/usb/host/uhci-hcd.h index 8ae5ccd26753..0688c3e5bfe2 100644 --- a/drivers/usb/host/uhci-hcd.h +++ b/drivers/usb/host/uhci-hcd.h @@ -314,7 +314,7 @@ struct uhci_td { * * There's a special skeleton QH for Isochronous QHs which never appears * on the schedule. Isochronous TDs go on the schedule before the - * the skeleton QHs. The hardware accesses them directly rather than + * skeleton QHs. The hardware accesses them directly rather than * through their QH, which is used only for bookkeeping purposes. * While the UHCI spec doesn't forbid the use of QHs for Isochronous, * it doesn't use them either. And the spec says that queues never diff --git a/drivers/usb/host/xhci-mtk.c b/drivers/usb/host/xhci-mtk.c index b1045f534a4b..01705e559c42 100644 --- a/drivers/usb/host/xhci-mtk.c +++ b/drivers/usb/host/xhci-mtk.c @@ -18,6 +18,7 @@ #include #include #include +#include #include "xhci.h" #include "xhci-mtk.h" @@ -550,6 +551,12 @@ static int xhci_mtk_probe(struct platform_device *pdev) if (ret) goto disable_ldos; + ret = device_reset_optional(dev); + if (ret) { + dev_err_probe(dev, ret, "failed to reset controller\n"); + goto disable_clk; + } + hcd = usb_create_hcd(driver, dev, dev_name(dev)); if (!hcd) { ret = -ENOMEM; diff --git a/drivers/usb/host/xhci-pci-renesas.c b/drivers/usb/host/xhci-pci-renesas.c index 52599d96634f..93f8b355bc70 100644 --- a/drivers/usb/host/xhci-pci-renesas.c +++ b/drivers/usb/host/xhci-pci-renesas.c @@ -120,7 +120,6 @@ static int renesas_fw_verify(const void *fw_data, size_t length) { u16 fw_version_pointer; - u16 fw_version; /* * The Firmware's Data Format is describe in @@ -150,9 +149,6 @@ static int renesas_fw_verify(const void *fw_data, return -EINVAL; } - fw_version = get_unaligned_le16(fw_data + fw_version_pointer); - pr_err("got firmware version: %02x.", fw_version); - return 0; } diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 46d0b9ad6f74..ad81e9a508b1 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -1964,7 +1964,7 @@ static void handle_port_status(struct xhci_hcd *xhci, /* * Check to see if xhci-hub.c is waiting on RExit to U0 transition (or - * RExit to a disconnect state). If so, let the the driver know it's + * RExit to a disconnect state). If so, let the driver know it's * out of the RExit state. */ if (!DEV_SUPERSPEED_ANY(portsc) && hcd->speed < HCD_USB3 && diff --git a/drivers/usb/host/xhci-tegra.c b/drivers/usb/host/xhci-tegra.c index 996958a6565c..bdb776553826 100644 --- a/drivers/usb/host/xhci-tegra.c +++ b/drivers/usb/host/xhci-tegra.c @@ -1010,15 +1010,15 @@ static int tegra_xusb_powerdomain_init(struct device *dev, int err; tegra->genpd_dev_host = dev_pm_domain_attach_by_name(dev, "xusb_host"); - if (IS_ERR(tegra->genpd_dev_host)) { - err = PTR_ERR(tegra->genpd_dev_host); + if (IS_ERR_OR_NULL(tegra->genpd_dev_host)) { + err = PTR_ERR(tegra->genpd_dev_host) ? : -ENODATA; dev_err(dev, "failed to get host pm-domain: %d\n", err); return err; } tegra->genpd_dev_ss = dev_pm_domain_attach_by_name(dev, "xusb_ss"); - if (IS_ERR(tegra->genpd_dev_ss)) { - err = PTR_ERR(tegra->genpd_dev_ss); + if (IS_ERR_OR_NULL(tegra->genpd_dev_ss)) { + err = PTR_ERR(tegra->genpd_dev_ss) ? : -ENODATA; dev_err(dev, "failed to get superspeed pm-domain: %d\n", err); return err; } diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 28aaf031f9a8..1960b47acfb2 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -2417,7 +2417,7 @@ static inline const char *xhci_decode_trb(char *str, size_t size, field3 & TRB_CYCLE ? 'C' : 'c'); break; case TRB_STOP_RING: - sprintf(str, + snprintf(str, size, "%s: slot %d sp %d ep %d flags %c", xhci_trb_type_string(type), TRB_TO_SLOT_ID(field3), diff --git a/drivers/usb/image/mdc800.c b/drivers/usb/image/mdc800.c index fc0e22cc6fda..67f098579fb4 100644 --- a/drivers/usb/image/mdc800.c +++ b/drivers/usb/image/mdc800.c @@ -38,7 +38,7 @@ * * version 0.7.3 * bugfix : The mdc800->state field gets set to READY after the - * the disconnect function sets it to NOT_CONNECTED. This makes the + * disconnect function sets it to NOT_CONNECTED. This makes the * driver running like the camera is connected and causes some * hang ups. * diff --git a/drivers/usb/misc/Kconfig b/drivers/usb/misc/Kconfig index 4c5ddbd75b7e..9367c12c7e6f 100644 --- a/drivers/usb/misc/Kconfig +++ b/drivers/usb/misc/Kconfig @@ -295,3 +295,19 @@ config BRCM_USB_PINMAP This option enables support for remapping some USB external signals, which are typically on dedicated pins on the chip, to any gpio. + +config USB_ONBOARD_HUB + tristate "Onboard USB hub support" + depends on OF || COMPILE_TEST + help + Say Y here if you want to support discrete onboard USB hubs that + don't require an additional control bus for initialization, but + need some non-trivial form of initialization, such as enabling a + power regulator. An example for such a hub is the Realtek + RTS5411. + + This driver can be used as a module but its state (module vs + builtin) must match the state of the USB subsystem. Enabling + this config will enable the driver and it will automatically + match the state of the USB subsystem. If this driver is a + module it will be called onboard_usb_hub. diff --git a/drivers/usb/misc/Makefile b/drivers/usb/misc/Makefile index 35bdb4b6c3b6..93581baec3a8 100644 --- a/drivers/usb/misc/Makefile +++ b/drivers/usb/misc/Makefile @@ -33,3 +33,4 @@ obj-$(CONFIG_USB_CHAOSKEY) += chaoskey.o obj-$(CONFIG_USB_SISUSBVGA) += sisusbvga/ obj-$(CONFIG_USB_LINK_LAYER_TEST) += lvstest.o obj-$(CONFIG_BRCM_USB_PINMAP) += brcmstb-usb-pinmap.o +obj-$(CONFIG_USB_ONBOARD_HUB) += onboard_usb_hub.o diff --git a/drivers/usb/misc/ldusb.c b/drivers/usb/misc/ldusb.c index dcc88df72df4..7cbef74dfc9a 100644 --- a/drivers/usb/misc/ldusb.c +++ b/drivers/usb/misc/ldusb.c @@ -716,9 +716,11 @@ static int ld_usb_probe(struct usb_interface *intf, const struct usb_device_id * dev->interrupt_out_urb = usb_alloc_urb(0, GFP_KERNEL); if (!dev->interrupt_out_urb) goto error; - dev->interrupt_in_interval = min_interrupt_in_interval > dev->interrupt_in_endpoint->bInterval ? min_interrupt_in_interval : dev->interrupt_in_endpoint->bInterval; + dev->interrupt_in_interval = max_t(int, min_interrupt_in_interval, + dev->interrupt_in_endpoint->bInterval); if (dev->interrupt_out_endpoint) - dev->interrupt_out_interval = min_interrupt_out_interval > dev->interrupt_out_endpoint->bInterval ? min_interrupt_out_interval : dev->interrupt_out_endpoint->bInterval; + dev->interrupt_out_interval = max_t(int, min_interrupt_out_interval, + dev->interrupt_out_endpoint->bInterval); /* we can register the device now, as it is ready */ usb_set_intfdata(intf, dev); diff --git a/drivers/usb/misc/onboard_usb_hub.c b/drivers/usb/misc/onboard_usb_hub.c new file mode 100644 index 000000000000..d1df153e7f5a --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.c @@ -0,0 +1,458 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Driver for onboard USB hubs + * + * Copyright (c) 2022, Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "onboard_usb_hub.h" + +static struct usb_device_driver onboard_hub_usbdev_driver; + +/************************** Platform driver **************************/ + +struct usbdev_node { + struct usb_device *udev; + struct list_head list; +}; + +struct onboard_hub { + struct regulator *vdd; + struct device *dev; + const struct onboard_hub_pdata *pdata; + struct gpio_desc *reset_gpio; + bool always_powered_in_suspend; + bool is_powered_on; + bool going_away; + struct list_head udev_list; + struct work_struct attach_usb_driver_work; + struct mutex lock; +}; + +static int onboard_hub_power_on(struct onboard_hub *hub) +{ + int err; + + err = regulator_enable(hub->vdd); + if (err) { + dev_err(hub->dev, "failed to enable regulator: %d\n", err); + return err; + } + + fsleep(hub->pdata->reset_us); + gpiod_set_value_cansleep(hub->reset_gpio, 0); + + hub->is_powered_on = true; + + return 0; +} + +static int onboard_hub_power_off(struct onboard_hub *hub) +{ + int err; + + if (hub->reset_gpio) { + gpiod_set_value_cansleep(hub->reset_gpio, 1); + fsleep(hub->pdata->reset_us); + } + + err = regulator_disable(hub->vdd); + if (err) { + dev_err(hub->dev, "failed to disable regulator: %d\n", err); + return err; + } + + hub->is_powered_on = false; + + return 0; +} + +static int __maybe_unused onboard_hub_suspend(struct device *dev) +{ + struct onboard_hub *hub = dev_get_drvdata(dev); + struct usbdev_node *node; + bool power_off = true; + + if (hub->always_powered_in_suspend) + return 0; + + mutex_lock(&hub->lock); + + list_for_each_entry(node, &hub->udev_list, list) { + if (!device_may_wakeup(node->udev->bus->controller)) + continue; + + if (usb_wakeup_enabled_descendants(node->udev)) { + power_off = false; + break; + } + } + + mutex_unlock(&hub->lock); + + if (!power_off) + return 0; + + return onboard_hub_power_off(hub); +} + +static int __maybe_unused onboard_hub_resume(struct device *dev) +{ + struct onboard_hub *hub = dev_get_drvdata(dev); + + if (hub->is_powered_on) + return 0; + + return onboard_hub_power_on(hub); +} + +static inline void get_udev_link_name(const struct usb_device *udev, char *buf, size_t size) +{ + snprintf(buf, size, "usb_dev.%s", dev_name(&udev->dev)); +} + +static int onboard_hub_add_usbdev(struct onboard_hub *hub, struct usb_device *udev) +{ + struct usbdev_node *node; + char link_name[64]; + int err; + + mutex_lock(&hub->lock); + + if (hub->going_away) { + err = -EINVAL; + goto error; + } + + node = kzalloc(sizeof(*node), GFP_KERNEL); + if (!node) { + err = -ENOMEM; + goto error; + } + + node->udev = udev; + + list_add(&node->list, &hub->udev_list); + + mutex_unlock(&hub->lock); + + get_udev_link_name(udev, link_name, sizeof(link_name)); + WARN_ON(sysfs_create_link(&hub->dev->kobj, &udev->dev.kobj, link_name)); + + return 0; + +error: + mutex_unlock(&hub->lock); + + return err; +} + +static void onboard_hub_remove_usbdev(struct onboard_hub *hub, const struct usb_device *udev) +{ + struct usbdev_node *node; + char link_name[64]; + + get_udev_link_name(udev, link_name, sizeof(link_name)); + sysfs_remove_link(&hub->dev->kobj, link_name); + + mutex_lock(&hub->lock); + + list_for_each_entry(node, &hub->udev_list, list) { + if (node->udev == udev) { + list_del(&node->list); + kfree(node); + break; + } + } + + mutex_unlock(&hub->lock); +} + +static ssize_t always_powered_in_suspend_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + const struct onboard_hub *hub = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", hub->always_powered_in_suspend); +} + +static ssize_t always_powered_in_suspend_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct onboard_hub *hub = dev_get_drvdata(dev); + bool val; + int ret; + + ret = kstrtobool(buf, &val); + if (ret < 0) + return ret; + + hub->always_powered_in_suspend = val; + + return count; +} +static DEVICE_ATTR_RW(always_powered_in_suspend); + +static struct attribute *onboard_hub_attrs[] = { + &dev_attr_always_powered_in_suspend.attr, + NULL, +}; +ATTRIBUTE_GROUPS(onboard_hub); + +static void onboard_hub_attach_usb_driver(struct work_struct *work) +{ + int err; + + err = driver_attach(&onboard_hub_usbdev_driver.drvwrap.driver); + if (err) + pr_err("Failed to attach USB driver: %d\n", err); +} + +static int onboard_hub_probe(struct platform_device *pdev) +{ + const struct of_device_id *of_id; + struct device *dev = &pdev->dev; + struct onboard_hub *hub; + int err; + + hub = devm_kzalloc(dev, sizeof(*hub), GFP_KERNEL); + if (!hub) + return -ENOMEM; + + of_id = of_match_device(onboard_hub_match, &pdev->dev); + if (!of_id) + return -ENODEV; + + hub->pdata = of_id->data; + if (!hub->pdata) + return -EINVAL; + + hub->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(hub->vdd)) + return PTR_ERR(hub->vdd); + + hub->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(hub->reset_gpio)) + return dev_err_probe(dev, PTR_ERR(hub->reset_gpio), "failed to get reset GPIO\n"); + + hub->dev = dev; + mutex_init(&hub->lock); + INIT_LIST_HEAD(&hub->udev_list); + + dev_set_drvdata(dev, hub); + + err = onboard_hub_power_on(hub); + if (err) + return err; + + /* + * The USB driver might have been detached from the USB devices by + * onboard_hub_remove() (e.g. through an 'unbind' by userspace), + * make sure to re-attach it if needed. + * + * This needs to be done deferred to avoid self-deadlocks on systems + * with nested onboard hubs. + */ + INIT_WORK(&hub->attach_usb_driver_work, onboard_hub_attach_usb_driver); + schedule_work(&hub->attach_usb_driver_work); + + return 0; +} + +static int onboard_hub_remove(struct platform_device *pdev) +{ + struct onboard_hub *hub = dev_get_drvdata(&pdev->dev); + struct usbdev_node *node; + struct usb_device *udev; + + hub->going_away = true; + + if (&hub->attach_usb_driver_work != current_work()) + cancel_work_sync(&hub->attach_usb_driver_work); + + mutex_lock(&hub->lock); + + /* unbind the USB devices to avoid dangling references to this device */ + while (!list_empty(&hub->udev_list)) { + node = list_first_entry(&hub->udev_list, struct usbdev_node, list); + udev = node->udev; + + /* + * Unbinding the driver will call onboard_hub_remove_usbdev(), + * which acquires hub->lock. We must release the lock first. + */ + get_device(&udev->dev); + mutex_unlock(&hub->lock); + device_release_driver(&udev->dev); + put_device(&udev->dev); + mutex_lock(&hub->lock); + } + + mutex_unlock(&hub->lock); + + return onboard_hub_power_off(hub); +} + +MODULE_DEVICE_TABLE(of, onboard_hub_match); + +static const struct dev_pm_ops __maybe_unused onboard_hub_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(onboard_hub_suspend, onboard_hub_resume) +}; + +static struct platform_driver onboard_hub_driver = { + .probe = onboard_hub_probe, + .remove = onboard_hub_remove, + + .driver = { + .name = "onboard-usb-hub", + .of_match_table = onboard_hub_match, + .pm = pm_ptr(&onboard_hub_pm_ops), + .dev_groups = onboard_hub_groups, + }, +}; + +/************************** USB driver **************************/ + +#define VENDOR_ID_MICROCHIP 0x0424 +#define VENDOR_ID_REALTEK 0x0bda +#define VENDOR_ID_TI 0x0451 + +/* + * Returns the onboard_hub platform device that is associated with the USB + * device passed as parameter. + */ +static struct onboard_hub *_find_onboard_hub(struct device *dev) +{ + struct platform_device *pdev; + struct device_node *np; + struct onboard_hub *hub; + + pdev = of_find_device_by_node(dev->of_node); + if (!pdev) { + np = of_parse_phandle(dev->of_node, "peer-hub", 0); + if (!np) { + dev_err(dev, "failed to find device node for peer hub\n"); + return ERR_PTR(-EINVAL); + } + + pdev = of_find_device_by_node(np); + of_node_put(np); + + if (!pdev) + return ERR_PTR(-ENODEV); + } + + hub = dev_get_drvdata(&pdev->dev); + put_device(&pdev->dev); + + /* + * The presence of drvdata ('hub') indicates that the platform driver + * finished probing. This handles the case where (conceivably) we could + * be running at the exact same time as the platform driver's probe. If + * we detect the race we request probe deferral and we'll come back and + * try again. + */ + if (!hub) + return ERR_PTR(-EPROBE_DEFER); + + return hub; +} + +static int onboard_hub_usbdev_probe(struct usb_device *udev) +{ + struct device *dev = &udev->dev; + struct onboard_hub *hub; + int err; + + /* ignore supported hubs without device tree node */ + if (!dev->of_node) + return -ENODEV; + + hub = _find_onboard_hub(dev); + if (IS_ERR(hub)) + return PTR_ERR(hub); + + dev_set_drvdata(dev, hub); + + err = onboard_hub_add_usbdev(hub, udev); + if (err) + return err; + + return 0; +} + +static void onboard_hub_usbdev_disconnect(struct usb_device *udev) +{ + struct onboard_hub *hub = dev_get_drvdata(&udev->dev); + + onboard_hub_remove_usbdev(hub, udev); +} + +static const struct usb_device_id onboard_hub_id_table[] = { + { USB_DEVICE(VENDOR_ID_MICROCHIP, 0x2514) }, /* USB2514B USB 2.0 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x0411) }, /* RTS5411 USB 3.1 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x5411) }, /* RTS5411 USB 2.1 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x0414) }, /* RTS5414 USB 3.2 */ + { USB_DEVICE(VENDOR_ID_REALTEK, 0x5414) }, /* RTS5414 USB 2.1 */ + { USB_DEVICE(VENDOR_ID_TI, 0x8140) }, /* TI USB8041 3.0 */ + { USB_DEVICE(VENDOR_ID_TI, 0x8142) }, /* TI USB8041 2.0 */ + {} +}; +MODULE_DEVICE_TABLE(usb, onboard_hub_id_table); + +static struct usb_device_driver onboard_hub_usbdev_driver = { + .name = "onboard-usb-hub", + .probe = onboard_hub_usbdev_probe, + .disconnect = onboard_hub_usbdev_disconnect, + .generic_subclass = 1, + .supports_autosuspend = 1, + .id_table = onboard_hub_id_table, +}; + +static int __init onboard_hub_init(void) +{ + int ret; + + ret = platform_driver_register(&onboard_hub_driver); + if (ret) + return ret; + + ret = usb_register_device_driver(&onboard_hub_usbdev_driver, THIS_MODULE); + if (ret) + platform_driver_unregister(&onboard_hub_driver); + + return ret; +} +module_init(onboard_hub_init); + +static void __exit onboard_hub_exit(void) +{ + usb_deregister_device_driver(&onboard_hub_usbdev_driver); + platform_driver_unregister(&onboard_hub_driver); +} +module_exit(onboard_hub_exit); + +MODULE_AUTHOR("Matthias Kaehlcke "); +MODULE_DESCRIPTION("Driver for discrete onboard USB hubs"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/misc/onboard_usb_hub.h b/drivers/usb/misc/onboard_usb_hub.h new file mode 100644 index 000000000000..34beab8bce3d --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (c) 2022, Google LLC + */ + +#ifndef _USB_MISC_ONBOARD_USB_HUB_H +#define _USB_MISC_ONBOARD_USB_HUB_H + +struct onboard_hub_pdata { + unsigned long reset_us; /* reset pulse width in us */ +}; + +static const struct onboard_hub_pdata microchip_usb424_data = { + .reset_us = 1, +}; + +static const struct onboard_hub_pdata realtek_rts5411_data = { + .reset_us = 0, +}; + +static const struct onboard_hub_pdata ti_tusb8041_data = { + .reset_us = 3000, +}; + +static const struct of_device_id onboard_hub_match[] = { + { .compatible = "usb424,2514", .data = µchip_usb424_data, }, + { .compatible = "usb451,8140", .data = &ti_tusb8041_data, }, + { .compatible = "usb451,8142", .data = &ti_tusb8041_data, }, + { .compatible = "usbbda,411", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,5411", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,414", .data = &realtek_rts5411_data, }, + { .compatible = "usbbda,5414", .data = &realtek_rts5411_data, }, + {} +}; + +#endif /* _USB_MISC_ONBOARD_USB_HUB_H */ diff --git a/drivers/usb/misc/onboard_usb_hub_pdevs.c b/drivers/usb/misc/onboard_usb_hub_pdevs.c new file mode 100644 index 000000000000..ed22a18f4ab7 --- /dev/null +++ b/drivers/usb/misc/onboard_usb_hub_pdevs.c @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * API for creating and destroying USB onboard hub platform devices + * + * Copyright (c) 2022, Google LLC + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "onboard_usb_hub.h" + +struct pdev_list_entry { + struct platform_device *pdev; + struct list_head node; +}; + +static bool of_is_onboard_usb_hub(const struct device_node *np) +{ + return !!of_match_node(onboard_hub_match, np); +} + +/** + * onboard_hub_create_pdevs -- create platform devices for onboard USB hubs + * @parent_hub : parent hub to scan for connected onboard hubs + * @pdev_list : list of onboard hub platform devices owned by the parent hub + * + * Creates a platform device for each supported onboard hub that is connected to + * the given parent hub. The platform device is in charge of initializing the + * hub (enable regulators, take the hub out of reset, ...) and can optionally + * control whether the hub remains powered during system suspend or not. + * + * To keep track of the platform devices they are added to a list that is owned + * by the parent hub. + * + * Some background about the logic in this function, which can be a bit hard + * to follow: + * + * Root hubs don't have dedicated device tree nodes, but use the node of their + * HCD. The primary and secondary HCD are usually represented by a single DT + * node. That means the root hubs of the primary and secondary HCD share the + * same device tree node (the HCD node). As a result this function can be called + * twice with the same DT node for root hubs. We only want to create a single + * platform device for each physical onboard hub, hence for root hubs the loop + * is only executed for the root hub of the primary HCD. Since the function + * scans through all child nodes it still creates pdevs for onboard hubs + * connected to the root hub of the secondary HCD if needed. + * + * Further there must be only one platform device for onboard hubs with a peer + * hub (the hub is a single physical device). To achieve this two measures are + * taken: pdevs for onboard hubs with a peer are only created when the function + * is called on behalf of the parent hub that is connected to the primary HCD + * (directly or through other hubs). For onboard hubs connected to root hubs + * the function processes the nodes of both peers. A platform device is only + * created if the peer hub doesn't have one already. + */ +void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list) +{ + int i; + struct usb_hcd *hcd = bus_to_hcd(parent_hub->bus); + struct device_node *np, *npc; + struct platform_device *pdev; + struct pdev_list_entry *pdle; + + if (!parent_hub->dev.of_node) + return; + + if (!parent_hub->parent && !usb_hcd_is_primary_hcd(hcd)) + return; + + for (i = 1; i <= parent_hub->maxchild; i++) { + np = usb_of_get_device_node(parent_hub, i); + if (!np) + continue; + + if (!of_is_onboard_usb_hub(np)) + goto node_put; + + npc = of_parse_phandle(np, "peer-hub", 0); + if (npc) { + if (!usb_hcd_is_primary_hcd(hcd)) { + of_node_put(npc); + goto node_put; + } + + pdev = of_find_device_by_node(npc); + of_node_put(npc); + + if (pdev) { + put_device(&pdev->dev); + goto node_put; + } + } + + pdev = of_platform_device_create(np, NULL, &parent_hub->dev); + if (!pdev) { + dev_err(&parent_hub->dev, + "failed to create platform device for onboard hub '%pOF'\n", np); + goto node_put; + } + + pdle = kzalloc(sizeof(*pdle), GFP_KERNEL); + if (!pdle) { + of_platform_device_destroy(&pdev->dev, NULL); + goto node_put; + } + + pdle->pdev = pdev; + list_add(&pdle->node, pdev_list); + +node_put: + of_node_put(np); + } +} +EXPORT_SYMBOL_GPL(onboard_hub_create_pdevs); + +/** + * onboard_hub_destroy_pdevs -- free resources of onboard hub platform devices + * @pdev_list : list of onboard hub platform devices + * + * Destroys the platform devices in the given list and frees the memory associated + * with the list entry. + */ +void onboard_hub_destroy_pdevs(struct list_head *pdev_list) +{ + struct pdev_list_entry *pdle, *tmp; + + list_for_each_entry_safe(pdle, tmp, pdev_list, node) { + list_del(&pdle->node); + of_platform_device_destroy(&pdle->pdev->dev, NULL); + kfree(pdle); + } +} +EXPORT_SYMBOL_GPL(onboard_hub_destroy_pdevs); diff --git a/drivers/usb/misc/usbsevseg.c b/drivers/usb/misc/usbsevseg.c index 4bc816bb09bb..c3114d9bd128 100644 --- a/drivers/usb/misc/usbsevseg.c +++ b/drivers/usb/misc/usbsevseg.c @@ -167,7 +167,7 @@ static ssize_t text_show(struct device *dev, struct usb_interface *intf = to_usb_interface(dev); struct usb_sevsegdev *mydev = usb_get_intfdata(intf); - return snprintf(buf, mydev->textlength, "%s\n", mydev->text); + return sysfs_emit(buf, "%s\n", mydev->text); } static ssize_t text_store(struct device *dev, diff --git a/drivers/usb/misc/usbtest.c b/drivers/usb/misc/usbtest.c index 150090ee4ec1..ac0d75ac2d2f 100644 --- a/drivers/usb/misc/usbtest.c +++ b/drivers/usb/misc/usbtest.c @@ -2638,7 +2638,7 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) * different busses) to use when testing, and allocate one thread per * test. So discovery is simplified, and we have no device naming issues. * - * Don't use these only as stress/load tests. Use them along with with + * Don't use these only as stress/load tests. Use them along with * other USB bus activity: plugging, unplugging, mousing, mp3 playback, * video capture, and so on. Run different tests at different times, in * different sequences. Nothing here should interact with other devices, diff --git a/drivers/usb/mtu3/mtu3.h b/drivers/usb/mtu3/mtu3.h index 022bbdc54e68..2d7b57e07eee 100644 --- a/drivers/usb/mtu3/mtu3.h +++ b/drivers/usb/mtu3/mtu3.h @@ -317,6 +317,7 @@ static inline struct ssusb_mtk *dev_to_ssusb(struct device *dev) * @ep0_req: dummy request used while handling standard USB requests * for GET_STATUS and SET_SEL * @setup_buf: ep0 response buffer for GET_STATUS and SET_SEL requests + * @u3_capable: is capable of supporting USB3 */ struct mtu3 { spinlock_t lock; @@ -353,10 +354,12 @@ struct mtu3 { unsigned softconnect:1; unsigned u1_enable:1; unsigned u2_enable:1; - unsigned is_u3_ip:1; + unsigned u3_capable:1; unsigned delayed_status:1; unsigned gen2cp:1; unsigned connected:1; + unsigned async_callbacks:1; + unsigned separate_fifo:1; u8 address; u8 test_mode_nr; diff --git a/drivers/usb/mtu3/mtu3_core.c b/drivers/usb/mtu3/mtu3_core.c index c4a2c37abf62..0ca173af87bb 100644 --- a/drivers/usb/mtu3/mtu3_core.c +++ b/drivers/usb/mtu3/mtu3_core.c @@ -100,7 +100,7 @@ static int mtu3_device_enable(struct mtu3 *mtu) mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); - if (mtu->is_u3_ip) { + if (mtu->u3_capable) { check_clk = SSUSB_U3_MAC_RST_B_STS; mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), (SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN | @@ -112,7 +112,7 @@ static int mtu3_device_enable(struct mtu3 *mtu) if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) { mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_setbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_DUAL_MODE); } @@ -124,7 +124,7 @@ static void mtu3_device_disable(struct mtu3 *mtu) { void __iomem *ibase = mtu->ippc_base; - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_setbits(ibase, SSUSB_U3_CTRL(0), (SSUSB_U3_PORT_DIS | SSUSB_U3_PORT_PDN)); @@ -133,7 +133,7 @@ static void mtu3_device_disable(struct mtu3 *mtu) if (mtu->ssusb->dr_mode == USB_DR_MODE_OTG) { mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_OTG_SEL); - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_DUAL_MODE); } @@ -146,7 +146,7 @@ static void mtu3_dev_power_on(struct mtu3 *mtu) void __iomem *ibase = mtu->ippc_base; mtu3_clrbits(ibase, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_clrbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); mtu3_clrbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); @@ -156,7 +156,7 @@ static void mtu3_dev_power_down(struct mtu3 *mtu) { void __iomem *ibase = mtu->ippc_base; - if (mtu->is_u3_ip) + if (mtu->u3_capable) mtu3_setbits(ibase, SSUSB_U3_CTRL(0), SSUSB_U3_PORT_PDN); mtu3_setbits(ibase, SSUSB_U2_CTRL(0), SSUSB_U2_PORT_PDN); @@ -213,7 +213,7 @@ static void mtu3_intr_enable(struct mtu3 *mtu) value = SUSPEND_INTR | RESUME_INTR | RESET_INTR; mtu3_writel(mbase, U3D_COMMON_USB_INTR_ENABLE, value); - if (mtu->is_u3_ip) { + if (mtu->u3_capable) { /* Enable U3 LTSSM interrupts */ value = HOT_RST_INTR | WARM_RST_INTR | ENTER_U3_INTR | EXIT_U3_INTR; @@ -273,7 +273,7 @@ static void mtu3_csr_init(struct mtu3 *mtu) { void __iomem *mbase = mtu->mac_base; - if (mtu->is_u3_ip) { + if (mtu->u3_capable) { /* disable LGO_U1/U2 by default */ mtu3_clrbits(mbase, U3D_LINK_POWER_CONTROL, SW_U1_REQUEST_ENABLE | SW_U2_REQUEST_ENABLE); @@ -341,7 +341,7 @@ void mtu3_ep_stall_set(struct mtu3_ep *mep, bool set) void mtu3_dev_on_off(struct mtu3 *mtu, int is_on) { - if (mtu->is_u3_ip && mtu->speed >= USB_SPEED_SUPER) + if (mtu->u3_capable && mtu->speed >= USB_SPEED_SUPER) mtu3_ss_func_set(mtu, is_on); else mtu3_hs_softconn_set(mtu, is_on); @@ -544,7 +544,7 @@ static void get_ep_fifo_config(struct mtu3 *mtu) struct mtu3_fifo_info *rx_fifo; u32 fifosize; - if (mtu->is_u3_ip) { + if (mtu->separate_fifo) { fifosize = mtu3_readl(mtu->mac_base, U3D_CAP_EPNTXFFSZ); tx_fifo = &mtu->tx_fifo; tx_fifo->base = 0; @@ -821,6 +821,10 @@ static irqreturn_t mtu3_irq(int irq, void *data) static void mtu3_check_params(struct mtu3 *mtu) { + /* device's u3 port (port0) is disabled */ + if (mtu->u3_capable && (mtu->ssusb->u3p_dis_msk & BIT(0))) + mtu->u3_capable = 0; + /* check the max_speed parameter */ switch (mtu->max_speed) { case USB_SPEED_FULL: @@ -838,7 +842,7 @@ static void mtu3_check_params(struct mtu3 *mtu) break; } - if (!mtu->is_u3_ip && (mtu->max_speed > USB_SPEED_HIGH)) + if (!mtu->u3_capable && (mtu->max_speed > USB_SPEED_HIGH)) mtu->max_speed = USB_SPEED_HIGH; mtu->speed = mtu->max_speed; @@ -857,10 +861,12 @@ static int mtu3_hw_init(struct mtu3 *mtu) mtu->gen2cp = !!(mtu->hw_version >= MTU3_TRUNK_VERS_1003); value = mtu3_readl(mtu->ippc_base, U3D_SSUSB_IP_DEV_CAP); - mtu->is_u3_ip = !!SSUSB_IP_DEV_U3_PORT_NUM(value); + mtu->u3_capable = !!SSUSB_IP_DEV_U3_PORT_NUM(value); + /* usb3 ip uses separate fifo */ + mtu->separate_fifo = mtu->u3_capable; dev_info(mtu->dev, "IP version 0x%x(%s IP)\n", mtu->hw_version, - mtu->is_u3_ip ? "U3" : "U2"); + mtu->u3_capable ? "U3" : "U2"); mtu3_check_params(mtu); @@ -965,7 +971,8 @@ int ssusb_gadget_init(struct ssusb_mtk *ssusb) goto dma_mask_err; } - ret = devm_request_irq(dev, mtu->irq, mtu3_irq, 0, dev_name(dev), mtu); + ret = devm_request_threaded_irq(dev, mtu->irq, NULL, mtu3_irq, + IRQF_ONESHOT, dev_name(dev), mtu); if (ret) { dev_err(dev, "request irq %d failed!\n", mtu->irq); goto irq_err; diff --git a/drivers/usb/mtu3/mtu3_debugfs.c b/drivers/usb/mtu3/mtu3_debugfs.c index d27de647c86a..f0de99858353 100644 --- a/drivers/usb/mtu3/mtu3_debugfs.c +++ b/drivers/usb/mtu3/mtu3_debugfs.c @@ -101,13 +101,13 @@ static int mtu3_ep_used_show(struct seq_file *sf, void *unused) for (i = 0; i < mtu->num_eps; i++) { mep = mtu->in_eps + i; if (mep->flags & MTU3_EP_ENABLED) { - seq_printf(sf, "%s - type: %d\n", mep->name, mep->type); + seq_printf(sf, "%s - type: %s\n", mep->name, usb_ep_type_string(mep->type)); used++; } mep = mtu->out_eps + i; if (mep->flags & MTU3_EP_ENABLED) { - seq_printf(sf, "%s - type: %d\n", mep->name, mep->type); + seq_printf(sf, "%s - type: %s\n", mep->name, usb_ep_type_string(mep->type)); used++; } } @@ -177,8 +177,8 @@ static int mtu3_ep_info_show(struct seq_file *sf, void *unused) unsigned long flags; spin_lock_irqsave(&mtu->lock, flags); - seq_printf(sf, "ep - type:%d, maxp:%d, slot:%d, flags:%x\n", - mep->type, mep->maxp, mep->slot, mep->flags); + seq_printf(sf, "ep - type:%s, maxp:%d, slot:%d, flags:%x\n", + usb_ep_type_string(mep->type), mep->maxp, mep->slot, mep->flags); spin_unlock_irqrestore(&mtu->lock, flags); return 0; diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c index 9977600616d7..80236e7b0895 100644 --- a/drivers/usb/mtu3/mtu3_gadget.c +++ b/drivers/usb/mtu3/mtu3_gadget.c @@ -433,6 +433,13 @@ static int mtu3_gadget_get_frame(struct usb_gadget *gadget) return (int)mtu3_readl(mtu->mac_base, U3D_USB20_FRAME_NUM); } +static void function_wake_notif(struct mtu3 *mtu, u8 intf) +{ + mtu3_writel(mtu->mac_base, U3D_DEV_NOTIF_0, + TYPE_FUNCTION_WAKE | DEV_NOTIF_VAL_FW(intf)); + mtu3_setbits(mtu->mac_base, U3D_DEV_NOTIF_0, SEND_DEV_NOTIF); +} + static int mtu3_gadget_wakeup(struct usb_gadget *gadget) { struct mtu3 *mtu = gadget_to_mtu3(gadget); @@ -446,7 +453,18 @@ static int mtu3_gadget_wakeup(struct usb_gadget *gadget) spin_lock_irqsave(&mtu->lock, flags); if (mtu->g.speed >= USB_SPEED_SUPER) { + /* + * class driver may do function wakeup even UFP is in U0, + * and UX_EXIT only takes effect in U1/U2/U3; + */ mtu3_setbits(mtu->mac_base, U3D_LINK_POWER_CONTROL, UX_EXIT); + /* + * Assume there's only one function on the composite device + * and enable remote wake for the first interface. + * FIXME if the IAD (interface association descriptor) shows + * there is more than one function. + */ + function_wake_notif(mtu, 0); } else { mtu3_setbits(mtu->mac_base, U3D_POWER_MANAGEMENT, RESUME); spin_unlock_irqrestore(&mtu->lock, flags); @@ -592,6 +610,18 @@ mtu3_gadget_set_speed(struct usb_gadget *g, enum usb_device_speed speed) spin_unlock_irqrestore(&mtu->lock, flags); } +static void mtu3_gadget_async_callbacks(struct usb_gadget *g, bool enable) +{ + struct mtu3 *mtu = gadget_to_mtu3(g); + unsigned long flags; + + dev_dbg(mtu->dev, "%s %s\n", __func__, enable ? "en" : "dis"); + + spin_lock_irqsave(&mtu->lock, flags); + mtu->async_callbacks = enable; + spin_unlock_irqrestore(&mtu->lock, flags); +} + static const struct usb_gadget_ops mtu3_gadget_ops = { .get_frame = mtu3_gadget_get_frame, .wakeup = mtu3_gadget_wakeup, @@ -600,6 +630,7 @@ static const struct usb_gadget_ops mtu3_gadget_ops = { .udc_start = mtu3_gadget_start, .udc_stop = mtu3_gadget_stop, .udc_set_speed = mtu3_gadget_set_speed, + .udc_async_callbacks = mtu3_gadget_async_callbacks, }; static void mtu3_state_reset(struct mtu3 *mtu) @@ -680,6 +711,7 @@ int mtu3_gadget_setup(struct mtu3 *mtu) mtu->g.speed = USB_SPEED_UNKNOWN; mtu->g.sg_supported = 0; mtu->g.name = MTU3_DRIVER_NAME; + mtu->g.irq = mtu->irq; mtu->is_active = 0; mtu->delayed_status = false; @@ -696,7 +728,7 @@ void mtu3_gadget_cleanup(struct mtu3 *mtu) void mtu3_gadget_resume(struct mtu3 *mtu) { dev_dbg(mtu->dev, "gadget RESUME\n"); - if (mtu->gadget_driver && mtu->gadget_driver->resume) { + if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->resume) { spin_unlock(&mtu->lock); mtu->gadget_driver->resume(&mtu->g); spin_lock(&mtu->lock); @@ -707,7 +739,7 @@ void mtu3_gadget_resume(struct mtu3 *mtu) void mtu3_gadget_suspend(struct mtu3 *mtu) { dev_dbg(mtu->dev, "gadget SUSPEND\n"); - if (mtu->gadget_driver && mtu->gadget_driver->suspend) { + if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->suspend) { spin_unlock(&mtu->lock); mtu->gadget_driver->suspend(&mtu->g); spin_lock(&mtu->lock); @@ -718,7 +750,7 @@ void mtu3_gadget_suspend(struct mtu3 *mtu) void mtu3_gadget_disconnect(struct mtu3 *mtu) { dev_dbg(mtu->dev, "gadget DISCONNECT\n"); - if (mtu->gadget_driver && mtu->gadget_driver->disconnect) { + if (mtu->async_callbacks && mtu->gadget_driver && mtu->gadget_driver->disconnect) { spin_unlock(&mtu->lock); mtu->gadget_driver->disconnect(&mtu->g); spin_lock(&mtu->lock); diff --git a/drivers/usb/mtu3/mtu3_gadget_ep0.c b/drivers/usb/mtu3/mtu3_gadget_ep0.c index 0ca47212f1ec..e4fd1bb14a55 100644 --- a/drivers/usb/mtu3/mtu3_gadget_ep0.c +++ b/drivers/usb/mtu3/mtu3_gadget_ep0.c @@ -66,7 +66,7 @@ __acquires(mtu->lock) { int ret; - if (!mtu->gadget_driver) + if (!mtu->gadget_driver || !mtu->async_callbacks) return -EOPNOTSUPP; spin_unlock(&mtu->lock); @@ -226,6 +226,8 @@ ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) break; case USB_RECIP_INTERFACE: + /* status of function remote wakeup, forward request */ + handled = 0; break; case USB_RECIP_ENDPOINT: epnum = (u8) le16_to_cpu(setup->wIndex); @@ -397,10 +399,8 @@ static int ep0_handle_feature(struct mtu3 *mtu, /* superspeed only */ if (value == USB_INTRF_FUNC_SUSPEND && mtu->g.speed >= USB_SPEED_SUPER) { - /* - * forward the request because function drivers - * should handle it - */ + /* forward the request for function suspend */ + mtu->may_wakeup = !!(index & USB_INTRF_FUNC_SUSPEND_RW); handled = 0; } break; diff --git a/drivers/usb/mtu3/mtu3_hw_regs.h b/drivers/usb/mtu3/mtu3_hw_regs.h index 072db1f6470e..519a58301f45 100644 --- a/drivers/usb/mtu3/mtu3_hw_regs.h +++ b/drivers/usb/mtu3/mtu3_hw_regs.h @@ -341,6 +341,8 @@ #define U3D_LINK_UX_INACT_TIMER (SSUSB_USB3_SYS_CSR_BASE + 0x020C) #define U3D_LINK_POWER_CONTROL (SSUSB_USB3_SYS_CSR_BASE + 0x0210) #define U3D_LINK_ERR_COUNT (SSUSB_USB3_SYS_CSR_BASE + 0x0214) +#define U3D_DEV_NOTIF_0 (SSUSB_USB3_SYS_CSR_BASE + 0x0290) +#define U3D_DEV_NOTIF_1 (SSUSB_USB3_SYS_CSR_BASE + 0x0294) /*---------------- SSUSB_USB3_SYS_CSR FIELD DEFINITION ----------------*/ @@ -365,6 +367,20 @@ #define CLR_LINK_ERR_CNT BIT(16) #define LINK_ERROR_COUNT GENMASK(15, 0) +/* U3D_DEV_NOTIF_0 */ +#define DEV_NOTIF_TYPE_SPECIFIC_LOW_MSK GENMASK(31, 8) +#define DEV_NOTIF_VAL_FW(x) (((x) & 0xff) << 8) +#define DEV_NOTIF_VAL_LTM(x) (((x) & 0xfff) << 8) +#define DEV_NOTIF_VAL_IAM(x) (((x) & 0xffff) << 8) +#define DEV_NOTIF_TYPE_MSK GENMASK(7, 4) +/* Notification Type */ +#define TYPE_FUNCTION_WAKE (0x1 << 4) +#define TYPE_LATENCY_TOLERANCE_MESSAGE (0x2 << 4) +#define TYPE_BUS_INTERVAL_ADJUST_MESSAGE (0x3 << 4) +#define TYPE_HOST_ROLE_REQUEST (0x4 << 4) +#define TYPE_SUBLINK_SPEED (0x5 << 4) +#define SEND_DEV_NOTIF BIT(0) + /*---------------- SSUSB_USB2_CSR REGISTER DEFINITION ----------------*/ #define U3D_POWER_MANAGEMENT (SSUSB_USB2_CSR_BASE + 0x0004) diff --git a/drivers/usb/mtu3/mtu3_plat.c b/drivers/usb/mtu3/mtu3_plat.c index 4309ed939178..4cb65346789d 100644 --- a/drivers/usb/mtu3/mtu3_plat.c +++ b/drivers/usb/mtu3/mtu3_plat.c @@ -13,6 +13,7 @@ #include #include #include +#include #include "mtu3.h" #include "mtu3_dr.h" @@ -189,6 +190,31 @@ static void ssusb_ip_sw_reset(struct ssusb_mtk *ssusb) mtu3_setbits(ssusb->ippc_base, U3D_SSUSB_IP_PW_CTRL2, SSUSB_IP_DEV_PDN); } +static void ssusb_u3_drd_check(struct ssusb_mtk *ssusb) +{ + struct otg_switch_mtk *otg_sx = &ssusb->otg_switch; + u32 dev_u3p_num; + u32 host_u3p_num; + u32 value; + + /* u3 port0 is disabled */ + if (ssusb->u3p_dis_msk & BIT(0)) { + otg_sx->is_u3_drd = false; + goto out; + } + + value = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_DEV_CAP); + dev_u3p_num = SSUSB_IP_DEV_U3_PORT_NUM(value); + + value = mtu3_readl(ssusb->ippc_base, U3D_SSUSB_IP_XHCI_CAP); + host_u3p_num = SSUSB_IP_XHCI_U3_PORT_NUM(value); + + otg_sx->is_u3_drd = !!(dev_u3p_num && host_u3p_num); + +out: + dev_info(ssusb->dev, "usb3-drd: %d\n", otg_sx->is_u3_drd); +} + static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) { struct device_node *node = pdev->dev.of_node; @@ -243,6 +269,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) if (ssusb->dr_mode == USB_DR_MODE_UNKNOWN) ssusb->dr_mode = USB_DR_MODE_OTG; + of_property_read_u32(node, "mediatek,u3p-dis-msk", &ssusb->u3p_dis_msk); + if (ssusb->dr_mode == USB_DR_MODE_PERIPHERAL) goto out; @@ -254,8 +282,6 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } /* optional property, ignore the error if it does not exist */ - of_property_read_u32(node, "mediatek,u3p-dis-msk", - &ssusb->u3p_dis_msk); of_property_read_u32(node, "mediatek,u2p-dis-msk", &ssusb->u2p_dis_msk); @@ -269,7 +295,6 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) goto out; /* if dual-role mode is supported */ - otg_sx->is_u3_drd = of_property_read_bool(node, "mediatek,usb3-drd"); otg_sx->manual_drd_enabled = of_property_read_bool(node, "enable-manual-drd"); otg_sx->role_sw_used = of_property_read_bool(node, "usb-role-switch"); @@ -289,9 +314,8 @@ static int get_ssusb_rscs(struct platform_device *pdev, struct ssusb_mtk *ssusb) } out: - dev_info(dev, "dr_mode: %d, is_u3_dr: %d, drd: %s\n", - ssusb->dr_mode, otg_sx->is_u3_drd, - otg_sx->manual_drd_enabled ? "manual" : "auto"); + dev_info(dev, "dr_mode: %d, drd: %s\n", ssusb->dr_mode, + otg_sx->manual_drd_enabled ? "manual" : "auto"); dev_info(dev, "u2p_dis_msk: %x, u3p_dis_msk: %x\n", ssusb->u2p_dis_msk, ssusb->u3p_dis_msk); @@ -345,7 +369,14 @@ static int mtu3_probe(struct platform_device *pdev) dev_info(dev, "wakeup irq %d\n", ssusb->wakeup_irq); } + ret = device_reset_optional(dev); + if (ret) { + dev_err_probe(dev, ret, "failed to reset controller\n"); + goto comm_exit; + } + ssusb_ip_sw_reset(ssusb); + ssusb_u3_drd_check(ssusb); if (IS_ENABLED(CONFIG_USB_MTU3_HOST)) ssusb->dr_mode = USB_DR_MODE_HOST; diff --git a/drivers/usb/mtu3/mtu3_trace.h b/drivers/usb/mtu3/mtu3_trace.h index 1b897636daf2..a09deae1146f 100644 --- a/drivers/usb/mtu3/mtu3_trace.h +++ b/drivers/usb/mtu3/mtu3_trace.h @@ -238,8 +238,8 @@ DECLARE_EVENT_CLASS(mtu3_log_ep, __entry->direction = mep->is_in; __entry->gpd_ring = &mep->gpd_ring; ), - TP_printk("%s: type %d maxp %d slot %d mult %d burst %d ring %p/%pad flags %c:%c%c%c:%c", - __get_str(name), __entry->type, + TP_printk("%s: type %s maxp %d slot %d mult %d burst %d ring %p/%pad flags %c:%c%c%c:%c", + __get_str(name), usb_ep_type_string(__entry->type), __entry->maxp, __entry->slot, __entry->mult, __entry->maxburst, __entry->gpd_ring, &__entry->gpd_ring->dma, diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig index 4d61df6a9b5c..f906dfd360d3 100644 --- a/drivers/usb/musb/Kconfig +++ b/drivers/usb/musb/Kconfig @@ -123,6 +123,17 @@ config USB_MUSB_MEDIATEK select GENERIC_PHY select USB_ROLE_SWITCH +config USB_MUSB_POLARFIRE_SOC + tristate "Microchip PolarFire SoC platforms" + depends on SOC_MICROCHIP_POLARFIRE || COMPILE_TEST + depends on NOP_USB_XCEIV + select USB_MUSB_DUAL_ROLE + help + Say Y here to enable support for USB on Microchip's PolarFire SoC. + + This support is also available as a module. If so, the module + will be called mpfs. + comment "MUSB DMA mode" config MUSB_PIO_ONLY @@ -146,7 +157,7 @@ config USB_UX500_DMA config USB_INVENTRA_DMA bool 'Inventra' - depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740 + depends on USB_MUSB_OMAP2PLUS || USB_MUSB_MEDIATEK || USB_MUSB_JZ4740 || USB_MUSB_POLARFIRE_SOC help Enable DMA transfers using Mentor's engine. diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile index 932247360a9f..51dd54a8de49 100644 --- a/drivers/usb/musb/Makefile +++ b/drivers/usb/musb/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_USB_MUSB_UX500) += ux500.o obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o obj-$(CONFIG_USB_MUSB_SUNXI) += sunxi.o obj-$(CONFIG_USB_MUSB_MEDIATEK) += mediatek.o +obj-$(CONFIG_USB_MUSB_POLARFIRE_SOC) += mpfs.o # the kconfig must guarantee that only one of the # possible I/O schemes will be enabled at a time ... diff --git a/drivers/usb/musb/mpfs.c b/drivers/usb/musb/mpfs.c new file mode 100644 index 000000000000..cea2e8108867 --- /dev/null +++ b/drivers/usb/musb/mpfs.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * PolarFire SoC (MPFS) MUSB Glue Layer + * + * Copyright (c) 2020-2022 Microchip Corporation. All rights reserved. + * Based on {omap2430,tusb6010,ux500}.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "musb_core.h" +#include "musb_dma.h" + +#define MPFS_MUSB_MAX_EP_NUM 8 +#define MPFS_MUSB_RAM_BITS 12 + +struct mpfs_glue { + struct device *dev; + struct platform_device *musb; + struct platform_device *phy; + struct clk *clk; +}; + +static struct musb_fifo_cfg mpfs_musb_mode_cfg[] = { + { .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 2, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_TX, .maxpacket = 512, }, + { .hw_ep_num = 3, .style = FIFO_RX, .maxpacket = 512, }, + { .hw_ep_num = 4, .style = FIFO_TX, .maxpacket = 1024, }, + { .hw_ep_num = 4, .style = FIFO_RX, .maxpacket = 4096, }, +}; + +static const struct musb_hdrc_config mpfs_musb_hdrc_config = { + .fifo_cfg = mpfs_musb_mode_cfg, + .fifo_cfg_size = ARRAY_SIZE(mpfs_musb_mode_cfg), + .multipoint = true, + .dyn_fifo = true, + .num_eps = MPFS_MUSB_MAX_EP_NUM, + .ram_bits = MPFS_MUSB_RAM_BITS, +}; + +static irqreturn_t mpfs_musb_interrupt(int irq, void *__hci) +{ + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + struct musb *musb = __hci; + + spin_lock_irqsave(&musb->lock, flags); + + musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB); + musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX); + musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX); + + if (musb->int_usb || musb->int_tx || musb->int_rx) { + musb_writeb(musb->mregs, MUSB_INTRUSB, musb->int_usb); + musb_writew(musb->mregs, MUSB_INTRTX, musb->int_tx); + musb_writew(musb->mregs, MUSB_INTRRX, musb->int_rx); + ret = musb_interrupt(musb); + } + + spin_unlock_irqrestore(&musb->lock, flags); + + return ret; +} + +static void mpfs_musb_set_vbus(struct musb *musb, int is_on) +{ + u8 devctl; + + /* + * HDRC controls CPEN, but beware current surges during device + * connect. They can trigger transient overcurrent conditions + * that must be ignored. + */ + devctl = musb_readb(musb->mregs, MUSB_DEVCTL); + + if (is_on) { + musb->is_active = 1; + musb->xceiv->otg->default_a = 1; + musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE; + devctl |= MUSB_DEVCTL_SESSION; + MUSB_HST_MODE(musb); + } else { + musb->is_active = 0; + + /* + * NOTE: skipping A_WAIT_VFALL -> A_IDLE and + * jumping right to B_IDLE... + */ + musb->xceiv->otg->default_a = 0; + musb->xceiv->otg->state = OTG_STATE_B_IDLE; + devctl &= ~MUSB_DEVCTL_SESSION; + + MUSB_DEV_MODE(musb); + } + + musb_writeb(musb->mregs, MUSB_DEVCTL, devctl); + + dev_dbg(musb->controller, "VBUS %s, devctl %02x\n", + usb_otg_state_string(musb->xceiv->otg->state), + musb_readb(musb->mregs, MUSB_DEVCTL)); +} + +static int mpfs_musb_init(struct musb *musb) +{ + struct device *dev = musb->controller; + + musb->xceiv = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2); + if (IS_ERR(musb->xceiv)) { + dev_err(dev, "HS UDC: no transceiver configured\n"); + return PTR_ERR(musb->xceiv); + } + + musb->dyn_fifo = true; + musb->isr = mpfs_musb_interrupt; + + musb_platform_set_vbus(musb, 1); + + return 0; +} + +static const struct musb_platform_ops mpfs_ops = { + .quirks = MUSB_DMA_INVENTRA, + .init = mpfs_musb_init, + .fifo_mode = 2, +#ifdef CONFIG_USB_INVENTRA_DMA + .dma_init = musbhs_dma_controller_create, + .dma_exit = musbhs_dma_controller_destroy, +#endif + .set_vbus = mpfs_musb_set_vbus +}; + +static int mpfs_probe(struct platform_device *pdev) +{ + struct musb_hdrc_platform_data *pdata = dev_get_platdata(&pdev->dev); + struct mpfs_glue *glue; + struct platform_device *musb_pdev; + struct device *dev = &pdev->dev; + struct clk *clk; + int ret; + + glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL); + if (!glue) + return -ENOMEM; + + musb_pdev = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO); + if (!musb_pdev) { + dev_err(dev, "failed to allocate musb device\n"); + return -ENOMEM; + } + + clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(clk)) { + dev_err(&pdev->dev, "failed to get clock\n"); + ret = PTR_ERR(clk); + goto err_phy_release; + } + + ret = clk_prepare_enable(clk); + if (ret) { + dev_err(&pdev->dev, "failed to enable clock\n"); + goto err_phy_release; + } + + musb_pdev->dev.parent = dev; + musb_pdev->dev.coherent_dma_mask = DMA_BIT_MASK(39); + musb_pdev->dev.dma_mask = &musb_pdev->dev.coherent_dma_mask; + device_set_of_node_from_dev(&musb_pdev->dev, dev); + + glue->dev = dev; + glue->musb = musb_pdev; + glue->clk = clk; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) { + ret = -ENOMEM; + goto err_clk_disable; + } + + pdata->config = &mpfs_musb_hdrc_config; + pdata->platform_ops = &mpfs_ops; + + pdata->mode = usb_get_dr_mode(dev); + if (pdata->mode == USB_DR_MODE_UNKNOWN) { + dev_info(dev, "No dr_mode property found, defaulting to otg\n"); + pdata->mode = USB_DR_MODE_OTG; + } + + glue->phy = usb_phy_generic_register(); + if (IS_ERR(glue->phy)) { + dev_err(dev, "failed to register usb-phy %ld\n", + PTR_ERR(glue->phy)); + ret = PTR_ERR(glue->phy); + goto err_clk_disable; + } + + platform_set_drvdata(pdev, glue); + + ret = platform_device_add_resources(musb_pdev, pdev->resource, pdev->num_resources); + if (ret) { + dev_err(dev, "failed to add resources\n"); + goto err_clk_disable; + } + + ret = platform_device_add_data(musb_pdev, pdata, sizeof(*pdata)); + if (ret) { + dev_err(dev, "failed to add platform_data\n"); + goto err_clk_disable; + } + + ret = platform_device_add(musb_pdev); + if (ret) { + dev_err(dev, "failed to register musb device\n"); + goto err_clk_disable; + } + + dev_info(&pdev->dev, "Registered MPFS MUSB driver\n"); + return 0; + +err_clk_disable: + clk_disable_unprepare(clk); + +err_phy_release: + usb_phy_generic_unregister(glue->phy); + platform_device_put(musb_pdev); + return ret; +} + +static int mpfs_remove(struct platform_device *pdev) +{ + struct mpfs_glue *glue = platform_get_drvdata(pdev); + + clk_disable_unprepare(glue->clk); + platform_device_unregister(glue->musb); + usb_phy_generic_unregister(pdev); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id mpfs_id_table[] = { + { .compatible = "microchip,mpfs-musb" }, + { } +}; +MODULE_DEVICE_TABLE(of, mpfs_id_table); +#endif + +static struct platform_driver mpfs_musb_driver = { + .probe = mpfs_probe, + .remove = mpfs_remove, + .driver = { + .name = "mpfs-musb", + .of_match_table = of_match_ptr(mpfs_id_table) + }, +}; + +module_platform_driver(mpfs_musb_driver); + +MODULE_DESCRIPTION("PolarFire SoC MUSB Glue Layer"); +MODULE_LICENSE("GPL"); diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index f7b1d5993f8c..bbbcfd49fb35 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -2684,13 +2684,7 @@ static void musb_save_context(struct musb *musb) musb->context.devctl = musb_readb(musb_base, MUSB_DEVCTL); for (i = 0; i < musb->config->num_eps; ++i) { - struct musb_hw_ep *hw_ep; - - hw_ep = &musb->endpoints[i]; - if (!hw_ep) - continue; - - epio = hw_ep->regs; + epio = musb->endpoints[i].regs; if (!epio) continue; @@ -2765,13 +2759,7 @@ static void musb_restore_context(struct musb *musb) musb_writeb(musb_base, MUSB_DEVCTL, musb->context.devctl); for (i = 0; i < musb->config->num_eps; ++i) { - struct musb_hw_ep *hw_ep; - - hw_ep = &musb->endpoints[i]; - if (!hw_ep) - continue; - - epio = hw_ep->regs; + epio = musb->endpoints[i].regs; if (!epio) continue; diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c index 7fbb8a307145..c963cb8565f2 100644 --- a/drivers/usb/musb/musb_cppi41.c +++ b/drivers/usb/musb/musb_cppi41.c @@ -286,7 +286,7 @@ static void cppi41_dma_callback(void *private_data, * receive a FIFO empty interrupt so the only thing we can do is * to poll for the bit. On HS it usually takes 2us, on FS around * 110us - 150us depending on the transfer size. - * We spin on HS (no longer than than 25us and setup a timer on + * We spin on HS (no longer than 25us and setup a timer on * FS to check for the bit and complete the transfer. */ if (is_host_active(musb)) { diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 51274b87f46c..daada4b66a92 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -1910,8 +1910,6 @@ static int musb_gadget_stop(struct usb_gadget *g) */ /* Force check of devctl register for PM runtime */ - schedule_delayed_work(&musb->irq_work, 0); - pm_runtime_mark_last_busy(musb->controller); pm_runtime_put_autosuspend(musb->controller); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index 7ed4cc348d99..5609b4e84d40 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -495,7 +495,7 @@ done: } /* - * Maybe put TUSB6010 into idle mode mode depending on USB link status, + * Maybe put TUSB6010 into idle mode depending on USB link status, * like "disconnected" or "suspended". We'll be woken out of it by * connect, resume, or disconnect. * diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c index 358d05cb643d..f75912279b39 100644 --- a/drivers/usb/phy/phy-keystone.c +++ b/drivers/usb/phy/phy-keystone.c @@ -59,7 +59,7 @@ static void keystone_usbphy_shutdown(struct usb_phy *phy) val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK); keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK, - val &= ~PHY_REF_SSP_EN); + val & ~PHY_REF_SSP_EN); } static int keystone_usbphy_probe(struct platform_device *pdev) diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c index 24de64edb674..2d77edefb4b3 100644 --- a/drivers/usb/renesas_usbhs/rza.c +++ b/drivers/usb/renesas_usbhs/rza.c @@ -23,6 +23,10 @@ static int usbhs_rza1_hardware_init(struct platform_device *pdev) extal_clk = of_find_node_by_name(NULL, "extal"); of_property_read_u32(usb_x1_clk, "clock-frequency", &freq_usb); of_property_read_u32(extal_clk, "clock-frequency", &freq_extal); + + of_node_put(usb_x1_clk); + of_node_put(extal_clk); + if (freq_usb == 0) { if (freq_extal == 12000000) { /* Select 12MHz XTAL */ diff --git a/drivers/usb/serial/cypress_m8.c b/drivers/usb/serial/cypress_m8.c index 6924fa95f6bd..5fbcc155e8f5 100644 --- a/drivers/usb/serial/cypress_m8.c +++ b/drivers/usb/serial/cypress_m8.c @@ -256,7 +256,7 @@ static int analyze_baud_rate(struct usb_serial_port *port, speed_t new_rate) /* * Mike Isely 2-Feb-2008: The * Cypress app note that describes this mechanism - * states the the low-speed part can't handle more + * states that the low-speed part can't handle more * than 800 bytes/sec, in which case 4800 baud is the * safest speed for a part like that. */ diff --git a/drivers/usb/serial/garmin_gps.c b/drivers/usb/serial/garmin_gps.c index e5c75944ebb7..f1a8d8343623 100644 --- a/drivers/usb/serial/garmin_gps.c +++ b/drivers/usb/serial/garmin_gps.c @@ -988,7 +988,7 @@ static int garmin_write_bulk(struct usb_serial_port *port, garmin_data_p->flags &= ~FLAGS_DROP_DATA; spin_unlock_irqrestore(&garmin_data_p->lock, flags); - buffer = kmalloc(count, GFP_ATOMIC); + buffer = kmemdup(buf, count, GFP_ATOMIC); if (!buffer) return -ENOMEM; @@ -998,8 +998,6 @@ static int garmin_write_bulk(struct usb_serial_port *port, return -ENOMEM; } - memcpy(buffer, buf, count); - usb_serial_debug_data(&port->dev, __func__, count, buffer); usb_fill_bulk_urb(urb, serial->dev, diff --git a/drivers/usb/serial/io_edgeport.c b/drivers/usb/serial/io_edgeport.c index bdee78cc4a07..ffa622539a25 100644 --- a/drivers/usb/serial/io_edgeport.c +++ b/drivers/usb/serial/io_edgeport.c @@ -220,7 +220,7 @@ struct edgeport_serial { __u8 rxHeader3; /* receive header byte 3 */ __u8 rxPort; /* the port that we are currently receiving data for */ __u8 rxStatusCode; /* the receive status code */ - __u8 rxStatusParam; /* the receive status paramater */ + __u8 rxStatusParam; /* the receive status parameter */ __s16 rxBytesRemaining; /* the number of port bytes left to read */ struct usb_serial *serial; /* loop back to the owner of this object */ }; @@ -901,7 +901,7 @@ static int edge_open(struct tty_struct *tty, struct usb_serial_port *port) if (!edge_port->open) { /* open timed out */ - dev_dbg(dev, "%s - open timedout\n", __func__); + dev_dbg(dev, "%s - open timeout\n", __func__); edge_port->openPending = false; return -ENODEV; } diff --git a/drivers/usb/serial/mos7720.c b/drivers/usb/serial/mos7720.c index 1e12b5f30dcc..23ccbba716c7 100644 --- a/drivers/usb/serial/mos7720.c +++ b/drivers/usb/serial/mos7720.c @@ -826,7 +826,7 @@ static int mos77xx_calc_num_ports(struct usb_serial *serial, /* * The 7715 uses the first bulk in/out endpoint pair for the * parallel port, and the second for the serial port. We swap - * the endpoint descriptors here so that the the first and + * the endpoint descriptors here so that the first and * only registered port structure uses the serial-port * endpoints. */ diff --git a/drivers/usb/serial/opticon.c b/drivers/usb/serial/opticon.c index aed28c35caff..e31a6d77da3a 100644 --- a/drivers/usb/serial/opticon.c +++ b/drivers/usb/serial/opticon.c @@ -208,7 +208,7 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, priv->outstanding_bytes += count; spin_unlock_irqrestore(&priv->lock, flags); - buffer = kmalloc(count, GFP_ATOMIC); + buffer = kmemdup(buf, count, GFP_ATOMIC); if (!buffer) goto error_no_buffer; @@ -216,8 +216,6 @@ static int opticon_write(struct tty_struct *tty, struct usb_serial_port *port, if (!urb) goto error_no_urb; - memcpy(buffer, buf, count); - usb_serial_debug_data(&port->dev, __func__, count, buffer); /* The connected devices do not have a bulk write endpoint, diff --git a/drivers/usb/serial/sierra.c b/drivers/usb/serial/sierra.c index 9d56138133a9..353b2549eaa8 100644 --- a/drivers/usb/serial/sierra.c +++ b/drivers/usb/serial/sierra.c @@ -453,7 +453,7 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, goto error_simple; } - buffer = kmalloc(writesize, GFP_ATOMIC); + buffer = kmemdup(buf, writesize, GFP_ATOMIC); if (!buffer) { retval = -ENOMEM; goto error_no_buffer; @@ -465,8 +465,6 @@ static int sierra_write(struct tty_struct *tty, struct usb_serial_port *port, goto error_no_urb; } - memcpy(buffer, buf, writesize); - usb_serial_debug_data(&port->dev, __func__, writesize, buffer); usb_fill_bulk_urb(urb, serial->dev, @@ -737,7 +735,8 @@ static void sierra_close(struct usb_serial_port *port) /* * Need to take susp_lock to make sure port is not already being - * resumed, but no need to hold it due to initialized + * resumed, but no need to hold it due to the tty-port initialized + * flag. */ spin_lock_irq(&intfdata->susp_lock); if (--intfdata->open_ports == 0) diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c index 24101bd7fcad..e35bea2235c1 100644 --- a/drivers/usb/serial/usb-serial.c +++ b/drivers/usb/serial/usb-serial.c @@ -295,7 +295,7 @@ static int serial_open(struct tty_struct *tty, struct file *filp) * * Shut down a USB serial port. Serialized against activate by the * tport mutex and kept to matching open/close pairs - * of calls by the initialized flag. + * of calls by the tty-port initialized flag. * * Not called if tty is console. */ diff --git a/drivers/usb/serial/usb_wwan.c b/drivers/usb/serial/usb_wwan.c index dab38b63eaf7..0017f6e969e1 100644 --- a/drivers/usb/serial/usb_wwan.c +++ b/drivers/usb/serial/usb_wwan.c @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include "usb-wwan.h" @@ -48,9 +49,9 @@ static int usb_wwan_send_setup(struct usb_serial_port *port) portdata = usb_get_serial_port_data(port); if (portdata->dtr_state) - val |= 0x01; + val |= USB_CDC_CTRL_DTR; if (portdata->rts_state) - val |= 0x02; + val |= USB_CDC_CTRL_RTS; ifnum = serial->interface->cur_altsetting->desc.bInterfaceNumber; @@ -59,8 +60,9 @@ static int usb_wwan_send_setup(struct usb_serial_port *port) return res; res = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0), - 0x22, 0x21, val, ifnum, NULL, 0, - USB_CTRL_SET_TIMEOUT); + USB_CDC_REQ_SET_CONTROL_LINE_STATE, + USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, + val, ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT); usb_autopm_put_interface(port->serial->interface); @@ -388,7 +390,8 @@ void usb_wwan_close(struct usb_serial_port *port) /* * Need to take susp_lock to make sure port is not already being - * resumed, but no need to hold it due to initialized + * resumed, but no need to hold it due to the tty-port initialized + * flag. */ spin_lock_irq(&intfdata->susp_lock); if (--intfdata->open_ports == 0) diff --git a/drivers/usb/storage/transport.c b/drivers/usb/storage/transport.c index 64d96d210e02..7449e379077a 100644 --- a/drivers/usb/storage/transport.c +++ b/drivers/usb/storage/transport.c @@ -1178,7 +1178,7 @@ int usb_stor_Bulk_transport(struct scsi_cmnd *srb, struct us_data *us) /* * If the device tried to send back more data than the * amount requested, the spec requires us to transfer - * the CSW anyway. Since there's no point retrying the + * the CSW anyway. Since there's no point retrying * the command, we'll return fake sense data indicating * Illegal Request, Invalid Field in CDB. */ diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig index ba24847fb245..5defdfead653 100644 --- a/drivers/usb/typec/Kconfig +++ b/drivers/usb/typec/Kconfig @@ -52,6 +52,17 @@ source "drivers/usb/typec/ucsi/Kconfig" source "drivers/usb/typec/tipd/Kconfig" +config TYPEC_ANX7411 + tristate "Analogix ANX7411 Type-C DRP Port controller driver" + depends on I2C + depends on USB_ROLE_SWITCH + help + Say Y or M here if your system has Analogix ANX7411 Type-C DRP Port + controller driver. + + If you choose to build this driver as a dynamically linked module, the + module will be called anx7411.ko. + config TYPEC_RT1719 tristate "Richtek RT1719 Sink Only Type-C controller driver" depends on USB_ROLE_SWITCH || !USB_ROLE_SWITCH diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 43626acc0aaf..4a83dad51a6c 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,11 +1,12 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o bus.o +typec-y := class.o mux.o bus.o pd.o retimer.o typec-$(CONFIG_ACPI) += port-mapper.o obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ obj-$(CONFIG_TYPEC_UCSI) += ucsi/ obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ +obj-$(CONFIG_TYPEC_ANX7411) += anx7411.o obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c new file mode 100644 index 000000000000..c0f0842d443c --- /dev/null +++ b/drivers/usb/typec/anx7411.c @@ -0,0 +1,1601 @@ +// SPDX-License-Identifier: GPL-2.0-only + +/* + * Driver for Analogix ANX7411 USB Type-C and PD controller + * + * Copyright(c) 2022, Analogix Semiconductor. All rights reserved. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define TCPC_ADDRESS1 0x58 +#define TCPC_ADDRESS2 0x56 +#define TCPC_ADDRESS3 0x54 +#define TCPC_ADDRESS4 0x52 +#define SPI_ADDRESS1 0x7e +#define SPI_ADDRESS2 0x6e +#define SPI_ADDRESS3 0x64 +#define SPI_ADDRESS4 0x62 + +struct anx7411_i2c_select { + u8 tcpc_address; + u8 spi_address; +}; + +#define VID_ANALOGIX 0x1F29 +#define PID_ANALOGIX 0x7411 + +/* TCPC register define */ + +#define ANALOG_CTRL_10 0xAA + +#define STATUS_LEN 2 +#define ALERT_0 0xCB +#define RECEIVED_MSG BIT(7) +#define SOFTWARE_INT BIT(6) +#define MSG_LEN 32 +#define HEADER_LEN 2 +#define MSG_HEADER 0x00 +#define MSG_TYPE 0x01 +#define MSG_RAWDATA 0x02 +#define MSG_LEN_MASK 0x1F + +#define ALERT_1 0xCC +#define INTP_POW_ON BIT(7) +#define INTP_POW_OFF BIT(6) + +#define VBUS_THRESHOLD_H 0xDD +#define VBUS_THRESHOLD_L 0xDE + +#define FW_CTRL_0 0xF0 +#define UNSTRUCT_VDM_EN BIT(0) +#define DELAY_200MS BIT(1) +#define VSAFE0 0 +#define VSAFE1 BIT(2) +#define VSAFE2 BIT(3) +#define VSAFE3 (BIT(2) | BIT(3)) +#define FRS_EN BIT(7) + +#define FW_PARAM 0xF1 +#define DONGLE_IOP BIT(0) + +#define FW_CTRL_2 0xF7 +#define SINK_CTRL_DIS_FLAG BIT(5) + +/* SPI register define */ +#define OCM_CTRL_0 0x6E +#define OCM_RESET BIT(6) + +#define MAX_VOLTAGE 0xAC +#define MAX_POWER 0xAD +#define MIN_POWER 0xAE + +#define REQUEST_VOLTAGE 0xAF +#define VOLTAGE_UNIT 100 /* mV per unit */ + +#define REQUEST_CURRENT 0xB1 +#define CURRENT_UNIT 50 /* mA per unit */ + +#define CMD_SEND_BUF 0xC0 +#define CMD_RECV_BUF 0xE0 + +#define REQ_VOL_20V_IN_100MV 0xC8 +#define REQ_CUR_2_25A_IN_50MA 0x2D +#define REQ_CUR_3_25A_IN_50MA 0x41 + +#define DEF_5V 5000 +#define DEF_1_5A 1500 + +#define LOBYTE(w) ((u8)((w) & 0xFF)) +#define HIBYTE(w) ((u8)(((u16)(w) >> 8) & 0xFF)) + +enum anx7411_typec_message_type { + TYPE_SRC_CAP = 0x00, + TYPE_SNK_CAP = 0x01, + TYPE_SNK_IDENTITY = 0x02, + TYPE_SVID = 0x03, + TYPE_SET_SNK_DP_CAP = 0x08, + TYPE_PSWAP_REQ = 0x10, + TYPE_DSWAP_REQ = 0x11, + TYPE_VDM = 0x14, + TYPE_OBJ_REQ = 0x16, + TYPE_DP_ALT_ENTER = 0x19, + TYPE_DP_DISCOVER_MODES_INFO = 0x27, + TYPE_GET_DP_CONFIG = 0x29, + TYPE_DP_CONFIGURE = 0x2A, + TYPE_GET_DP_DISCOVER_MODES_INFO = 0x2E, + TYPE_GET_DP_ALT_ENTER = 0x2F, +}; + +#define FW_CTRL_1 0xB2 +#define AUTO_PD_EN BIT(1) +#define TRYSRC_EN BIT(2) +#define TRYSNK_EN BIT(3) +#define FORCE_SEND_RDO BIT(6) + +#define FW_VER 0xB4 +#define FW_SUBVER 0xB5 + +#define INT_MASK 0xB6 +#define INT_STS 0xB7 +#define OCM_BOOT_UP BIT(0) +#define OC_OV_EVENT BIT(1) +#define VCONN_CHANGE BIT(2) +#define VBUS_CHANGE BIT(3) +#define CC_STATUS_CHANGE BIT(4) +#define DATA_ROLE_CHANGE BIT(5) +#define PR_CONSUMER_GOT_POWER BIT(6) +#define HPD_STATUS_CHANGE BIT(7) + +#define SYSTEM_STSTUS 0xB8 +/* 0: SINK off; 1: SINK on */ +#define SINK_STATUS BIT(1) +/* 0: VCONN off; 1: VCONN on*/ +#define VCONN_STATUS BIT(2) +/* 0: vbus off; 1: vbus on*/ +#define VBUS_STATUS BIT(3) +/* 1: host; 0:device*/ +#define DATA_ROLE BIT(5) +/* 0: Chunking; 1: Unchunked*/ +#define SUPPORT_UNCHUNKING BIT(6) +/* 0: HPD low; 1: HPD high*/ +#define HPD_STATUS BIT(7) + +#define DATA_DFP 1 +#define DATA_UFP 2 +#define POWER_SOURCE 1 +#define POWER_SINK 2 + +#define CC_STATUS 0xB9 +#define CC1_RD BIT(0) +#define CC2_RD BIT(4) +#define CC1_RA BIT(1) +#define CC2_RA BIT(5) +#define CC1_RD BIT(0) +#define CC1_RP(cc) (((cc) >> 2) & 0x03) +#define CC2_RP(cc) (((cc) >> 6) & 0x03) + +#define PD_REV_INIT 0xBA + +#define PD_EXT_MSG_CTRL 0xBB +#define SRC_CAP_EXT_REPLY BIT(0) +#define MANUFACTURER_INFO_REPLY BIT(1) +#define BATTERY_STS_REPLY BIT(2) +#define BATTERY_CAP_REPLY BIT(3) +#define ALERT_REPLY BIT(4) +#define STATUS_REPLY BIT(5) +#define PPS_STATUS_REPLY BIT(6) +#define SNK_CAP_EXT_REPLY BIT(7) + +#define NO_CONNECT 0x00 +#define USB3_1_CONNECTED 0x01 +#define DP_ALT_4LANES 0x02 +#define USB3_1_DP_2LANES 0x03 +#define CC1_CONNECTED 0x01 +#define CC2_CONNECTED 0x02 +#define SELECT_PIN_ASSIGMENT_C 0x04 +#define SELECT_PIN_ASSIGMENT_D 0x08 +#define SELECT_PIN_ASSIGMENT_E 0x10 +#define SELECT_PIN_ASSIGMENT_U 0x00 +#define REDRIVER_ADDRESS 0x20 +#define REDRIVER_OFFSET 0x00 + +#define DP_SVID 0xFF01 +#define VDM_ACK 0x40 +#define VDM_CMD_RES 0x00 +#define VDM_CMD_DIS_ID 0x01 +#define VDM_CMD_DIS_SVID 0x02 +#define VDM_CMD_DIS_MOD 0x03 +#define VDM_CMD_ENTER_MODE 0x04 +#define VDM_CMD_EXIT_MODE 0x05 +#define VDM_CMD_ATTENTION 0x06 +#define VDM_CMD_GET_STS 0x10 +#define VDM_CMD_AND_ACK_MASK 0x5F + +#define MAX_ALTMODE 2 + +#define HAS_SOURCE_CAP BIT(0) +#define HAS_SINK_CAP BIT(1) +#define HAS_SINK_WATT BIT(2) + +enum anx7411_psy_state { + /* copy from drivers/usb/typec/tcpm */ + ANX7411_PSY_OFFLINE = 0, + ANX7411_PSY_FIXED_ONLINE, + + /* private */ + /* PD keep in, but disconnct power to bq25700, + * this state can be active when higher capacity adapter plug in, + * and change to ONLINE state when higher capacity adapter plug out + */ + ANX7411_PSY_HANG = 0xff, +}; + +struct typec_params { + int request_current; /* ma */ + int request_voltage; /* mv */ + int cc_connect; + int cc_orientation_valid; + int cc_status; + int data_role; + int power_role; + int vconn_role; + int dp_altmode_enter; + int cust_altmode_enter; + struct usb_role_switch *role_sw; + struct typec_port *port; + struct typec_partner *partner; + struct typec_mux_dev *typec_mux; + struct typec_switch_dev *typec_switch; + struct typec_altmode *amode[MAX_ALTMODE]; + struct typec_altmode *port_amode[MAX_ALTMODE]; + struct typec_displayport_data data; + int pin_assignment; + struct typec_capability caps; + u32 src_pdo[PDO_MAX_OBJECTS]; + u32 sink_pdo[PDO_MAX_OBJECTS]; + u8 caps_flags; + u8 src_pdo_nr; + u8 sink_pdo_nr; + u8 sink_watt; + u8 sink_voltage; +}; + +#define MAX_BUF_LEN 30 +struct fw_msg { + u8 msg_len; + u8 msg_type; + u8 buf[MAX_BUF_LEN]; +} __packed; + +struct anx7411_data { + int fw_version; + int fw_subversion; + struct i2c_client *tcpc_client; + struct i2c_client *spi_client; + struct fw_msg send_msg; + struct fw_msg recv_msg; + struct gpio_desc *intp_gpiod; + struct fwnode_handle *connector_fwnode; + struct typec_params typec; + int intp_irq; + struct work_struct work; + struct workqueue_struct *workqueue; + /* Lock for interrupt work queue */ + struct mutex lock; + + enum anx7411_psy_state psy_online; + enum power_supply_usb_type usb_type; + struct power_supply *psy; + struct power_supply_desc psy_desc; + struct device *dev; +}; + +static u8 snk_identity[] = { + LOBYTE(VID_ANALOGIX), HIBYTE(VID_ANALOGIX), 0x00, 0x82, /* snk_id_hdr */ + 0x00, 0x00, 0x00, 0x00, /* snk_cert */ + 0x00, 0x00, LOBYTE(PID_ANALOGIX), HIBYTE(PID_ANALOGIX), /* 5snk_ama */ +}; + +static u8 dp_caps[4] = {0xC6, 0x00, 0x00, 0x00}; + +static int anx7411_reg_read(struct i2c_client *client, + u8 reg_addr) +{ + return i2c_smbus_read_byte_data(client, reg_addr); +} + +static int anx7411_reg_block_read(struct i2c_client *client, + u8 reg_addr, u8 len, u8 *buf) +{ + return i2c_smbus_read_i2c_block_data(client, reg_addr, len, buf); +} + +static int anx7411_reg_write(struct i2c_client *client, + u8 reg_addr, u8 reg_val) +{ + return i2c_smbus_write_byte_data(client, reg_addr, reg_val); +} + +static int anx7411_reg_block_write(struct i2c_client *client, + u8 reg_addr, u8 len, u8 *buf) +{ + return i2c_smbus_write_i2c_block_data(client, reg_addr, len, buf); +} + +static struct anx7411_i2c_select anx7411_i2c_addr[] = { + {TCPC_ADDRESS1, SPI_ADDRESS1}, + {TCPC_ADDRESS2, SPI_ADDRESS2}, + {TCPC_ADDRESS3, SPI_ADDRESS3}, + {TCPC_ADDRESS4, SPI_ADDRESS4}, +}; + +static int anx7411_detect_power_mode(struct anx7411_data *ctx) +{ + int ret; + int mode; + + ret = anx7411_reg_read(ctx->spi_client, REQUEST_CURRENT); + if (ret < 0) + return ret; + + ctx->typec.request_current = ret * CURRENT_UNIT; /* 50ma per unit */ + + ret = anx7411_reg_read(ctx->spi_client, REQUEST_VOLTAGE); + if (ret < 0) + return ret; + + ctx->typec.request_voltage = ret * VOLTAGE_UNIT; /* 100mv per unit */ + + if (ctx->psy_online == ANX7411_PSY_OFFLINE) { + ctx->psy_online = ANX7411_PSY_FIXED_ONLINE; + ctx->usb_type = POWER_SUPPLY_USB_TYPE_PD; + power_supply_changed(ctx->psy); + } + + if (!ctx->typec.cc_orientation_valid) + return 0; + + if (ctx->typec.cc_connect == CC1_CONNECTED) + mode = CC1_RP(ctx->typec.cc_status); + else + mode = CC2_RP(ctx->typec.cc_status); + if (mode) { + typec_set_pwr_opmode(ctx->typec.port, mode - 1); + return 0; + } + + typec_set_pwr_opmode(ctx->typec.port, TYPEC_PWR_MODE_PD); + + return 0; +} + +static int anx7411_register_partner(struct anx7411_data *ctx, + int pd, int accessory) +{ + struct typec_partner_desc desc; + struct typec_partner *partner; + + if (ctx->typec.partner) + return 0; + + desc.usb_pd = pd; + desc.accessory = accessory; + desc.identity = NULL; + partner = typec_register_partner(ctx->typec.port, &desc); + if (IS_ERR(partner)) + return PTR_ERR(partner); + + ctx->typec.partner = partner; + + return 0; +} + +static int anx7411_detect_cc_orientation(struct anx7411_data *ctx) +{ + struct device *dev = &ctx->spi_client->dev; + int ret; + int cc1_rd, cc2_rd; + int cc1_ra, cc2_ra; + int cc1_rp, cc2_rp; + + ret = anx7411_reg_read(ctx->spi_client, CC_STATUS); + if (ret < 0) + return ret; + + ctx->typec.cc_status = ret; + + cc1_rd = ret & CC1_RD ? 1 : 0; + cc2_rd = ret & CC2_RD ? 1 : 0; + cc1_ra = ret & CC1_RA ? 1 : 0; + cc2_ra = ret & CC2_RA ? 1 : 0; + cc1_rp = CC1_RP(ret); + cc2_rp = CC2_RP(ret); + + /* Debug cable, nothing to do */ + if (cc1_rd && cc2_rd) { + ctx->typec.cc_orientation_valid = 0; + return anx7411_register_partner(ctx, 0, TYPEC_ACCESSORY_DEBUG); + } + + if (cc1_ra && cc2_ra) { + ctx->typec.cc_orientation_valid = 0; + return anx7411_register_partner(ctx, 0, TYPEC_ACCESSORY_AUDIO); + } + + ctx->typec.cc_orientation_valid = 1; + + ret = anx7411_register_partner(ctx, 1, TYPEC_ACCESSORY_NONE); + if (ret) { + dev_err(dev, "register partner\n"); + return ret; + } + + if (cc1_rd || cc1_rp) { + typec_set_orientation(ctx->typec.port, TYPEC_ORIENTATION_NORMAL); + ctx->typec.cc_connect = CC1_CONNECTED; + } + + if (cc2_rd || cc2_rp) { + typec_set_orientation(ctx->typec.port, TYPEC_ORIENTATION_REVERSE); + ctx->typec.cc_connect = CC2_CONNECTED; + } + + return 0; +} + +static int anx7411_set_mux(struct anx7411_data *ctx, int pin_assignment) +{ + int mode = TYPEC_STATE_SAFE; + + switch (pin_assignment) { + case SELECT_PIN_ASSIGMENT_U: + /* default 4 line USB 3.1 */ + mode = TYPEC_STATE_MODAL; + break; + case SELECT_PIN_ASSIGMENT_C: + case SELECT_PIN_ASSIGMENT_E: + /* 4 line DP */ + mode = TYPEC_STATE_SAFE; + break; + case SELECT_PIN_ASSIGMENT_D: + /* 2 line DP, 2 line USB */ + mode = TYPEC_MODE_USB3; + break; + default: + mode = TYPEC_STATE_SAFE; + break; + } + + ctx->typec.pin_assignment = pin_assignment; + + return typec_set_mode(ctx->typec.port, mode); +} + +static int anx7411_set_usb_role(struct anx7411_data *ctx, enum usb_role role) +{ + if (!ctx->typec.role_sw) + return 0; + + return usb_role_switch_set_role(ctx->typec.role_sw, role); +} + +static int anx7411_data_role_detect(struct anx7411_data *ctx) +{ + int ret; + + ret = anx7411_reg_read(ctx->spi_client, SYSTEM_STSTUS); + if (ret < 0) + return ret; + + ctx->typec.data_role = (ret & DATA_ROLE) ? TYPEC_HOST : TYPEC_DEVICE; + ctx->typec.vconn_role = (ret & VCONN_STATUS) ? TYPEC_SOURCE : TYPEC_SINK; + + typec_set_data_role(ctx->typec.port, ctx->typec.data_role); + + typec_set_vconn_role(ctx->typec.port, ctx->typec.vconn_role); + + if (ctx->typec.data_role == TYPEC_HOST) + return anx7411_set_usb_role(ctx, USB_ROLE_HOST); + + return anx7411_set_usb_role(ctx, USB_ROLE_DEVICE); +} + +static int anx7411_power_role_detect(struct anx7411_data *ctx) +{ + int ret; + + ret = anx7411_reg_read(ctx->spi_client, SYSTEM_STSTUS); + if (ret < 0) + return ret; + + ctx->typec.power_role = (ret & SINK_STATUS) ? TYPEC_SINK : TYPEC_SOURCE; + + if (ctx->typec.power_role == TYPEC_SOURCE) { + ctx->typec.request_current = DEF_1_5A; + ctx->typec.request_voltage = DEF_5V; + } + + typec_set_pwr_role(ctx->typec.port, ctx->typec.power_role); + + return 0; +} + +static int anx7411_cc_status_detect(struct anx7411_data *ctx) +{ + anx7411_detect_cc_orientation(ctx); + anx7411_detect_power_mode(ctx); + + return 0; +} + +static void anx7411_partner_unregister_altmode(struct anx7411_data *ctx) +{ + int i; + + ctx->typec.dp_altmode_enter = 0; + ctx->typec.cust_altmode_enter = 0; + + for (i = 0; i < MAX_ALTMODE; i++) + if (ctx->typec.amode[i]) { + typec_unregister_altmode(ctx->typec.amode[i]); + ctx->typec.amode[i] = NULL; + } + + ctx->typec.pin_assignment = 0; +} + +static int anx7411_typec_register_altmode(struct anx7411_data *ctx, + int svid, int vdo) +{ + struct device *dev = &ctx->spi_client->dev; + struct typec_altmode_desc desc; + int err; + int i; + + desc.svid = svid; + desc.vdo = vdo; + + for (i = 0; i < MAX_ALTMODE; i++) + if (!ctx->typec.amode[i]) + break; + + desc.mode = i + 1; /* start with 1 */ + + if (i >= MAX_ALTMODE) { + dev_err(dev, "no altmode space for registering\n"); + return -ENOMEM; + } + + ctx->typec.amode[i] = typec_partner_register_altmode(ctx->typec.partner, + &desc); + if (IS_ERR(ctx->typec.amode[i])) { + dev_err(dev, "failed to register altmode\n"); + err = PTR_ERR(ctx->typec.amode[i]); + ctx->typec.amode[i] = NULL; + return err; + } + + return 0; +} + +static void anx7411_unregister_partner(struct anx7411_data *ctx) +{ + if (ctx->typec.partner) { + typec_unregister_partner(ctx->typec.partner); + ctx->typec.partner = NULL; + } +} + +static int anx7411_update_altmode(struct anx7411_data *ctx, int svid) +{ + int i; + + if (svid == DP_SVID) + ctx->typec.dp_altmode_enter = 1; + else + ctx->typec.cust_altmode_enter = 1; + + for (i = 0; i < MAX_ALTMODE; i++) { + if (!ctx->typec.amode[i]) + continue; + + if (ctx->typec.amode[i]->svid == svid) { + typec_altmode_update_active(ctx->typec.amode[i], true); + typec_altmode_notify(ctx->typec.amode[i], + ctx->typec.pin_assignment, + &ctx->typec.data); + break; + } + } + + return 0; +} + +static int anx7411_register_altmode(struct anx7411_data *ctx, + bool dp_altmode, u8 *buf) +{ + int ret; + int svid; + int mid; + + if (!ctx->typec.partner) + return 0; + + svid = DP_SVID; + if (dp_altmode) { + mid = buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24); + + return anx7411_typec_register_altmode(ctx, svid, mid); + } + + svid = (buf[3] << 8) | buf[2]; + if ((buf[0] & VDM_CMD_AND_ACK_MASK) != (VDM_ACK | VDM_CMD_ENTER_MODE)) + return anx7411_update_altmode(ctx, svid); + + if ((buf[0] & VDM_CMD_AND_ACK_MASK) != (VDM_ACK | VDM_CMD_DIS_MOD)) + return 0; + + mid = buf[4] | (buf[5] << 8) | (buf[6] << 16) | (buf[7] << 24); + + ret = anx7411_typec_register_altmode(ctx, svid, mid); + if (ctx->typec.cust_altmode_enter) + ret |= anx7411_update_altmode(ctx, svid); + + return ret; +} + +static int anx7411_parse_cmd(struct anx7411_data *ctx, u8 type, u8 *buf, u8 len) +{ + struct device *dev = &ctx->spi_client->dev; + u8 cur_50ma, vol_100mv; + + switch (type) { + case TYPE_SRC_CAP: + cur_50ma = anx7411_reg_read(ctx->spi_client, REQUEST_CURRENT); + vol_100mv = anx7411_reg_read(ctx->spi_client, REQUEST_VOLTAGE); + + ctx->typec.request_voltage = vol_100mv * VOLTAGE_UNIT; + ctx->typec.request_current = cur_50ma * CURRENT_UNIT; + + ctx->psy_online = ANX7411_PSY_FIXED_ONLINE; + ctx->usb_type = POWER_SUPPLY_USB_TYPE_PD; + power_supply_changed(ctx->psy); + break; + case TYPE_SNK_CAP: + break; + case TYPE_SVID: + break; + case TYPE_SNK_IDENTITY: + break; + case TYPE_GET_DP_ALT_ENTER: + /* DP alt mode enter success */ + if (buf[0]) + anx7411_update_altmode(ctx, DP_SVID); + break; + case TYPE_DP_ALT_ENTER: + /* Update DP altmode */ + anx7411_update_altmode(ctx, DP_SVID); + break; + case TYPE_OBJ_REQ: + anx7411_detect_power_mode(ctx); + break; + case TYPE_DP_CONFIGURE: + anx7411_set_mux(ctx, buf[1]); + break; + case TYPE_DP_DISCOVER_MODES_INFO: + /* Make sure discover modes valid */ + if (buf[0] | buf[1]) + /* Register DP Altmode */ + anx7411_register_altmode(ctx, 1, buf); + break; + case TYPE_VDM: + /* Register other altmode */ + anx7411_register_altmode(ctx, 0, buf); + break; + default: + dev_err(dev, "ignore message(0x%.02x).\n", type); + break; + } + + return 0; +} + +static u8 checksum(struct device *dev, u8 *buf, u8 len) +{ + u8 ret = 0; + u8 i; + + for (i = 0; i < len; i++) + ret += buf[i]; + + return ret; +} + +static int anx7411_read_msg_ctrl_status(struct i2c_client *client) +{ + return anx7411_reg_read(client, CMD_SEND_BUF); +} + +static int anx7411_wait_msg_empty(struct i2c_client *client) +{ + int val; + + return readx_poll_timeout(anx7411_read_msg_ctrl_status, + client, val, (val < 0) || (val == 0), + 2000, 2000 * 150); +} + +static int anx7411_send_msg(struct anx7411_data *ctx, u8 type, u8 *buf, u8 size) +{ + struct device *dev = &ctx->spi_client->dev; + struct fw_msg *msg = &ctx->send_msg; + u8 crc; + int ret; + + size = min_t(u8, size, (u8)MAX_BUF_LEN); + memcpy(msg->buf, buf, size); + msg->msg_type = type; + /* msg len equals buffer length + msg_type */ + msg->msg_len = size + 1; + + /* Do CRC check for all buffer data and msg_len and msg_type */ + crc = checksum(dev, (u8 *)msg, size + HEADER_LEN); + msg->buf[size] = 0 - crc; + + ret = anx7411_wait_msg_empty(ctx->spi_client); + if (ret) + return ret; + + ret = anx7411_reg_block_write(ctx->spi_client, + CMD_SEND_BUF + 1, size + HEADER_LEN, + &msg->msg_type); + ret |= anx7411_reg_write(ctx->spi_client, CMD_SEND_BUF, + msg->msg_len); + return ret; +} + +static int anx7411_process_cmd(struct anx7411_data *ctx) +{ + struct device *dev = &ctx->spi_client->dev; + struct fw_msg *msg = &ctx->recv_msg; + u8 len; + u8 crc; + int ret; + + /* Read message from firmware */ + ret = anx7411_reg_block_read(ctx->spi_client, CMD_RECV_BUF, + MSG_LEN, (u8 *)msg); + if (ret < 0) + return 0; + + if (!msg->msg_len) + return 0; + + ret = anx7411_reg_write(ctx->spi_client, CMD_RECV_BUF, 0); + if (ret) + return ret; + + len = msg->msg_len & MSG_LEN_MASK; + crc = checksum(dev, (u8 *)msg, len + HEADER_LEN); + if (crc) { + dev_err(dev, "message error crc(0x%.02x)\n", crc); + return -ERANGE; + } + + return anx7411_parse_cmd(ctx, msg->msg_type, msg->buf, len - 1); +} + +static void anx7411_translate_payload(struct device *dev, __le32 *payload, + u32 *pdo, int nr, const char *type) +{ + int i; + + if (nr > PDO_MAX_OBJECTS) { + dev_err(dev, "nr(%d) exceed PDO_MAX_OBJECTS(%d)\n", + nr, PDO_MAX_OBJECTS); + + return; + } + + for (i = 0; i < nr; i++) + payload[i] = cpu_to_le32(pdo[i]); +} + +static int anx7411_config(struct anx7411_data *ctx) +{ + struct device *dev = &ctx->spi_client->dev; + struct typec_params *typecp = &ctx->typec; + __le32 payload[PDO_MAX_OBJECTS]; + int ret; + + /* Config PD FW work under PD 2.0 */ + ret = anx7411_reg_write(ctx->spi_client, PD_REV_INIT, PD_REV20); + ret |= anx7411_reg_write(ctx->tcpc_client, FW_CTRL_0, + UNSTRUCT_VDM_EN | DELAY_200MS | + VSAFE1 | FRS_EN); + ret |= anx7411_reg_write(ctx->spi_client, FW_CTRL_1, + AUTO_PD_EN | FORCE_SEND_RDO); + + /* Set VBUS current threshold */ + ret |= anx7411_reg_write(ctx->tcpc_client, VBUS_THRESHOLD_H, 0xff); + ret |= anx7411_reg_write(ctx->tcpc_client, VBUS_THRESHOLD_L, 0x03); + + /* Fix dongle compatible issue */ + ret |= anx7411_reg_write(ctx->tcpc_client, FW_PARAM, + anx7411_reg_read(ctx->tcpc_client, FW_PARAM) | + DONGLE_IOP); + ret |= anx7411_reg_write(ctx->spi_client, INT_MASK, 0); + + ret |= anx7411_reg_write(ctx->spi_client, PD_EXT_MSG_CTRL, 0xFF); + if (ret) + return ret; + + if (typecp->caps_flags & HAS_SOURCE_CAP) { + anx7411_translate_payload(dev, payload, typecp->src_pdo, + typecp->src_pdo_nr, "source"); + anx7411_send_msg(ctx, TYPE_SRC_CAP, (u8 *)&payload, + typecp->src_pdo_nr * 4); + anx7411_send_msg(ctx, TYPE_SNK_IDENTITY, snk_identity, + sizeof(snk_identity)); + anx7411_send_msg(ctx, TYPE_SET_SNK_DP_CAP, dp_caps, + sizeof(dp_caps)); + } + + if (typecp->caps_flags & HAS_SINK_CAP) { + anx7411_translate_payload(dev, payload, typecp->sink_pdo, + typecp->sink_pdo_nr, "sink"); + anx7411_send_msg(ctx, TYPE_SNK_CAP, (u8 *)&payload, + typecp->sink_pdo_nr * 4); + } + + if (typecp->caps_flags & HAS_SINK_WATT) { + if (typecp->sink_watt) { + ret |= anx7411_reg_write(ctx->spi_client, MAX_POWER, + typecp->sink_watt); + /* Set min power to 1W */ + ret |= anx7411_reg_write(ctx->spi_client, MIN_POWER, 2); + } + + if (typecp->sink_voltage) + ret |= anx7411_reg_write(ctx->spi_client, MAX_VOLTAGE, + typecp->sink_voltage); + if (ret) + return ret; + } + + if (!typecp->caps_flags) + usleep_range(5000, 6000); + + ctx->fw_version = anx7411_reg_read(ctx->spi_client, FW_VER); + ctx->fw_subversion = anx7411_reg_read(ctx->spi_client, FW_SUBVER); + + return 0; +} + +static void anx7411_chip_standby(struct anx7411_data *ctx) +{ + int ret; + u8 cc1, cc2; + struct device *dev = &ctx->spi_client->dev; + + ret = anx7411_reg_write(ctx->spi_client, OCM_CTRL_0, + anx7411_reg_read(ctx->spi_client, OCM_CTRL_0) | + OCM_RESET); + ret |= anx7411_reg_write(ctx->tcpc_client, ANALOG_CTRL_10, 0x80); + /* Set TCPC to RD and DRP enable */ + cc1 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC1_SHIFT; + cc2 = TCPC_ROLE_CTRL_CC_RD << TCPC_ROLE_CTRL_CC2_SHIFT; + ret |= anx7411_reg_write(ctx->tcpc_client, TCPC_ROLE_CTRL, + TCPC_ROLE_CTRL_DRP | cc1 | cc2); + + /* Send DRP toggle command */ + ret |= anx7411_reg_write(ctx->tcpc_client, TCPC_COMMAND, + TCPC_CMD_LOOK4CONNECTION); + + /* Send TCPC enter standby command */ + ret |= anx7411_reg_write(ctx->tcpc_client, + TCPC_COMMAND, TCPC_CMD_I2C_IDLE); + if (ret) + dev_err(dev, "Chip standby failed\n"); +} + +static void anx7411_work_func(struct work_struct *work) +{ + int ret; + u8 buf[STATUS_LEN]; + u8 int_change; /* Interrupt change */ + u8 int_status; /* Firmware status update */ + u8 alert0, alert1; /* Interrupt alert source */ + struct anx7411_data *ctx = container_of(work, struct anx7411_data, work); + struct device *dev = &ctx->spi_client->dev; + + mutex_lock(&ctx->lock); + + /* Read interrupt change status */ + ret = anx7411_reg_block_read(ctx->spi_client, INT_STS, STATUS_LEN, buf); + if (ret < 0) { + /* Power standby mode, just return */ + goto unlock; + } + int_change = buf[0]; + int_status = buf[1]; + + /* Read alert register */ + ret = anx7411_reg_block_read(ctx->tcpc_client, ALERT_0, STATUS_LEN, buf); + if (ret < 0) + goto unlock; + + alert0 = buf[0]; + alert1 = buf[1]; + + /* Clear interrupt and alert status */ + ret = anx7411_reg_write(ctx->spi_client, INT_STS, 0); + ret |= anx7411_reg_write(ctx->tcpc_client, ALERT_0, alert0); + ret |= anx7411_reg_write(ctx->tcpc_client, ALERT_1, alert1); + if (ret) + goto unlock; + + if (alert1 & INTP_POW_OFF) { + anx7411_partner_unregister_altmode(ctx); + if (anx7411_set_usb_role(ctx, USB_ROLE_NONE)) + dev_err(dev, "Set usb role\n"); + anx7411_unregister_partner(ctx); + ctx->psy_online = ANX7411_PSY_OFFLINE; + ctx->usb_type = POWER_SUPPLY_USB_TYPE_C; + ctx->typec.request_voltage = 0; + ctx->typec.request_current = 0; + power_supply_changed(ctx->psy); + anx7411_chip_standby(ctx); + goto unlock; + } + + if ((alert0 & SOFTWARE_INT) && (int_change & OCM_BOOT_UP)) { + if (anx7411_config(ctx)) + dev_err(dev, "Config failed\n"); + if (anx7411_data_role_detect(ctx)) + dev_err(dev, "set PD data role\n"); + if (anx7411_power_role_detect(ctx)) + dev_err(dev, "set PD power role\n"); + anx7411_set_mux(ctx, SELECT_PIN_ASSIGMENT_C); + } + + if (alert0 & RECEIVED_MSG) + anx7411_process_cmd(ctx); + + ret = (int_status & DATA_ROLE) ? TYPEC_HOST : TYPEC_DEVICE; + if (ctx->typec.data_role != ret) + if (anx7411_data_role_detect(ctx)) + dev_err(dev, "set PD data role\n"); + + ret = (int_status & SINK_STATUS) ? TYPEC_SINK : TYPEC_SOURCE; + if (ctx->typec.power_role != ret) + if (anx7411_power_role_detect(ctx)) + dev_err(dev, "set PD power role\n"); + + if ((alert0 & SOFTWARE_INT) && (int_change & CC_STATUS_CHANGE)) + anx7411_cc_status_detect(ctx); + +unlock: + mutex_unlock(&ctx->lock); +} + +static irqreturn_t anx7411_intr_isr(int irq, void *data) +{ + struct anx7411_data *ctx = (struct anx7411_data *)data; + + queue_work(ctx->workqueue, &ctx->work); + + return IRQ_HANDLED; +} + +static int anx7411_register_i2c_dummy_clients(struct anx7411_data *ctx, + struct i2c_client *client) +{ + int i; + u8 spi_addr; + + for (i = 0; i < ARRAY_SIZE(anx7411_i2c_addr); i++) { + if (client->addr == (anx7411_i2c_addr[i].tcpc_address >> 1)) { + spi_addr = anx7411_i2c_addr[i].spi_address >> 1; + ctx->spi_client = i2c_new_dummy_device(client->adapter, + spi_addr); + if (!IS_ERR(ctx->spi_client)) + return 0; + } + } + + dev_err(&client->dev, "unable to get SPI slave\n"); + return -ENOMEM; +} + +static void anx7411_port_unregister_altmodes(struct typec_altmode **adev) +{ + int i; + + for (i = 0; i < MAX_ALTMODE; i++) + if (adev[i]) { + typec_unregister_altmode(adev[i]); + adev[i] = NULL; + } +} + +static int anx7411_usb_mux_set(struct typec_mux_dev *mux, + struct typec_mux_state *state) +{ + struct anx7411_data *ctx = typec_mux_get_drvdata(mux); + struct device *dev = &ctx->spi_client->dev; + int has_dp; + + has_dp = (state->alt && state->alt->svid == USB_TYPEC_DP_SID && + state->alt->mode == USB_TYPEC_DP_MODE); + if (!has_dp) + dev_err(dev, "dp altmode not register\n"); + + return 0; +} + +static int anx7411_usb_set_orientation(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + /* No need set */ + + return 0; +} + +static int anx7411_register_switch(struct anx7411_data *ctx, + struct device *dev, + struct fwnode_handle *fwnode) +{ + struct typec_switch_desc sw_desc = { }; + + sw_desc.fwnode = fwnode; + sw_desc.drvdata = ctx; + sw_desc.name = fwnode_get_name(fwnode); + sw_desc.set = anx7411_usb_set_orientation; + + ctx->typec.typec_switch = typec_switch_register(dev, &sw_desc); + if (IS_ERR(ctx->typec.typec_switch)) { + dev_err(dev, "switch register failed\n"); + return PTR_ERR(ctx->typec.typec_switch); + } + + return 0; +} + +static int anx7411_register_mux(struct anx7411_data *ctx, + struct device *dev, + struct fwnode_handle *fwnode) +{ + struct typec_mux_desc mux_desc = { }; + + mux_desc.fwnode = fwnode; + mux_desc.drvdata = ctx; + mux_desc.name = fwnode_get_name(fwnode); + mux_desc.set = anx7411_usb_mux_set; + + ctx->typec.typec_mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(ctx->typec.typec_mux)) { + dev_err(dev, "mux register failed\n"); + return PTR_ERR(ctx->typec.typec_mux); + } + + return 0; +} + +static void anx7411_unregister_mux(struct anx7411_data *ctx) +{ + if (ctx->typec.typec_mux) { + typec_mux_unregister(ctx->typec.typec_mux); + ctx->typec.typec_mux = NULL; + } +} + +static void anx7411_unregister_switch(struct anx7411_data *ctx) +{ + if (ctx->typec.typec_switch) { + typec_switch_unregister(ctx->typec.typec_switch); + ctx->typec.typec_switch = NULL; + } +} + +static int anx7411_typec_switch_probe(struct anx7411_data *ctx, + struct device *dev) +{ + int ret; + struct device_node *node; + + node = of_find_node_by_name(dev->of_node, "orientation_switch"); + if (!node) + return 0; + + ret = anx7411_register_switch(ctx, dev, &node->fwnode); + if (ret) { + dev_err(dev, "failed register switch"); + return ret; + } + + node = of_find_node_by_name(dev->of_node, "mode_switch"); + if (!node) { + dev_err(dev, "no typec mux exist"); + ret = -ENODEV; + goto unregister_switch; + } + + ret = anx7411_register_mux(ctx, dev, &node->fwnode); + if (ret) { + dev_err(dev, "failed register mode switch"); + ret = -ENODEV; + goto unregister_switch; + } + + return 0; + +unregister_switch: + anx7411_unregister_switch(ctx); + + return ret; +} + +static int anx7411_typec_port_probe(struct anx7411_data *ctx, + struct device *dev) +{ + struct typec_capability *cap = &ctx->typec.caps; + struct typec_params *typecp = &ctx->typec; + struct fwnode_handle *fwnode; + const char *buf; + int ret, i; + + fwnode = device_get_named_child_node(dev, "connector"); + if (!fwnode) + return -EINVAL; + + ret = fwnode_property_read_string(fwnode, "power-role", &buf); + if (ret) { + dev_err(dev, "power-role not found: %d\n", ret); + return ret; + } + + ret = typec_find_port_power_role(buf); + if (ret < 0) + return ret; + cap->type = ret; + + ret = fwnode_property_read_string(fwnode, "data-role", &buf); + if (ret) { + dev_err(dev, "data-role not found: %d\n", ret); + return ret; + } + + ret = typec_find_port_data_role(buf); + if (ret < 0) + return ret; + cap->data = ret; + + ret = fwnode_property_read_string(fwnode, "try-power-role", &buf); + if (ret) { + dev_err(dev, "try-power-role not found: %d\n", ret); + return ret; + } + + ret = typec_find_power_role(buf); + if (ret < 0) + return ret; + cap->prefer_role = ret; + + /* Get source pdos */ + ret = fwnode_property_count_u32(fwnode, "source-pdos"); + if (ret > 0) { + typecp->src_pdo_nr = min_t(u8, ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "source-pdos", + typecp->src_pdo, + typecp->src_pdo_nr); + if (ret < 0) { + dev_err(dev, "source cap validate failed: %d\n", ret); + return -EINVAL; + } + + typecp->caps_flags |= HAS_SOURCE_CAP; + } + + ret = fwnode_property_count_u32(fwnode, "sink-pdos"); + if (ret > 0) { + typecp->sink_pdo_nr = min_t(u8, ret, PDO_MAX_OBJECTS); + ret = fwnode_property_read_u32_array(fwnode, "sink-pdos", + typecp->sink_pdo, + typecp->sink_pdo_nr); + if (ret < 0) { + dev_err(dev, "sink cap validate failed: %d\n", ret); + return -EINVAL; + } + + for (i = 0; i < typecp->sink_pdo_nr; i++) { + ret = 0; + switch (pdo_type(typecp->sink_pdo[i])) { + case PDO_TYPE_FIXED: + ret = pdo_fixed_voltage(typecp->sink_pdo[i]); + break; + case PDO_TYPE_BATT: + case PDO_TYPE_VAR: + ret = pdo_max_voltage(typecp->sink_pdo[i]); + break; + case PDO_TYPE_APDO: + default: + ret = 0; + break; + } + + /* 100mv per unit */ + typecp->sink_voltage = max(5000, ret) / 100; + } + + typecp->caps_flags |= HAS_SINK_CAP; + } + + if (!fwnode_property_read_u32(fwnode, "op-sink-microwatt", &ret)) { + typecp->sink_watt = ret / 500000; /* 500mw per unit */ + typecp->caps_flags |= HAS_SINK_WATT; + } + + cap->fwnode = fwnode; + + ctx->typec.role_sw = usb_role_switch_get(dev); + if (IS_ERR(ctx->typec.role_sw)) { + dev_err(dev, "USB role switch not found.\n"); + ctx->typec.role_sw = NULL; + } + + ctx->typec.port = typec_register_port(dev, cap); + if (IS_ERR(ctx->typec.port)) { + ret = PTR_ERR(ctx->typec.port); + ctx->typec.port = NULL; + dev_err(dev, "Failed to register type c port %d\n", ret); + return ret; + } + + typec_port_register_altmodes(ctx->typec.port, NULL, ctx, + ctx->typec.port_amode, + MAX_ALTMODE); + return 0; +} + +static int anx7411_typec_check_connection(struct anx7411_data *ctx) +{ + int ret; + + ret = anx7411_reg_read(ctx->spi_client, FW_VER); + if (ret < 0) + return 0; /* No device attached in typec port */ + + /* Clear interrupt and alert status */ + ret = anx7411_reg_write(ctx->spi_client, INT_STS, 0); + ret |= anx7411_reg_write(ctx->tcpc_client, ALERT_0, 0xFF); + ret |= anx7411_reg_write(ctx->tcpc_client, ALERT_1, 0xFF); + if (ret) + return ret; + + ret = anx7411_cc_status_detect(ctx); + ret |= anx7411_power_role_detect(ctx); + ret |= anx7411_data_role_detect(ctx); + ret |= anx7411_set_mux(ctx, SELECT_PIN_ASSIGMENT_C); + if (ret) + return ret; + + ret = anx7411_send_msg(ctx, TYPE_GET_DP_ALT_ENTER, NULL, 0); + ret |= anx7411_send_msg(ctx, TYPE_GET_DP_DISCOVER_MODES_INFO, NULL, 0); + + return ret; +} + +static int __maybe_unused anx7411_runtime_pm_suspend(struct device *dev) +{ + struct anx7411_data *ctx = dev_get_drvdata(dev); + + mutex_lock(&ctx->lock); + + anx7411_partner_unregister_altmode(ctx); + + if (ctx->typec.partner) + anx7411_unregister_partner(ctx); + + mutex_unlock(&ctx->lock); + + return 0; +} + +static int __maybe_unused anx7411_runtime_pm_resume(struct device *dev) +{ + struct anx7411_data *ctx = dev_get_drvdata(dev); + + mutex_lock(&ctx->lock); + /* Detect PD connection */ + if (anx7411_typec_check_connection(ctx)) + dev_err(dev, "check connection"); + + mutex_unlock(&ctx->lock); + + return 0; +} + +static const struct dev_pm_ops anx7411_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(anx7411_runtime_pm_suspend, + anx7411_runtime_pm_resume, NULL) +}; + +static void anx7411_get_gpio_irq(struct anx7411_data *ctx) +{ + struct device *dev = &ctx->tcpc_client->dev; + + ctx->intp_gpiod = devm_gpiod_get_optional(dev, "interrupt", GPIOD_IN); + if (IS_ERR_OR_NULL(ctx->intp_gpiod)) { + dev_err(dev, "no interrupt gpio property\n"); + return; + } + + ctx->intp_irq = gpiod_to_irq(ctx->intp_gpiod); + if (ctx->intp_irq < 0) + dev_err(dev, "failed to get GPIO IRQ\n"); +} + +static enum power_supply_usb_type anx7411_psy_usb_types[] = { + POWER_SUPPLY_USB_TYPE_C, + POWER_SUPPLY_USB_TYPE_PD, + POWER_SUPPLY_USB_TYPE_PD_PPS, +}; + +static enum power_supply_property anx7411_psy_props[] = { + POWER_SUPPLY_PROP_USB_TYPE, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_CURRENT_MAX, + POWER_SUPPLY_PROP_CURRENT_NOW, +}; + +static int anx7411_psy_set_prop(struct power_supply *psy, + enum power_supply_property psp, + const union power_supply_propval *val) +{ + struct anx7411_data *ctx = power_supply_get_drvdata(psy); + int ret = 0; + + if (psp == POWER_SUPPLY_PROP_ONLINE) + ctx->psy_online = val->intval; + else + ret = -EINVAL; + + power_supply_changed(ctx->psy); + return ret; +} + +static int anx7411_psy_prop_writeable(struct power_supply *psy, + enum power_supply_property psp) +{ + return psp == POWER_SUPPLY_PROP_ONLINE; +} + +static int anx7411_psy_get_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct anx7411_data *ctx = power_supply_get_drvdata(psy); + int ret = 0; + + switch (psp) { + case POWER_SUPPLY_PROP_USB_TYPE: + val->intval = ctx->usb_type; + break; + case POWER_SUPPLY_PROP_ONLINE: + val->intval = ctx->psy_online; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + case POWER_SUPPLY_PROP_VOLTAGE_MIN: + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + val->intval = (ctx->psy_online) ? + ctx->typec.request_voltage * 1000 : 0; + break; + case POWER_SUPPLY_PROP_CURRENT_NOW: + case POWER_SUPPLY_PROP_CURRENT_MAX: + val->intval = (ctx->psy_online) ? + ctx->typec.request_current * 1000 : 0; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static int anx7411_psy_register(struct anx7411_data *ctx) +{ + struct power_supply_desc *psy_desc = &ctx->psy_desc; + struct power_supply_config psy_cfg = {}; + char *psy_name; + + psy_name = devm_kasprintf(ctx->dev, GFP_KERNEL, "anx7411-source-psy-%s", + dev_name(ctx->dev)); + if (!psy_name) + return -ENOMEM; + + psy_desc->name = psy_name; + psy_desc->type = POWER_SUPPLY_TYPE_USB; + psy_desc->usb_types = anx7411_psy_usb_types; + psy_desc->num_usb_types = ARRAY_SIZE(anx7411_psy_usb_types); + psy_desc->properties = anx7411_psy_props; + psy_desc->num_properties = ARRAY_SIZE(anx7411_psy_props); + + psy_desc->get_property = anx7411_psy_get_prop; + psy_desc->set_property = anx7411_psy_set_prop; + psy_desc->property_is_writeable = anx7411_psy_prop_writeable; + + ctx->usb_type = POWER_SUPPLY_USB_TYPE_C; + ctx->psy = devm_power_supply_register(ctx->dev, psy_desc, &psy_cfg); + + if (IS_ERR(ctx->psy)) + dev_warn(ctx->dev, "unable to register psy\n"); + + return PTR_ERR_OR_ZERO(ctx->psy); +} + +static int anx7411_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct anx7411_data *plat; + struct device *dev = &client->dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_I2C_BLOCK)) + return -ENODEV; + + plat = devm_kzalloc(dev, sizeof(*plat), GFP_KERNEL); + if (!plat) + return -ENOMEM; + + plat->tcpc_client = client; + i2c_set_clientdata(client, plat); + + mutex_init(&plat->lock); + + ret = anx7411_register_i2c_dummy_clients(plat, client); + if (ret) { + dev_err(dev, "fail to reserve I2C bus\n"); + return ret; + } + + ret = anx7411_typec_switch_probe(plat, dev); + if (ret) { + dev_err(dev, "fail to probe typec switch\n"); + goto free_i2c_dummy; + } + + ret = anx7411_typec_port_probe(plat, dev); + if (ret) { + dev_err(dev, "fail to probe typec property.\n"); + ret = -ENODEV; + goto free_typec_switch; + } + + plat->intp_irq = client->irq; + if (!client->irq) + anx7411_get_gpio_irq(plat); + + if (!plat->intp_irq) { + dev_err(dev, "fail to get interrupt IRQ\n"); + ret = -EINVAL; + goto free_typec_port; + } + + plat->dev = dev; + plat->psy_online = ANX7411_PSY_OFFLINE; + ret = anx7411_psy_register(plat); + if (ret) { + dev_err(dev, "register psy\n"); + goto free_typec_port; + } + + INIT_WORK(&plat->work, anx7411_work_func); + plat->workqueue = alloc_workqueue("anx7411_work", + WQ_FREEZABLE | + WQ_MEM_RECLAIM, + 1); + if (!plat->workqueue) { + dev_err(dev, "fail to create work queue\n"); + ret = -ENOMEM; + goto free_typec_port; + } + + ret = devm_request_threaded_irq(dev, plat->intp_irq, + NULL, anx7411_intr_isr, + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "anx7411-intp", plat); + if (ret) { + dev_err(dev, "fail to request irq\n"); + goto free_wq; + } + + if (anx7411_typec_check_connection(plat)) + dev_err(dev, "check status\n"); + + pm_runtime_enable(dev); + + return 0; + +free_wq: + destroy_workqueue(plat->workqueue); + +free_typec_port: + typec_unregister_port(plat->typec.port); + anx7411_port_unregister_altmodes(plat->typec.port_amode); + +free_typec_switch: + anx7411_unregister_switch(plat); + anx7411_unregister_mux(plat); + +free_i2c_dummy: + i2c_unregister_device(plat->spi_client); + + return ret; +} + +static int anx7411_i2c_remove(struct i2c_client *client) +{ + struct anx7411_data *plat = i2c_get_clientdata(client); + + anx7411_partner_unregister_altmode(plat); + anx7411_unregister_partner(plat); + + if (plat->workqueue) + destroy_workqueue(plat->workqueue); + + if (plat->spi_client) + i2c_unregister_device(plat->spi_client); + + if (plat->typec.role_sw) + usb_role_switch_put(plat->typec.role_sw); + + anx7411_unregister_mux(plat); + + anx7411_unregister_switch(plat); + + if (plat->typec.port) + typec_unregister_port(plat->typec.port); + + anx7411_port_unregister_altmodes(plat->typec.port_amode); + + return 0; +} + +static const struct i2c_device_id anx7411_id[] = { + {"anx7411", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, anx7411_id); + +static const struct of_device_id anx_match_table[] = { + {.compatible = "analogix,anx7411",}, + {}, +}; + +static struct i2c_driver anx7411_driver = { + .driver = { + .name = "anx7411", + .of_match_table = anx_match_table, + .pm = &anx7411_pm_ops, + }, + .probe = anx7411_i2c_probe, + .remove = anx7411_i2c_remove, + + .id_table = anx7411_id, +}; + +module_i2c_driver(anx7411_driver); + +MODULE_DESCRIPTION("Anx7411 USB Type-C PD driver"); +MODULE_AUTHOR("Xin Ji "); +MODULE_LICENSE("GPL"); +MODULE_VERSION("0.1.5"); diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index c4724750c81a..ebc29ec20e3f 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -12,9 +12,11 @@ #include #include #include +#include #include "bus.h" #include "class.h" +#include "pd.h" static DEFINE_IDA(typec_index_ida); @@ -720,6 +722,39 @@ void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revisio } EXPORT_SYMBOL_GPL(typec_partner_set_pd_revision); +/** + * typec_partner_set_usb_power_delivery - Declare USB Power Delivery Contract. + * @partner: The partner device. + * @pd: The USB PD instance. + * + * This routine can be used to declare USB Power Delivery Contract with @partner + * by linking @partner to @pd which contains the objects that were used during the + * negotiation of the contract. + * + * If @pd is NULL, the link is removed and the contract with @partner has ended. + */ +int typec_partner_set_usb_power_delivery(struct typec_partner *partner, + struct usb_power_delivery *pd) +{ + int ret; + + if (IS_ERR_OR_NULL(partner) || partner->pd == pd) + return 0; + + if (pd) { + ret = usb_power_delivery_link_device(pd, &partner->dev); + if (ret) + return ret; + } else { + usb_power_delivery_unlink_device(partner->pd, &partner->dev); + } + + partner->pd = pd; + + return 0; +} +EXPORT_SYMBOL_GPL(typec_partner_set_usb_power_delivery); + /** * typec_partner_set_num_altmodes - Set the number of available partner altmodes * @partner: The partner to be updated. @@ -1170,6 +1205,104 @@ EXPORT_SYMBOL_GPL(typec_unregister_cable); /* ------------------------------------------------------------------------- */ /* USB Type-C ports */ +/** + * typec_port_set_usb_power_delivery - Assign USB PD for port. + * @port: USB Type-C port. + * @pd: USB PD instance. + * + * This routine can be used to set the USB Power Delivery Capabilities for @port + * that it will advertise to the partner. + * + * If @pd is NULL, the assignment is removed. + */ +int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_delivery *pd) +{ + int ret; + + if (IS_ERR_OR_NULL(port) || port->pd == pd) + return 0; + + if (pd) { + ret = usb_power_delivery_link_device(pd, &port->dev); + if (ret) + return ret; + } else { + usb_power_delivery_unlink_device(port->pd, &port->dev); + } + + port->pd = pd; + + return 0; +} +EXPORT_SYMBOL_GPL(typec_port_set_usb_power_delivery); + +static ssize_t select_usb_power_delivery_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + struct usb_power_delivery *pd; + + if (!port->ops || !port->ops->pd_set) + return -EOPNOTSUPP; + + pd = usb_power_delivery_find(buf); + if (!pd) + return -EINVAL; + + return port->ops->pd_set(port, pd); +} + +static ssize_t select_usb_power_delivery_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct typec_port *port = to_typec_port(dev); + struct usb_power_delivery **pds; + struct usb_power_delivery *pd; + int ret = 0; + + if (!port->ops || !port->ops->pd_get) + return -EOPNOTSUPP; + + pds = port->ops->pd_get(port); + if (!pds) + return 0; + + for (pd = pds[0]; pd; pd++) { + if (pd == port->pd) + ret += sysfs_emit(buf + ret, "[%s] ", dev_name(&pd->dev)); + else + ret += sysfs_emit(buf + ret, "%s ", dev_name(&pd->dev)); + } + + buf[ret - 1] = '\n'; + + return ret; +} +static DEVICE_ATTR_RW(select_usb_power_delivery); + +static struct attribute *port_attrs[] = { + &dev_attr_select_usb_power_delivery.attr, + NULL +}; + +static umode_t port_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct typec_port *port = to_typec_port(kobj_to_dev(kobj)); + + if (!port->pd || !port->ops || !port->ops->pd_get) + return 0; + if (!port->ops->pd_set) + return 0444; + + return attr->mode; +} + +static const struct attribute_group pd_group = { + .is_visible = port_attr_is_visible, + .attrs = port_attrs, +}; + static const char * const typec_orientations[] = { [TYPEC_ORIENTATION_NONE] = "unknown", [TYPEC_ORIENTATION_NORMAL] = "normal", @@ -1581,6 +1714,7 @@ static const struct attribute_group typec_group = { static const struct attribute_group *typec_groups[] = { &typec_group, + &pd_group, NULL }; @@ -1603,6 +1737,7 @@ static void typec_release(struct device *dev) ida_destroy(&port->mode_ids); typec_switch_put(port->sw); typec_mux_put(port->mux); + typec_retimer_put(port->retimer); kfree(port->cap); kfree(port); } @@ -2117,6 +2252,13 @@ struct typec_port *typec_register_port(struct device *parent, return ERR_PTR(ret); } + port->retimer = typec_retimer_get(&port->dev); + if (IS_ERR(port->retimer)) { + ret = PTR_ERR(port->retimer); + put_device(&port->dev); + return ERR_PTR(ret); + } + ret = device_add(&port->dev); if (ret) { dev_err(parent, "failed to register port (%d)\n", ret); @@ -2124,6 +2266,13 @@ struct typec_port *typec_register_port(struct device *parent, return ERR_PTR(ret); } + ret = typec_port_set_usb_power_delivery(port, cap->pd); + if (ret) { + dev_err(&port->dev, "failed to link pd\n"); + device_unregister(&port->dev); + return ERR_PTR(ret); + } + ret = typec_link_ports(port); if (ret) dev_warn(&port->dev, "failed to create symlinks (%d)\n", ret); @@ -2142,6 +2291,7 @@ void typec_unregister_port(struct typec_port *port) { if (!IS_ERR_OR_NULL(port)) { typec_unlink_ports(port); + typec_port_set_usb_power_delivery(port, NULL); device_unregister(&port->dev); } } @@ -2159,12 +2309,26 @@ static int __init typec_init(void) if (ret) goto err_unregister_bus; - ret = class_register(&typec_class); + ret = class_register(&retimer_class); if (ret) goto err_unregister_mux_class; + ret = class_register(&typec_class); + if (ret) + goto err_unregister_retimer_class; + + ret = usb_power_delivery_init(); + if (ret) + goto err_unregister_class; + return 0; +err_unregister_class: + class_unregister(&typec_class); + +err_unregister_retimer_class: + class_unregister(&retimer_class); + err_unregister_mux_class: class_unregister(&typec_mux_class); @@ -2177,6 +2341,7 @@ subsys_initcall(typec_init); static void __exit typec_exit(void) { + usb_power_delivery_exit(); class_unregister(&typec_class); ida_destroy(&typec_index_ida); bus_unregister(&typec_bus); diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 0f1bd6d19d67..673b2952b074 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -33,6 +33,8 @@ struct typec_partner { int num_altmodes; u16 pd_revision; /* 0300H = "3.0" */ enum usb_pd_svdm_ver svdm_version; + + struct usb_power_delivery *pd; }; struct typec_port { @@ -40,6 +42,8 @@ struct typec_port { struct device dev; struct ida mode_ids; + struct usb_power_delivery *pd; + int prefer_role; enum typec_data_role data_role; enum typec_role pwr_role; @@ -51,6 +55,7 @@ struct typec_port { enum typec_orientation orientation; struct typec_switch *sw; struct typec_mux *mux; + struct typec_retimer *retimer; const struct typec_capability *cap; const struct typec_operations *ops; @@ -72,6 +77,7 @@ extern const struct device_type typec_port_dev_type; #define is_typec_port(dev) ((dev)->type == &typec_port_dev_type) extern struct class typec_mux_class; +extern struct class retimer_class; extern struct class typec_class; #if defined(CONFIG_ACPI) diff --git a/drivers/usb/typec/mux.c b/drivers/usb/typec/mux.c index fd55c2c516a5..464330776cd6 100644 --- a/drivers/usb/typec/mux.c +++ b/drivers/usb/typec/mux.c @@ -281,9 +281,13 @@ static void *typec_mux_match(struct fwnode_handle *fwnode, const char *id, if (match) goto find_mux; - /* Accessory Mode muxes */ if (!desc) { - match = fwnode_property_present(fwnode, "accessory"); + /* + * Accessory Mode muxes & muxes which explicitly specify + * the required identifier can avoid SVID matching. + */ + match = fwnode_property_present(fwnode, "accessory") || + fwnode_property_present(fwnode, id); if (match) goto find_mux; return NULL; diff --git a/drivers/usb/typec/pd.c b/drivers/usb/typec/pd.c new file mode 100644 index 000000000000..dc72005d68db --- /dev/null +++ b/drivers/usb/typec/pd.c @@ -0,0 +1,708 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * USB Power Delivery sysfs entries + * + * Copyright (C) 2022, Intel Corporation + * Author: Heikki Krogerus + */ + +#include +#include + +#include "pd.h" + +static DEFINE_IDA(pd_ida); + +static struct class pd_class = { + .name = "usb_power_delivery", + .owner = THIS_MODULE, +}; + +#define to_pdo(o) container_of(o, struct pdo, dev) + +struct pdo { + struct device dev; + int object_position; + u32 pdo; +}; + +static void pdo_release(struct device *dev) +{ + kfree(to_pdo(dev)); +} + +/* -------------------------------------------------------------------------- */ +/* Fixed Supply */ + +static ssize_t +dual_role_power_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_DUAL_ROLE)); +} +static DEVICE_ATTR_RO(dual_role_power); + +static ssize_t +usb_suspend_supported_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_SUSPEND)); +} +static DEVICE_ATTR_RO(usb_suspend_supported); + +static ssize_t +unconstrained_power_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_EXTPOWER)); +} +static DEVICE_ATTR_RO(unconstrained_power); + +static ssize_t +usb_communication_capable_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_USB_COMM)); +} +static DEVICE_ATTR_RO(usb_communication_capable); + +static ssize_t +dual_role_data_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_DATA_SWAP)); +} +static DEVICE_ATTR_RO(dual_role_data); + +static ssize_t +unchunked_extended_messages_supported_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & PDO_FIXED_UNCHUNK_EXT)); +} +static DEVICE_ATTR_RO(unchunked_extended_messages_supported); + +/* + * REVISIT: Peak Current requires access also to the RDO. +static ssize_t +peak_current_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + ... +} +*/ + +static ssize_t +fast_role_swap_current_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", to_pdo(dev)->pdo >> PDO_FIXED_FRS_CURR_SHIFT) & 3; +} +static DEVICE_ATTR_RO(fast_role_swap_current); + +static ssize_t voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umV\n", pdo_fixed_voltage(to_pdo(dev)->pdo)); +} +static DEVICE_ATTR_RO(voltage); + +/* Shared with Variable supplies, both source and sink */ +static ssize_t current_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", pdo_max_current(to_pdo(dev)->pdo)); +} + +/* Shared with Variable type supplies */ +static struct device_attribute maximum_current_attr = { + .attr = { + .name = "maximum_current", + .mode = 0444, + }, + .show = current_show, +}; + +static struct device_attribute operational_current_attr = { + .attr = { + .name = "operational_current", + .mode = 0444, + }, + .show = current_show, +}; + +static struct attribute *source_fixed_supply_attrs[] = { + &dev_attr_dual_role_power.attr, + &dev_attr_usb_suspend_supported.attr, + &dev_attr_unconstrained_power.attr, + &dev_attr_usb_communication_capable.attr, + &dev_attr_dual_role_data.attr, + &dev_attr_unchunked_extended_messages_supported.attr, + /*&dev_attr_peak_current.attr,*/ + &dev_attr_voltage.attr, + &maximum_current_attr.attr, + NULL +}; + +static umode_t fixed_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + if (to_pdo(kobj_to_dev(kobj))->object_position && + /*attr != &dev_attr_peak_current.attr &&*/ + attr != &dev_attr_voltage.attr && + attr != &maximum_current_attr.attr && + attr != &operational_current_attr.attr) + return 0; + + return attr->mode; +} + +static const struct attribute_group source_fixed_supply_group = { + .is_visible = fixed_attr_is_visible, + .attrs = source_fixed_supply_attrs, +}; +__ATTRIBUTE_GROUPS(source_fixed_supply); + +static struct device_type source_fixed_supply_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_fixed_supply_groups, +}; + +static struct attribute *sink_fixed_supply_attrs[] = { + &dev_attr_dual_role_power.attr, + &dev_attr_usb_suspend_supported.attr, + &dev_attr_unconstrained_power.attr, + &dev_attr_usb_communication_capable.attr, + &dev_attr_dual_role_data.attr, + &dev_attr_unchunked_extended_messages_supported.attr, + &dev_attr_fast_role_swap_current.attr, + &dev_attr_voltage.attr, + &operational_current_attr.attr, + NULL +}; + +static const struct attribute_group sink_fixed_supply_group = { + .is_visible = fixed_attr_is_visible, + .attrs = sink_fixed_supply_attrs, +}; +__ATTRIBUTE_GROUPS(sink_fixed_supply); + +static struct device_type sink_fixed_supply_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_fixed_supply_groups, +}; + +/* -------------------------------------------------------------------------- */ +/* Variable Supply */ + +static ssize_t +maximum_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umV\n", pdo_max_voltage(to_pdo(dev)->pdo)); +} +static DEVICE_ATTR_RO(maximum_voltage); + +static ssize_t +minimum_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umV\n", pdo_min_voltage(to_pdo(dev)->pdo)); +} +static DEVICE_ATTR_RO(minimum_voltage); + +static struct attribute *source_variable_supply_attrs[] = { + &dev_attr_maximum_voltage.attr, + &dev_attr_minimum_voltage.attr, + &maximum_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(source_variable_supply); + +static struct device_type source_variable_supply_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_variable_supply_groups, +}; + +static struct attribute *sink_variable_supply_attrs[] = { + &dev_attr_maximum_voltage.attr, + &dev_attr_minimum_voltage.attr, + &operational_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(sink_variable_supply); + +static struct device_type sink_variable_supply_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_variable_supply_groups, +}; + +/* -------------------------------------------------------------------------- */ +/* Battery */ + +static ssize_t +maximum_power_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umW\n", pdo_max_power(to_pdo(dev)->pdo)); +} +static DEVICE_ATTR_RO(maximum_power); + +static ssize_t +operational_power_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umW\n", pdo_max_power(to_pdo(dev)->pdo)); +} +static DEVICE_ATTR_RO(operational_power); + +static struct attribute *source_battery_attrs[] = { + &dev_attr_maximum_voltage.attr, + &dev_attr_minimum_voltage.attr, + &dev_attr_maximum_power.attr, + NULL +}; +ATTRIBUTE_GROUPS(source_battery); + +static struct device_type source_battery_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_battery_groups, +}; + +static struct attribute *sink_battery_attrs[] = { + &dev_attr_maximum_voltage.attr, + &dev_attr_minimum_voltage.attr, + &dev_attr_operational_power.attr, + NULL +}; +ATTRIBUTE_GROUPS(sink_battery); + +static struct device_type sink_battery_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_battery_groups, +}; + +/* -------------------------------------------------------------------------- */ +/* Standard Power Range (SPR) Programmable Power Supply (PPS) */ + +static ssize_t +pps_power_limited_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", !!(to_pdo(dev)->pdo & BIT(27))); +} +static DEVICE_ATTR_RO(pps_power_limited); + +static ssize_t +pps_max_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umV\n", pdo_pps_apdo_max_voltage(to_pdo(dev)->pdo)); +} + +static ssize_t +pps_min_voltage_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umV\n", pdo_pps_apdo_min_voltage(to_pdo(dev)->pdo)); +} + +static ssize_t +pps_max_current_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%umA\n", pdo_pps_apdo_max_current(to_pdo(dev)->pdo)); +} + +static struct device_attribute pps_max_voltage_attr = { + .attr = { + .name = "maximum_voltage", + .mode = 0444, + }, + .show = pps_max_voltage_show, +}; + +static struct device_attribute pps_min_voltage_attr = { + .attr = { + .name = "minimum_voltage", + .mode = 0444, + }, + .show = pps_min_voltage_show, +}; + +static struct device_attribute pps_max_current_attr = { + .attr = { + .name = "maximum_current", + .mode = 0444, + }, + .show = pps_max_current_show, +}; + +static struct attribute *source_pps_attrs[] = { + &dev_attr_pps_power_limited.attr, + &pps_max_voltage_attr.attr, + &pps_min_voltage_attr.attr, + &pps_max_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(source_pps); + +static struct device_type source_pps_type = { + .name = "pdo", + .release = pdo_release, + .groups = source_pps_groups, +}; + +static struct attribute *sink_pps_attrs[] = { + &pps_max_voltage_attr.attr, + &pps_min_voltage_attr.attr, + &pps_max_current_attr.attr, + NULL +}; +ATTRIBUTE_GROUPS(sink_pps); + +static struct device_type sink_pps_type = { + .name = "pdo", + .release = pdo_release, + .groups = sink_pps_groups, +}; + +/* -------------------------------------------------------------------------- */ + +static const char * const supply_name[] = { + [PDO_TYPE_FIXED] = "fixed_supply", + [PDO_TYPE_BATT] = "battery", + [PDO_TYPE_VAR] = "variable_supply", +}; + +static const char * const apdo_supply_name[] = { + [APDO_TYPE_PPS] = "programmable_supply", +}; + +static struct device_type *source_type[] = { + [PDO_TYPE_FIXED] = &source_fixed_supply_type, + [PDO_TYPE_BATT] = &source_battery_type, + [PDO_TYPE_VAR] = &source_variable_supply_type, +}; + +static struct device_type *source_apdo_type[] = { + [APDO_TYPE_PPS] = &source_pps_type, +}; + +static struct device_type *sink_type[] = { + [PDO_TYPE_FIXED] = &sink_fixed_supply_type, + [PDO_TYPE_BATT] = &sink_battery_type, + [PDO_TYPE_VAR] = &sink_variable_supply_type, +}; + +static struct device_type *sink_apdo_type[] = { + [APDO_TYPE_PPS] = &sink_pps_type, +}; + +/* REVISIT: Export when EPR_*_Capabilities need to be supported. */ +static int add_pdo(struct usb_power_delivery_capabilities *cap, u32 pdo, int position) +{ + struct device_type *type; + const char *name; + struct pdo *p; + int ret; + + p = kzalloc(sizeof(*p), GFP_KERNEL); + if (!p) + return -ENOMEM; + + p->pdo = pdo; + p->object_position = position; + + if (pdo_type(pdo) == PDO_TYPE_APDO) { + /* FIXME: Only PPS supported for now! Skipping others. */ + if (pdo_apdo_type(pdo) > APDO_TYPE_PPS) { + dev_warn(&cap->dev, "Unknown APDO type. PDO 0x%08x\n", pdo); + kfree(p); + return 0; + } + + if (is_source(cap->role)) + type = source_apdo_type[pdo_apdo_type(pdo)]; + else + type = sink_apdo_type[pdo_apdo_type(pdo)]; + + name = apdo_supply_name[pdo_apdo_type(pdo)]; + } else { + if (is_source(cap->role)) + type = source_type[pdo_type(pdo)]; + else + type = sink_type[pdo_type(pdo)]; + + name = supply_name[pdo_type(pdo)]; + } + + p->dev.parent = &cap->dev; + p->dev.type = type; + dev_set_name(&p->dev, "%u:%s", position + 1, name); + + ret = device_register(&p->dev); + if (ret) { + put_device(&p->dev); + return ret; + } + + return 0; +} + +static int remove_pdo(struct device *dev, void *data) +{ + device_unregister(dev); + return 0; +} + +/* -------------------------------------------------------------------------- */ + +static const char * const cap_name[] = { + [TYPEC_SINK] = "sink-capabilities", + [TYPEC_SOURCE] = "source-capabilities", +}; + +static void pd_capabilities_release(struct device *dev) +{ + kfree(to_usb_power_delivery_capabilities(dev)); +} + +static struct device_type pd_capabilities_type = { + .name = "capabilities", + .release = pd_capabilities_release, +}; + +/** + * usb_power_delivery_register_capabilities - Register a set of capabilities. + * @pd: The USB PD instance that the capabilities belong to. + * @desc: Description of the Capablities Message. + * + * This function registers a Capabilities Message described in @desc. The + * capabilities will have their own sub-directory under @pd in sysfs. + * + * The function returns pointer to struct usb_power_delivery_capabilities, or + * ERR_PRT(errno). + */ +struct usb_power_delivery_capabilities * +usb_power_delivery_register_capabilities(struct usb_power_delivery *pd, + struct usb_power_delivery_capabilities_desc *desc) +{ + struct usb_power_delivery_capabilities *cap; + int ret; + int i; + + cap = kzalloc(sizeof(*cap), GFP_KERNEL); + if (!cap) + return ERR_PTR(-ENOMEM); + + cap->pd = pd; + cap->role = desc->role; + + cap->dev.parent = &pd->dev; + cap->dev.type = &pd_capabilities_type; + dev_set_name(&cap->dev, "%s", cap_name[cap->role]); + + ret = device_register(&cap->dev); + if (ret) { + put_device(&cap->dev); + return ERR_PTR(ret); + } + + for (i = 0; i < PDO_MAX_OBJECTS && desc->pdo[i]; i++) { + ret = add_pdo(cap, desc->pdo[i], i); + if (ret) { + usb_power_delivery_unregister_capabilities(cap); + return ERR_PTR(ret); + } + } + + return cap; +} +EXPORT_SYMBOL_GPL(usb_power_delivery_register_capabilities); + +/** + * usb_power_delivery_unregister_capabilities - Unregister a set of capabilities + * @cap: The capabilities + */ +void usb_power_delivery_unregister_capabilities(struct usb_power_delivery_capabilities *cap) +{ + if (!cap) + return; + + device_for_each_child(&cap->dev, NULL, remove_pdo); + device_unregister(&cap->dev); +} +EXPORT_SYMBOL_GPL(usb_power_delivery_unregister_capabilities); + +/* -------------------------------------------------------------------------- */ + +static ssize_t revision_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_power_delivery *pd = to_usb_power_delivery(dev); + + return sysfs_emit(buf, "%u.%u\n", (pd->revision >> 8) & 0xff, (pd->revision >> 4) & 0xf); +} +static DEVICE_ATTR_RO(revision); + +static ssize_t version_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct usb_power_delivery *pd = to_usb_power_delivery(dev); + + return sysfs_emit(buf, "%u.%u\n", (pd->version >> 8) & 0xff, (pd->version >> 4) & 0xf); +} +static DEVICE_ATTR_RO(version); + +static struct attribute *pd_attrs[] = { + &dev_attr_revision.attr, + &dev_attr_version.attr, + NULL +}; + +static umode_t pd_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) +{ + struct usb_power_delivery *pd = to_usb_power_delivery(kobj_to_dev(kobj)); + + if (attr == &dev_attr_version.attr && !pd->version) + return 0; + + return attr->mode; +} + +static const struct attribute_group pd_group = { + .is_visible = pd_attr_is_visible, + .attrs = pd_attrs, +}; +__ATTRIBUTE_GROUPS(pd); + +static void pd_release(struct device *dev) +{ + struct usb_power_delivery *pd = to_usb_power_delivery(dev); + + ida_simple_remove(&pd_ida, pd->id); + kfree(pd); +} + +static struct device_type pd_type = { + .name = "usb_power_delivery", + .release = pd_release, + .groups = pd_groups, +}; + +struct usb_power_delivery *usb_power_delivery_find(const char *name) +{ + struct device *dev; + + dev = class_find_device_by_name(&pd_class, name); + + return dev ? to_usb_power_delivery(dev) : NULL; +} + +/** + * usb_power_delivery_register - Register USB Power Delivery Support. + * @parent: Parent device. + * @desc: Description of the USB PD contract. + * + * This routine can be used to register USB Power Delivery capabilities that a + * device or devices can support. These capabilities represent all the + * capabilities that can be negotiated with a partner, so not only the Power + * Capabilities that are negotiated using the USB PD Capabilities Message. + * + * The USB Power Delivery Support object that this routine generates can be used + * as the parent object for all the actual USB Power Delivery Messages and + * objects that can be negotiated with the partner. + * + * Returns handle to struct usb_power_delivery or ERR_PTR. + */ +struct usb_power_delivery * +usb_power_delivery_register(struct device *parent, struct usb_power_delivery_desc *desc) +{ + struct usb_power_delivery *pd; + int ret; + + pd = kzalloc(sizeof(*pd), GFP_KERNEL); + if (!pd) + return ERR_PTR(-ENOMEM); + + ret = ida_simple_get(&pd_ida, 0, 0, GFP_KERNEL); + if (ret < 0) { + kfree(pd); + return ERR_PTR(ret); + } + + pd->id = ret; + pd->revision = desc->revision; + pd->version = desc->version; + + pd->dev.parent = parent; + pd->dev.type = &pd_type; + pd->dev.class = &pd_class; + dev_set_name(&pd->dev, "pd%d", pd->id); + + ret = device_register(&pd->dev); + if (ret) { + put_device(&pd->dev); + return ERR_PTR(ret); + } + + return pd; +} +EXPORT_SYMBOL_GPL(usb_power_delivery_register); + +/** + * usb_power_delivery_unregister - Unregister USB Power Delivery Support. + * @pd: The USB PD contract. + */ +void usb_power_delivery_unregister(struct usb_power_delivery *pd) +{ + if (IS_ERR_OR_NULL(pd)) + return; + + device_unregister(&pd->dev); +} +EXPORT_SYMBOL_GPL(usb_power_delivery_unregister); + +/** + * usb_power_delivery_link_device - Link device to its USB PD object. + * @pd: The USB PD instance. + * @dev: The device. + * + * This function can be used to create a symlink named "usb_power_delivery" for + * @dev that points to @pd. + */ +int usb_power_delivery_link_device(struct usb_power_delivery *pd, struct device *dev) +{ + int ret; + + if (IS_ERR_OR_NULL(pd) || !dev) + return 0; + + ret = sysfs_create_link(&dev->kobj, &pd->dev.kobj, "usb_power_delivery"); + if (ret) + return ret; + + get_device(&pd->dev); + get_device(dev); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_power_delivery_link_device); + +/** + * usb_power_delivery_unlink_device - Unlink device from its USB PD object. + * @pd: The USB PD instance. + * @dev: The device. + * + * Remove the symlink that was previously created with pd_link_device(). + */ +void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct device *dev) +{ + if (IS_ERR_OR_NULL(pd) || !dev) + return; + + sysfs_remove_link(&dev->kobj, "usb_power_delivery"); + put_device(&pd->dev); + put_device(dev); +} +EXPORT_SYMBOL_GPL(usb_power_delivery_unlink_device); + +/* -------------------------------------------------------------------------- */ + +int __init usb_power_delivery_init(void) +{ + return class_register(&pd_class); +} + +void __exit usb_power_delivery_exit(void) +{ + ida_destroy(&pd_ida); + class_unregister(&pd_class); +} diff --git a/drivers/usb/typec/pd.h b/drivers/usb/typec/pd.h new file mode 100644 index 000000000000..049a1aad440a --- /dev/null +++ b/drivers/usb/typec/pd.h @@ -0,0 +1,30 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_POWER_DELIVERY__ +#define __USB_POWER_DELIVERY__ + +#include +#include + +struct usb_power_delivery { + struct device dev; + int id; + u16 revision; + u16 version; +}; + +struct usb_power_delivery_capabilities { + struct device dev; + struct usb_power_delivery *pd; + enum typec_role role; +}; + +#define to_usb_power_delivery_capabilities(o) container_of(o, struct usb_power_delivery_capabilities, dev) +#define to_usb_power_delivery(o) container_of(o, struct usb_power_delivery, dev) + +struct usb_power_delivery *usb_power_delivery_find(const char *name); + +int usb_power_delivery_init(void); +void usb_power_delivery_exit(void); + +#endif /* __USB_POWER_DELIVERY__ */ diff --git a/drivers/usb/typec/retimer.c b/drivers/usb/typec/retimer.c new file mode 100644 index 000000000000..2003731f1bee --- /dev/null +++ b/drivers/usb/typec/retimer.c @@ -0,0 +1,173 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright 2022 Google LLC + * + * USB Type-C Retimer support. + * Author: Prashant Malani + * + */ + +#include +#include +#include +#include +#include +#include + +#include "class.h" +#include "retimer.h" + +static bool dev_name_ends_with(struct device *dev, const char *suffix) +{ + const char *name = dev_name(dev); + const int name_len = strlen(name); + const int suffix_len = strlen(suffix); + + if (suffix_len > name_len) + return false; + + return strcmp(name + (name_len - suffix_len), suffix) == 0; +} + +static int retimer_fwnode_match(struct device *dev, const void *fwnode) +{ + return dev_fwnode(dev) == fwnode && dev_name_ends_with(dev, "-retimer"); +} + +static void *typec_retimer_match(struct fwnode_handle *fwnode, const char *id, void *data) +{ + struct device *dev; + + if (id && !fwnode_property_present(fwnode, id)) + return NULL; + + dev = class_find_device(&retimer_class, NULL, fwnode, + retimer_fwnode_match); + + return dev ? to_typec_retimer(dev) : ERR_PTR(-EPROBE_DEFER); +} + +/** + * fwnode_typec_retimer_get - Find USB Type-C retimer. + * @fwnode: The caller device node. + * + * Finds a retimer linked to the caller. This function is primarily meant for the + * Type-C drivers. Returns a reference to the retimer on success, NULL if no + * matching connection was found, or ERR_PTR(-EPROBE_DEFER) when a connection + * was found but the retimer has not been enumerated yet. + */ +struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode) +{ + struct typec_retimer *retimer; + + retimer = fwnode_connection_find_match(fwnode, "retimer-switch", NULL, typec_retimer_match); + if (!IS_ERR_OR_NULL(retimer)) + WARN_ON(!try_module_get(retimer->dev.parent->driver->owner)); + + return retimer; +} +EXPORT_SYMBOL_GPL(fwnode_typec_retimer_get); + +/** + * typec_retimer_put - Release handle to a retimer. + * @retimer: USB Type-C Connector Retimer. + * + * Decrements reference count for @retimer. + */ +void typec_retimer_put(struct typec_retimer *retimer) +{ + if (!IS_ERR_OR_NULL(retimer)) { + module_put(retimer->dev.parent->driver->owner); + put_device(&retimer->dev); + } +} +EXPORT_SYMBOL_GPL(typec_retimer_put); + +int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state) +{ + if (IS_ERR_OR_NULL(retimer)) + return 0; + + return retimer->set(retimer, state); +} +EXPORT_SYMBOL_GPL(typec_retimer_set); + +static void typec_retimer_release(struct device *dev) +{ + kfree(to_typec_retimer(dev)); +} + +static const struct device_type typec_retimer_dev_type = { + .name = "typec_retimer", + .release = typec_retimer_release, +}; + +/** + * typec_retimer_register - Register a retimer device. + * @parent: Parent device. + * @desc: Retimer description. + * + * Some USB Type-C connectors have their physical lines routed through retimers before they + * reach muxes or host controllers. In some cases (for example: using alternate modes) + * these retimers need to be reconfigured appropriately. This function registers retimer + * switches which route and potentially modify the signals on the Type C physical lines + * enroute to the host controllers. + */ +struct typec_retimer * +typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc) +{ + struct typec_retimer *retimer; + int ret; + + if (!desc || !desc->set) + return ERR_PTR(-EINVAL); + + retimer = kzalloc(sizeof(*retimer), GFP_KERNEL); + if (!retimer) + return ERR_PTR(-ENOMEM); + + retimer->set = desc->set; + + device_initialize(&retimer->dev); + retimer->dev.parent = parent; + retimer->dev.fwnode = desc->fwnode; + retimer->dev.class = &retimer_class; + retimer->dev.type = &typec_retimer_dev_type; + retimer->dev.driver_data = desc->drvdata; + dev_set_name(&retimer->dev, "%s-retimer", + desc->name ? desc->name : dev_name(parent)); + + ret = device_add(&retimer->dev); + if (ret) { + dev_err(parent, "failed to register retimer (%d)\n", ret); + put_device(&retimer->dev); + return ERR_PTR(ret); + } + + return retimer; +} +EXPORT_SYMBOL_GPL(typec_retimer_register); + +/** + * typec_retimer_unregister - Unregister retimer device. + * @retimer: USB Type-C Connector retimer. + * + * Unregister retimer that was registered with typec_retimer_register(). + */ +void typec_retimer_unregister(struct typec_retimer *retimer) +{ + if (!IS_ERR_OR_NULL(retimer)) + device_unregister(&retimer->dev); +} +EXPORT_SYMBOL_GPL(typec_retimer_unregister); + +void *typec_retimer_get_drvdata(struct typec_retimer *retimer) +{ + return dev_get_drvdata(&retimer->dev); +} +EXPORT_SYMBOL_GPL(typec_retimer_get_drvdata); + +struct class retimer_class = { + .name = "retimer", + .owner = THIS_MODULE, +}; diff --git a/drivers/usb/typec/retimer.h b/drivers/usb/typec/retimer.h new file mode 100644 index 000000000000..fa15951d4846 --- /dev/null +++ b/drivers/usb/typec/retimer.h @@ -0,0 +1,15 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_RETIMER__ +#define __USB_TYPEC_RETIMER__ + +#include + +struct typec_retimer { + struct device dev; + typec_retimer_set_fn_t set; +}; + +#define to_typec_retimer(_dev_) container_of(_dev_, struct typec_retimer, dev) + +#endif /* __USB_TYPEC_RETIMER__ */ diff --git a/drivers/usb/typec/tcpm/tcpci.c b/drivers/usb/typec/tcpm/tcpci.c index f33e08eb7670..812784702d53 100644 --- a/drivers/usb/typec/tcpm/tcpci.c +++ b/drivers/usb/typec/tcpm/tcpci.c @@ -13,11 +13,10 @@ #include #include #include +#include #include #include -#include "tcpci.h" - #define PD_RETRY_COUNT_DEFAULT 3 #define PD_RETRY_COUNT_3_0_OR_HIGHER 2 #define AUTO_DISCHARGE_DEFAULT_THRESHOLD_MV 3500 diff --git a/drivers/usb/typec/tcpm/tcpci_maxim.c b/drivers/usb/typec/tcpm/tcpci_maxim.c index df2505570f07..4b6705f3d7b7 100644 --- a/drivers/usb/typec/tcpm/tcpci_maxim.c +++ b/drivers/usb/typec/tcpm/tcpci_maxim.c @@ -11,11 +11,10 @@ #include #include #include +#include #include #include -#include "tcpci.h" - #define PD_ACTIVITY_TIMEOUT_MS 10000 #define TCPC_VENDOR_ALERT 0x80 diff --git a/drivers/usb/typec/tcpm/tcpci_mt6360.c b/drivers/usb/typec/tcpm/tcpci_mt6360.c index 8a952eaf9016..1b7c31278ebb 100644 --- a/drivers/usb/typec/tcpm/tcpci_mt6360.c +++ b/drivers/usb/typec/tcpm/tcpci_mt6360.c @@ -11,10 +11,9 @@ #include #include #include +#include #include -#include "tcpci.h" - #define MT6360_REG_PHYCTRL1 0x80 #define MT6360_REG_PHYCTRL3 0x82 #define MT6360_REG_PHYCTRL7 0x86 diff --git a/drivers/usb/typec/tcpm/tcpci_rt1711h.c b/drivers/usb/typec/tcpm/tcpci_rt1711h.c index b56a0880a044..3291ca4948da 100644 --- a/drivers/usb/typec/tcpm/tcpci_rt1711h.c +++ b/drivers/usb/typec/tcpm/tcpci_rt1711h.c @@ -10,9 +10,9 @@ #include #include #include +#include #include #include -#include "tcpci.h" #define RT1711H_VID 0x29CF #define RT1711H_PID 0x1711 diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c index 3bc2f4ebd1fe..ea5a917c51b1 100644 --- a/drivers/usb/typec/tcpm/tcpm.c +++ b/drivers/usb/typec/tcpm/tcpm.c @@ -394,6 +394,14 @@ struct tcpm_port { bool explicit_contract; unsigned int rx_msgid; + /* USB PD objects */ + struct usb_power_delivery *pd; + struct usb_power_delivery_capabilities *port_source_caps; + struct usb_power_delivery_capabilities *port_sink_caps; + struct usb_power_delivery *partner_pd; + struct usb_power_delivery_capabilities *partner_source_caps; + struct usb_power_delivery_capabilities *partner_sink_caps; + /* Partner capabilities/requests */ u32 sink_request; u32 source_caps[PDO_MAX_OBJECTS]; @@ -471,7 +479,7 @@ struct tcpm_port { /* * When set, port requests PD_P_SNK_STDBY_MW upon entering SNK_DISCOVERY and - * the actual currrent limit after RX of PD_CTRL_PSRDY for PD link, + * the actual current limit after RX of PD_CTRL_PSRDY for PD link, * SNK_READY for non-pd link. */ bool slow_charger_loop; @@ -2352,6 +2360,52 @@ static void tcpm_pd_handle_msg(struct tcpm_port *port, } } +static int tcpm_register_source_caps(struct tcpm_port *port) +{ + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; + + if (!port->partner_pd) + port->partner_pd = usb_power_delivery_register(NULL, &desc); + if (IS_ERR(port->partner_pd)) + return PTR_ERR(port->partner_pd); + + memcpy(caps.pdo, port->source_caps, sizeof(u32) * port->nr_source_caps); + caps.role = TYPEC_SOURCE; + + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); + + port->partner_source_caps = cap; + + return 0; +} + +static int tcpm_register_sink_caps(struct tcpm_port *port) +{ + struct usb_power_delivery_desc desc = { port->negotiated_rev }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; + + if (!port->partner_pd) + port->partner_pd = usb_power_delivery_register(NULL, &desc); + if (IS_ERR(port->partner_pd)) + return PTR_ERR(port->partner_pd); + + memcpy(caps.pdo, port->sink_caps, sizeof(u32) * port->nr_sink_caps); + caps.role = TYPEC_SINK; + + cap = usb_power_delivery_register_capabilities(port->partner_pd, &caps); + if (IS_ERR(cap)) + return PTR_ERR(cap); + + port->partner_sink_caps = cap; + + return 0; +} + static void tcpm_pd_data_request(struct tcpm_port *port, const struct pd_message *msg) { @@ -2381,6 +2435,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port, tcpm_validate_caps(port, port->source_caps, port->nr_source_caps); + tcpm_register_source_caps(port); + /* * Adjust revision in subsequent message headers, as required, * to comply with 6.2.1.1.5 of the USB PD 3.0 spec. We don't @@ -2488,6 +2544,8 @@ static void tcpm_pd_data_request(struct tcpm_port *port, port->nr_sink_caps = cnt; port->sink_cap_done = true; + tcpm_register_sink_caps(port); + if (port->ams == GET_SINK_CAPABILITIES) tcpm_set_state(port, ready_state(port), 0); /* Unexpected Sink Capabilities */ @@ -3554,6 +3612,7 @@ static void tcpm_typec_connect(struct tcpm_port *port) port->partner = typec_register_partner(port->typec_port, &port->partner_desc); port->connected = true; + typec_partner_set_usb_power_delivery(port->partner, port->partner_pd); } } @@ -3622,6 +3681,7 @@ out_disable_mux: static void tcpm_typec_disconnect(struct tcpm_port *port) { if (port->connected) { + typec_partner_set_usb_power_delivery(port->partner, NULL); typec_unregister_partner(port->partner); port->partner = NULL; port->connected = false; @@ -3684,6 +3744,13 @@ static void tcpm_reset_port(struct tcpm_port *port) port->sink_cap_done = false; if (port->tcpc->enable_frs) port->tcpc->enable_frs(port->tcpc, false); + + usb_power_delivery_unregister_capabilities(port->partner_sink_caps); + port->partner_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(port->partner_source_caps); + port->partner_source_caps = NULL; + usb_power_delivery_unregister(port->partner_pd); + port->partner_pd = NULL; } static void tcpm_detach(struct tcpm_port *port) @@ -4453,7 +4520,7 @@ static void run_state_machine(struct tcpm_port *port) * The specification suggests that dual mode ports in sink * mode should transition to state PE_SRC_Transition_to_default. * See USB power delivery specification chapter 8.3.3.6.1.3. - * This would mean to to + * This would mean to * - turn off VCONN, reset power supply * - request hardware reset * - turn on VCONN @@ -5924,6 +5991,68 @@ void tcpm_tcpc_reset(struct tcpm_port *port) } EXPORT_SYMBOL_GPL(tcpm_tcpc_reset); +static void tcpm_port_unregister_pd(struct tcpm_port *port) +{ + usb_power_delivery_unregister_capabilities(port->port_sink_caps); + port->port_sink_caps = NULL; + usb_power_delivery_unregister_capabilities(port->port_source_caps); + port->port_source_caps = NULL; + usb_power_delivery_unregister(port->pd); + port->pd = NULL; +} + +static int tcpm_port_register_pd(struct tcpm_port *port) +{ + struct usb_power_delivery_desc desc = { port->typec_caps.pd_revision }; + struct usb_power_delivery_capabilities_desc caps = { }; + struct usb_power_delivery_capabilities *cap; + int ret; + + if (!port->nr_src_pdo && !port->nr_snk_pdo) + return 0; + + port->pd = usb_power_delivery_register(port->dev, &desc); + if (IS_ERR(port->pd)) { + ret = PTR_ERR(port->pd); + goto err_unregister; + } + + if (port->nr_src_pdo) { + memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->src_pdo, + port->nr_src_pdo * sizeof(u32), 0); + caps.role = TYPEC_SOURCE; + + cap = usb_power_delivery_register_capabilities(port->pd, &caps); + if (IS_ERR(cap)) { + ret = PTR_ERR(cap); + goto err_unregister; + } + + port->port_source_caps = cap; + } + + if (port->nr_snk_pdo) { + memcpy_and_pad(caps.pdo, sizeof(caps.pdo), port->snk_pdo, + port->nr_snk_pdo * sizeof(u32), 0); + caps.role = TYPEC_SINK; + + cap = usb_power_delivery_register_capabilities(port->pd, &caps); + if (IS_ERR(cap)) { + ret = PTR_ERR(cap); + goto err_unregister; + } + + port->port_sink_caps = cap; + } + + return 0; + +err_unregister: + tcpm_port_unregister_pd(port); + + return ret; +} + static int tcpm_fw_get_caps(struct tcpm_port *port, struct fwnode_handle *fwnode) { @@ -6382,10 +6511,16 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) goto out_role_sw_put; power_supply_changed(port->psy); + err = tcpm_port_register_pd(port); + if (err) + goto out_role_sw_put; + + port->typec_caps.pd = port->pd; + port->typec_port = typec_register_port(port->dev, &port->typec_caps); if (IS_ERR(port->typec_port)) { err = PTR_ERR(port->typec_port); - goto out_role_sw_put; + goto out_unregister_pd; } typec_port_register_altmodes(port->typec_port, @@ -6400,6 +6535,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc) tcpm_log(port, "%s: registered", dev_name(dev)); return port; +out_unregister_pd: + tcpm_port_unregister_pd(port); out_role_sw_put: usb_role_switch_put(port->role_sw); out_destroy_wq: @@ -6422,6 +6559,9 @@ void tcpm_unregister_port(struct tcpm_port *port) hrtimer_cancel(&port->state_machine_timer); tcpm_reset_port(port); + + tcpm_port_unregister_pd(port); + for (i = 0; i < ARRAY_SIZE(port->port_altmode); i++) typec_unregister_altmode(port->port_altmode[i]); typec_unregister_port(port->typec_port); diff --git a/drivers/usb/typec/ucsi/Kconfig b/drivers/usb/typec/ucsi/Kconfig index 5e9b37b3f25e..8f9c4b9f31f7 100644 --- a/drivers/usb/typec/ucsi/Kconfig +++ b/drivers/usb/typec/ucsi/Kconfig @@ -48,4 +48,14 @@ config UCSI_ACPI To compile the driver as a module, choose M here: the module will be called ucsi_acpi +config UCSI_STM32G0 + tristate "UCSI Interface Driver for STM32G0" + depends on I2C + help + This driver enables UCSI support on platforms that expose a STM32G0 + Type-C controller over I2C interface. + + To compile the driver as a module, choose M here: the module will be + called ucsi_stm32g0. + endif diff --git a/drivers/usb/typec/ucsi/Makefile b/drivers/usb/typec/ucsi/Makefile index 8a8eb5cb8e0f..480d533d762f 100644 --- a/drivers/usb/typec/ucsi/Makefile +++ b/drivers/usb/typec/ucsi/Makefile @@ -17,3 +17,4 @@ endif obj-$(CONFIG_UCSI_ACPI) += ucsi_acpi.o obj-$(CONFIG_UCSI_CCG) += ucsi_ccg.o +obj-$(CONFIG_UCSI_STM32G0) += ucsi_stm32g0.o diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c index cbd862f9f2a1..1aea46493b85 100644 --- a/drivers/usb/typec/ucsi/ucsi.c +++ b/drivers/usb/typec/ucsi/ucsi.c @@ -76,6 +76,10 @@ static int ucsi_read_error(struct ucsi *ucsi) if (ret) return ret; + ret = ucsi_acknowledge_command(ucsi); + if (ret) + return ret; + switch (error) { case UCSI_ERROR_INCOMPATIBLE_PARTNER: return -EOPNOTSUPP; diff --git a/drivers/usb/typec/ucsi/ucsi_ccg.c b/drivers/usb/typec/ucsi/ucsi_ccg.c index 6db7c8ddd51c..5c0bf48be766 100644 --- a/drivers/usb/typec/ucsi/ucsi_ccg.c +++ b/drivers/usb/typec/ucsi/ucsi_ccg.c @@ -627,6 +627,16 @@ err_clear_irq: return IRQ_HANDLED; } +static int ccg_request_irq(struct ucsi_ccg *uc) +{ + unsigned long flags = IRQF_ONESHOT; + + if (!has_acpi_companion(uc->dev)) + flags |= IRQF_TRIGGER_HIGH; + + return request_threaded_irq(uc->irq, NULL, ccg_irq_handler, flags, dev_name(uc->dev), uc); +} + static void ccg_pm_workaround_work(struct work_struct *pm_work) { ccg_irq_handler(0, container_of(pm_work, struct ucsi_ccg, pm_work)); @@ -1250,9 +1260,7 @@ static int ccg_restart(struct ucsi_ccg *uc) return status; } - status = request_threaded_irq(uc->irq, NULL, ccg_irq_handler, - IRQF_ONESHOT | IRQF_TRIGGER_HIGH, - dev_name(dev), uc); + status = ccg_request_irq(uc); if (status < 0) { dev_err(dev, "request_threaded_irq failed - %d\n", status); return status; @@ -1331,6 +1339,7 @@ static int ucsi_ccg_probe(struct i2c_client *client, uc->dev = dev; uc->client = client; + uc->irq = client->irq; mutex_init(&uc->lock); init_completion(&uc->complete); INIT_WORK(&uc->work, ccg_update_firmware); @@ -1366,16 +1375,12 @@ static int ucsi_ccg_probe(struct i2c_client *client, ucsi_set_drvdata(uc->ucsi, uc); - status = request_threaded_irq(client->irq, NULL, ccg_irq_handler, - IRQF_ONESHOT | IRQF_TRIGGER_HIGH, - dev_name(dev), uc); + status = ccg_request_irq(uc); if (status < 0) { dev_err(uc->dev, "request_threaded_irq failed - %d\n", status); goto out_ucsi_destroy; } - uc->irq = client->irq; - status = ucsi_register(uc->ucsi); if (status) goto out_free_irq; @@ -1418,6 +1423,12 @@ static const struct i2c_device_id ucsi_ccg_device_id[] = { }; MODULE_DEVICE_TABLE(i2c, ucsi_ccg_device_id); +static const struct acpi_device_id amd_i2c_ucsi_match[] = { + {"AMDI0042"}, + {} +}; +MODULE_DEVICE_TABLE(acpi, amd_i2c_ucsi_match); + static int ucsi_ccg_resume(struct device *dev) { struct i2c_client *client = to_i2c_client(dev); @@ -1459,6 +1470,7 @@ static struct i2c_driver ucsi_ccg_driver = { .name = "ucsi_ccg", .pm = &ucsi_ccg_pm, .dev_groups = ucsi_ccg_groups, + .acpi_match_table = amd_i2c_ucsi_match, }, .probe = ucsi_ccg_probe, .remove = ucsi_ccg_remove, diff --git a/drivers/usb/typec/ucsi/ucsi_stm32g0.c b/drivers/usb/typec/ucsi/ucsi_stm32g0.c new file mode 100644 index 000000000000..061551d464f1 --- /dev/null +++ b/drivers/usb/typec/ucsi/ucsi_stm32g0.c @@ -0,0 +1,777 @@ +// SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +/* + * UCSI driver for STMicroelectronics STM32G0 Type-C PD controller + * + * Copyright (C) 2022, STMicroelectronics - All Rights Reserved + * Author: Fabrice Gasnier . + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "ucsi.h" + +/* STM32G0 I2C bootloader addr: 0b1010001x (See AN2606) */ +#define STM32G0_I2C_BL_ADDR (0xa2 >> 1) + +/* STM32G0 I2C bootloader max data size */ +#define STM32G0_I2C_BL_SZ 256 + +/* STM32 I2C bootloader commands (See AN4221) */ +#define STM32_CMD_GVR 0x01 /* Gets the bootloader version */ +#define STM32_CMD_GVR_LEN 1 +#define STM32_CMD_RM 0x11 /* Reag memory */ +#define STM32_CMD_WM 0x31 /* Write memory */ +#define STM32_CMD_ADDR_LEN 5 /* Address len for go, mem write... */ +#define STM32_CMD_ERASE 0x44 /* Erase page, bank or all */ +#define STM32_CMD_ERASE_SPECIAL_LEN 3 +#define STM32_CMD_GLOBAL_MASS_ERASE 0xffff /* All-bank erase */ + +/* STM32 I2C bootloader answer status */ +#define STM32G0_I2C_BL_ACK 0x79 +#define STM32G0_I2C_BL_NACK 0x1f +#define STM32G0_I2C_BL_BUSY 0x76 + +/* STM32G0 flash definitions */ +#define STM32G0_USER_OPTION_BYTES 0x1fff7800 +#define STM32G0_USER_OB_NBOOT0 BIT(26) +#define STM32G0_USER_OB_NBOOT_SEL BIT(24) +#define STM32G0_USER_OB_BOOT_MAIN (STM32G0_USER_OB_NBOOT0 | STM32G0_USER_OB_NBOOT_SEL) +#define STM32G0_MAIN_MEM_ADDR 0x08000000 + +/* STM32 Firmware definitions: additional commands */ +#define STM32G0_FW_GETVER 0x00 /* Gets the firmware version */ +#define STM32G0_FW_GETVER_LEN 4 +#define STM32G0_FW_RSTGOBL 0x21 /* Reset and go to bootloader */ +#define STM32G0_FW_KEYWORD 0xa56959a6 + +/* ucsi_stm32g0_fw_info located at the end of the firmware */ +struct ucsi_stm32g0_fw_info { + u32 version; + u32 keyword; +}; + +struct ucsi_stm32g0 { + struct i2c_client *client; + struct i2c_client *i2c_bl; + bool in_bootloader; + u8 bl_version; + struct completion complete; + struct device *dev; + unsigned long flags; + const char *fw_name; + struct ucsi *ucsi; + bool suspended; + bool wakeup_event; +}; + +/* + * Bootloader commands helpers: + * - send command (2 bytes) + * - check ack + * Then either: + * - receive data + * - receive data + check ack + * - send data + check ack + * These operations depends on the command and have various length. + */ +static int ucsi_stm32g0_bl_check_ack(struct ucsi *ucsi) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->i2c_bl; + unsigned char ack; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = 1, + .buf = &ack, + }, + }; + int ret; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(g0->dev, "i2c bl ack (%02x), error: %d\n", client->addr, ret); + + return ret < 0 ? ret : -EIO; + } + + /* The 'ack' byte should contain bootloader answer: ack/nack/busy */ + switch (ack) { + case STM32G0_I2C_BL_ACK: + return 0; + case STM32G0_I2C_BL_NACK: + return -ENOENT; + case STM32G0_I2C_BL_BUSY: + return -EBUSY; + default: + dev_err(g0->dev, "i2c bl ack (%02x), invalid byte: %02x\n", + client->addr, ack); + return -EINVAL; + } +} + +static int ucsi_stm32g0_bl_cmd_check_ack(struct ucsi *ucsi, unsigned int cmd, bool check_ack) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->i2c_bl; + unsigned char buf[2]; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = sizeof(buf), + .buf = buf, + }, + }; + int ret; + + /* + * Send STM32 bootloader command format is two bytes: + * - command code + * - XOR'ed command code + */ + buf[0] = cmd; + buf[1] = cmd ^ 0xff; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_dbg(g0->dev, "i2c bl cmd %d (%02x), error: %d\n", cmd, client->addr, ret); + + return ret < 0 ? ret : -EIO; + } + + if (check_ack) + return ucsi_stm32g0_bl_check_ack(ucsi); + + return 0; +} + +static int ucsi_stm32g0_bl_cmd(struct ucsi *ucsi, unsigned int cmd) +{ + return ucsi_stm32g0_bl_cmd_check_ack(ucsi, cmd, true); +} + +static int ucsi_stm32g0_bl_rcv_check_ack(struct ucsi *ucsi, void *data, size_t len, bool check_ack) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->i2c_bl; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = data, + }, + }; + int ret; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(g0->dev, "i2c bl rcv %02x, error: %d\n", client->addr, ret); + + return ret < 0 ? ret : -EIO; + } + + if (check_ack) + return ucsi_stm32g0_bl_check_ack(ucsi); + + return 0; +} + +static int ucsi_stm32g0_bl_rcv(struct ucsi *ucsi, void *data, size_t len) +{ + return ucsi_stm32g0_bl_rcv_check_ack(ucsi, data, len, true); +} + +static int ucsi_stm32g0_bl_rcv_woack(struct ucsi *ucsi, void *data, size_t len) +{ + return ucsi_stm32g0_bl_rcv_check_ack(ucsi, data, len, false); +} + +static int ucsi_stm32g0_bl_send(struct ucsi *ucsi, void *data, size_t len) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->i2c_bl; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = len, + .buf = data, + }, + }; + int ret; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(g0->dev, "i2c bl send %02x, error: %d\n", client->addr, ret); + + return ret < 0 ? ret : -EIO; + } + + return ucsi_stm32g0_bl_check_ack(ucsi); +} + +/* Bootloader commands */ +static int ucsi_stm32g0_bl_get_version(struct ucsi *ucsi, u8 *bl_version) +{ + int ret; + + ret = ucsi_stm32g0_bl_cmd(ucsi, STM32_CMD_GVR); + if (ret) + return ret; + + return ucsi_stm32g0_bl_rcv(ucsi, bl_version, STM32_CMD_GVR_LEN); +} + +static int ucsi_stm32g0_bl_send_addr(struct ucsi *ucsi, u32 addr) +{ + u8 data8[STM32_CMD_ADDR_LEN]; + + /* Address format: 4 bytes addr (MSB first) + XOR'ed addr bytes */ + put_unaligned_be32(addr, data8); + data8[4] = data8[0] ^ data8[1] ^ data8[2] ^ data8[3]; + + return ucsi_stm32g0_bl_send(ucsi, data8, STM32_CMD_ADDR_LEN); +} + +static int ucsi_stm32g0_bl_global_mass_erase(struct ucsi *ucsi) +{ + u8 data8[4]; + u16 *data16 = (u16 *)&data8[0]; + int ret; + + data16[0] = STM32_CMD_GLOBAL_MASS_ERASE; + data8[2] = data8[0] ^ data8[1]; + + ret = ucsi_stm32g0_bl_cmd(ucsi, STM32_CMD_ERASE); + if (ret) + return ret; + + return ucsi_stm32g0_bl_send(ucsi, data8, STM32_CMD_ERASE_SPECIAL_LEN); +} + +static int ucsi_stm32g0_bl_write(struct ucsi *ucsi, u32 addr, const void *data, size_t len) +{ + u8 *data8; + int i, ret; + + if (!len || len > STM32G0_I2C_BL_SZ) + return -EINVAL; + + /* Write memory: len bytes -1, data up to 256 bytes + XOR'ed bytes */ + data8 = kmalloc(STM32G0_I2C_BL_SZ + 2, GFP_KERNEL); + if (!data8) + return -ENOMEM; + + ret = ucsi_stm32g0_bl_cmd(ucsi, STM32_CMD_WM); + if (ret) + goto free; + + ret = ucsi_stm32g0_bl_send_addr(ucsi, addr); + if (ret) + goto free; + + data8[0] = len - 1; + memcpy(data8 + 1, data, len); + data8[len + 1] = data8[0]; + for (i = 1; i <= len; i++) + data8[len + 1] ^= data8[i]; + + ret = ucsi_stm32g0_bl_send(ucsi, data8, len + 2); +free: + kfree(data8); + + return ret; +} + +static int ucsi_stm32g0_bl_read(struct ucsi *ucsi, u32 addr, void *data, size_t len) +{ + int ret; + + if (!len || len > STM32G0_I2C_BL_SZ) + return -EINVAL; + + ret = ucsi_stm32g0_bl_cmd(ucsi, STM32_CMD_RM); + if (ret) + return ret; + + ret = ucsi_stm32g0_bl_send_addr(ucsi, addr); + if (ret) + return ret; + + ret = ucsi_stm32g0_bl_cmd(ucsi, len - 1); + if (ret) + return ret; + + return ucsi_stm32g0_bl_rcv_woack(ucsi, data, len); +} + +/* Firmware commands (the same address as the bootloader) */ +static int ucsi_stm32g0_fw_cmd(struct ucsi *ucsi, unsigned int cmd) +{ + return ucsi_stm32g0_bl_cmd_check_ack(ucsi, cmd, false); +} + +static int ucsi_stm32g0_fw_rcv(struct ucsi *ucsi, void *data, size_t len) +{ + return ucsi_stm32g0_bl_rcv_woack(ucsi, data, len); +} + +/* UCSI ops */ +static int ucsi_stm32g0_read(struct ucsi *ucsi, unsigned int offset, void *val, size_t len) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->client; + u8 reg = offset; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + .len = 1, + .buf = ®, + }, + { + .addr = client->addr, + .flags = I2C_M_RD, + .len = len, + .buf = val, + }, + }; + int ret; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + if (ret != ARRAY_SIZE(msg)) { + dev_err(g0->dev, "i2c read %02x, %02x error: %d\n", client->addr, reg, ret); + + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int ucsi_stm32g0_async_write(struct ucsi *ucsi, unsigned int offset, const void *val, + size_t len) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->client; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .flags = 0, + } + }; + unsigned char *buf; + int ret; + + buf = kmalloc(len + 1, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + buf[0] = offset; + memcpy(&buf[1], val, len); + msg[0].len = len + 1; + msg[0].buf = buf; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + kfree(buf); + if (ret != ARRAY_SIZE(msg)) { + dev_err(g0->dev, "i2c write %02x, %02x error: %d\n", client->addr, offset, ret); + + return ret < 0 ? ret : -EIO; + } + + return 0; +} + +static int ucsi_stm32g0_sync_write(struct ucsi *ucsi, unsigned int offset, const void *val, + size_t len) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + int ret; + + set_bit(COMMAND_PENDING, &g0->flags); + + ret = ucsi_stm32g0_async_write(ucsi, offset, val, len); + if (ret) + goto out_clear_bit; + + if (!wait_for_completion_timeout(&g0->complete, msecs_to_jiffies(5000))) + ret = -ETIMEDOUT; + +out_clear_bit: + clear_bit(COMMAND_PENDING, &g0->flags); + + return ret; +} + +static irqreturn_t ucsi_stm32g0_irq_handler(int irq, void *data) +{ + struct ucsi_stm32g0 *g0 = data; + u32 cci; + int ret; + + if (g0->suspended) + g0->wakeup_event = true; + + ret = ucsi_stm32g0_read(g0->ucsi, UCSI_CCI, &cci, sizeof(cci)); + if (ret) + return IRQ_NONE; + + if (UCSI_CCI_CONNECTOR(cci)) + ucsi_connector_change(g0->ucsi, UCSI_CCI_CONNECTOR(cci)); + + if (test_bit(COMMAND_PENDING, &g0->flags) && + cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) + complete(&g0->complete); + + return IRQ_HANDLED; +} + +static const struct ucsi_operations ucsi_stm32g0_ops = { + .read = ucsi_stm32g0_read, + .sync_write = ucsi_stm32g0_sync_write, + .async_write = ucsi_stm32g0_async_write, +}; + +static int ucsi_stm32g0_register(struct ucsi *ucsi) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->client; + int ret; + + /* Request alert interrupt */ + ret = request_threaded_irq(client->irq, NULL, ucsi_stm32g0_irq_handler, IRQF_ONESHOT, + dev_name(g0->dev), g0); + if (ret) { + dev_err(g0->dev, "request IRQ failed: %d\n", ret); + return ret; + } + + ret = ucsi_register(ucsi); + if (ret) { + dev_err_probe(g0->dev, ret, "ucsi_register failed\n"); + free_irq(client->irq, g0); + return ret; + } + + return 0; +} + +static void ucsi_stm32g0_unregister(struct ucsi *ucsi) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + struct i2c_client *client = g0->client; + + ucsi_unregister(ucsi); + free_irq(client->irq, g0); +} + +static void ucsi_stm32g0_fw_cb(const struct firmware *fw, void *context) +{ + struct ucsi_stm32g0 *g0; + const u8 *data, *end; + const struct ucsi_stm32g0_fw_info *fw_info; + u32 addr = STM32G0_MAIN_MEM_ADDR, ob, fw_version; + int ret, size; + + if (!context) + return; + + g0 = ucsi_get_drvdata(context); + + if (!fw) + goto fw_release; + + fw_info = (struct ucsi_stm32g0_fw_info *)(fw->data + fw->size - sizeof(*fw_info)); + + if (!g0->in_bootloader) { + /* Read running firmware version */ + ret = ucsi_stm32g0_fw_cmd(g0->ucsi, STM32G0_FW_GETVER); + if (ret) { + dev_err(g0->dev, "Get version cmd failed %d\n", ret); + goto fw_release; + } + ret = ucsi_stm32g0_fw_rcv(g0->ucsi, &fw_version, + STM32G0_FW_GETVER_LEN); + if (ret) { + dev_err(g0->dev, "Get version failed %d\n", ret); + goto fw_release; + } + + /* Sanity check on keyword and firmware version */ + if (fw_info->keyword != STM32G0_FW_KEYWORD || fw_info->version == fw_version) + goto fw_release; + + dev_info(g0->dev, "Flashing FW: %08x (%08x cur)\n", fw_info->version, fw_version); + + /* Switch to bootloader mode */ + ucsi_stm32g0_unregister(g0->ucsi); + ret = ucsi_stm32g0_fw_cmd(g0->ucsi, STM32G0_FW_RSTGOBL); + if (ret) { + dev_err(g0->dev, "bootloader cmd failed %d\n", ret); + goto fw_release; + } + g0->in_bootloader = true; + + /* STM32G0 reboot delay */ + msleep(100); + } + + ret = ucsi_stm32g0_bl_global_mass_erase(g0->ucsi); + if (ret) { + dev_err(g0->dev, "Erase failed %d\n", ret); + goto fw_release; + } + + data = fw->data; + end = fw->data + fw->size; + while (data < end) { + if ((end - data) < STM32G0_I2C_BL_SZ) + size = end - data; + else + size = STM32G0_I2C_BL_SZ; + + ret = ucsi_stm32g0_bl_write(g0->ucsi, addr, data, size); + if (ret) { + dev_err(g0->dev, "Write failed %d\n", ret); + goto fw_release; + } + addr += size; + data += size; + } + + dev_dbg(g0->dev, "Configure to boot from main flash\n"); + + ret = ucsi_stm32g0_bl_read(g0->ucsi, STM32G0_USER_OPTION_BYTES, &ob, sizeof(ob)); + if (ret) { + dev_err(g0->dev, "read user option bytes failed %d\n", ret); + goto fw_release; + } + + dev_dbg(g0->dev, "STM32G0_USER_OPTION_BYTES 0x%08x\n", ob); + + /* Configure user option bytes to boot from main flash next time */ + ob |= STM32G0_USER_OB_BOOT_MAIN; + + /* Writing option bytes will also reset G0 for updates to be loaded */ + ret = ucsi_stm32g0_bl_write(g0->ucsi, STM32G0_USER_OPTION_BYTES, &ob, sizeof(ob)); + if (ret) { + dev_err(g0->dev, "write user option bytes failed %d\n", ret); + goto fw_release; + } + + dev_info(g0->dev, "Starting, option bytes:0x%08x\n", ob); + + /* STM32G0 FW boot delay */ + msleep(500); + + /* Register UCSI interface */ + if (!ucsi_stm32g0_register(g0->ucsi)) + g0->in_bootloader = false; + +fw_release: + release_firmware(fw); +} + +static int ucsi_stm32g0_probe_bootloader(struct ucsi *ucsi) +{ + struct ucsi_stm32g0 *g0 = ucsi_get_drvdata(ucsi); + int ret; + u16 ucsi_version; + + /* firmware-name is optional */ + if (device_property_present(g0->dev, "firmware-name")) { + ret = device_property_read_string(g0->dev, "firmware-name", &g0->fw_name); + if (ret < 0) + return dev_err_probe(g0->dev, ret, "Error reading firmware-name\n"); + } + + if (g0->fw_name) { + /* STM32G0 in bootloader mode communicates at reserved address 0x51 */ + g0->i2c_bl = i2c_new_dummy_device(g0->client->adapter, STM32G0_I2C_BL_ADDR); + if (IS_ERR(g0->i2c_bl)) { + ret = dev_err_probe(g0->dev, PTR_ERR(g0->i2c_bl), + "Failed to register booloader I2C address\n"); + return ret; + } + } + + /* + * Try to guess if the STM32G0 is running a UCSI firmware. First probe the UCSI FW at its + * i2c address. Fallback to bootloader i2c address only if firmware-name is specified. + */ + ret = ucsi_stm32g0_read(ucsi, UCSI_VERSION, &ucsi_version, sizeof(ucsi_version)); + if (!ret || !g0->fw_name) + return ret; + + /* Speculatively read the bootloader version that has a known length. */ + ret = ucsi_stm32g0_bl_get_version(ucsi, &g0->bl_version); + if (ret < 0) { + i2c_unregister_device(g0->i2c_bl); + return ret; + } + + /* Device in bootloader mode */ + g0->in_bootloader = true; + dev_info(g0->dev, "Bootloader Version 0x%02x\n", g0->bl_version); + + return 0; +} + +static int ucsi_stm32g0_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct ucsi_stm32g0 *g0; + int ret; + + g0 = devm_kzalloc(dev, sizeof(*g0), GFP_KERNEL); + if (!g0) + return -ENOMEM; + + g0->dev = dev; + g0->client = client; + init_completion(&g0->complete); + i2c_set_clientdata(client, g0); + + g0->ucsi = ucsi_create(dev, &ucsi_stm32g0_ops); + if (IS_ERR(g0->ucsi)) + return PTR_ERR(g0->ucsi); + + ucsi_set_drvdata(g0->ucsi, g0); + + ret = ucsi_stm32g0_probe_bootloader(g0->ucsi); + if (ret < 0) + goto destroy; + + /* + * Don't register in bootloader mode: wait for the firmware to be loaded and started before + * registering UCSI device. + */ + if (!g0->in_bootloader) { + ret = ucsi_stm32g0_register(g0->ucsi); + if (ret < 0) + goto freei2c; + } + + if (g0->fw_name) { + /* + * Asynchronously flash (e.g. bootloader mode) or update the running firmware, + * not to hang the boot process + */ + ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_UEVENT, g0->fw_name, g0->dev, + GFP_KERNEL, g0->ucsi, ucsi_stm32g0_fw_cb); + if (ret < 0) { + dev_err_probe(dev, ret, "firmware request failed\n"); + goto unregister; + } + } + + return 0; + +unregister: + if (!g0->in_bootloader) + ucsi_stm32g0_unregister(g0->ucsi); +freei2c: + if (g0->fw_name) + i2c_unregister_device(g0->i2c_bl); +destroy: + ucsi_destroy(g0->ucsi); + + return ret; +} + +static int ucsi_stm32g0_remove(struct i2c_client *client) +{ + struct ucsi_stm32g0 *g0 = i2c_get_clientdata(client); + + if (!g0->in_bootloader) + ucsi_stm32g0_unregister(g0->ucsi); + if (g0->fw_name) + i2c_unregister_device(g0->i2c_bl); + ucsi_destroy(g0->ucsi); + + return 0; +} + +static int ucsi_stm32g0_suspend(struct device *dev) +{ + struct ucsi_stm32g0 *g0 = dev_get_drvdata(dev); + struct i2c_client *client = g0->client; + + if (g0->in_bootloader) + return 0; + + /* Keep the interrupt disabled until the i2c bus has been resumed */ + disable_irq(client->irq); + + g0->suspended = true; + g0->wakeup_event = false; + + if (device_may_wakeup(dev) || device_wakeup_path(dev)) + enable_irq_wake(client->irq); + + return 0; +} + +static int ucsi_stm32g0_resume(struct device *dev) +{ + struct ucsi_stm32g0 *g0 = dev_get_drvdata(dev); + struct i2c_client *client = g0->client; + + if (g0->in_bootloader) + return 0; + + if (device_may_wakeup(dev) || device_wakeup_path(dev)) + disable_irq_wake(client->irq); + + enable_irq(client->irq); + + /* Enforce any pending handler gets called to signal a wakeup_event */ + synchronize_irq(client->irq); + + if (g0->wakeup_event) + pm_wakeup_event(g0->dev, 0); + + g0->suspended = false; + + return 0; +} + +static DEFINE_SIMPLE_DEV_PM_OPS(ucsi_stm32g0_pm_ops, ucsi_stm32g0_suspend, ucsi_stm32g0_resume); + +static const struct of_device_id __maybe_unused ucsi_stm32g0_typec_of_match[] = { + { .compatible = "st,stm32g0-typec" }, + {}, +}; +MODULE_DEVICE_TABLE(of, ucsi_stm32g0_typec_of_match); + +static const struct i2c_device_id ucsi_stm32g0_typec_i2c_devid[] = { + {"stm32g0-typec", 0}, + {}, +}; +MODULE_DEVICE_TABLE(i2c, ucsi_stm32g0_typec_i2c_devid); + +static struct i2c_driver ucsi_stm32g0_i2c_driver = { + .driver = { + .name = "ucsi-stm32g0-i2c", + .of_match_table = of_match_ptr(ucsi_stm32g0_typec_of_match), + .pm = pm_sleep_ptr(&ucsi_stm32g0_pm_ops), + }, + .probe = ucsi_stm32g0_probe, + .remove = ucsi_stm32g0_remove, + .id_table = ucsi_stm32g0_typec_i2c_devid +}; +module_i2c_driver(ucsi_stm32g0_i2c_driver); + +MODULE_AUTHOR("Fabrice Gasnier "); +MODULE_DESCRIPTION("STMicroelectronics STM32G0 Type-C controller"); +MODULE_LICENSE("Dual BSD/GPL"); +MODULE_ALIAS("platform:ucsi-stm32g0"); diff --git a/drivers/usb/usbip/vudc_rx.c b/drivers/usb/usbip/vudc_rx.c index 1e8a23d92cb4..d4a2f30a7580 100644 --- a/drivers/usb/usbip/vudc_rx.c +++ b/drivers/usb/usbip/vudc_rx.c @@ -104,18 +104,18 @@ static int v_recv_cmd_submit(struct vudc *udc, if (pdu->base.direction == USBIP_DIR_IN) address |= USB_DIR_IN; - spin_lock_irq(&udc->lock); + spin_lock_irqsave(&udc->lock, flags); urb_p->ep = vudc_find_endpoint(udc, address); if (!urb_p->ep) { /* we don't know the type, there may be isoc data! */ dev_err(&udc->pdev->dev, "request to nonexistent endpoint"); - spin_unlock_irq(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); usbip_event_add(&udc->ud, VUDC_EVENT_ERROR_TCP); ret = -EPIPE; goto free_urbp; } urb_p->type = urb_p->ep->type; - spin_unlock_irq(&udc->lock); + spin_unlock_irqrestore(&udc->lock, flags); urb_p->new = 1; urb_p->seqnum = pdu->base.seqnum; diff --git a/drivers/usb/usbip/vudc_sysfs.c b/drivers/usb/usbip/vudc_sysfs.c index d1cf6b51bf85..c95e6b2bfd32 100644 --- a/drivers/usb/usbip/vudc_sysfs.c +++ b/drivers/usb/usbip/vudc_sysfs.c @@ -128,7 +128,7 @@ static ssize_t usbip_sockfd_store(struct device *dev, goto unlock; } - spin_lock_irq(&udc->ud.lock); + spin_lock(&udc->ud.lock); if (udc->ud.status != SDEV_ST_AVAILABLE) { ret = -EINVAL; @@ -150,7 +150,7 @@ static ssize_t usbip_sockfd_store(struct device *dev, } /* unlock and create threads and get tasks */ - spin_unlock_irq(&udc->ud.lock); + spin_unlock(&udc->ud.lock); spin_unlock_irqrestore(&udc->lock, flags); tcp_rx = kthread_create(&v_rx_loop, &udc->ud, "vudc_rx"); @@ -173,14 +173,14 @@ static ssize_t usbip_sockfd_store(struct device *dev, /* lock and update udc->ud state */ spin_lock_irqsave(&udc->lock, flags); - spin_lock_irq(&udc->ud.lock); + spin_lock(&udc->ud.lock); udc->ud.tcp_socket = socket; udc->ud.tcp_rx = tcp_rx; udc->ud.tcp_tx = tcp_tx; udc->ud.status = SDEV_ST_USED; - spin_unlock_irq(&udc->ud.lock); + spin_unlock(&udc->ud.lock); ktime_get_ts64(&udc->start_time); v_start_timer(udc); @@ -201,12 +201,12 @@ static ssize_t usbip_sockfd_store(struct device *dev, goto unlock; } - spin_lock_irq(&udc->ud.lock); + spin_lock(&udc->ud.lock); if (udc->ud.status != SDEV_ST_USED) { ret = -EINVAL; goto unlock_ud; } - spin_unlock_irq(&udc->ud.lock); + spin_unlock(&udc->ud.lock); usbip_event_add(&udc->ud, VUDC_EVENT_DOWN); } @@ -219,7 +219,7 @@ static ssize_t usbip_sockfd_store(struct device *dev, sock_err: sockfd_put(socket); unlock_ud: - spin_unlock_irq(&udc->ud.lock); + spin_unlock(&udc->ud.lock); unlock: spin_unlock_irqrestore(&udc->lock, flags); mutex_unlock(&udc->ud.sysfs_lock); diff --git a/drivers/vfio/pci/Kconfig b/drivers/vfio/pci/Kconfig index 4da1914425e1..f9d0c908e738 100644 --- a/drivers/vfio/pci/Kconfig +++ b/drivers/vfio/pci/Kconfig @@ -44,6 +44,17 @@ config VFIO_PCI_IGD To enable Intel IGD assignment through vfio-pci, say Y. endif +config VFIO_PCI_ZDEV_KVM + bool "VFIO PCI extensions for s390x KVM passthrough" + depends on S390 && KVM + default y + help + Support s390x-specific extensions to enable support for enhancements + to KVM passthrough capabilities, such as interpretive execution of + zPCI instructions. + + To enable s390x KVM vfio-pci extensions, say Y. + source "drivers/vfio/pci/mlx5/Kconfig" source "drivers/vfio/pci/hisilicon/Kconfig" diff --git a/drivers/vfio/pci/Makefile b/drivers/vfio/pci/Makefile index 7052ebd893e0..24c524224da5 100644 --- a/drivers/vfio/pci/Makefile +++ b/drivers/vfio/pci/Makefile @@ -1,7 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only vfio-pci-core-y := vfio_pci_core.o vfio_pci_intrs.o vfio_pci_rdwr.o vfio_pci_config.o -vfio-pci-core-$(CONFIG_S390) += vfio_pci_zdev.o +vfio-pci-core-$(CONFIG_VFIO_PCI_ZDEV_KVM) += vfio_pci_zdev.o obj-$(CONFIG_VFIO_PCI_CORE) += vfio-pci-core.o vfio-pci-y := vfio_pci.o diff --git a/drivers/vfio/pci/vfio_pci_core.c b/drivers/vfio/pci/vfio_pci_core.c index 756d049bd9cf..0d8d4cdfb2f0 100644 --- a/drivers/vfio/pci/vfio_pci_core.c +++ b/drivers/vfio/pci/vfio_pci_core.c @@ -317,10 +317,14 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) pci_write_config_word(pdev, PCI_COMMAND, cmd); } - ret = vfio_config_init(vdev); + ret = vfio_pci_zdev_open_device(vdev); if (ret) goto out_free_state; + ret = vfio_config_init(vdev); + if (ret) + goto out_free_zdev; + msix_pos = pdev->msix_cap; if (msix_pos) { u16 flags; @@ -341,6 +345,8 @@ int vfio_pci_core_enable(struct vfio_pci_core_device *vdev) return 0; +out_free_zdev: + vfio_pci_zdev_close_device(vdev); out_free_state: kfree(vdev->pci_saved_state); vdev->pci_saved_state = NULL; @@ -419,6 +425,8 @@ void vfio_pci_core_disable(struct vfio_pci_core_device *vdev) vdev->needs_reset = true; + vfio_pci_zdev_close_device(vdev); + /* * If we have saved state, restore it. If we can reset the device, * even better. Resetting with current state seems better than diff --git a/drivers/vfio/pci/vfio_pci_zdev.c b/drivers/vfio/pci/vfio_pci_zdev.c index ea4c0d2b0663..e163aa9f6144 100644 --- a/drivers/vfio/pci/vfio_pci_zdev.c +++ b/drivers/vfio/pci/vfio_pci_zdev.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -23,14 +24,15 @@ static int zpci_base_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) { struct vfio_device_info_cap_zpci_base cap = { .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_BASE, - .header.version = 1, + .header.version = 2, .start_dma = zdev->start_dma, .end_dma = zdev->end_dma, .pchid = zdev->pchid, .vfn = zdev->vfn, .fmb_length = zdev->fmb_length, .pft = zdev->pft, - .gid = zdev->pfgid + .gid = zdev->pfgid, + .fh = zdev->fh }; return vfio_info_add_capability(caps, &cap.header, sizeof(cap)); @@ -43,14 +45,16 @@ static int zpci_group_cap(struct zpci_dev *zdev, struct vfio_info_cap *caps) { struct vfio_device_info_cap_zpci_group cap = { .header.id = VFIO_DEVICE_INFO_CAP_ZPCI_GROUP, - .header.version = 1, + .header.version = 2, .dasm = zdev->dma_mask, .msi_addr = zdev->msi_addr, .flags = VFIO_DEVICE_INFO_ZPCI_FLAG_REFRESH, .mui = zdev->fmb_update, .noi = zdev->max_msi, .maxstbl = ZPCI_MAX_WRITE_SIZE, - .version = zdev->version + .version = zdev->version, + .reserved = 0, + .imaxstbl = zdev->maxstbl }; return vfio_info_add_capability(caps, &cap.header, sizeof(cap)); @@ -136,3 +140,26 @@ int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, return ret; } + +int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) +{ + struct zpci_dev *zdev = to_zpci(vdev->pdev); + + if (!zdev) + return -ENODEV; + + if (!vdev->vdev.kvm) + return 0; + + return kvm_s390_pci_register_kvm(zdev, vdev->vdev.kvm); +} + +void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev) +{ + struct zpci_dev *zdev = to_zpci(vdev->pdev); + + if (!zdev || !vdev->vdev.kvm) + return; + + kvm_s390_pci_unregister_kvm(zdev); +} diff --git a/drivers/video/backlight/ltv350qv.c b/drivers/video/backlight/ltv350qv.c index b6d373af6e3f..d54f501e4285 100644 --- a/drivers/video/backlight/ltv350qv.c +++ b/drivers/video/backlight/ltv350qv.c @@ -27,8 +27,7 @@ struct ltv350qv { /* * The power-on and power-off sequences are taken from the * LTV350QV-F04 data sheet from Samsung. The register definitions are - * taken from the S6F2002 command list also from Samsung. Both - * documents are distributed with the AVR32 Linux BSP CD from Atmel. + * taken from the S6F2002 command list also from Samsung. * * There's still some voodoo going on here, but it's a lot better than * in the first incarnation of the driver where all we had was the raw diff --git a/drivers/video/backlight/tps65217_bl.c b/drivers/video/backlight/tps65217_bl.c index 8457166f357f..d96d713fe7db 100644 --- a/drivers/video/backlight/tps65217_bl.c +++ b/drivers/video/backlight/tps65217_bl.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * tps65217_bl.c * @@ -5,15 +6,6 @@ * * Copyright (C) 2012 Matthias Kaehlcke * Author: Matthias Kaehlcke - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include diff --git a/drivers/virtio/Kconfig b/drivers/virtio/Kconfig index 6be003233240..c5a05f51f0ef 100644 --- a/drivers/virtio/Kconfig +++ b/drivers/virtio/Kconfig @@ -1,6 +1,10 @@ # SPDX-License-Identifier: GPL-2.0-only +config VIRTIO_ANCHOR + bool + config VIRTIO tristate + select VIRTIO_ANCHOR help This option is selected by any driver which implements the virtio bus, such as CONFIG_VIRTIO_PCI, CONFIG_VIRTIO_MMIO, CONFIG_RPMSG diff --git a/drivers/virtio/Makefile b/drivers/virtio/Makefile index 0a82d0873248..8e98d24917cc 100644 --- a/drivers/virtio/Makefile +++ b/drivers/virtio/Makefile @@ -1,5 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_VIRTIO) += virtio.o virtio_ring.o +obj-$(CONFIG_VIRTIO_ANCHOR) += virtio_anchor.o obj-$(CONFIG_VIRTIO_PCI_LIB) += virtio_pci_modern_dev.o obj-$(CONFIG_VIRTIO_PCI_LIB_LEGACY) += virtio_pci_legacy_dev.o obj-$(CONFIG_VIRTIO_MMIO) += virtio_mmio.o diff --git a/drivers/virtio/virtio.c b/drivers/virtio/virtio.c index 7deeed30d1f3..14c142d77fba 100644 --- a/drivers/virtio/virtio.c +++ b/drivers/virtio/virtio.c @@ -2,10 +2,10 @@ #include #include #include +#include #include #include #include -#include #include /* Unique numbering for virtio devices. */ @@ -174,7 +174,7 @@ static int virtio_features_ok(struct virtio_device *dev) might_sleep(); - if (platform_has(PLATFORM_VIRTIO_RESTRICTED_MEM_ACCESS)) { + if (virtio_check_mem_acc_cb(dev)) { if (!virtio_has_feature(dev, VIRTIO_F_VERSION_1)) { dev_warn(&dev->dev, "device must provide VIRTIO_F_VERSION_1\n"); diff --git a/drivers/virtio/virtio_anchor.c b/drivers/virtio/virtio_anchor.c new file mode 100644 index 000000000000..4d6a5d269b55 --- /dev/null +++ b/drivers/virtio/virtio_anchor.c @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include +#include + +bool virtio_require_restricted_mem_acc(struct virtio_device *dev) +{ + return true; +} +EXPORT_SYMBOL_GPL(virtio_require_restricted_mem_acc); + +static bool virtio_no_restricted_mem_acc(struct virtio_device *dev) +{ + return false; +} + +bool (*virtio_check_mem_acc_cb)(struct virtio_device *dev) = + virtio_no_restricted_mem_acc; +EXPORT_SYMBOL_GPL(virtio_check_mem_acc_cb); diff --git a/drivers/vme/Kconfig b/drivers/vme/Kconfig deleted file mode 100644 index c13dd9d2a604..000000000000 --- a/drivers/vme/Kconfig +++ /dev/null @@ -1,18 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# VME configuration. -# - -menuconfig VME_BUS - bool "VME bridge support" - depends on PCI - help - If you say Y here you get support for the VME bridge Framework. - -if VME_BUS - -source "drivers/vme/bridges/Kconfig" - -source "drivers/vme/boards/Kconfig" - -endif # VME diff --git a/drivers/vme/Makefile b/drivers/vme/Makefile deleted file mode 100644 index 8bfe4b370c41..000000000000 --- a/drivers/vme/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the VME bridge device drivers. -# -obj-$(CONFIG_VME_BUS) += vme.o - -obj-y += bridges/ -obj-y += boards/ diff --git a/drivers/vme/boards/Kconfig b/drivers/vme/boards/Kconfig deleted file mode 100644 index 7a255f72980b..000000000000 --- a/drivers/vme/boards/Kconfig +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -comment "VME Board Drivers" - -config VMIVME_7805 - tristate "VMIVME-7805" - help - If you say Y here you get support for the VMIVME-7805 board. - This board has an additional control interface to the Universe II - chip. This driver has to be included if you want to access VME bus - with VMIVME-7805 board. diff --git a/drivers/vme/boards/Makefile b/drivers/vme/boards/Makefile deleted file mode 100644 index 87122381452c..000000000000 --- a/drivers/vme/boards/Makefile +++ /dev/null @@ -1,6 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for the VME board drivers. -# - -obj-$(CONFIG_VMIVME_7805) += vme_vmivme7805.o diff --git a/drivers/vme/boards/vme_vmivme7805.c b/drivers/vme/boards/vme_vmivme7805.c deleted file mode 100644 index 51e056bae943..000000000000 --- a/drivers/vme/boards/vme_vmivme7805.c +++ /dev/null @@ -1,106 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Support for the VMIVME-7805 board access to the Universe II bridge. - * - * Author: Arthur Benilov - * Copyright 2010 Ion Beam Application, Inc. - */ - -#include -#include -#include -#include -#include -#include - -#include "vme_vmivme7805.h" - -static int vmic_probe(struct pci_dev *, const struct pci_device_id *); -static void vmic_remove(struct pci_dev *); - -/** Base address to access FPGA register */ -static void __iomem *vmic_base; - -static const char driver_name[] = "vmivme_7805"; - -static const struct pci_device_id vmic_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_VMIC, PCI_DEVICE_ID_VTIMR) }, - { }, -}; - -static struct pci_driver vmic_driver = { - .name = driver_name, - .id_table = vmic_ids, - .probe = vmic_probe, - .remove = vmic_remove, -}; - -static int vmic_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int retval; - u32 data; - - /* Enable the device */ - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "Unable to enable device\n"); - goto err; - } - - /* Map Registers */ - retval = pci_request_regions(pdev, driver_name); - if (retval) { - dev_err(&pdev->dev, "Unable to reserve resources\n"); - goto err_resource; - } - - /* Map registers in BAR 0 */ - vmic_base = ioremap(pci_resource_start(pdev, 0), 16); - if (!vmic_base) { - dev_err(&pdev->dev, "Unable to remap CRG region\n"); - retval = -EIO; - goto err_remap; - } - - /* Clear the FPGA VME IF contents */ - iowrite32(0, vmic_base + VME_CONTROL); - - /* Clear any initial BERR */ - data = ioread32(vmic_base + VME_CONTROL) & 0x00000FFF; - data |= BM_VME_CONTROL_BERRST; - iowrite32(data, vmic_base + VME_CONTROL); - - /* Enable the vme interface and byte swapping */ - data = ioread32(vmic_base + VME_CONTROL) & 0x00000FFF; - data = data | BM_VME_CONTROL_MASTER_ENDIAN | - BM_VME_CONTROL_SLAVE_ENDIAN | - BM_VME_CONTROL_ABLE | - BM_VME_CONTROL_BERRI | - BM_VME_CONTROL_BPENA | - BM_VME_CONTROL_VBENA; - iowrite32(data, vmic_base + VME_CONTROL); - - return 0; - -err_remap: - pci_release_regions(pdev); -err_resource: - pci_disable_device(pdev); -err: - return retval; -} - -static void vmic_remove(struct pci_dev *pdev) -{ - iounmap(vmic_base); - pci_release_regions(pdev); - pci_disable_device(pdev); - -} - -module_pci_driver(vmic_driver); - -MODULE_DESCRIPTION("VMIVME-7805 board support driver"); -MODULE_AUTHOR("Arthur Benilov "); -MODULE_LICENSE("GPL"); - diff --git a/drivers/vme/boards/vme_vmivme7805.h b/drivers/vme/boards/vme_vmivme7805.h deleted file mode 100644 index c2c5e3053d3f..000000000000 --- a/drivers/vme/boards/vme_vmivme7805.h +++ /dev/null @@ -1,33 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * vmivme_7805.h - * - * Support for the VMIVME-7805 board access to the Universe II bridge. - * - * Author: Arthur Benilov - * Copyright 2010 Ion Beam Application, Inc. - */ - - -#ifndef _VMIVME_7805_H -#define _VMIVME_7805_H - -#ifndef PCI_VENDOR_ID_VMIC -#define PCI_VENDOR_ID_VMIC 0x114A -#endif - -#ifndef PCI_DEVICE_ID_VTIMR -#define PCI_DEVICE_ID_VTIMR 0x0004 -#endif - -#define VME_CONTROL 0x0000 -#define BM_VME_CONTROL_MASTER_ENDIAN 0x0001 -#define BM_VME_CONTROL_SLAVE_ENDIAN 0x0002 -#define BM_VME_CONTROL_ABLE 0x0004 -#define BM_VME_CONTROL_BERRI 0x0040 -#define BM_VME_CONTROL_BERRST 0x0080 -#define BM_VME_CONTROL_BPENA 0x0400 -#define BM_VME_CONTROL_VBENA 0x0800 - -#endif /* _VMIVME_7805_H */ - diff --git a/drivers/vme/bridges/Kconfig b/drivers/vme/bridges/Kconfig deleted file mode 100644 index cb3baed64914..000000000000 --- a/drivers/vme/bridges/Kconfig +++ /dev/null @@ -1,24 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -comment "VME Bridge Drivers" - -config VME_CA91CX42 - tristate "Universe II" - depends on VIRT_TO_BUS - help - If you say Y here you get support for the Tundra CA91C142 - (Universe II) VME bridge chip. - -config VME_TSI148 - tristate "Tempe" - depends on HAS_DMA - help - If you say Y here you get support for the Tundra TSI148 VME bridge - chip. - -config VME_FAKE - tristate "Fake" - help - If you say Y here you get support for the fake VME bridge. This - provides a virtualised VME Bus for devices with no VME bridge. This - is mainly useful for VME development (in the absence of VME - hardware). diff --git a/drivers/vme/bridges/Makefile b/drivers/vme/bridges/Makefile deleted file mode 100644 index 0a6cf843438a..000000000000 --- a/drivers/vme/bridges/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_VME_CA91CX42) += vme_ca91cx42.o -obj-$(CONFIG_VME_TSI148) += vme_tsi148.o -obj-$(CONFIG_VME_FAKE) += vme_fake.o diff --git a/drivers/vme/bridges/vme_ca91cx42.c b/drivers/vme/bridges/vme_ca91cx42.c deleted file mode 100644 index 439b0edeca08..000000000000 --- a/drivers/vme/bridges/vme_ca91cx42.c +++ /dev/null @@ -1,1928 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Support for the Tundra Universe I/II VME-PCI Bridge Chips - * - * Author: Martyn Welch - * Copyright 2008 GE Intelligent Platforms Embedded Systems, Inc. - * - * Based on work by Tom Armistead and Ajit Prem - * Copyright 2004 Motorola Inc. - * - * Derived from ca91c042.c by Michael Wyrick - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../vme_bridge.h" -#include "vme_ca91cx42.h" - -static int ca91cx42_probe(struct pci_dev *, const struct pci_device_id *); -static void ca91cx42_remove(struct pci_dev *); - -/* Module parameters */ -static int geoid; - -static const char driver_name[] = "vme_ca91cx42"; - -static const struct pci_device_id ca91cx42_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_TUNDRA, PCI_DEVICE_ID_TUNDRA_CA91C142) }, - { }, -}; - -MODULE_DEVICE_TABLE(pci, ca91cx42_ids); - -static struct pci_driver ca91cx42_driver = { - .name = driver_name, - .id_table = ca91cx42_ids, - .probe = ca91cx42_probe, - .remove = ca91cx42_remove, -}; - -static u32 ca91cx42_DMA_irqhandler(struct ca91cx42_driver *bridge) -{ - wake_up(&bridge->dma_queue); - - return CA91CX42_LINT_DMA; -} - -static u32 ca91cx42_LM_irqhandler(struct ca91cx42_driver *bridge, u32 stat) -{ - int i; - u32 serviced = 0; - - for (i = 0; i < 4; i++) { - if (stat & CA91CX42_LINT_LM[i]) { - /* We only enable interrupts if the callback is set */ - bridge->lm_callback[i](bridge->lm_data[i]); - serviced |= CA91CX42_LINT_LM[i]; - } - } - - return serviced; -} - -/* XXX This needs to be split into 4 queues */ -static u32 ca91cx42_MB_irqhandler(struct ca91cx42_driver *bridge, int mbox_mask) -{ - wake_up(&bridge->mbox_queue); - - return CA91CX42_LINT_MBOX; -} - -static u32 ca91cx42_IACK_irqhandler(struct ca91cx42_driver *bridge) -{ - wake_up(&bridge->iack_queue); - - return CA91CX42_LINT_SW_IACK; -} - -static u32 ca91cx42_VERR_irqhandler(struct vme_bridge *ca91cx42_bridge) -{ - int val; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - val = ioread32(bridge->base + DGCS); - - if (!(val & 0x00000800)) { - dev_err(ca91cx42_bridge->parent, "ca91cx42_VERR_irqhandler DMA " - "Read Error DGCS=%08X\n", val); - } - - return CA91CX42_LINT_VERR; -} - -static u32 ca91cx42_LERR_irqhandler(struct vme_bridge *ca91cx42_bridge) -{ - int val; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - val = ioread32(bridge->base + DGCS); - - if (!(val & 0x00000800)) - dev_err(ca91cx42_bridge->parent, "ca91cx42_LERR_irqhandler DMA " - "Read Error DGCS=%08X\n", val); - - return CA91CX42_LINT_LERR; -} - - -static u32 ca91cx42_VIRQ_irqhandler(struct vme_bridge *ca91cx42_bridge, - int stat) -{ - int vec, i, serviced = 0; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - - for (i = 7; i > 0; i--) { - if (stat & (1 << i)) { - vec = ioread32(bridge->base + - CA91CX42_V_STATID[i]) & 0xff; - - vme_irq_handler(ca91cx42_bridge, i, vec); - - serviced |= (1 << i); - } - } - - return serviced; -} - -static irqreturn_t ca91cx42_irqhandler(int irq, void *ptr) -{ - u32 stat, enable, serviced = 0; - struct vme_bridge *ca91cx42_bridge; - struct ca91cx42_driver *bridge; - - ca91cx42_bridge = ptr; - - bridge = ca91cx42_bridge->driver_priv; - - enable = ioread32(bridge->base + LINT_EN); - stat = ioread32(bridge->base + LINT_STAT); - - /* Only look at unmasked interrupts */ - stat &= enable; - - if (unlikely(!stat)) - return IRQ_NONE; - - if (stat & CA91CX42_LINT_DMA) - serviced |= ca91cx42_DMA_irqhandler(bridge); - if (stat & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 | - CA91CX42_LINT_LM3)) - serviced |= ca91cx42_LM_irqhandler(bridge, stat); - if (stat & CA91CX42_LINT_MBOX) - serviced |= ca91cx42_MB_irqhandler(bridge, stat); - if (stat & CA91CX42_LINT_SW_IACK) - serviced |= ca91cx42_IACK_irqhandler(bridge); - if (stat & CA91CX42_LINT_VERR) - serviced |= ca91cx42_VERR_irqhandler(ca91cx42_bridge); - if (stat & CA91CX42_LINT_LERR) - serviced |= ca91cx42_LERR_irqhandler(ca91cx42_bridge); - if (stat & (CA91CX42_LINT_VIRQ1 | CA91CX42_LINT_VIRQ2 | - CA91CX42_LINT_VIRQ3 | CA91CX42_LINT_VIRQ4 | - CA91CX42_LINT_VIRQ5 | CA91CX42_LINT_VIRQ6 | - CA91CX42_LINT_VIRQ7)) - serviced |= ca91cx42_VIRQ_irqhandler(ca91cx42_bridge, stat); - - /* Clear serviced interrupts */ - iowrite32(serviced, bridge->base + LINT_STAT); - - return IRQ_HANDLED; -} - -static int ca91cx42_irq_init(struct vme_bridge *ca91cx42_bridge) -{ - int result, tmp; - struct pci_dev *pdev; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - /* Need pdev */ - pdev = to_pci_dev(ca91cx42_bridge->parent); - - /* Disable interrupts from PCI to VME */ - iowrite32(0, bridge->base + VINT_EN); - - /* Disable PCI interrupts */ - iowrite32(0, bridge->base + LINT_EN); - /* Clear Any Pending PCI Interrupts */ - iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); - - result = request_irq(pdev->irq, ca91cx42_irqhandler, IRQF_SHARED, - driver_name, ca91cx42_bridge); - if (result) { - dev_err(&pdev->dev, "Can't get assigned pci irq vector %02X\n", - pdev->irq); - return result; - } - - /* Ensure all interrupts are mapped to PCI Interrupt 0 */ - iowrite32(0, bridge->base + LINT_MAP0); - iowrite32(0, bridge->base + LINT_MAP1); - iowrite32(0, bridge->base + LINT_MAP2); - - /* Enable DMA, mailbox & LM Interrupts */ - tmp = CA91CX42_LINT_MBOX3 | CA91CX42_LINT_MBOX2 | CA91CX42_LINT_MBOX1 | - CA91CX42_LINT_MBOX0 | CA91CX42_LINT_SW_IACK | - CA91CX42_LINT_VERR | CA91CX42_LINT_LERR | CA91CX42_LINT_DMA; - - iowrite32(tmp, bridge->base + LINT_EN); - - return 0; -} - -static void ca91cx42_irq_exit(struct ca91cx42_driver *bridge, - struct pci_dev *pdev) -{ - struct vme_bridge *ca91cx42_bridge; - - /* Disable interrupts from PCI to VME */ - iowrite32(0, bridge->base + VINT_EN); - - /* Disable PCI interrupts */ - iowrite32(0, bridge->base + LINT_EN); - /* Clear Any Pending PCI Interrupts */ - iowrite32(0x00FFFFFF, bridge->base + LINT_STAT); - - ca91cx42_bridge = container_of((void *)bridge, struct vme_bridge, - driver_priv); - free_irq(pdev->irq, ca91cx42_bridge); -} - -static int ca91cx42_iack_received(struct ca91cx42_driver *bridge, int level) -{ - u32 tmp; - - tmp = ioread32(bridge->base + LINT_STAT); - - if (tmp & (1 << level)) - return 0; - else - return 1; -} - -/* - * Set up an VME interrupt - */ -static void ca91cx42_irq_set(struct vme_bridge *ca91cx42_bridge, int level, - int state, int sync) - -{ - struct pci_dev *pdev; - u32 tmp; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - /* Enable IRQ level */ - tmp = ioread32(bridge->base + LINT_EN); - - if (state == 0) - tmp &= ~CA91CX42_LINT_VIRQ[level]; - else - tmp |= CA91CX42_LINT_VIRQ[level]; - - iowrite32(tmp, bridge->base + LINT_EN); - - if ((state == 0) && (sync != 0)) { - pdev = to_pci_dev(ca91cx42_bridge->parent); - - synchronize_irq(pdev->irq); - } -} - -static int ca91cx42_irq_generate(struct vme_bridge *ca91cx42_bridge, int level, - int statid) -{ - u32 tmp; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - /* Universe can only generate even vectors */ - if (statid & 1) - return -EINVAL; - - mutex_lock(&bridge->vme_int); - - tmp = ioread32(bridge->base + VINT_EN); - - /* Set Status/ID */ - iowrite32(statid << 24, bridge->base + STATID); - - /* Assert VMEbus IRQ */ - tmp = tmp | (1 << (level + 24)); - iowrite32(tmp, bridge->base + VINT_EN); - - /* Wait for IACK */ - wait_event_interruptible(bridge->iack_queue, - ca91cx42_iack_received(bridge, level)); - - /* Return interrupt to low state */ - tmp = ioread32(bridge->base + VINT_EN); - tmp = tmp & ~(1 << (level + 24)); - iowrite32(tmp, bridge->base + VINT_EN); - - mutex_unlock(&bridge->vme_int); - - return 0; -} - -static int ca91cx42_slave_set(struct vme_slave_resource *image, int enabled, - unsigned long long vme_base, unsigned long long size, - dma_addr_t pci_base, u32 aspace, u32 cycle) -{ - unsigned int i, addr = 0, granularity; - unsigned int temp_ctl = 0; - unsigned int vme_bound, pci_offset; - struct vme_bridge *ca91cx42_bridge; - struct ca91cx42_driver *bridge; - - ca91cx42_bridge = image->parent; - - bridge = ca91cx42_bridge->driver_priv; - - i = image->number; - - switch (aspace) { - case VME_A16: - addr |= CA91CX42_VSI_CTL_VAS_A16; - break; - case VME_A24: - addr |= CA91CX42_VSI_CTL_VAS_A24; - break; - case VME_A32: - addr |= CA91CX42_VSI_CTL_VAS_A32; - break; - case VME_USER1: - addr |= CA91CX42_VSI_CTL_VAS_USER1; - break; - case VME_USER2: - addr |= CA91CX42_VSI_CTL_VAS_USER2; - break; - case VME_A64: - case VME_CRCSR: - case VME_USER3: - case VME_USER4: - default: - dev_err(ca91cx42_bridge->parent, "Invalid address space\n"); - return -EINVAL; - break; - } - - /* - * Bound address is a valid address for the window, adjust - * accordingly - */ - vme_bound = vme_base + size; - pci_offset = pci_base - vme_base; - - if ((i == 0) || (i == 4)) - granularity = 0x1000; - else - granularity = 0x10000; - - if (vme_base & (granularity - 1)) { - dev_err(ca91cx42_bridge->parent, "Invalid VME base " - "alignment\n"); - return -EINVAL; - } - if (vme_bound & (granularity - 1)) { - dev_err(ca91cx42_bridge->parent, "Invalid VME bound " - "alignment\n"); - return -EINVAL; - } - if (pci_offset & (granularity - 1)) { - dev_err(ca91cx42_bridge->parent, "Invalid PCI Offset " - "alignment\n"); - return -EINVAL; - } - - /* Disable while we are mucking around */ - temp_ctl = ioread32(bridge->base + CA91CX42_VSI_CTL[i]); - temp_ctl &= ~CA91CX42_VSI_CTL_EN; - iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); - - /* Setup mapping */ - iowrite32(vme_base, bridge->base + CA91CX42_VSI_BS[i]); - iowrite32(vme_bound, bridge->base + CA91CX42_VSI_BD[i]); - iowrite32(pci_offset, bridge->base + CA91CX42_VSI_TO[i]); - - /* Setup address space */ - temp_ctl &= ~CA91CX42_VSI_CTL_VAS_M; - temp_ctl |= addr; - - /* Setup cycle types */ - temp_ctl &= ~(CA91CX42_VSI_CTL_PGM_M | CA91CX42_VSI_CTL_SUPER_M); - if (cycle & VME_SUPER) - temp_ctl |= CA91CX42_VSI_CTL_SUPER_SUPR; - if (cycle & VME_USER) - temp_ctl |= CA91CX42_VSI_CTL_SUPER_NPRIV; - if (cycle & VME_PROG) - temp_ctl |= CA91CX42_VSI_CTL_PGM_PGM; - if (cycle & VME_DATA) - temp_ctl |= CA91CX42_VSI_CTL_PGM_DATA; - - /* Write ctl reg without enable */ - iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); - - if (enabled) - temp_ctl |= CA91CX42_VSI_CTL_EN; - - iowrite32(temp_ctl, bridge->base + CA91CX42_VSI_CTL[i]); - - return 0; -} - -static int ca91cx42_slave_get(struct vme_slave_resource *image, int *enabled, - unsigned long long *vme_base, unsigned long long *size, - dma_addr_t *pci_base, u32 *aspace, u32 *cycle) -{ - unsigned int i, granularity = 0, ctl = 0; - unsigned long long vme_bound, pci_offset; - struct ca91cx42_driver *bridge; - - bridge = image->parent->driver_priv; - - i = image->number; - - if ((i == 0) || (i == 4)) - granularity = 0x1000; - else - granularity = 0x10000; - - /* Read Registers */ - ctl = ioread32(bridge->base + CA91CX42_VSI_CTL[i]); - - *vme_base = ioread32(bridge->base + CA91CX42_VSI_BS[i]); - vme_bound = ioread32(bridge->base + CA91CX42_VSI_BD[i]); - pci_offset = ioread32(bridge->base + CA91CX42_VSI_TO[i]); - - *pci_base = (dma_addr_t)*vme_base + pci_offset; - *size = (unsigned long long)((vme_bound - *vme_base) + granularity); - - *enabled = 0; - *aspace = 0; - *cycle = 0; - - if (ctl & CA91CX42_VSI_CTL_EN) - *enabled = 1; - - if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A16) - *aspace = VME_A16; - if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A24) - *aspace = VME_A24; - if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_A32) - *aspace = VME_A32; - if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_USER1) - *aspace = VME_USER1; - if ((ctl & CA91CX42_VSI_CTL_VAS_M) == CA91CX42_VSI_CTL_VAS_USER2) - *aspace = VME_USER2; - - if (ctl & CA91CX42_VSI_CTL_SUPER_SUPR) - *cycle |= VME_SUPER; - if (ctl & CA91CX42_VSI_CTL_SUPER_NPRIV) - *cycle |= VME_USER; - if (ctl & CA91CX42_VSI_CTL_PGM_PGM) - *cycle |= VME_PROG; - if (ctl & CA91CX42_VSI_CTL_PGM_DATA) - *cycle |= VME_DATA; - - return 0; -} - -/* - * Allocate and map PCI Resource - */ -static int ca91cx42_alloc_resource(struct vme_master_resource *image, - unsigned long long size) -{ - unsigned long long existing_size; - int retval = 0; - struct pci_dev *pdev; - struct vme_bridge *ca91cx42_bridge; - - ca91cx42_bridge = image->parent; - - /* Find pci_dev container of dev */ - if (!ca91cx42_bridge->parent) { - dev_err(ca91cx42_bridge->parent, "Dev entry NULL\n"); - return -EINVAL; - } - pdev = to_pci_dev(ca91cx42_bridge->parent); - - existing_size = (unsigned long long)(image->bus_resource.end - - image->bus_resource.start); - - /* If the existing size is OK, return */ - if (existing_size == (size - 1)) - return 0; - - if (existing_size != 0) { - iounmap(image->kern_base); - image->kern_base = NULL; - kfree(image->bus_resource.name); - release_resource(&image->bus_resource); - memset(&image->bus_resource, 0, sizeof(image->bus_resource)); - } - - if (!image->bus_resource.name) { - image->bus_resource.name = kmalloc(VMENAMSIZ+3, GFP_ATOMIC); - if (!image->bus_resource.name) { - retval = -ENOMEM; - goto err_name; - } - } - - sprintf((char *)image->bus_resource.name, "%s.%d", - ca91cx42_bridge->name, image->number); - - image->bus_resource.start = 0; - image->bus_resource.end = (unsigned long)size; - image->bus_resource.flags = IORESOURCE_MEM; - - retval = pci_bus_alloc_resource(pdev->bus, - &image->bus_resource, size, 0x10000, PCIBIOS_MIN_MEM, - 0, NULL, NULL); - if (retval) { - dev_err(ca91cx42_bridge->parent, "Failed to allocate mem " - "resource for window %d size 0x%lx start 0x%lx\n", - image->number, (unsigned long)size, - (unsigned long)image->bus_resource.start); - goto err_resource; - } - - image->kern_base = ioremap( - image->bus_resource.start, size); - if (!image->kern_base) { - dev_err(ca91cx42_bridge->parent, "Failed to remap resource\n"); - retval = -ENOMEM; - goto err_remap; - } - - return 0; - -err_remap: - release_resource(&image->bus_resource); -err_resource: - kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(image->bus_resource)); -err_name: - return retval; -} - -/* - * Free and unmap PCI Resource - */ -static void ca91cx42_free_resource(struct vme_master_resource *image) -{ - iounmap(image->kern_base); - image->kern_base = NULL; - release_resource(&image->bus_resource); - kfree(image->bus_resource.name); - memset(&image->bus_resource, 0, sizeof(image->bus_resource)); -} - - -static int ca91cx42_master_set(struct vme_master_resource *image, int enabled, - unsigned long long vme_base, unsigned long long size, u32 aspace, - u32 cycle, u32 dwidth) -{ - int retval = 0; - unsigned int i, granularity = 0; - unsigned int temp_ctl = 0; - unsigned long long pci_bound, vme_offset, pci_base; - struct vme_bridge *ca91cx42_bridge; - struct ca91cx42_driver *bridge; - - ca91cx42_bridge = image->parent; - - bridge = ca91cx42_bridge->driver_priv; - - i = image->number; - - if ((i == 0) || (i == 4)) - granularity = 0x1000; - else - granularity = 0x10000; - - /* Verify input data */ - if (vme_base & (granularity - 1)) { - dev_err(ca91cx42_bridge->parent, "Invalid VME Window " - "alignment\n"); - retval = -EINVAL; - goto err_window; - } - if (size & (granularity - 1)) { - dev_err(ca91cx42_bridge->parent, "Invalid VME Window " - "alignment\n"); - retval = -EINVAL; - goto err_window; - } - - spin_lock(&image->lock); - - /* - * Let's allocate the resource here rather than further up the stack as - * it avoids pushing loads of bus dependent stuff up the stack - */ - retval = ca91cx42_alloc_resource(image, size); - if (retval) { - spin_unlock(&image->lock); - dev_err(ca91cx42_bridge->parent, "Unable to allocate memory " - "for resource name\n"); - retval = -ENOMEM; - goto err_res; - } - - pci_base = (unsigned long long)image->bus_resource.start; - - /* - * Bound address is a valid address for the window, adjust - * according to window granularity. - */ - pci_bound = pci_base + size; - vme_offset = vme_base - pci_base; - - /* Disable while we are mucking around */ - temp_ctl = ioread32(bridge->base + CA91CX42_LSI_CTL[i]); - temp_ctl &= ~CA91CX42_LSI_CTL_EN; - iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); - - /* Setup cycle types */ - temp_ctl &= ~CA91CX42_LSI_CTL_VCT_M; - if (cycle & VME_BLT) - temp_ctl |= CA91CX42_LSI_CTL_VCT_BLT; - if (cycle & VME_MBLT) - temp_ctl |= CA91CX42_LSI_CTL_VCT_MBLT; - - /* Setup data width */ - temp_ctl &= ~CA91CX42_LSI_CTL_VDW_M; - switch (dwidth) { - case VME_D8: - temp_ctl |= CA91CX42_LSI_CTL_VDW_D8; - break; - case VME_D16: - temp_ctl |= CA91CX42_LSI_CTL_VDW_D16; - break; - case VME_D32: - temp_ctl |= CA91CX42_LSI_CTL_VDW_D32; - break; - case VME_D64: - temp_ctl |= CA91CX42_LSI_CTL_VDW_D64; - break; - default: - spin_unlock(&image->lock); - dev_err(ca91cx42_bridge->parent, "Invalid data width\n"); - retval = -EINVAL; - goto err_dwidth; - break; - } - - /* Setup address space */ - temp_ctl &= ~CA91CX42_LSI_CTL_VAS_M; - switch (aspace) { - case VME_A16: - temp_ctl |= CA91CX42_LSI_CTL_VAS_A16; - break; - case VME_A24: - temp_ctl |= CA91CX42_LSI_CTL_VAS_A24; - break; - case VME_A32: - temp_ctl |= CA91CX42_LSI_CTL_VAS_A32; - break; - case VME_CRCSR: - temp_ctl |= CA91CX42_LSI_CTL_VAS_CRCSR; - break; - case VME_USER1: - temp_ctl |= CA91CX42_LSI_CTL_VAS_USER1; - break; - case VME_USER2: - temp_ctl |= CA91CX42_LSI_CTL_VAS_USER2; - break; - case VME_A64: - case VME_USER3: - case VME_USER4: - default: - spin_unlock(&image->lock); - dev_err(ca91cx42_bridge->parent, "Invalid address space\n"); - retval = -EINVAL; - goto err_aspace; - break; - } - - temp_ctl &= ~(CA91CX42_LSI_CTL_PGM_M | CA91CX42_LSI_CTL_SUPER_M); - if (cycle & VME_SUPER) - temp_ctl |= CA91CX42_LSI_CTL_SUPER_SUPR; - if (cycle & VME_PROG) - temp_ctl |= CA91CX42_LSI_CTL_PGM_PGM; - - /* Setup mapping */ - iowrite32(pci_base, bridge->base + CA91CX42_LSI_BS[i]); - iowrite32(pci_bound, bridge->base + CA91CX42_LSI_BD[i]); - iowrite32(vme_offset, bridge->base + CA91CX42_LSI_TO[i]); - - /* Write ctl reg without enable */ - iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); - - if (enabled) - temp_ctl |= CA91CX42_LSI_CTL_EN; - - iowrite32(temp_ctl, bridge->base + CA91CX42_LSI_CTL[i]); - - spin_unlock(&image->lock); - return 0; - -err_aspace: -err_dwidth: - ca91cx42_free_resource(image); -err_res: -err_window: - return retval; -} - -static int __ca91cx42_master_get(struct vme_master_resource *image, - int *enabled, unsigned long long *vme_base, unsigned long long *size, - u32 *aspace, u32 *cycle, u32 *dwidth) -{ - unsigned int i, ctl; - unsigned long long pci_base, pci_bound, vme_offset; - struct ca91cx42_driver *bridge; - - bridge = image->parent->driver_priv; - - i = image->number; - - ctl = ioread32(bridge->base + CA91CX42_LSI_CTL[i]); - - pci_base = ioread32(bridge->base + CA91CX42_LSI_BS[i]); - vme_offset = ioread32(bridge->base + CA91CX42_LSI_TO[i]); - pci_bound = ioread32(bridge->base + CA91CX42_LSI_BD[i]); - - *vme_base = pci_base + vme_offset; - *size = (unsigned long long)(pci_bound - pci_base); - - *enabled = 0; - *aspace = 0; - *cycle = 0; - *dwidth = 0; - - if (ctl & CA91CX42_LSI_CTL_EN) - *enabled = 1; - - /* Setup address space */ - switch (ctl & CA91CX42_LSI_CTL_VAS_M) { - case CA91CX42_LSI_CTL_VAS_A16: - *aspace = VME_A16; - break; - case CA91CX42_LSI_CTL_VAS_A24: - *aspace = VME_A24; - break; - case CA91CX42_LSI_CTL_VAS_A32: - *aspace = VME_A32; - break; - case CA91CX42_LSI_CTL_VAS_CRCSR: - *aspace = VME_CRCSR; - break; - case CA91CX42_LSI_CTL_VAS_USER1: - *aspace = VME_USER1; - break; - case CA91CX42_LSI_CTL_VAS_USER2: - *aspace = VME_USER2; - break; - } - - /* XXX Not sure howto check for MBLT */ - /* Setup cycle types */ - if (ctl & CA91CX42_LSI_CTL_VCT_BLT) - *cycle |= VME_BLT; - else - *cycle |= VME_SCT; - - if (ctl & CA91CX42_LSI_CTL_SUPER_SUPR) - *cycle |= VME_SUPER; - else - *cycle |= VME_USER; - - if (ctl & CA91CX42_LSI_CTL_PGM_PGM) - *cycle = VME_PROG; - else - *cycle = VME_DATA; - - /* Setup data width */ - switch (ctl & CA91CX42_LSI_CTL_VDW_M) { - case CA91CX42_LSI_CTL_VDW_D8: - *dwidth = VME_D8; - break; - case CA91CX42_LSI_CTL_VDW_D16: - *dwidth = VME_D16; - break; - case CA91CX42_LSI_CTL_VDW_D32: - *dwidth = VME_D32; - break; - case CA91CX42_LSI_CTL_VDW_D64: - *dwidth = VME_D64; - break; - } - - return 0; -} - -static int ca91cx42_master_get(struct vme_master_resource *image, int *enabled, - unsigned long long *vme_base, unsigned long long *size, u32 *aspace, - u32 *cycle, u32 *dwidth) -{ - int retval; - - spin_lock(&image->lock); - - retval = __ca91cx42_master_get(image, enabled, vme_base, size, aspace, - cycle, dwidth); - - spin_unlock(&image->lock); - - return retval; -} - -static ssize_t ca91cx42_master_read(struct vme_master_resource *image, - void *buf, size_t count, loff_t offset) -{ - ssize_t retval; - void __iomem *addr = image->kern_base + offset; - unsigned int done = 0; - unsigned int count32; - - if (count == 0) - return 0; - - spin_lock(&image->lock); - - /* The following code handles VME address alignment. We cannot use - * memcpy_xxx here because it may cut data transfers in to 8-bit - * cycles when D16 or D32 cycles are required on the VME bus. - * On the other hand, the bridge itself assures that the maximum data - * cycle configured for the transfer is used and splits it - * automatically for non-aligned addresses, so we don't want the - * overhead of needlessly forcing small transfers for the entire cycle. - */ - if ((uintptr_t)addr & 0x1) { - *(u8 *)buf = ioread8(addr); - done += 1; - if (done == count) - goto out; - } - if ((uintptr_t)(addr + done) & 0x2) { - if ((count - done) < 2) { - *(u8 *)(buf + done) = ioread8(addr + done); - done += 1; - goto out; - } else { - *(u16 *)(buf + done) = ioread16(addr + done); - done += 2; - } - } - - count32 = (count - done) & ~0x3; - while (done < count32) { - *(u32 *)(buf + done) = ioread32(addr + done); - done += 4; - } - - if ((count - done) & 0x2) { - *(u16 *)(buf + done) = ioread16(addr + done); - done += 2; - } - if ((count - done) & 0x1) { - *(u8 *)(buf + done) = ioread8(addr + done); - done += 1; - } -out: - retval = count; - spin_unlock(&image->lock); - - return retval; -} - -static ssize_t ca91cx42_master_write(struct vme_master_resource *image, - void *buf, size_t count, loff_t offset) -{ - ssize_t retval; - void __iomem *addr = image->kern_base + offset; - unsigned int done = 0; - unsigned int count32; - - if (count == 0) - return 0; - - spin_lock(&image->lock); - - /* Here we apply for the same strategy we do in master_read - * function in order to assure the correct cycles. - */ - if ((uintptr_t)addr & 0x1) { - iowrite8(*(u8 *)buf, addr); - done += 1; - if (done == count) - goto out; - } - if ((uintptr_t)(addr + done) & 0x2) { - if ((count - done) < 2) { - iowrite8(*(u8 *)(buf + done), addr + done); - done += 1; - goto out; - } else { - iowrite16(*(u16 *)(buf + done), addr + done); - done += 2; - } - } - - count32 = (count - done) & ~0x3; - while (done < count32) { - iowrite32(*(u32 *)(buf + done), addr + done); - done += 4; - } - - if ((count - done) & 0x2) { - iowrite16(*(u16 *)(buf + done), addr + done); - done += 2; - } - if ((count - done) & 0x1) { - iowrite8(*(u8 *)(buf + done), addr + done); - done += 1; - } -out: - retval = count; - - spin_unlock(&image->lock); - - return retval; -} - -static unsigned int ca91cx42_master_rmw(struct vme_master_resource *image, - unsigned int mask, unsigned int compare, unsigned int swap, - loff_t offset) -{ - u32 result; - uintptr_t pci_addr; - struct ca91cx42_driver *bridge; - struct device *dev; - - bridge = image->parent->driver_priv; - dev = image->parent->parent; - - /* Find the PCI address that maps to the desired VME address */ - - /* Locking as we can only do one of these at a time */ - mutex_lock(&bridge->vme_rmw); - - /* Lock image */ - spin_lock(&image->lock); - - pci_addr = (uintptr_t)image->kern_base + offset; - - /* Address must be 4-byte aligned */ - if (pci_addr & 0x3) { - dev_err(dev, "RMW Address not 4-byte aligned\n"); - result = -EINVAL; - goto out; - } - - /* Ensure RMW Disabled whilst configuring */ - iowrite32(0, bridge->base + SCYC_CTL); - - /* Configure registers */ - iowrite32(mask, bridge->base + SCYC_EN); - iowrite32(compare, bridge->base + SCYC_CMP); - iowrite32(swap, bridge->base + SCYC_SWP); - iowrite32(pci_addr, bridge->base + SCYC_ADDR); - - /* Enable RMW */ - iowrite32(CA91CX42_SCYC_CTL_CYC_RMW, bridge->base + SCYC_CTL); - - /* Kick process off with a read to the required address. */ - result = ioread32(image->kern_base + offset); - - /* Disable RMW */ - iowrite32(0, bridge->base + SCYC_CTL); - -out: - spin_unlock(&image->lock); - - mutex_unlock(&bridge->vme_rmw); - - return result; -} - -static int ca91cx42_dma_list_add(struct vme_dma_list *list, - struct vme_dma_attr *src, struct vme_dma_attr *dest, size_t count) -{ - struct ca91cx42_dma_entry *entry, *prev; - struct vme_dma_pci *pci_attr; - struct vme_dma_vme *vme_attr; - dma_addr_t desc_ptr; - int retval = 0; - struct device *dev; - - dev = list->parent->parent->parent; - - /* XXX descriptor must be aligned on 64-bit boundaries */ - entry = kmalloc(sizeof(*entry), GFP_KERNEL); - if (!entry) { - retval = -ENOMEM; - goto err_mem; - } - - /* Test descriptor alignment */ - if ((unsigned long)&entry->descriptor & CA91CX42_DCPP_M) { - dev_err(dev, "Descriptor not aligned to 16 byte boundary as " - "required: %p\n", &entry->descriptor); - retval = -EINVAL; - goto err_align; - } - - memset(&entry->descriptor, 0, sizeof(entry->descriptor)); - - if (dest->type == VME_DMA_VME) { - entry->descriptor.dctl |= CA91CX42_DCTL_L2V; - vme_attr = dest->private; - pci_attr = src->private; - } else { - vme_attr = src->private; - pci_attr = dest->private; - } - - /* Check we can do fulfill required attributes */ - if ((vme_attr->aspace & ~(VME_A16 | VME_A24 | VME_A32 | VME_USER1 | - VME_USER2)) != 0) { - - dev_err(dev, "Unsupported cycle type\n"); - retval = -EINVAL; - goto err_aspace; - } - - if ((vme_attr->cycle & ~(VME_SCT | VME_BLT | VME_SUPER | VME_USER | - VME_PROG | VME_DATA)) != 0) { - - dev_err(dev, "Unsupported cycle type\n"); - retval = -EINVAL; - goto err_cycle; - } - - /* Check to see if we can fulfill source and destination */ - if (!(((src->type == VME_DMA_PCI) && (dest->type == VME_DMA_VME)) || - ((src->type == VME_DMA_VME) && (dest->type == VME_DMA_PCI)))) { - - dev_err(dev, "Cannot perform transfer with this " - "source-destination combination\n"); - retval = -EINVAL; - goto err_direct; - } - - /* Setup cycle types */ - if (vme_attr->cycle & VME_BLT) - entry->descriptor.dctl |= CA91CX42_DCTL_VCT_BLT; - - /* Setup data width */ - switch (vme_attr->dwidth) { - case VME_D8: - entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D8; - break; - case VME_D16: - entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D16; - break; - case VME_D32: - entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D32; - break; - case VME_D64: - entry->descriptor.dctl |= CA91CX42_DCTL_VDW_D64; - break; - default: - dev_err(dev, "Invalid data width\n"); - return -EINVAL; - } - - /* Setup address space */ - switch (vme_attr->aspace) { - case VME_A16: - entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A16; - break; - case VME_A24: - entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A24; - break; - case VME_A32: - entry->descriptor.dctl |= CA91CX42_DCTL_VAS_A32; - break; - case VME_USER1: - entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER1; - break; - case VME_USER2: - entry->descriptor.dctl |= CA91CX42_DCTL_VAS_USER2; - break; - default: - dev_err(dev, "Invalid address space\n"); - return -EINVAL; - break; - } - - if (vme_attr->cycle & VME_SUPER) - entry->descriptor.dctl |= CA91CX42_DCTL_SUPER_SUPR; - if (vme_attr->cycle & VME_PROG) - entry->descriptor.dctl |= CA91CX42_DCTL_PGM_PGM; - - entry->descriptor.dtbc = count; - entry->descriptor.dla = pci_attr->address; - entry->descriptor.dva = vme_attr->address; - entry->descriptor.dcpp = CA91CX42_DCPP_NULL; - - /* Add to list */ - list_add_tail(&entry->list, &list->entries); - - /* Fill out previous descriptors "Next Address" */ - if (entry->list.prev != &list->entries) { - prev = list_entry(entry->list.prev, struct ca91cx42_dma_entry, - list); - /* We need the bus address for the pointer */ - desc_ptr = virt_to_bus(&entry->descriptor); - prev->descriptor.dcpp = desc_ptr & ~CA91CX42_DCPP_M; - } - - return 0; - -err_cycle: -err_aspace: -err_direct: -err_align: - kfree(entry); -err_mem: - return retval; -} - -static int ca91cx42_dma_busy(struct vme_bridge *ca91cx42_bridge) -{ - u32 tmp; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - tmp = ioread32(bridge->base + DGCS); - - if (tmp & CA91CX42_DGCS_ACT) - return 0; - else - return 1; -} - -static int ca91cx42_dma_list_exec(struct vme_dma_list *list) -{ - struct vme_dma_resource *ctrlr; - struct ca91cx42_dma_entry *entry; - int retval; - dma_addr_t bus_addr; - u32 val; - struct device *dev; - struct ca91cx42_driver *bridge; - - ctrlr = list->parent; - - bridge = ctrlr->parent->driver_priv; - dev = ctrlr->parent->parent; - - mutex_lock(&ctrlr->mtx); - - if (!(list_empty(&ctrlr->running))) { - /* - * XXX We have an active DMA transfer and currently haven't - * sorted out the mechanism for "pending" DMA transfers. - * Return busy. - */ - /* Need to add to pending here */ - mutex_unlock(&ctrlr->mtx); - return -EBUSY; - } else { - list_add(&list->list, &ctrlr->running); - } - - /* Get first bus address and write into registers */ - entry = list_first_entry(&list->entries, struct ca91cx42_dma_entry, - list); - - bus_addr = virt_to_bus(&entry->descriptor); - - mutex_unlock(&ctrlr->mtx); - - iowrite32(0, bridge->base + DTBC); - iowrite32(bus_addr & ~CA91CX42_DCPP_M, bridge->base + DCPP); - - /* Start the operation */ - val = ioread32(bridge->base + DGCS); - - /* XXX Could set VMEbus On and Off Counters here */ - val &= (CA91CX42_DGCS_VON_M | CA91CX42_DGCS_VOFF_M); - - val |= (CA91CX42_DGCS_CHAIN | CA91CX42_DGCS_STOP | CA91CX42_DGCS_HALT | - CA91CX42_DGCS_DONE | CA91CX42_DGCS_LERR | CA91CX42_DGCS_VERR | - CA91CX42_DGCS_PERR); - - iowrite32(val, bridge->base + DGCS); - - val |= CA91CX42_DGCS_GO; - - iowrite32(val, bridge->base + DGCS); - - retval = wait_event_interruptible(bridge->dma_queue, - ca91cx42_dma_busy(ctrlr->parent)); - - if (retval) { - val = ioread32(bridge->base + DGCS); - iowrite32(val | CA91CX42_DGCS_STOP_REQ, bridge->base + DGCS); - /* Wait for the operation to abort */ - wait_event(bridge->dma_queue, - ca91cx42_dma_busy(ctrlr->parent)); - retval = -EINTR; - goto exit; - } - - /* - * Read status register, this register is valid until we kick off a - * new transfer. - */ - val = ioread32(bridge->base + DGCS); - - if (val & (CA91CX42_DGCS_LERR | CA91CX42_DGCS_VERR | - CA91CX42_DGCS_PERR)) { - - dev_err(dev, "ca91c042: DMA Error. DGCS=%08X\n", val); - val = ioread32(bridge->base + DCTL); - retval = -EIO; - } - -exit: - /* Remove list from running list */ - mutex_lock(&ctrlr->mtx); - list_del(&list->list); - mutex_unlock(&ctrlr->mtx); - - return retval; - -} - -static int ca91cx42_dma_list_empty(struct vme_dma_list *list) -{ - struct list_head *pos, *temp; - struct ca91cx42_dma_entry *entry; - - /* detach and free each entry */ - list_for_each_safe(pos, temp, &list->entries) { - list_del(pos); - entry = list_entry(pos, struct ca91cx42_dma_entry, list); - kfree(entry); - } - - return 0; -} - -/* - * All 4 location monitors reside at the same base - this is therefore a - * system wide configuration. - * - * This does not enable the LM monitor - that should be done when the first - * callback is attached and disabled when the last callback is removed. - */ -static int ca91cx42_lm_set(struct vme_lm_resource *lm, - unsigned long long lm_base, u32 aspace, u32 cycle) -{ - u32 temp_base, lm_ctl = 0; - int i; - struct ca91cx42_driver *bridge; - struct device *dev; - - bridge = lm->parent->driver_priv; - dev = lm->parent->parent; - - /* Check the alignment of the location monitor */ - temp_base = (u32)lm_base; - if (temp_base & 0xffff) { - dev_err(dev, "Location monitor must be aligned to 64KB " - "boundary"); - return -EINVAL; - } - - mutex_lock(&lm->mtx); - - /* If we already have a callback attached, we can't move it! */ - for (i = 0; i < lm->monitors; i++) { - if (bridge->lm_callback[i]) { - mutex_unlock(&lm->mtx); - dev_err(dev, "Location monitor callback attached, " - "can't reset\n"); - return -EBUSY; - } - } - - switch (aspace) { - case VME_A16: - lm_ctl |= CA91CX42_LM_CTL_AS_A16; - break; - case VME_A24: - lm_ctl |= CA91CX42_LM_CTL_AS_A24; - break; - case VME_A32: - lm_ctl |= CA91CX42_LM_CTL_AS_A32; - break; - default: - mutex_unlock(&lm->mtx); - dev_err(dev, "Invalid address space\n"); - return -EINVAL; - break; - } - - if (cycle & VME_SUPER) - lm_ctl |= CA91CX42_LM_CTL_SUPR; - if (cycle & VME_USER) - lm_ctl |= CA91CX42_LM_CTL_NPRIV; - if (cycle & VME_PROG) - lm_ctl |= CA91CX42_LM_CTL_PGM; - if (cycle & VME_DATA) - lm_ctl |= CA91CX42_LM_CTL_DATA; - - iowrite32(lm_base, bridge->base + LM_BS); - iowrite32(lm_ctl, bridge->base + LM_CTL); - - mutex_unlock(&lm->mtx); - - return 0; -} - -/* Get configuration of the callback monitor and return whether it is enabled - * or disabled. - */ -static int ca91cx42_lm_get(struct vme_lm_resource *lm, - unsigned long long *lm_base, u32 *aspace, u32 *cycle) -{ - u32 lm_ctl, enabled = 0; - struct ca91cx42_driver *bridge; - - bridge = lm->parent->driver_priv; - - mutex_lock(&lm->mtx); - - *lm_base = (unsigned long long)ioread32(bridge->base + LM_BS); - lm_ctl = ioread32(bridge->base + LM_CTL); - - if (lm_ctl & CA91CX42_LM_CTL_EN) - enabled = 1; - - if ((lm_ctl & CA91CX42_LM_CTL_AS_M) == CA91CX42_LM_CTL_AS_A16) - *aspace = VME_A16; - if ((lm_ctl & CA91CX42_LM_CTL_AS_M) == CA91CX42_LM_CTL_AS_A24) - *aspace = VME_A24; - if ((lm_ctl & CA91CX42_LM_CTL_AS_M) == CA91CX42_LM_CTL_AS_A32) - *aspace = VME_A32; - - *cycle = 0; - if (lm_ctl & CA91CX42_LM_CTL_SUPR) - *cycle |= VME_SUPER; - if (lm_ctl & CA91CX42_LM_CTL_NPRIV) - *cycle |= VME_USER; - if (lm_ctl & CA91CX42_LM_CTL_PGM) - *cycle |= VME_PROG; - if (lm_ctl & CA91CX42_LM_CTL_DATA) - *cycle |= VME_DATA; - - mutex_unlock(&lm->mtx); - - return enabled; -} - -/* - * Attach a callback to a specific location monitor. - * - * Callback will be passed the monitor triggered. - */ -static int ca91cx42_lm_attach(struct vme_lm_resource *lm, int monitor, - void (*callback)(void *), void *data) -{ - u32 lm_ctl, tmp; - struct ca91cx42_driver *bridge; - struct device *dev; - - bridge = lm->parent->driver_priv; - dev = lm->parent->parent; - - mutex_lock(&lm->mtx); - - /* Ensure that the location monitor is configured - need PGM or DATA */ - lm_ctl = ioread32(bridge->base + LM_CTL); - if ((lm_ctl & (CA91CX42_LM_CTL_PGM | CA91CX42_LM_CTL_DATA)) == 0) { - mutex_unlock(&lm->mtx); - dev_err(dev, "Location monitor not properly configured\n"); - return -EINVAL; - } - - /* Check that a callback isn't already attached */ - if (bridge->lm_callback[monitor]) { - mutex_unlock(&lm->mtx); - dev_err(dev, "Existing callback attached\n"); - return -EBUSY; - } - - /* Attach callback */ - bridge->lm_callback[monitor] = callback; - bridge->lm_data[monitor] = data; - - /* Enable Location Monitor interrupt */ - tmp = ioread32(bridge->base + LINT_EN); - tmp |= CA91CX42_LINT_LM[monitor]; - iowrite32(tmp, bridge->base + LINT_EN); - - /* Ensure that global Location Monitor Enable set */ - if ((lm_ctl & CA91CX42_LM_CTL_EN) == 0) { - lm_ctl |= CA91CX42_LM_CTL_EN; - iowrite32(lm_ctl, bridge->base + LM_CTL); - } - - mutex_unlock(&lm->mtx); - - return 0; -} - -/* - * Detach a callback function forn a specific location monitor. - */ -static int ca91cx42_lm_detach(struct vme_lm_resource *lm, int monitor) -{ - u32 tmp; - struct ca91cx42_driver *bridge; - - bridge = lm->parent->driver_priv; - - mutex_lock(&lm->mtx); - - /* Disable Location Monitor and ensure previous interrupts are clear */ - tmp = ioread32(bridge->base + LINT_EN); - tmp &= ~CA91CX42_LINT_LM[monitor]; - iowrite32(tmp, bridge->base + LINT_EN); - - iowrite32(CA91CX42_LINT_LM[monitor], - bridge->base + LINT_STAT); - - /* Detach callback */ - bridge->lm_callback[monitor] = NULL; - bridge->lm_data[monitor] = NULL; - - /* If all location monitors disabled, disable global Location Monitor */ - if ((tmp & (CA91CX42_LINT_LM0 | CA91CX42_LINT_LM1 | CA91CX42_LINT_LM2 | - CA91CX42_LINT_LM3)) == 0) { - tmp = ioread32(bridge->base + LM_CTL); - tmp &= ~CA91CX42_LM_CTL_EN; - iowrite32(tmp, bridge->base + LM_CTL); - } - - mutex_unlock(&lm->mtx); - - return 0; -} - -static int ca91cx42_slot_get(struct vme_bridge *ca91cx42_bridge) -{ - u32 slot = 0; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - if (!geoid) { - slot = ioread32(bridge->base + VCSR_BS); - slot = ((slot & CA91CX42_VCSR_BS_SLOT_M) >> 27); - } else - slot = geoid; - - return (int)slot; - -} - -static void *ca91cx42_alloc_consistent(struct device *parent, size_t size, - dma_addr_t *dma) -{ - struct pci_dev *pdev; - - /* Find pci_dev container of dev */ - pdev = to_pci_dev(parent); - - return dma_alloc_coherent(&pdev->dev, size, dma, GFP_KERNEL); -} - -static void ca91cx42_free_consistent(struct device *parent, size_t size, - void *vaddr, dma_addr_t dma) -{ - struct pci_dev *pdev; - - /* Find pci_dev container of dev */ - pdev = to_pci_dev(parent); - - dma_free_coherent(&pdev->dev, size, vaddr, dma); -} - -/* - * Configure CR/CSR space - * - * Access to the CR/CSR can be configured at power-up. The location of the - * CR/CSR registers in the CR/CSR address space is determined by the boards - * Auto-ID or Geographic address. This function ensures that the window is - * enabled at an offset consistent with the boards geopgraphic address. - */ -static int ca91cx42_crcsr_init(struct vme_bridge *ca91cx42_bridge, - struct pci_dev *pdev) -{ - unsigned int crcsr_addr; - int tmp, slot; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - slot = ca91cx42_slot_get(ca91cx42_bridge); - - /* Write CSR Base Address if slot ID is supplied as a module param */ - if (geoid) - iowrite32(geoid << 27, bridge->base + VCSR_BS); - - dev_info(&pdev->dev, "CR/CSR Offset: %d\n", slot); - if (slot == 0) { - dev_err(&pdev->dev, "Slot number is unset, not configuring " - "CR/CSR space\n"); - return -EINVAL; - } - - /* Allocate mem for CR/CSR image */ - bridge->crcsr_kernel = dma_alloc_coherent(&pdev->dev, - VME_CRCSR_BUF_SIZE, - &bridge->crcsr_bus, GFP_KERNEL); - if (!bridge->crcsr_kernel) { - dev_err(&pdev->dev, "Failed to allocate memory for CR/CSR " - "image\n"); - return -ENOMEM; - } - - crcsr_addr = slot * (512 * 1024); - iowrite32(bridge->crcsr_bus - crcsr_addr, bridge->base + VCSR_TO); - - tmp = ioread32(bridge->base + VCSR_CTL); - tmp |= CA91CX42_VCSR_CTL_EN; - iowrite32(tmp, bridge->base + VCSR_CTL); - - return 0; -} - -static void ca91cx42_crcsr_exit(struct vme_bridge *ca91cx42_bridge, - struct pci_dev *pdev) -{ - u32 tmp; - struct ca91cx42_driver *bridge; - - bridge = ca91cx42_bridge->driver_priv; - - /* Turn off CR/CSR space */ - tmp = ioread32(bridge->base + VCSR_CTL); - tmp &= ~CA91CX42_VCSR_CTL_EN; - iowrite32(tmp, bridge->base + VCSR_CTL); - - /* Free image */ - iowrite32(0, bridge->base + VCSR_TO); - - dma_free_coherent(&pdev->dev, VME_CRCSR_BUF_SIZE, - bridge->crcsr_kernel, bridge->crcsr_bus); -} - -static int ca91cx42_probe(struct pci_dev *pdev, const struct pci_device_id *id) -{ - int retval, i; - u32 data; - struct list_head *pos = NULL, *n; - struct vme_bridge *ca91cx42_bridge; - struct ca91cx42_driver *ca91cx42_device; - struct vme_master_resource *master_image; - struct vme_slave_resource *slave_image; - struct vme_dma_resource *dma_ctrlr; - struct vme_lm_resource *lm; - - /* We want to support more than one of each bridge so we need to - * dynamically allocate the bridge structure - */ - ca91cx42_bridge = kzalloc(sizeof(*ca91cx42_bridge), GFP_KERNEL); - if (!ca91cx42_bridge) { - retval = -ENOMEM; - goto err_struct; - } - vme_init_bridge(ca91cx42_bridge); - - ca91cx42_device = kzalloc(sizeof(*ca91cx42_device), GFP_KERNEL); - if (!ca91cx42_device) { - retval = -ENOMEM; - goto err_driver; - } - - ca91cx42_bridge->driver_priv = ca91cx42_device; - - /* Enable the device */ - retval = pci_enable_device(pdev); - if (retval) { - dev_err(&pdev->dev, "Unable to enable device\n"); - goto err_enable; - } - - /* Map Registers */ - retval = pci_request_regions(pdev, driver_name); - if (retval) { - dev_err(&pdev->dev, "Unable to reserve resources\n"); - goto err_resource; - } - - /* map registers in BAR 0 */ - ca91cx42_device->base = ioremap(pci_resource_start(pdev, 0), - 4096); - if (!ca91cx42_device->base) { - dev_err(&pdev->dev, "Unable to remap CRG region\n"); - retval = -EIO; - goto err_remap; - } - - /* Check to see if the mapping worked out */ - data = ioread32(ca91cx42_device->base + CA91CX42_PCI_ID) & 0x0000FFFF; - if (data != PCI_VENDOR_ID_TUNDRA) { - dev_err(&pdev->dev, "PCI_ID check failed\n"); - retval = -EIO; - goto err_test; - } - - /* Initialize wait queues & mutual exclusion flags */ - init_waitqueue_head(&ca91cx42_device->dma_queue); - init_waitqueue_head(&ca91cx42_device->iack_queue); - mutex_init(&ca91cx42_device->vme_int); - mutex_init(&ca91cx42_device->vme_rmw); - - ca91cx42_bridge->parent = &pdev->dev; - strcpy(ca91cx42_bridge->name, driver_name); - - /* Setup IRQ */ - retval = ca91cx42_irq_init(ca91cx42_bridge); - if (retval != 0) { - dev_err(&pdev->dev, "Chip Initialization failed.\n"); - goto err_irq; - } - - /* Add master windows to list */ - for (i = 0; i < CA91C142_MAX_MASTER; i++) { - master_image = kmalloc(sizeof(*master_image), GFP_KERNEL); - if (!master_image) { - retval = -ENOMEM; - goto err_master; - } - master_image->parent = ca91cx42_bridge; - spin_lock_init(&master_image->lock); - master_image->locked = 0; - master_image->number = i; - master_image->address_attr = VME_A16 | VME_A24 | VME_A32 | - VME_CRCSR | VME_USER1 | VME_USER2; - master_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | - VME_SUPER | VME_USER | VME_PROG | VME_DATA; - master_image->width_attr = VME_D8 | VME_D16 | VME_D32 | VME_D64; - memset(&master_image->bus_resource, 0, - sizeof(master_image->bus_resource)); - master_image->kern_base = NULL; - list_add_tail(&master_image->list, - &ca91cx42_bridge->master_resources); - } - - /* Add slave windows to list */ - for (i = 0; i < CA91C142_MAX_SLAVE; i++) { - slave_image = kmalloc(sizeof(*slave_image), GFP_KERNEL); - if (!slave_image) { - retval = -ENOMEM; - goto err_slave; - } - slave_image->parent = ca91cx42_bridge; - mutex_init(&slave_image->mtx); - slave_image->locked = 0; - slave_image->number = i; - slave_image->address_attr = VME_A24 | VME_A32 | VME_USER1 | - VME_USER2; - - /* Only windows 0 and 4 support A16 */ - if (i == 0 || i == 4) - slave_image->address_attr |= VME_A16; - - slave_image->cycle_attr = VME_SCT | VME_BLT | VME_MBLT | - VME_SUPER | VME_USER | VME_PROG | VME_DATA; - list_add_tail(&slave_image->list, - &ca91cx42_bridge->slave_resources); - } - - /* Add dma engines to list */ - for (i = 0; i < CA91C142_MAX_DMA; i++) { - dma_ctrlr = kmalloc(sizeof(*dma_ctrlr), GFP_KERNEL); - if (!dma_ctrlr) { - retval = -ENOMEM; - goto err_dma; - } - dma_ctrlr->parent = ca91cx42_bridge; - mutex_init(&dma_ctrlr->mtx); - dma_ctrlr->locked = 0; - dma_ctrlr->number = i; - dma_ctrlr->route_attr = VME_DMA_VME_TO_MEM | - VME_DMA_MEM_TO_VME; - INIT_LIST_HEAD(&dma_ctrlr->pending); - INIT_LIST_HEAD(&dma_ctrlr->running); - list_add_tail(&dma_ctrlr->list, - &ca91cx42_bridge->dma_resources); - } - - /* Add location monitor to list */ - lm = kmalloc(sizeof(*lm), GFP_KERNEL); - if (!lm) { - retval = -ENOMEM; - goto err_lm; - } - lm->parent = ca91cx42_bridge; - mutex_init(&lm->mtx); - lm->locked = 0; - lm->number = 1; - lm->monitors = 4; - list_add_tail(&lm->list, &ca91cx42_bridge->lm_resources); - - ca91cx42_bridge->slave_get = ca91cx42_slave_get; - ca91cx42_bridge->slave_set = ca91cx42_slave_set; - ca91cx42_bridge->master_get = ca91cx42_master_get; - ca91cx42_bridge->master_set = ca91cx42_master_set; - ca91cx42_bridge->master_read = ca91cx42_master_read; - ca91cx42_bridge->master_write = ca91cx42_master_write; - ca91cx42_bridge->master_rmw = ca91cx42_master_rmw; - ca91cx42_bridge->dma_list_add = ca91cx42_dma_list_add; - ca91cx42_bridge->dma_list_exec = ca91cx42_dma_list_exec; - ca91cx42_bridge->dma_list_empty = ca91cx42_dma_list_empty; - ca91cx42_bridge->irq_set = ca91cx42_irq_set; - ca91cx42_bridge->irq_generate = ca91cx42_irq_generate; - ca91cx42_bridge->lm_set = ca91cx42_lm_set; - ca91cx42_bridge->lm_get = ca91cx42_lm_get; - ca91cx42_bridge->lm_attach = ca91cx42_lm_attach; - ca91cx42_bridge->lm_detach = ca91cx42_lm_detach; - ca91cx42_bridge->slot_get = ca91cx42_slot_get; - ca91cx42_bridge->alloc_consistent = ca91cx42_alloc_consistent; - ca91cx42_bridge->free_consistent = ca91cx42_free_consistent; - - data = ioread32(ca91cx42_device->base + MISC_CTL); - dev_info(&pdev->dev, "Board is%s the VME system controller\n", - (data & CA91CX42_MISC_CTL_SYSCON) ? "" : " not"); - dev_info(&pdev->dev, "Slot ID is %d\n", - ca91cx42_slot_get(ca91cx42_bridge)); - - if (ca91cx42_crcsr_init(ca91cx42_bridge, pdev)) - dev_err(&pdev->dev, "CR/CSR configuration failed.\n"); - - /* Need to save ca91cx42_bridge pointer locally in link list for use in - * ca91cx42_remove() - */ - retval = vme_register_bridge(ca91cx42_bridge); - if (retval != 0) { - dev_err(&pdev->dev, "Chip Registration failed.\n"); - goto err_reg; - } - - pci_set_drvdata(pdev, ca91cx42_bridge); - - return 0; - -err_reg: - ca91cx42_crcsr_exit(ca91cx42_bridge, pdev); -err_lm: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) { - lm = list_entry(pos, struct vme_lm_resource, list); - list_del(pos); - kfree(lm); - } -err_dma: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) { - dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); - list_del(pos); - kfree(dma_ctrlr); - } -err_slave: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) { - slave_image = list_entry(pos, struct vme_slave_resource, list); - list_del(pos); - kfree(slave_image); - } -err_master: - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) { - master_image = list_entry(pos, struct vme_master_resource, - list); - list_del(pos); - kfree(master_image); - } - - ca91cx42_irq_exit(ca91cx42_device, pdev); -err_irq: -err_test: - iounmap(ca91cx42_device->base); -err_remap: - pci_release_regions(pdev); -err_resource: - pci_disable_device(pdev); -err_enable: - kfree(ca91cx42_device); -err_driver: - kfree(ca91cx42_bridge); -err_struct: - return retval; - -} - -static void ca91cx42_remove(struct pci_dev *pdev) -{ - struct list_head *pos = NULL, *n; - struct vme_master_resource *master_image; - struct vme_slave_resource *slave_image; - struct vme_dma_resource *dma_ctrlr; - struct vme_lm_resource *lm; - struct ca91cx42_driver *bridge; - struct vme_bridge *ca91cx42_bridge = pci_get_drvdata(pdev); - - bridge = ca91cx42_bridge->driver_priv; - - - /* Turn off Ints */ - iowrite32(0, bridge->base + LINT_EN); - - /* Turn off the windows */ - iowrite32(0x00800000, bridge->base + LSI0_CTL); - iowrite32(0x00800000, bridge->base + LSI1_CTL); - iowrite32(0x00800000, bridge->base + LSI2_CTL); - iowrite32(0x00800000, bridge->base + LSI3_CTL); - iowrite32(0x00800000, bridge->base + LSI4_CTL); - iowrite32(0x00800000, bridge->base + LSI5_CTL); - iowrite32(0x00800000, bridge->base + LSI6_CTL); - iowrite32(0x00800000, bridge->base + LSI7_CTL); - iowrite32(0x00F00000, bridge->base + VSI0_CTL); - iowrite32(0x00F00000, bridge->base + VSI1_CTL); - iowrite32(0x00F00000, bridge->base + VSI2_CTL); - iowrite32(0x00F00000, bridge->base + VSI3_CTL); - iowrite32(0x00F00000, bridge->base + VSI4_CTL); - iowrite32(0x00F00000, bridge->base + VSI5_CTL); - iowrite32(0x00F00000, bridge->base + VSI6_CTL); - iowrite32(0x00F00000, bridge->base + VSI7_CTL); - - vme_unregister_bridge(ca91cx42_bridge); - - ca91cx42_crcsr_exit(ca91cx42_bridge, pdev); - - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->lm_resources) { - lm = list_entry(pos, struct vme_lm_resource, list); - list_del(pos); - kfree(lm); - } - - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->dma_resources) { - dma_ctrlr = list_entry(pos, struct vme_dma_resource, list); - list_del(pos); - kfree(dma_ctrlr); - } - - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->slave_resources) { - slave_image = list_entry(pos, struct vme_slave_resource, list); - list_del(pos); - kfree(slave_image); - } - - /* resources are stored in link list */ - list_for_each_safe(pos, n, &ca91cx42_bridge->master_resources) { - master_image = list_entry(pos, struct vme_master_resource, - list); - list_del(pos); - kfree(master_image); - } - - ca91cx42_irq_exit(bridge, pdev); - - iounmap(bridge->base); - - pci_release_regions(pdev); - - pci_disable_device(pdev); - - kfree(ca91cx42_bridge); -} - -module_pci_driver(ca91cx42_driver); - -MODULE_PARM_DESC(geoid, "Override geographical addressing"); -module_param(geoid, int, 0); - -MODULE_DESCRIPTION("VME driver for the Tundra Universe II VME bridge"); -MODULE_LICENSE("GPL"); diff --git a/drivers/vme/bridges/vme_ca91cx42.h b/drivers/vme/bridges/vme_ca91cx42.h deleted file mode 100644 index 34a8c25de613..000000000000 --- a/drivers/vme/bridges/vme_ca91cx42.h +++ /dev/null @@ -1,579 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-or-later */ -/* - * ca91c042.h - * - * Support for the Tundra Universe 1 and Universe II VME bridge chips - * - * Author: Tom Armistead - * Updated by Ajit Prem - * Copyright 2004 Motorola Inc. - * - * Further updated by Martyn Welch - * Copyright 2009 GE Intelligent Platforms Embedded Systems, Inc. - * - * Derived from ca91c042.h by Michael Wyrick - */ - -#ifndef _CA91CX42_H -#define _CA91CX42_H - -#ifndef PCI_VENDOR_ID_TUNDRA -#define PCI_VENDOR_ID_TUNDRA 0x10e3 -#endif - -#ifndef PCI_DEVICE_ID_TUNDRA_CA91C142 -#define PCI_DEVICE_ID_TUNDRA_CA91C142 0x0000 -#endif - -/* - * Define the number of each that the CA91C142 supports. - */ -#define CA91C142_MAX_MASTER 8 /* Max Master Windows */ -#define CA91C142_MAX_SLAVE 8 /* Max Slave Windows */ -#define CA91C142_MAX_DMA 1 /* Max DMA Controllers */ -#define CA91C142_MAX_MAILBOX 4 /* Max Mail Box registers */ - -/* Structure used to hold driver specific information */ -struct ca91cx42_driver { - void __iomem *base; /* Base Address of device registers */ - wait_queue_head_t dma_queue; - wait_queue_head_t iack_queue; - wait_queue_head_t mbox_queue; - void (*lm_callback[4])(void *); /* Called in interrupt handler */ - void *lm_data[4]; - void *crcsr_kernel; - dma_addr_t crcsr_bus; - struct mutex vme_rmw; /* Only one RMW cycle at a time */ - struct mutex vme_int; /* - * Only one VME interrupt can be - * generated at a time, provide locking - */ -}; - -/* See Page 2-77 in the Universe User Manual */ -struct ca91cx42_dma_descriptor { - unsigned int dctl; /* DMA Control */ - unsigned int dtbc; /* Transfer Byte Count */ - unsigned int dla; /* PCI Address */ - unsigned int res1; /* Reserved */ - unsigned int dva; /* Vme Address */ - unsigned int res2; /* Reserved */ - unsigned int dcpp; /* Pointer to Numed Cmd Packet with rPN */ - unsigned int res3; /* Reserved */ -}; - -struct ca91cx42_dma_entry { - struct ca91cx42_dma_descriptor descriptor; - struct list_head list; -}; - -/* Universe Register Offsets */ -/* general PCI configuration registers */ -#define CA91CX42_PCI_ID 0x000 -#define CA91CX42_PCI_CSR 0x004 -#define CA91CX42_PCI_CLASS 0x008 -#define CA91CX42_PCI_MISC0 0x00C -#define CA91CX42_PCI_BS 0x010 -#define CA91CX42_PCI_MISC1 0x03C - -#define LSI0_CTL 0x0100 -#define LSI0_BS 0x0104 -#define LSI0_BD 0x0108 -#define LSI0_TO 0x010C - -#define LSI1_CTL 0x0114 -#define LSI1_BS 0x0118 -#define LSI1_BD 0x011C -#define LSI1_TO 0x0120 - -#define LSI2_CTL 0x0128 -#define LSI2_BS 0x012C -#define LSI2_BD 0x0130 -#define LSI2_TO 0x0134 - -#define LSI3_CTL 0x013C -#define LSI3_BS 0x0140 -#define LSI3_BD 0x0144 -#define LSI3_TO 0x0148 - -#define LSI4_CTL 0x01A0 -#define LSI4_BS 0x01A4 -#define LSI4_BD 0x01A8 -#define LSI4_TO 0x01AC - -#define LSI5_CTL 0x01B4 -#define LSI5_BS 0x01B8 -#define LSI5_BD 0x01BC -#define LSI5_TO 0x01C0 - -#define LSI6_CTL 0x01C8 -#define LSI6_BS 0x01CC -#define LSI6_BD 0x01D0 -#define LSI6_TO 0x01D4 - -#define LSI7_CTL 0x01DC -#define LSI7_BS 0x01E0 -#define LSI7_BD 0x01E4 -#define LSI7_TO 0x01E8 - -static const int CA91CX42_LSI_CTL[] = { LSI0_CTL, LSI1_CTL, LSI2_CTL, LSI3_CTL, - LSI4_CTL, LSI5_CTL, LSI6_CTL, LSI7_CTL }; - -static const int CA91CX42_LSI_BS[] = { LSI0_BS, LSI1_BS, LSI2_BS, LSI3_BS, - LSI4_BS, LSI5_BS, LSI6_BS, LSI7_BS }; - -static const int CA91CX42_LSI_BD[] = { LSI0_BD, LSI1_BD, LSI2_BD, LSI3_BD, - LSI4_BD, LSI5_BD, LSI6_BD, LSI7_BD }; - -static const int CA91CX42_LSI_TO[] = { LSI0_TO, LSI1_TO, LSI2_TO, LSI3_TO, - LSI4_TO, LSI5_TO, LSI6_TO, LSI7_TO }; - -#define SCYC_CTL 0x0170 -#define SCYC_ADDR 0x0174 -#define SCYC_EN 0x0178 -#define SCYC_CMP 0x017C -#define SCYC_SWP 0x0180 -#define LMISC 0x0184 -#define SLSI 0x0188 -#define L_CMDERR 0x018C -#define LAERR 0x0190 - -#define DCTL 0x0200 -#define DTBC 0x0204 -#define DLA 0x0208 -#define DVA 0x0210 -#define DCPP 0x0218 -#define DGCS 0x0220 -#define D_LLUE 0x0224 - -#define LINT_EN 0x0300 -#define LINT_STAT 0x0304 -#define LINT_MAP0 0x0308 -#define LINT_MAP1 0x030C -#define VINT_EN 0x0310 -#define VINT_STAT 0x0314 -#define VINT_MAP0 0x0318 -#define VINT_MAP1 0x031C -#define STATID 0x0320 - -#define V1_STATID 0x0324 -#define V2_STATID 0x0328 -#define V3_STATID 0x032C -#define V4_STATID 0x0330 -#define V5_STATID 0x0334 -#define V6_STATID 0x0338 -#define V7_STATID 0x033C - -static const int CA91CX42_V_STATID[8] = { 0, V1_STATID, V2_STATID, V3_STATID, - V4_STATID, V5_STATID, V6_STATID, - V7_STATID }; - -#define LINT_MAP2 0x0340 -#define VINT_MAP2 0x0344 - -#define MBOX0 0x0348 -#define MBOX1 0x034C -#define MBOX2 0x0350 -#define MBOX3 0x0354 -#define SEMA0 0x0358 -#define SEMA1 0x035C - -#define MAST_CTL 0x0400 -#define MISC_CTL 0x0404 -#define MISC_STAT 0x0408 -#define USER_AM 0x040C - -#define VSI0_CTL 0x0F00 -#define VSI0_BS 0x0F04 -#define VSI0_BD 0x0F08 -#define VSI0_TO 0x0F0C - -#define VSI1_CTL 0x0F14 -#define VSI1_BS 0x0F18 -#define VSI1_BD 0x0F1C -#define VSI1_TO 0x0F20 - -#define VSI2_CTL 0x0F28 -#define VSI2_BS 0x0F2C -#define VSI2_BD 0x0F30 -#define VSI2_TO 0x0F34 - -#define VSI3_CTL 0x0F3C -#define VSI3_BS 0x0F40 -#define VSI3_BD 0x0F44 -#define VSI3_TO 0x0F48 - -#define LM_CTL 0x0F64 -#define LM_BS 0x0F68 - -#define VRAI_CTL 0x0F70 - -#define VRAI_BS 0x0F74 -#define VCSR_CTL 0x0F80 -#define VCSR_TO 0x0F84 -#define V_AMERR 0x0F88 -#define VAERR 0x0F8C - -#define VSI4_CTL 0x0F90 -#define VSI4_BS 0x0F94 -#define VSI4_BD 0x0F98 -#define VSI4_TO 0x0F9C - -#define VSI5_CTL 0x0FA4 -#define VSI5_BS 0x0FA8 -#define VSI5_BD 0x0FAC -#define VSI5_TO 0x0FB0 - -#define VSI6_CTL 0x0FB8 -#define VSI6_BS 0x0FBC -#define VSI6_BD 0x0FC0 -#define VSI6_TO 0x0FC4 - -#define VSI7_CTL 0x0FCC -#define VSI7_BS 0x0FD0 -#define VSI7_BD 0x0FD4 -#define VSI7_TO 0x0FD8 - -static const int CA91CX42_VSI_CTL[] = { VSI0_CTL, VSI1_CTL, VSI2_CTL, VSI3_CTL, - VSI4_CTL, VSI5_CTL, VSI6_CTL, VSI7_CTL }; - -static const int CA91CX42_VSI_BS[] = { VSI0_BS, VSI1_BS, VSI2_BS, VSI3_BS, - VSI4_BS, VSI5_BS, VSI6_BS, VSI7_BS }; - -static const int CA91CX42_VSI_BD[] = { VSI0_BD, VSI1_BD, VSI2_BD, VSI3_BD, - VSI4_BD, VSI5_BD, VSI6_BD, VSI7_BD }; - -static const int CA91CX42_VSI_TO[] = { VSI0_TO, VSI1_TO, VSI2_TO, VSI3_TO, - VSI4_TO, VSI5_TO, VSI6_TO, VSI7_TO }; - -#define VCSR_CLR 0x0FF4 -#define VCSR_SET 0x0FF8 -#define VCSR_BS 0x0FFC - -/* - * PCI Class Register - * offset 008 - */ -#define CA91CX42_BM_PCI_CLASS_BASE 0xFF000000 -#define CA91CX42_OF_PCI_CLASS_BASE 24 -#define CA91CX42_BM_PCI_CLASS_SUB 0x00FF0000 -#define CA91CX42_OF_PCI_CLASS_SUB 16 -#define CA91CX42_BM_PCI_CLASS_PROG 0x0000FF00 -#define CA91CX42_OF_PCI_CLASS_PROG 8 -#define CA91CX42_BM_PCI_CLASS_RID 0x000000FF -#define CA91CX42_OF_PCI_CLASS_RID 0 - -#define CA91CX42_OF_PCI_CLASS_RID_UNIVERSE_I 0 -#define CA91CX42_OF_PCI_CLASS_RID_UNIVERSE_II 1 - -/* - * PCI Misc Register - * offset 00C - */ -#define CA91CX42_BM_PCI_MISC0_BISTC 0x80000000 -#define CA91CX42_BM_PCI_MISC0_SBIST 0x60000000 -#define CA91CX42_BM_PCI_MISC0_CCODE 0x0F000000 -#define CA91CX42_BM_PCI_MISC0_MFUNCT 0x00800000 -#define CA91CX42_BM_PCI_MISC0_LAYOUT 0x007F0000 -#define CA91CX42_BM_PCI_MISC0_LTIMER 0x0000FF00 -#define CA91CX42_OF_PCI_MISC0_LTIMER 8 - - -/* - * LSI Control Register - * offset 100 - */ -#define CA91CX42_LSI_CTL_EN (1<<31) -#define CA91CX42_LSI_CTL_PWEN (1<<30) - -#define CA91CX42_LSI_CTL_VDW_M (3<<22) -#define CA91CX42_LSI_CTL_VDW_D8 0 -#define CA91CX42_LSI_CTL_VDW_D16 (1<<22) -#define CA91CX42_LSI_CTL_VDW_D32 (1<<23) -#define CA91CX42_LSI_CTL_VDW_D64 (3<<22) - -#define CA91CX42_LSI_CTL_VAS_M (7<<16) -#define CA91CX42_LSI_CTL_VAS_A16 0 -#define CA91CX42_LSI_CTL_VAS_A24 (1<<16) -#define CA91CX42_LSI_CTL_VAS_A32 (1<<17) -#define CA91CX42_LSI_CTL_VAS_CRCSR (5<<16) -#define CA91CX42_LSI_CTL_VAS_USER1 (3<<17) -#define CA91CX42_LSI_CTL_VAS_USER2 (7<<16) - -#define CA91CX42_LSI_CTL_PGM_M (1<<14) -#define CA91CX42_LSI_CTL_PGM_DATA 0 -#define CA91CX42_LSI_CTL_PGM_PGM (1<<14) - -#define CA91CX42_LSI_CTL_SUPER_M (1<<12) -#define CA91CX42_LSI_CTL_SUPER_NPRIV 0 -#define CA91CX42_LSI_CTL_SUPER_SUPR (1<<12) - -#define CA91CX42_LSI_CTL_VCT_M (1<<8) -#define CA91CX42_LSI_CTL_VCT_BLT (1<<8) -#define CA91CX42_LSI_CTL_VCT_MBLT (1<<8) -#define CA91CX42_LSI_CTL_LAS (1<<0) - -/* - * SCYC_CTL Register - * offset 178 - */ -#define CA91CX42_SCYC_CTL_LAS_PCIMEM 0 -#define CA91CX42_SCYC_CTL_LAS_PCIIO (1<<2) - -#define CA91CX42_SCYC_CTL_CYC_M (3<<0) -#define CA91CX42_SCYC_CTL_CYC_RMW (1<<0) -#define CA91CX42_SCYC_CTL_CYC_ADOH (1<<1) - -/* - * LMISC Register - * offset 184 - */ -#define CA91CX42_BM_LMISC_CRT 0xF0000000 -#define CA91CX42_OF_LMISC_CRT 28 -#define CA91CX42_BM_LMISC_CWT 0x0F000000 -#define CA91CX42_OF_LMISC_CWT 24 - -/* - * SLSI Register - * offset 188 - */ -#define CA91CX42_BM_SLSI_EN 0x80000000 -#define CA91CX42_BM_SLSI_PWEN 0x40000000 -#define CA91CX42_BM_SLSI_VDW 0x00F00000 -#define CA91CX42_OF_SLSI_VDW 20 -#define CA91CX42_BM_SLSI_PGM 0x0000F000 -#define CA91CX42_OF_SLSI_PGM 12 -#define CA91CX42_BM_SLSI_SUPER 0x00000F00 -#define CA91CX42_OF_SLSI_SUPER 8 -#define CA91CX42_BM_SLSI_BS 0x000000F6 -#define CA91CX42_OF_SLSI_BS 2 -#define CA91CX42_BM_SLSI_LAS 0x00000003 -#define CA91CX42_OF_SLSI_LAS 0 -#define CA91CX42_BM_SLSI_RESERVED 0x3F0F0000 - -/* - * DCTL Register - * offset 200 - */ -#define CA91CX42_DCTL_L2V (1<<31) -#define CA91CX42_DCTL_VDW_M (3<<22) -#define CA91CX42_DCTL_VDW_D8 0 -#define CA91CX42_DCTL_VDW_D16 (1<<22) -#define CA91CX42_DCTL_VDW_D32 (1<<23) -#define CA91CX42_DCTL_VDW_D64 (3<<22) - -#define CA91CX42_DCTL_VAS_M (7<<16) -#define CA91CX42_DCTL_VAS_A16 0 -#define CA91CX42_DCTL_VAS_A24 (1<<16) -#define CA91CX42_DCTL_VAS_A32 (1<<17) -#define CA91CX42_DCTL_VAS_USER1 (3<<17) -#define CA91CX42_DCTL_VAS_USER2 (7<<16) - -#define CA91CX42_DCTL_PGM_M (1<<14) -#define CA91CX42_DCTL_PGM_DATA 0 -#define CA91CX42_DCTL_PGM_PGM (1<<14) - -#define CA91CX42_DCTL_SUPER_M (1<<12) -#define CA91CX42_DCTL_SUPER_NPRIV 0 -#define CA91CX42_DCTL_SUPER_SUPR (1<<12) - -#define CA91CX42_DCTL_VCT_M (1<<8) -#define CA91CX42_DCTL_VCT_BLT (1<<8) -#define CA91CX42_DCTL_LD64EN (1<<7) - -/* - * DCPP Register - * offset 218 - */ -#define CA91CX42_DCPP_M 0xf -#define CA91CX42_DCPP_NULL (1<<0) - -/* - * DMA General Control/Status Register (DGCS) - * offset 220 - */ -#define CA91CX42_DGCS_GO (1<<31) -#define CA91CX42_DGCS_STOP_REQ (1<<30) -#define CA91CX42_DGCS_HALT_REQ (1<<29) -#define CA91CX42_DGCS_CHAIN (1<<27) - -#define CA91CX42_DGCS_VON_M (7<<20) - -#define CA91CX42_DGCS_VOFF_M (0xf<<16) - -#define CA91CX42_DGCS_ACT (1<<15) -#define CA91CX42_DGCS_STOP (1<<14) -#define CA91CX42_DGCS_HALT (1<<13) -#define CA91CX42_DGCS_DONE (1<<11) -#define CA91CX42_DGCS_LERR (1<<10) -#define CA91CX42_DGCS_VERR (1<<9) -#define CA91CX42_DGCS_PERR (1<<8) -#define CA91CX42_DGCS_INT_STOP (1<<6) -#define CA91CX42_DGCS_INT_HALT (1<<5) -#define CA91CX42_DGCS_INT_DONE (1<<3) -#define CA91CX42_DGCS_INT_LERR (1<<2) -#define CA91CX42_DGCS_INT_VERR (1<<1) -#define CA91CX42_DGCS_INT_PERR (1<<0) - -/* - * PCI Interrupt Enable Register - * offset 300 - */ -#define CA91CX42_LINT_LM3 0x00800000 -#define CA91CX42_LINT_LM2 0x00400000 -#define CA91CX42_LINT_LM1 0x00200000 -#define CA91CX42_LINT_LM0 0x00100000 -#define CA91CX42_LINT_MBOX3 0x00080000 -#define CA91CX42_LINT_MBOX2 0x00040000 -#define CA91CX42_LINT_MBOX1 0x00020000 -#define CA91CX42_LINT_MBOX0 0x00010000 -#define CA91CX42_LINT_ACFAIL 0x00008000 -#define CA91CX42_LINT_SYSFAIL 0x00004000 -#define CA91CX42_LINT_SW_INT 0x00002000 -#define CA91CX42_LINT_SW_IACK 0x00001000 - -#define CA91CX42_LINT_VERR 0x00000400 -#define CA91CX42_LINT_LERR 0x00000200 -#define CA91CX42_LINT_DMA 0x00000100 -#define CA91CX42_LINT_VIRQ7 0x00000080 -#define CA91CX42_LINT_VIRQ6 0x00000040 -#define CA91CX42_LINT_VIRQ5 0x00000020 -#define CA91CX42_LINT_VIRQ4 0x00000010 -#define CA91CX42_LINT_VIRQ3 0x00000008 -#define CA91CX42_LINT_VIRQ2 0x00000004 -#define CA91CX42_LINT_VIRQ1 0x00000002 -#define CA91CX42_LINT_VOWN 0x00000001 - -static const int CA91CX42_LINT_VIRQ[] = { 0, CA91CX42_LINT_VIRQ1, - CA91CX42_LINT_VIRQ2, CA91CX42_LINT_VIRQ3, - CA91CX42_LINT_VIRQ4, CA91CX42_LINT_VIRQ5, - CA91CX42_LINT_VIRQ6, CA91CX42_LINT_VIRQ7 }; - -#define CA91CX42_LINT_MBOX 0x000F0000 - -static const int CA91CX42_LINT_LM[] = { CA91CX42_LINT_LM0, CA91CX42_LINT_LM1, - CA91CX42_LINT_LM2, CA91CX42_LINT_LM3 }; - -/* - * MAST_CTL Register - * offset 400 - */ -#define CA91CX42_BM_MAST_CTL_MAXRTRY 0xF0000000 -#define CA91CX42_OF_MAST_CTL_MAXRTRY 28 -#define CA91CX42_BM_MAST_CTL_PWON 0x0F000000 -#define CA91CX42_OF_MAST_CTL_PWON 24 -#define CA91CX42_BM_MAST_CTL_VRL 0x00C00000 -#define CA91CX42_OF_MAST_CTL_VRL 22 -#define CA91CX42_BM_MAST_CTL_VRM 0x00200000 -#define CA91CX42_BM_MAST_CTL_VREL 0x00100000 -#define CA91CX42_BM_MAST_CTL_VOWN 0x00080000 -#define CA91CX42_BM_MAST_CTL_VOWN_ACK 0x00040000 -#define CA91CX42_BM_MAST_CTL_PABS 0x00001000 -#define CA91CX42_BM_MAST_CTL_BUS_NO 0x0000000F -#define CA91CX42_OF_MAST_CTL_BUS_NO 0 - -/* - * MISC_CTL Register - * offset 404 - */ -#define CA91CX42_MISC_CTL_VBTO 0xF0000000 -#define CA91CX42_MISC_CTL_VARB 0x04000000 -#define CA91CX42_MISC_CTL_VARBTO 0x03000000 -#define CA91CX42_MISC_CTL_SW_LRST 0x00800000 -#define CA91CX42_MISC_CTL_SW_SRST 0x00400000 -#define CA91CX42_MISC_CTL_BI 0x00100000 -#define CA91CX42_MISC_CTL_ENGBI 0x00080000 -#define CA91CX42_MISC_CTL_RESCIND 0x00040000 -#define CA91CX42_MISC_CTL_SYSCON 0x00020000 -#define CA91CX42_MISC_CTL_V64AUTO 0x00010000 -#define CA91CX42_MISC_CTL_RESERVED 0x0820FFFF - -#define CA91CX42_OF_MISC_CTL_VARBTO 24 -#define CA91CX42_OF_MISC_CTL_VBTO 28 - -/* - * MISC_STAT Register - * offset 408 - */ -#define CA91CX42_BM_MISC_STAT_ENDIAN 0x80000000 -#define CA91CX42_BM_MISC_STAT_LCLSIZE 0x40000000 -#define CA91CX42_BM_MISC_STAT_DY4AUTO 0x08000000 -#define CA91CX42_BM_MISC_STAT_MYBBSY 0x00200000 -#define CA91CX42_BM_MISC_STAT_DY4DONE 0x00080000 -#define CA91CX42_BM_MISC_STAT_TXFE 0x00040000 -#define CA91CX42_BM_MISC_STAT_RXFE 0x00020000 -#define CA91CX42_BM_MISC_STAT_DY4AUTOID 0x0000FF00 -#define CA91CX42_OF_MISC_STAT_DY4AUTOID 8 - -/* - * VSI Control Register - * offset F00 - */ -#define CA91CX42_VSI_CTL_EN (1<<31) -#define CA91CX42_VSI_CTL_PWEN (1<<30) -#define CA91CX42_VSI_CTL_PREN (1<<29) - -#define CA91CX42_VSI_CTL_PGM_M (3<<22) -#define CA91CX42_VSI_CTL_PGM_DATA (1<<22) -#define CA91CX42_VSI_CTL_PGM_PGM (1<<23) - -#define CA91CX42_VSI_CTL_SUPER_M (3<<20) -#define CA91CX42_VSI_CTL_SUPER_NPRIV (1<<20) -#define CA91CX42_VSI_CTL_SUPER_SUPR (1<<21) - -#define CA91CX42_VSI_CTL_VAS_M (7<<16) -#define CA91CX42_VSI_CTL_VAS_A16 0 -#define CA91CX42_VSI_CTL_VAS_A24 (1<<16) -#define CA91CX42_VSI_CTL_VAS_A32 (1<<17) -#define CA91CX42_VSI_CTL_VAS_USER1 (3<<17) -#define CA91CX42_VSI_CTL_VAS_USER2 (7<<16) - -#define CA91CX42_VSI_CTL_LD64EN (1<<7) -#define CA91CX42_VSI_CTL_LLRMW (1<<6) - -#define CA91CX42_VSI_CTL_LAS_M (3<<0) -#define CA91CX42_VSI_CTL_LAS_PCI_MS 0 -#define CA91CX42_VSI_CTL_LAS_PCI_IO (1<<0) -#define CA91CX42_VSI_CTL_LAS_PCI_CONF (1<<1) - -/* LM_CTL Register - * offset F64 - */ -#define CA91CX42_LM_CTL_EN (1<<31) -#define CA91CX42_LM_CTL_PGM (1<<23) -#define CA91CX42_LM_CTL_DATA (1<<22) -#define CA91CX42_LM_CTL_SUPR (1<<21) -#define CA91CX42_LM_CTL_NPRIV (1<<20) -#define CA91CX42_LM_CTL_AS_M (7<<16) -#define CA91CX42_LM_CTL_AS_A16 0 -#define CA91CX42_LM_CTL_AS_A24 (1<<16) -#define CA91CX42_LM_CTL_AS_A32 (1<<17) - -/* - * VRAI_CTL Register - * offset F70 - */ -#define CA91CX42_BM_VRAI_CTL_EN 0x80000000 -#define CA91CX42_BM_VRAI_CTL_PGM 0x00C00000 -#define CA91CX42_OF_VRAI_CTL_PGM 22 -#define CA91CX42_BM_VRAI_CTL_SUPER 0x00300000 -#define CA91CX42_OF_VRAI_CTL_SUPER 20 -#define CA91CX42_BM_VRAI_CTL_VAS 0x00030000 -#define CA91CX42_OF_VRAI_CTL_VAS 16 - -/* VCSR_CTL Register - * offset F80 - */ -#define CA91CX42_VCSR_CTL_EN (1<<31) - -#define CA91CX42_VCSR_CTL_LAS_M (3<<0) -#define CA91CX42_VCSR_CTL_LAS_PCI_MS 0 -#define CA91CX42_VCSR_CTL_LAS_PCI_IO (1<<0) -#define CA91CX42_VCSR_CTL_LAS_PCI_CONF (1<<1) - -/* VCSR_BS Register - * offset FFC - */ -#define CA91CX42_VCSR_BS_SLOT_M (0x1F<<27) - -#endif /* _CA91CX42_H */ diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index 32fd37698932..0796f6a9e8ff 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -1647,6 +1647,7 @@ config SIEMENS_SIMATIC_IPC_WDT tristate "Siemens Simatic IPC Watchdog" depends on SIEMENS_SIMATIC_IPC select WATCHDOG_CORE + select P2SB help This driver adds support for several watchdogs found in Industrial PCs from Siemens. diff --git a/drivers/watchdog/simatic-ipc-wdt.c b/drivers/watchdog/simatic-ipc-wdt.c index 8bac793c63fb..6599695dc672 100644 --- a/drivers/watchdog/simatic-ipc-wdt.c +++ b/drivers/watchdog/simatic-ipc-wdt.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -54,9 +55,9 @@ static struct resource io_resource_trigger = DEFINE_RES_IO_NAMED(WD_TRIGGER_IOADR, SZ_1, KBUILD_MODNAME " WD_TRIGGER_IOADR"); -/* the actual start will be discovered with pci, 0 is a placeholder */ +/* the actual start will be discovered with p2sb, 0 is a placeholder */ static struct resource mem_resource = - DEFINE_RES_MEM_NAMED(0, SZ_4, "WD_RESET_BASE_ADR"); + DEFINE_RES_MEM_NAMED(0, 0, "WD_RESET_BASE_ADR"); static u32 wd_timeout_table[] = {2, 4, 6, 8, 16, 32, 48, 64 }; static void __iomem *wd_reset_base_addr; @@ -150,6 +151,7 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev) struct simatic_ipc_platform *plat = pdev->dev.platform_data; struct device *dev = &pdev->dev; struct resource *res; + int ret; switch (plat->devmode) { case SIMATIC_IPC_DEVICE_227E: @@ -190,15 +192,14 @@ static int simatic_ipc_wdt_probe(struct platform_device *pdev) if (plat->devmode == SIMATIC_IPC_DEVICE_427E) { res = &mem_resource; - /* get GPIO base from PCI */ - res->start = simatic_ipc_get_membase0(PCI_DEVFN(0x1f, 1)); - if (res->start == 0) - return -ENODEV; + ret = p2sb_bar(NULL, 0, res); + if (ret) + return ret; /* do the final address calculation */ res->start = res->start + (GPIO_COMMUNITY0_PORT_ID << 16) + PAD_CFG_DW0_GPP_A_23; - res->end += res->start; + res->end = res->start + SZ_4 - 1; wd_reset_base_addr = devm_ioremap_resource(dev, res); if (IS_ERR(wd_reset_base_addr)) diff --git a/drivers/xen/Kconfig b/drivers/xen/Kconfig index bfd5f4f706bc..a65bd92121a5 100644 --- a/drivers/xen/Kconfig +++ b/drivers/xen/Kconfig @@ -355,4 +355,13 @@ config XEN_VIRTIO If in doubt, say n. +config XEN_VIRTIO_FORCE_GRANT + bool "Require Xen virtio support to use grants" + depends on XEN_VIRTIO + help + Require virtio for Xen guests to use grant mappings. + This will avoid the need to give the backend the right to map all + of the guest memory. This will need support on the backend side + (e.g. qemu or kernel, depending on the virtio device types used). + endmenu diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index fc0142484001..8973fc1e9ccc 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include #include @@ -287,6 +289,14 @@ bool xen_is_grant_dma_device(struct device *dev) return has_iommu; } +bool xen_virtio_mem_acc(struct virtio_device *dev) +{ + if (IS_ENABLED(CONFIG_XEN_VIRTIO_FORCE_GRANT)) + return true; + + return xen_is_grant_dma_device(dev->dev.parent); +} + void xen_grant_setup_dma_ops(struct device *dev) { struct xen_grant_dma_data *data; diff --git a/drivers/xen/manage.c b/drivers/xen/manage.c index 3d5a384d65f7..c16df629907e 100644 --- a/drivers/xen/manage.c +++ b/drivers/xen/manage.c @@ -205,7 +205,7 @@ static void do_poweroff(void) static void do_reboot(void) { shutting_down = SHUTDOWN_POWEROFF; /* ? */ - ctrl_alt_del(); + orderly_reboot(); } static struct shutdown_handler shutdown_handlers[] = { diff --git a/drivers/xen/xen-front-pgdir-shbuf.c b/drivers/xen/xen-front-pgdir-shbuf.c index bef8d72a6ca6..5c0b5cb5b419 100644 --- a/drivers/xen/xen-front-pgdir-shbuf.c +++ b/drivers/xen/xen-front-pgdir-shbuf.c @@ -89,7 +89,7 @@ EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_get_dir_start); * shared by the frontend itself) or map the provided granted * references onto the backing storage (buf->pages). * - * \param buf shared buffer which grants to be maped. + * \param buf shared buffer which grants to be mapped. * \return zero on success or a negative number on failure. */ int xen_front_pgdir_shbuf_map(struct xen_front_pgdir_shbuf *buf) @@ -110,7 +110,7 @@ EXPORT_SYMBOL_GPL(xen_front_pgdir_shbuf_map); * shared by the frontend itself) or unmap the provided granted * references. * - * \param buf shared buffer which grants to be unmaped. + * \param buf shared buffer which grants to be unmapped. * \return zero on success or a negative number on failure. */ int xen_front_pgdir_shbuf_unmap(struct xen_front_pgdir_shbuf *buf) diff --git a/fs/ext2/xattr.c b/fs/ext2/xattr.c index 841fa6d9d744..641abfa4b718 100644 --- a/fs/ext2/xattr.c +++ b/fs/ext2/xattr.c @@ -517,36 +517,36 @@ bad_block: /* Here we know that we can set the new attribute. */ if (header) { - /* assert(header == HDR(bh)); */ + int offset; + lock_buffer(bh); if (header->h_refcount == cpu_to_le32(1)) { __u32 hash = le32_to_cpu(header->h_hash); + struct mb_cache_entry *oe; - ea_bdebug(bh, "modifying in-place"); + oe = mb_cache_entry_delete_or_get(EA_BLOCK_CACHE(inode), + hash, bh->b_blocknr); + if (!oe) { + ea_bdebug(bh, "modifying in-place"); + goto update_block; + } /* - * This must happen under buffer lock for - * ext2_xattr_set2() to reliably detect modified block + * Someone is trying to reuse the block, leave it alone */ - mb_cache_entry_delete(EA_BLOCK_CACHE(inode), hash, - bh->b_blocknr); - - /* keep the buffer locked while modifying it. */ - } else { - int offset; - - unlock_buffer(bh); - ea_bdebug(bh, "cloning"); - header = kmemdup(HDR(bh), bh->b_size, GFP_KERNEL); - error = -ENOMEM; - if (header == NULL) - goto cleanup; - header->h_refcount = cpu_to_le32(1); - - offset = (char *)here - bh->b_data; - here = ENTRY((char *)header + offset); - offset = (char *)last - bh->b_data; - last = ENTRY((char *)header + offset); + mb_cache_entry_put(EA_BLOCK_CACHE(inode), oe); } + unlock_buffer(bh); + ea_bdebug(bh, "cloning"); + header = kmemdup(HDR(bh), bh->b_size, GFP_KERNEL); + error = -ENOMEM; + if (header == NULL) + goto cleanup; + header->h_refcount = cpu_to_le32(1); + + offset = (char *)here - bh->b_data; + here = ENTRY((char *)header + offset); + offset = (char *)last - bh->b_data; + last = ENTRY((char *)header + offset); } else { /* Allocate a buffer where we construct the new block. */ header = kzalloc(sb->s_blocksize, GFP_KERNEL); @@ -559,6 +559,7 @@ bad_block: last = here = ENTRY(header+1); } +update_block: /* Iff we are modifying the block in-place, bh is locked here. */ if (not_found) { @@ -651,6 +652,55 @@ cleanup: return error; } +static void ext2_xattr_release_block(struct inode *inode, + struct buffer_head *bh) +{ + struct mb_cache *ea_block_cache = EA_BLOCK_CACHE(inode); + +retry_ref: + lock_buffer(bh); + if (HDR(bh)->h_refcount == cpu_to_le32(1)) { + __u32 hash = le32_to_cpu(HDR(bh)->h_hash); + struct mb_cache_entry *oe; + + /* + * This must happen under buffer lock to properly + * serialize with ext2_xattr_set() reusing the block. + */ + oe = mb_cache_entry_delete_or_get(ea_block_cache, hash, + bh->b_blocknr); + if (oe) { + /* + * Someone is trying to reuse the block. Wait + * and retry. + */ + unlock_buffer(bh); + mb_cache_entry_wait_unused(oe); + mb_cache_entry_put(ea_block_cache, oe); + goto retry_ref; + } + + /* Free the old block. */ + ea_bdebug(bh, "freeing"); + ext2_free_blocks(inode, bh->b_blocknr, 1); + /* We let our caller release bh, so we + * need to duplicate the buffer before. */ + get_bh(bh); + bforget(bh); + unlock_buffer(bh); + } else { + /* Decrement the refcount only. */ + le32_add_cpu(&HDR(bh)->h_refcount, -1); + dquot_free_block(inode, 1); + mark_buffer_dirty(bh); + unlock_buffer(bh); + ea_bdebug(bh, "refcount now=%d", + le32_to_cpu(HDR(bh)->h_refcount)); + if (IS_SYNC(inode)) + sync_dirty_buffer(bh); + } +} + /* * Second half of ext2_xattr_set(): Update the file system. */ @@ -747,34 +797,7 @@ ext2_xattr_set2(struct inode *inode, struct buffer_head *old_bh, * If there was an old block and we are no longer using it, * release the old block. */ - lock_buffer(old_bh); - if (HDR(old_bh)->h_refcount == cpu_to_le32(1)) { - __u32 hash = le32_to_cpu(HDR(old_bh)->h_hash); - - /* - * This must happen under buffer lock for - * ext2_xattr_set2() to reliably detect freed block - */ - mb_cache_entry_delete(ea_block_cache, hash, - old_bh->b_blocknr); - /* Free the old block. */ - ea_bdebug(old_bh, "freeing"); - ext2_free_blocks(inode, old_bh->b_blocknr, 1); - mark_inode_dirty(inode); - /* We let our caller release old_bh, so we - * need to duplicate the buffer before. */ - get_bh(old_bh); - bforget(old_bh); - } else { - /* Decrement the refcount only. */ - le32_add_cpu(&HDR(old_bh)->h_refcount, -1); - dquot_free_block_nodirty(inode, 1); - mark_inode_dirty(inode); - mark_buffer_dirty(old_bh); - ea_bdebug(old_bh, "refcount now=%d", - le32_to_cpu(HDR(old_bh)->h_refcount)); - } - unlock_buffer(old_bh); + ext2_xattr_release_block(inode, old_bh); } cleanup: @@ -828,30 +851,7 @@ ext2_xattr_delete_inode(struct inode *inode) EXT2_I(inode)->i_file_acl); goto cleanup; } - lock_buffer(bh); - if (HDR(bh)->h_refcount == cpu_to_le32(1)) { - __u32 hash = le32_to_cpu(HDR(bh)->h_hash); - - /* - * This must happen under buffer lock for ext2_xattr_set2() to - * reliably detect freed block - */ - mb_cache_entry_delete(EA_BLOCK_CACHE(inode), hash, - bh->b_blocknr); - ext2_free_blocks(inode, EXT2_I(inode)->i_file_acl, 1); - get_bh(bh); - bforget(bh); - unlock_buffer(bh); - } else { - le32_add_cpu(&HDR(bh)->h_refcount, -1); - ea_bdebug(bh, "refcount now=%d", - le32_to_cpu(HDR(bh)->h_refcount)); - unlock_buffer(bh); - mark_buffer_dirty(bh); - if (IS_SYNC(inode)) - sync_dirty_buffer(bh); - dquot_free_block_nodirty(inode, 1); - } + ext2_xattr_release_block(inode, bh); EXT2_I(inode)->i_file_acl = 0; cleanup: @@ -943,7 +943,7 @@ ext2_xattr_cache_find(struct inode *inode, struct ext2_xattr_header *header) if (!header->h_hash) return NULL; /* never share */ ea_idebug(inode, "looking for cached blocks [%x]", (int)hash); -again: + ce = mb_cache_entry_find_first(ea_block_cache, hash); while (ce) { struct buffer_head *bh; @@ -955,22 +955,8 @@ again: inode->i_ino, (unsigned long) ce->e_value); } else { lock_buffer(bh); - /* - * We have to be careful about races with freeing or - * rehashing of xattr block. Once we hold buffer lock - * xattr block's state is stable so we can check - * whether the block got freed / rehashed or not. - * Since we unhash mbcache entry under buffer lock when - * freeing / rehashing xattr block, checking whether - * entry is still hashed is reliable. - */ - if (hlist_bl_unhashed(&ce->e_hash_list)) { - mb_cache_entry_put(ea_block_cache, ce); - unlock_buffer(bh); - brelse(bh); - goto again; - } else if (le32_to_cpu(HDR(bh)->h_refcount) > - EXT2_XATTR_REFCOUNT_MAX) { + if (le32_to_cpu(HDR(bh)->h_refcount) > + EXT2_XATTR_REFCOUNT_MAX) { ea_idebug(inode, "block %ld refcount %d>%d", (unsigned long) ce->e_value, le32_to_cpu(HDR(bh)->h_refcount), diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 78ee3ef795ae..8ff4b9192a9f 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -666,7 +666,7 @@ int ext4_should_retry_alloc(struct super_block *sb, int *retries) * it's possible we've just missed a transaction commit here, * so ignore the returned status */ - jbd_debug(1, "%s: retrying operation after ENOSPC\n", sb->s_id); + ext4_debug("%s: retrying operation after ENOSPC\n", sb->s_id); (void) jbd2_journal_force_commit_nested(sbi->s_journal); return 1; } diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 29fc575a4eb6..9bca5565547b 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -724,6 +724,8 @@ enum { #define EXT4_IOC_GETSTATE _IOW('f', 41, __u32) #define EXT4_IOC_GET_ES_CACHE _IOWR('f', 42, struct fiemap) #define EXT4_IOC_CHECKPOINT _IOW('f', 43, __u32) +#define EXT4_IOC_GETFSUUID _IOR('f', 44, struct fsuuid) +#define EXT4_IOC_SETFSUUID _IOW('f', 44, struct fsuuid) #define EXT4_IOC_SHUTDOWN _IOR ('X', 125, __u32) @@ -753,6 +755,15 @@ enum { EXT4_IOC_CHECKPOINT_FLAG_ZEROOUT | \ EXT4_IOC_CHECKPOINT_FLAG_DRY_RUN) +/* + * Structure for EXT4_IOC_GETFSUUID/EXT4_IOC_SETFSUUID + */ +struct fsuuid { + __u32 fsu_len; + __u32 fsu_flags; + __u8 fsu_uuid[]; +}; + #if defined(__KERNEL__) && defined(CONFIG_COMPAT) /* * ioctl commands in 32 bit emulation @@ -3016,7 +3027,7 @@ int ext4_fileattr_set(struct user_namespace *mnt_userns, struct dentry *dentry, struct fileattr *fa); int ext4_fileattr_get(struct dentry *dentry, struct fileattr *fa); extern void ext4_reset_inode_seed(struct inode *inode); -int ext4_update_overhead(struct super_block *sb); +int ext4_update_overhead(struct super_block *sb, bool force); /* migrate.c */ extern int ext4_ext_migrate(struct inode *); @@ -3583,6 +3594,7 @@ extern struct buffer_head *ext4_get_first_inline_block(struct inode *inode, extern int ext4_inline_data_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, int *has_inline, __u64 start, __u64 len); +extern void *ext4_read_inline_link(struct inode *inode); struct iomap; extern int ext4_inline_data_iomap(struct inode *inode, struct iomap *iomap); @@ -3799,7 +3811,7 @@ static inline void set_bitmap_uptodate(struct buffer_head *bh) extern wait_queue_head_t ext4__ioend_wq[EXT4_WQ_HASH_SZ]; extern int ext4_resize_begin(struct super_block *sb); -extern void ext4_resize_end(struct super_block *sb); +extern int ext4_resize_end(struct super_block *sb, bool update_backups); static inline void ext4_set_io_unwritten_flag(struct inode *inode, struct ext4_io_end *io_end) diff --git a/fs/ext4/ext4_jbd2.c b/fs/ext4/ext4_jbd2.c index 3477a16d08ae..8e1fb18f465e 100644 --- a/fs/ext4/ext4_jbd2.c +++ b/fs/ext4/ext4_jbd2.c @@ -267,8 +267,7 @@ int __ext4_forget(const char *where, unsigned int line, handle_t *handle, trace_ext4_forget(inode, is_metadata, blocknr); BUFFER_TRACE(bh, "enter"); - jbd_debug(4, "forgetting bh %p: is_metadata = %d, mode %o, " - "data mode %x\n", + ext4_debug("forgetting bh %p: is_metadata=%d, mode %o, data mode %x\n", bh, is_metadata, inode->i_mode, test_opt(inode->i_sb, DATA_FLAGS)); diff --git a/fs/ext4/fast_commit.c b/fs/ext4/fast_commit.c index eb4c8ad1bb61..2af962cbb835 100644 --- a/fs/ext4/fast_commit.c +++ b/fs/ext4/fast_commit.c @@ -917,8 +917,8 @@ static int ext4_fc_write_inode_data(struct inode *inode, u32 *crc) mutex_unlock(&ei->i_fc_lock); cur_lblk_off = old_blk_size; - jbd_debug(1, "%s: will try writing %d to %d for inode %ld\n", - __func__, cur_lblk_off, new_blk_size, inode->i_ino); + ext4_debug("will try writing %d to %d for inode %ld\n", + cur_lblk_off, new_blk_size, inode->i_ino); while (cur_lblk_off <= new_blk_size) { map.m_lblk = cur_lblk_off; @@ -1168,7 +1168,7 @@ static void ext4_fc_update_stats(struct super_block *sb, int status, { struct ext4_fc_stats *stats = &EXT4_SB(sb)->s_fc_stats; - jbd_debug(1, "Fast commit ended with status = %d for tid %u", + ext4_debug("Fast commit ended with status = %d for tid %u", status, commit_tid); if (status == EXT4_FC_STATUS_OK) { stats->fc_num_commits++; @@ -1375,14 +1375,14 @@ static int ext4_fc_replay_unlink(struct super_block *sb, struct ext4_fc_tl *tl, inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "Inode %d not found", darg.ino); + ext4_debug("Inode %d not found", darg.ino); return 0; } old_parent = ext4_iget(sb, darg.parent_ino, EXT4_IGET_NORMAL); if (IS_ERR(old_parent)) { - jbd_debug(1, "Dir with inode %d not found", darg.parent_ino); + ext4_debug("Dir with inode %d not found", darg.parent_ino); iput(inode); return 0; } @@ -1407,21 +1407,21 @@ static int ext4_fc_replay_link_internal(struct super_block *sb, dir = ext4_iget(sb, darg->parent_ino, EXT4_IGET_NORMAL); if (IS_ERR(dir)) { - jbd_debug(1, "Dir with inode %d not found.", darg->parent_ino); + ext4_debug("Dir with inode %d not found.", darg->parent_ino); dir = NULL; goto out; } dentry_dir = d_obtain_alias(dir); if (IS_ERR(dentry_dir)) { - jbd_debug(1, "Failed to obtain dentry"); + ext4_debug("Failed to obtain dentry"); dentry_dir = NULL; goto out; } dentry_inode = d_alloc(dentry_dir, &qstr_dname); if (!dentry_inode) { - jbd_debug(1, "Inode dentry not created."); + ext4_debug("Inode dentry not created."); ret = -ENOMEM; goto out; } @@ -1434,7 +1434,7 @@ static int ext4_fc_replay_link_internal(struct super_block *sb, * could complete. */ if (ret && ret != -EEXIST) { - jbd_debug(1, "Failed to link\n"); + ext4_debug("Failed to link\n"); goto out; } @@ -1468,7 +1468,7 @@ static int ext4_fc_replay_link(struct super_block *sb, struct ext4_fc_tl *tl, inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "Inode not found."); + ext4_debug("Inode not found."); return 0; } @@ -1576,7 +1576,7 @@ static int ext4_fc_replay_inode(struct super_block *sb, struct ext4_fc_tl *tl, /* Given that we just wrote the inode on disk, this SHOULD succeed. */ inode = ext4_iget(sb, ino, EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "Inode not found."); + ext4_debug("Inode not found."); return -EFSCORRUPTED; } @@ -1630,7 +1630,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl, inode = ext4_iget(sb, darg.ino, EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "inode %d not found.", darg.ino); + ext4_debug("inode %d not found.", darg.ino); inode = NULL; ret = -EINVAL; goto out; @@ -1643,7 +1643,7 @@ static int ext4_fc_replay_create(struct super_block *sb, struct ext4_fc_tl *tl, */ dir = ext4_iget(sb, darg.parent_ino, EXT4_IGET_NORMAL); if (IS_ERR(dir)) { - jbd_debug(1, "Dir %d not found.", darg.ino); + ext4_debug("Dir %d not found.", darg.ino); goto out; } ret = ext4_init_new_dir(NULL, dir, inode); @@ -1727,7 +1727,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb, inode = ext4_iget(sb, le32_to_cpu(fc_add_ex.fc_ino), EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "Inode not found."); + ext4_debug("Inode not found."); return 0; } @@ -1741,7 +1741,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb, cur = start; remaining = len; - jbd_debug(1, "ADD_RANGE, lblk %d, pblk %lld, len %d, unwritten %d, inode %ld\n", + ext4_debug("ADD_RANGE, lblk %d, pblk %lld, len %d, unwritten %d, inode %ld\n", start, start_pblk, len, ext4_ext_is_unwritten(ex), inode->i_ino); @@ -1802,7 +1802,7 @@ static int ext4_fc_replay_add_range(struct super_block *sb, } /* Range is mapped and needs a state change */ - jbd_debug(1, "Converting from %ld to %d %lld", + ext4_debug("Converting from %ld to %d %lld", map.m_flags & EXT4_MAP_UNWRITTEN, ext4_ext_is_unwritten(ex), map.m_pblk); ret = ext4_ext_replay_update_ex(inode, cur, map.m_len, @@ -1845,7 +1845,7 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl, inode = ext4_iget(sb, le32_to_cpu(lrange.fc_ino), EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "Inode %d not found", le32_to_cpu(lrange.fc_ino)); + ext4_debug("Inode %d not found", le32_to_cpu(lrange.fc_ino)); return 0; } @@ -1853,7 +1853,7 @@ ext4_fc_replay_del_range(struct super_block *sb, struct ext4_fc_tl *tl, if (ret) goto out; - jbd_debug(1, "DEL_RANGE, inode %ld, lblk %d, len %d\n", + ext4_debug("DEL_RANGE, inode %ld, lblk %d, len %d\n", inode->i_ino, le32_to_cpu(lrange.fc_lblk), le32_to_cpu(lrange.fc_len)); while (remaining > 0) { @@ -1902,7 +1902,7 @@ static void ext4_fc_set_bitmaps_and_counters(struct super_block *sb) inode = ext4_iget(sb, state->fc_modified_inodes[i], EXT4_IGET_NORMAL); if (IS_ERR(inode)) { - jbd_debug(1, "Inode %d not found.", + ext4_debug("Inode %d not found.", state->fc_modified_inodes[i]); continue; } @@ -2031,7 +2031,7 @@ static int ext4_fc_replay_scan(journal_t *journal, for (cur = start; cur < end; cur = cur + sizeof(tl) + le16_to_cpu(tl.fc_len)) { memcpy(&tl, cur, sizeof(tl)); val = cur + sizeof(tl); - jbd_debug(3, "Scan phase, tag:%s, blk %lld\n", + ext4_debug("Scan phase, tag:%s, blk %lld\n", tag2str(le16_to_cpu(tl.fc_tag)), bh->b_blocknr); switch (le16_to_cpu(tl.fc_tag)) { case EXT4_FC_TAG_ADD_RANGE: @@ -2126,7 +2126,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, sbi->s_mount_state |= EXT4_FC_REPLAY; } if (!sbi->s_fc_replay_state.fc_replay_num_tags) { - jbd_debug(1, "Replay stops\n"); + ext4_debug("Replay stops\n"); ext4_fc_set_bitmaps_and_counters(sb); return 0; } @@ -2150,7 +2150,7 @@ static int ext4_fc_replay(journal_t *journal, struct buffer_head *bh, ext4_fc_set_bitmaps_and_counters(sb); break; } - jbd_debug(3, "Replay phase, tag:%s\n", + ext4_debug("Replay phase, tag:%s\n", tag2str(le16_to_cpu(tl.fc_tag))); state->fc_replay_num_tags--; switch (le16_to_cpu(tl.fc_tag)) { diff --git a/fs/ext4/indirect.c b/fs/ext4/indirect.c index 07a8c75b65ed..860fc5119009 100644 --- a/fs/ext4/indirect.c +++ b/fs/ext4/indirect.c @@ -460,7 +460,7 @@ static int ext4_splice_branch(handle_t *handle, * the new i_size. But that is not done here - it is done in * generic_commit_write->__mark_inode_dirty->ext4_dirty_inode. */ - jbd_debug(5, "splicing indirect only\n"); + ext4_debug("splicing indirect only\n"); BUFFER_TRACE(where->bh, "call ext4_handle_dirty_metadata"); err = ext4_handle_dirty_metadata(handle, ar->inode, where->bh); if (err) @@ -472,7 +472,7 @@ static int ext4_splice_branch(handle_t *handle, err = ext4_mark_inode_dirty(handle, ar->inode); if (unlikely(err)) goto err_out; - jbd_debug(5, "splicing direct\n"); + ext4_debug("splicing direct\n"); } return err; diff --git a/fs/ext4/inline.c b/fs/ext4/inline.c index cff52ff6549d..a4fbe825694b 100644 --- a/fs/ext4/inline.c +++ b/fs/ext4/inline.c @@ -6,6 +6,7 @@ #include #include +#include #include #include @@ -35,6 +36,9 @@ static int get_max_inline_xattr_value_size(struct inode *inode, struct ext4_inode *raw_inode; int free, min_offs; + if (!EXT4_INODE_HAS_XATTR_SPACE(inode)) + return 0; + min_offs = EXT4_SB(inode->i_sb)->s_inode_size - EXT4_GOOD_OLD_INODE_SIZE - EXT4_I(inode)->i_extra_isize - @@ -1588,6 +1592,35 @@ out: return ret; } +void *ext4_read_inline_link(struct inode *inode) +{ + struct ext4_iloc iloc; + int ret, inline_size; + void *link; + + ret = ext4_get_inode_loc(inode, &iloc); + if (ret) + return ERR_PTR(ret); + + ret = -ENOMEM; + inline_size = ext4_get_inline_size(inode); + link = kmalloc(inline_size + 1, GFP_NOFS); + if (!link) + goto out; + + ret = ext4_read_inline_data(inode, link, inline_size, &iloc); + if (ret < 0) { + kfree(link); + goto out; + } + nd_terminate_link(link, inode->i_size, ret); +out: + if (ret < 0) + link = ERR_PTR(ret); + brelse(iloc.bh); + return link; +} + struct buffer_head *ext4_get_first_inline_block(struct inode *inode, struct ext4_dir_entry_2 **parent_de, int *retval) diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index 9fd60fc8ba4c..601214453c3a 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -177,6 +177,8 @@ void ext4_evict_inode(struct inode *inode) trace_ext4_evict_inode(inode); + if (EXT4_I(inode)->i_flags & EXT4_EA_INODE_FL) + ext4_evict_ea_inode(inode); if (inode->i_nlink) { /* * When journalling data dirty buffers are tracked only in the @@ -1571,7 +1573,14 @@ static void mpage_release_unused_pages(struct mpage_da_data *mpd, ext4_lblk_t start, last; start = index << (PAGE_SHIFT - inode->i_blkbits); last = end << (PAGE_SHIFT - inode->i_blkbits); + + /* + * avoid racing with extent status tree scans made by + * ext4_insert_delayed_block() + */ + down_write(&EXT4_I(inode)->i_data_sem); ext4_es_remove_extent(inode, start, last - start + 1); + up_write(&EXT4_I(inode)->i_data_sem); } folio_batch_init(&fbatch); @@ -3142,13 +3151,15 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block) { struct inode *inode = mapping->host; journal_t *journal; + sector_t ret = 0; int err; + inode_lock_shared(inode); /* * We can get here for an inline file via the FIBMAP ioctl */ if (ext4_has_inline_data(inode)) - return 0; + goto out; if (mapping_tagged(mapping, PAGECACHE_TAG_DIRTY) && test_opt(inode->i_sb, DELALLOC)) { @@ -3187,10 +3198,14 @@ static sector_t ext4_bmap(struct address_space *mapping, sector_t block) jbd2_journal_unlock_updates(journal); if (err) - return 0; + goto out; } - return iomap_bmap(mapping, block, &ext4_iomap_ops); + ret = iomap_bmap(mapping, block, &ext4_iomap_ops); + +out: + inode_unlock_shared(inode); + return ret; } static int ext4_read_folio(struct file *file, struct folio *folio) @@ -4687,8 +4702,7 @@ static inline int ext4_iget_extra_inode(struct inode *inode, __le32 *magic = (void *)raw_inode + EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize; - if (EXT4_GOOD_OLD_INODE_SIZE + ei->i_extra_isize + sizeof(__le32) <= - EXT4_INODE_SIZE(inode->i_sb) && + if (EXT4_INODE_HAS_XATTR_SPACE(inode) && *magic == cpu_to_le32(EXT4_XATTR_MAGIC)) { ext4_set_inode_state(inode, EXT4_STATE_XATTR); return ext4_find_inline_data_nolock(inode); @@ -5215,7 +5229,7 @@ int ext4_write_inode(struct inode *inode, struct writeback_control *wbc) if (EXT4_SB(inode->i_sb)->s_journal) { if (ext4_journal_current_handle()) { - jbd_debug(1, "called recursively, non-PF_MEMALLOC!\n"); + ext4_debug("called recursively, non-PF_MEMALLOC!\n"); dump_stack(); return -EIO; } diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c index cb01c1da0f9d..3cf3ec4b1c21 100644 --- a/fs/ext4/ioctl.c +++ b/fs/ext4/ioctl.c @@ -20,6 +20,7 @@ #include #include #include +#include #include "ext4_jbd2.h" #include "ext4.h" #include @@ -41,6 +42,15 @@ static void ext4_sb_setlabel(struct ext4_super_block *es, const void *arg) memcpy(es->s_volume_name, (char *)arg, EXT4_LABEL_MAX); } +/* + * Superblock modification callback function for changing file system + * UUID. + */ +static void ext4_sb_setuuid(struct ext4_super_block *es, const void *arg) +{ + memcpy(es->s_uuid, (__u8 *)arg, UUID_SIZE); +} + static int ext4_update_primary_sb(struct super_block *sb, handle_t *handle, ext4_update_sb_callback func, @@ -944,7 +954,9 @@ static long ext4_ioctl_group_add(struct file *file, test_opt(sb, INIT_INODE_TABLE)) err = ext4_register_li_request(sb, input->group); group_add_out: - ext4_resize_end(sb); + err2 = ext4_resize_end(sb, false); + if (err == 0) + err = err2; return err; } @@ -1131,6 +1143,73 @@ static int ext4_ioctl_getlabel(struct ext4_sb_info *sbi, char __user *user_label return 0; } +static int ext4_ioctl_getuuid(struct ext4_sb_info *sbi, + struct fsuuid __user *ufsuuid) +{ + struct fsuuid fsuuid; + __u8 uuid[UUID_SIZE]; + + if (copy_from_user(&fsuuid, ufsuuid, sizeof(fsuuid))) + return -EFAULT; + + if (fsuuid.fsu_len == 0) { + fsuuid.fsu_len = UUID_SIZE; + if (copy_to_user(ufsuuid, &fsuuid, sizeof(fsuuid.fsu_len))) + return -EFAULT; + return -EINVAL; + } + + if (fsuuid.fsu_len != UUID_SIZE || fsuuid.fsu_flags != 0) + return -EINVAL; + + lock_buffer(sbi->s_sbh); + memcpy(uuid, sbi->s_es->s_uuid, UUID_SIZE); + unlock_buffer(sbi->s_sbh); + + if (copy_to_user(&ufsuuid->fsu_uuid[0], uuid, UUID_SIZE)) + return -EFAULT; + return 0; +} + +static int ext4_ioctl_setuuid(struct file *filp, + const struct fsuuid __user *ufsuuid) +{ + int ret = 0; + struct super_block *sb = file_inode(filp)->i_sb; + struct fsuuid fsuuid; + __u8 uuid[UUID_SIZE]; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + /* + * If any checksums (group descriptors or metadata) are being used + * then the checksum seed feature is required to change the UUID. + */ + if (((ext4_has_feature_gdt_csum(sb) || ext4_has_metadata_csum(sb)) + && !ext4_has_feature_csum_seed(sb)) + || ext4_has_feature_stable_inodes(sb)) + return -EOPNOTSUPP; + + if (copy_from_user(&fsuuid, ufsuuid, sizeof(fsuuid))) + return -EFAULT; + + if (fsuuid.fsu_len != UUID_SIZE || fsuuid.fsu_flags != 0) + return -EINVAL; + + if (copy_from_user(uuid, &ufsuuid->fsu_uuid[0], UUID_SIZE)) + return -EFAULT; + + ret = mnt_want_write_file(filp); + if (ret) + return ret; + + ret = ext4_update_superblocks_fn(sb, ext4_sb_setuuid, &uuid); + mnt_drop_write_file(filp); + + return ret; +} + static long __ext4_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { struct inode *inode = file_inode(filp); @@ -1223,7 +1302,9 @@ setversion_out: err = err2; mnt_drop_write_file(filp); group_extend_out: - ext4_resize_end(sb); + err2 = ext4_resize_end(sb, false); + if (err == 0) + err = err2; return err; } @@ -1371,7 +1452,9 @@ mext_out: err = ext4_register_li_request(sb, o_group); resizefs_out: - ext4_resize_end(sb); + err2 = ext4_resize_end(sb, true); + if (err == 0) + err = err2; return err; } @@ -1509,6 +1592,10 @@ resizefs_out: return ext4_ioctl_setlabel(filp, (const void __user *)arg); + case EXT4_IOC_GETFSUUID: + return ext4_ioctl_getuuid(EXT4_SB(sb), (void __user *)arg); + case EXT4_IOC_SETFSUUID: + return ext4_ioctl_setuuid(filp, (const void __user *)arg); default: return -ENOTTY; } @@ -1586,6 +1673,8 @@ long ext4_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) case EXT4_IOC_CHECKPOINT: case FS_IOC_GETFSLABEL: case FS_IOC_SETFSLABEL: + case EXT4_IOC_GETFSUUID: + case EXT4_IOC_SETFSUUID: break; default: return -ENOIOCTLCMD; @@ -1599,13 +1688,15 @@ static void set_overhead(struct ext4_super_block *es, const void *arg) es->s_overhead_clusters = cpu_to_le32(*((unsigned long *) arg)); } -int ext4_update_overhead(struct super_block *sb) +int ext4_update_overhead(struct super_block *sb, bool force) { struct ext4_sb_info *sbi = EXT4_SB(sb); - if (sb_rdonly(sb) || sbi->s_overhead == 0 || - sbi->s_overhead == le32_to_cpu(sbi->s_es->s_overhead_clusters)) + if (sb_rdonly(sb)) + return 0; + if (!force && + (sbi->s_overhead == 0 || + sbi->s_overhead == le32_to_cpu(sbi->s_es->s_overhead_clusters))) return 0; - return ext4_update_superblocks_fn(sb, set_overhead, &sbi->s_overhead); } diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index 9e06334771a3..bd8f8b5c3d30 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -1933,6 +1933,7 @@ static int mb_mark_used(struct ext4_buddy *e4b, struct ext4_free_extent *ex) unsigned ret = 0; int len0 = len; void *buddy; + bool split = false; BUG_ON(start + len > (e4b->bd_sb->s_blocksize << 3)); BUG_ON(e4b->bd_group != ex->fe_group); @@ -1957,12 +1958,16 @@ static int mb_mark_used(struct ext4_buddy *e4b, struct ext4_free_extent *ex) /* let's maintain buddy itself */ while (len) { - ord = mb_find_order_for_block(e4b, start); + if (!split) + ord = mb_find_order_for_block(e4b, start); if (((start >> ord) << ord) == start && len >= (1 << ord)) { /* the whole chunk may be allocated at once! */ mlen = 1 << ord; - buddy = mb_find_buddy(e4b, ord, &max); + if (!split) + buddy = mb_find_buddy(e4b, ord, &max); + else + split = false; BUG_ON((start >> ord) >= max); mb_set_bit(start >> ord, buddy); e4b->bd_info->bb_counters[ord]--; @@ -1989,6 +1994,7 @@ static int mb_mark_used(struct ext4_buddy *e4b, struct ext4_free_extent *ex) mb_clear_bit(cur + 1, buddy); e4b->bd_info->bb_counters[ord]++; e4b->bd_info->bb_counters[ord]++; + split = true; } mb_set_largest_free_order(e4b->bd_sb, e4b->bd_info); @@ -5928,6 +5934,15 @@ static void ext4_mb_clear_bb(handle_t *handle, struct inode *inode, sbi = EXT4_SB(sb); + if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) && + !ext4_inode_block_valid(inode, block, count)) { + ext4_error(sb, "Freeing blocks in system zone - " + "Block = %llu, count = %lu", block, count); + /* err = 0. ext4_std_error should be a no op */ + goto error_return; + } + flags |= EXT4_FREE_BLOCKS_VALIDATED; + do_more: overflow = 0; ext4_get_group_no_and_offset(sb, block, &block_group, &bit); @@ -5944,6 +5959,8 @@ do_more: overflow = EXT4_C2B(sbi, bit) + count - EXT4_BLOCKS_PER_GROUP(sb); count -= overflow; + /* The range changed so it's no longer validated */ + flags &= ~EXT4_FREE_BLOCKS_VALIDATED; } count_clusters = EXT4_NUM_B2C(sbi, count); bitmap_bh = ext4_read_block_bitmap(sb, block_group); @@ -5958,7 +5975,8 @@ do_more: goto error_return; } - if (!ext4_inode_block_valid(inode, block, count)) { + if (!(flags & EXT4_FREE_BLOCKS_VALIDATED) && + !ext4_inode_block_valid(inode, block, count)) { ext4_error(sb, "Freeing blocks in system zone - " "Block = %llu, count = %lu", block, count); /* err = 0. ext4_std_error should be a no op */ @@ -6081,6 +6099,8 @@ do_more: block += count; count = overflow; put_bh(bitmap_bh); + /* The range changed so it's no longer validated */ + flags &= ~EXT4_FREE_BLOCKS_VALIDATED; goto do_more; } error_return: @@ -6127,6 +6147,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, "block = %llu, count = %lu", block, count); return; } + flags |= EXT4_FREE_BLOCKS_VALIDATED; ext4_debug("freeing block %llu\n", block); trace_ext4_free_blocks(inode, block, count, flags); @@ -6158,6 +6179,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, block -= overflow; count += overflow; } + /* The range changed so it's no longer validated */ + flags &= ~EXT4_FREE_BLOCKS_VALIDATED; } overflow = EXT4_LBLK_COFF(sbi, count); if (overflow) { @@ -6168,6 +6191,8 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode, return; } else count += sbi->s_cluster_ratio - overflow; + /* The range changed so it's no longer validated */ + flags &= ~EXT4_FREE_BLOCKS_VALIDATED; } if (!bh && (flags & EXT4_FREE_BLOCKS_FORGET)) { diff --git a/fs/ext4/migrate.c b/fs/ext4/migrate.c index 42f590518b4c..54e7d3c95fd7 100644 --- a/fs/ext4/migrate.c +++ b/fs/ext4/migrate.c @@ -417,7 +417,7 @@ int ext4_ext_migrate(struct inode *inode) struct inode *tmp_inode = NULL; struct migrate_struct lb; unsigned long max_entries; - __u32 goal; + __u32 goal, tmp_csum_seed; uid_t owner[2]; /* @@ -465,6 +465,7 @@ int ext4_ext_migrate(struct inode *inode) * the migration. */ ei = EXT4_I(inode); + tmp_csum_seed = EXT4_I(tmp_inode)->i_csum_seed; EXT4_I(tmp_inode)->i_csum_seed = ei->i_csum_seed; i_size_write(tmp_inode, i_size_read(inode)); /* @@ -575,6 +576,7 @@ err_out: * the inode is not visible to user space. */ tmp_inode->i_blocks = 0; + EXT4_I(tmp_inode)->i_csum_seed = tmp_csum_seed; /* Reset the extent details */ ext4_ext_tree_init(handle, tmp_inode); diff --git a/fs/ext4/namei.c b/fs/ext4/namei.c index db4ba99d1ceb..3a31b662f661 100644 --- a/fs/ext4/namei.c +++ b/fs/ext4/namei.c @@ -54,6 +54,7 @@ static struct buffer_head *ext4_append(handle_t *handle, struct inode *inode, ext4_lblk_t *block) { + struct ext4_map_blocks map; struct buffer_head *bh; int err; @@ -63,6 +64,21 @@ static struct buffer_head *ext4_append(handle_t *handle, return ERR_PTR(-ENOSPC); *block = inode->i_size >> inode->i_sb->s_blocksize_bits; + map.m_lblk = *block; + map.m_len = 1; + + /* + * We're appending new directory block. Make sure the block is not + * allocated yet, otherwise we will end up corrupting the + * directory. + */ + err = ext4_map_blocks(NULL, inode, &map, 0); + if (err < 0) + return ERR_PTR(err); + if (err) { + EXT4_ERROR_INODE(inode, "Logical block already allocated"); + return ERR_PTR(-EFSCORRUPTED); + } bh = ext4_bread(handle, inode, *block, EXT4_GET_BLOCKS_CREATE); if (IS_ERR(bh)) @@ -110,6 +126,13 @@ static struct buffer_head *__ext4_read_dirblock(struct inode *inode, struct ext4_dir_entry *dirent; int is_dx_block = 0; + if (block >= inode->i_size) { + ext4_error_inode(inode, func, line, block, + "Attempting to read directory block (%u) that is past i_size (%llu)", + block, inode->i_size); + return ERR_PTR(-EFSCORRUPTED); + } + if (ext4_simulate_fail(inode->i_sb, EXT4_SIM_DIRBLOCK_EIO)) bh = ERR_PTR(-EIO); else @@ -3067,11 +3090,8 @@ bool ext4_empty_dir(struct inode *inode) de = (struct ext4_dir_entry_2 *) (bh->b_data + (offset & (sb->s_blocksize - 1))); if (ext4_check_dir_entry(inode, NULL, de, bh, - bh->b_data, bh->b_size, offset)) { - offset = (offset | (sb->s_blocksize - 1)) + 1; - continue; - } - if (le32_to_cpu(de->inode)) { + bh->b_data, bh->b_size, offset) || + le32_to_cpu(de->inode)) { brelse(bh); return false; } diff --git a/fs/ext4/orphan.c b/fs/ext4/orphan.c index 7de0612eb42d..69a9cf9137a6 100644 --- a/fs/ext4/orphan.c +++ b/fs/ext4/orphan.c @@ -181,8 +181,8 @@ int ext4_orphan_add(handle_t *handle, struct inode *inode) } else brelse(iloc.bh); - jbd_debug(4, "superblock will point to %lu\n", inode->i_ino); - jbd_debug(4, "orphan inode %lu will point to %d\n", + ext4_debug("superblock will point to %lu\n", inode->i_ino); + ext4_debug("orphan inode %lu will point to %d\n", inode->i_ino, NEXT_ORPHAN(inode)); out: ext4_std_error(sb, err); @@ -251,7 +251,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) } mutex_lock(&sbi->s_orphan_lock); - jbd_debug(4, "remove inode %lu from orphan list\n", inode->i_ino); + ext4_debug("remove inode %lu from orphan list\n", inode->i_ino); prev = ei->i_orphan.prev; list_del_init(&ei->i_orphan); @@ -267,7 +267,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) ino_next = NEXT_ORPHAN(inode); if (prev == &sbi->s_orphan) { - jbd_debug(4, "superblock will point to %u\n", ino_next); + ext4_debug("superblock will point to %u\n", ino_next); BUFFER_TRACE(sbi->s_sbh, "get_write_access"); err = ext4_journal_get_write_access(handle, inode->i_sb, sbi->s_sbh, EXT4_JTR_NONE); @@ -286,7 +286,7 @@ int ext4_orphan_del(handle_t *handle, struct inode *inode) struct inode *i_prev = &list_entry(prev, struct ext4_inode_info, i_orphan)->vfs_inode; - jbd_debug(4, "orphan inode %lu will point to %u\n", + ext4_debug("orphan inode %lu will point to %u\n", i_prev->i_ino, ino_next); err = ext4_reserve_inode_write(handle, i_prev, &iloc2); if (err) { @@ -332,8 +332,8 @@ static void ext4_process_orphan(struct inode *inode, ext4_msg(sb, KERN_DEBUG, "%s: truncating inode %lu to %lld bytes", __func__, inode->i_ino, inode->i_size); - jbd_debug(2, "truncating inode %lu to %lld bytes\n", - inode->i_ino, inode->i_size); + ext4_debug("truncating inode %lu to %lld bytes\n", + inode->i_ino, inode->i_size); inode_lock(inode); truncate_inode_pages(inode->i_mapping, inode->i_size); ret = ext4_truncate(inode); @@ -353,8 +353,8 @@ static void ext4_process_orphan(struct inode *inode, ext4_msg(sb, KERN_DEBUG, "%s: deleting unreferenced inode %lu", __func__, inode->i_ino); - jbd_debug(2, "deleting unreferenced inode %lu\n", - inode->i_ino); + ext4_debug("deleting unreferenced inode %lu\n", + inode->i_ino); (*nr_orphans)++; } iput(inode); /* The delete magic happens here! */ @@ -391,7 +391,7 @@ void ext4_orphan_cleanup(struct super_block *sb, struct ext4_super_block *es) int inodes_per_ob = ext4_inodes_per_orphan_block(sb); if (!es->s_last_orphan && !oi->of_blocks) { - jbd_debug(4, "no orphan inodes to clean up\n"); + ext4_debug("no orphan inodes to clean up\n"); return; } @@ -415,7 +415,7 @@ void ext4_orphan_cleanup(struct super_block *sb, struct ext4_super_block *es) "clearing orphan list.\n"); es->s_last_orphan = 0; } - jbd_debug(1, "Skipping orphan recovery on fs with errors.\n"); + ext4_debug("Skipping orphan recovery on fs with errors.\n"); return; } @@ -459,7 +459,7 @@ void ext4_orphan_cleanup(struct super_block *sb, struct ext4_super_block *es) * so, skip the rest. */ if (EXT4_SB(sb)->s_mount_state & EXT4_ERROR_FS) { - jbd_debug(1, "Skipping orphan recovery on fs with errors.\n"); + ext4_debug("Skipping orphan recovery on fs with errors.\n"); es->s_last_orphan = 0; break; } diff --git a/fs/ext4/resize.c b/fs/ext4/resize.c index 8b70a4701293..fea2a68d067b 100644 --- a/fs/ext4/resize.c +++ b/fs/ext4/resize.c @@ -97,10 +97,13 @@ int ext4_resize_begin(struct super_block *sb) return ret; } -void ext4_resize_end(struct super_block *sb) +int ext4_resize_end(struct super_block *sb, bool update_backups) { clear_bit_unlock(EXT4_FLAGS_RESIZING, &EXT4_SB(sb)->s_ext4_flags); smp_mb__after_atomic(); + if (update_backups) + return ext4_update_overhead(sb, true); + return 0; } static ext4_group_t ext4_meta_bg_first_group(struct super_block *sb, @@ -1380,6 +1383,17 @@ static int ext4_setup_new_descs(handle_t *handle, struct super_block *sb, return err; } +static void ext4_add_overhead(struct super_block *sb, + const ext4_fsblk_t overhead) +{ + struct ext4_sb_info *sbi = EXT4_SB(sb); + struct ext4_super_block *es = sbi->s_es; + + sbi->s_overhead += overhead; + es->s_overhead_clusters = cpu_to_le32(sbi->s_overhead); + smp_wmb(); +} + /* * ext4_update_super() updates the super block so that the newly added * groups can be seen by the filesystem. @@ -1481,9 +1495,18 @@ static void ext4_update_super(struct super_block *sb, } /* - * Update the fs overhead information + * Update the fs overhead information. + * + * For bigalloc, if the superblock already has a properly calculated + * overhead, update it with a value based on numbers already computed + * above for the newly allocated capacity. */ - ext4_calculate_overhead(sb); + if (ext4_has_feature_bigalloc(sb) && (sbi->s_overhead != 0)) + ext4_add_overhead(sb, + EXT4_NUM_B2C(sbi, blocks_count - free_blocks)); + else + ext4_calculate_overhead(sb); + es->s_overhead_clusters = cpu_to_le32(sbi->s_overhead); if (test_opt(sb, DEBUG)) printk(KERN_DEBUG "EXT4-fs: added group %u:" @@ -1988,6 +2011,16 @@ int ext4_resize_fs(struct super_block *sb, ext4_fsblk_t n_blocks_count) } brelse(bh); + /* + * For bigalloc, trim the requested size to the nearest cluster + * boundary to avoid creating an unusable filesystem. We do this + * silently, instead of returning an error, to avoid breaking + * callers that blindly resize the filesystem to the full size of + * the underlying block device. + */ + if (ext4_has_feature_bigalloc(sb)) + n_blocks_count &= ~((1 << EXT4_CLUSTER_BITS(sb)) - 1); + retry: o_blocks_count = ext4_blocks_count(es); diff --git a/fs/ext4/super.c b/fs/ext4/super.c index 2c68dec63e54..8f907e9d9c76 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -3011,6 +3011,15 @@ static int _ext4_show_options(struct seq_file *seq, struct super_block *sb, } else if (test_opt2(sb, DAX_INODE)) { SEQ_OPTS_PUTS("dax=inode"); } + + if (sbi->s_groups_count >= MB_DEFAULT_LINEAR_SCAN_THRESHOLD && + !test_opt2(sb, MB_OPTIMIZE_SCAN)) { + SEQ_OPTS_PUTS("mb_optimize_scan=0"); + } else if (sbi->s_groups_count < MB_DEFAULT_LINEAR_SCAN_THRESHOLD && + test_opt2(sb, MB_OPTIMIZE_SCAN)) { + SEQ_OPTS_PUTS("mb_optimize_scan=1"); + } + ext4_show_quota_options(seq, sb); return 0; } @@ -5523,7 +5532,7 @@ static int ext4_fill_super(struct super_block *sb, struct fs_context *fc) "Quota mode: %s.", descr, ext4_quota_mode(sb)); /* Update the s_overhead_clusters if necessary */ - ext4_update_overhead(sb); + ext4_update_overhead(sb, false); return 0; free_sbi: @@ -5585,7 +5594,7 @@ static struct inode *ext4_get_journal_inode(struct super_block *sb, return NULL; } - jbd_debug(2, "Journal inode found at %p: %lld bytes\n", + ext4_debug("Journal inode found at %p: %lld bytes\n", journal_inode, journal_inode->i_size); if (!S_ISREG(journal_inode->i_mode)) { ext4_msg(sb, KERN_ERR, "invalid journal inode"); diff --git a/fs/ext4/symlink.c b/fs/ext4/symlink.c index d281f5bcc526..3d3ed3c38f56 100644 --- a/fs/ext4/symlink.c +++ b/fs/ext4/symlink.c @@ -74,6 +74,21 @@ static const char *ext4_get_link(struct dentry *dentry, struct inode *inode, struct delayed_call *callback) { struct buffer_head *bh; + char *inline_link; + + /* + * Create a new inlined symlink is not supported, just provide a + * method to read the leftovers. + */ + if (ext4_has_inline_data(inode)) { + if (!dentry) + return ERR_PTR(-ECHILD); + + inline_link = ext4_read_inline_link(inode); + if (!IS_ERR(inline_link)) + set_delayed_call(callback, kfree_link, inline_link); + return inline_link; + } if (!dentry) { bh = ext4_getblk(NULL, inode, 0, EXT4_GET_BLOCKS_CACHED_NOWAIT); diff --git a/fs/ext4/xattr.c b/fs/ext4/xattr.c index 564e28a1aa94..533216e80fa2 100644 --- a/fs/ext4/xattr.c +++ b/fs/ext4/xattr.c @@ -436,6 +436,21 @@ error: return err; } +/* Remove entry from mbcache when EA inode is getting evicted */ +void ext4_evict_ea_inode(struct inode *inode) +{ + struct mb_cache_entry *oe; + + if (!EA_INODE_CACHE(inode)) + return; + /* Wait for entry to get unused so that we can remove it */ + while ((oe = mb_cache_entry_delete_or_get(EA_INODE_CACHE(inode), + ext4_xattr_inode_get_hash(inode), inode->i_ino))) { + mb_cache_entry_wait_unused(oe); + mb_cache_entry_put(EA_INODE_CACHE(inode), oe); + } +} + static int ext4_xattr_inode_verify_hashes(struct inode *ea_inode, struct ext4_xattr_entry *entry, void *buffer, @@ -976,10 +991,8 @@ int __ext4_xattr_set_credits(struct super_block *sb, struct inode *inode, static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, int ref_change) { - struct mb_cache *ea_inode_cache = EA_INODE_CACHE(ea_inode); struct ext4_iloc iloc; s64 ref_count; - u32 hash; int ret; inode_lock(ea_inode); @@ -1002,14 +1015,6 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, set_nlink(ea_inode, 1); ext4_orphan_del(handle, ea_inode); - - if (ea_inode_cache) { - hash = ext4_xattr_inode_get_hash(ea_inode); - mb_cache_entry_create(ea_inode_cache, - GFP_NOFS, hash, - ea_inode->i_ino, - true /* reusable */); - } } } else { WARN_ONCE(ref_count < 0, "EA inode %lu ref_count=%lld", @@ -1022,12 +1027,6 @@ static int ext4_xattr_inode_update_ref(handle_t *handle, struct inode *ea_inode, clear_nlink(ea_inode); ext4_orphan_add(handle, ea_inode); - - if (ea_inode_cache) { - hash = ext4_xattr_inode_get_hash(ea_inode); - mb_cache_entry_delete(ea_inode_cache, hash, - ea_inode->i_ino); - } } } @@ -1237,6 +1236,7 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, if (error) goto out; +retry_ref: lock_buffer(bh); hash = le32_to_cpu(BHDR(bh)->h_hash); ref = le32_to_cpu(BHDR(bh)->h_refcount); @@ -1246,9 +1246,18 @@ ext4_xattr_release_block(handle_t *handle, struct inode *inode, * This must happen under buffer lock for * ext4_xattr_block_set() to reliably detect freed block */ - if (ea_block_cache) - mb_cache_entry_delete(ea_block_cache, hash, - bh->b_blocknr); + if (ea_block_cache) { + struct mb_cache_entry *oe; + + oe = mb_cache_entry_delete_or_get(ea_block_cache, hash, + bh->b_blocknr); + if (oe) { + unlock_buffer(bh); + mb_cache_entry_wait_unused(oe); + mb_cache_entry_put(ea_block_cache, oe); + goto retry_ref; + } + } get_bh(bh); unlock_buffer(bh); @@ -1858,6 +1867,8 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, #define header(x) ((struct ext4_xattr_header *)(x)) if (s->base) { + int offset = (char *)s->here - bs->bh->b_data; + BUFFER_TRACE(bs->bh, "get_write_access"); error = ext4_journal_get_write_access(handle, sb, bs->bh, EXT4_JTR_NONE); @@ -1873,9 +1884,20 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, * ext4_xattr_block_set() to reliably detect modified * block */ - if (ea_block_cache) - mb_cache_entry_delete(ea_block_cache, hash, - bs->bh->b_blocknr); + if (ea_block_cache) { + struct mb_cache_entry *oe; + + oe = mb_cache_entry_delete_or_get(ea_block_cache, + hash, bs->bh->b_blocknr); + if (oe) { + /* + * Xattr block is getting reused. Leave + * it alone. + */ + mb_cache_entry_put(ea_block_cache, oe); + goto clone_block; + } + } ea_bdebug(bs->bh, "modifying in-place"); error = ext4_xattr_set_entry(i, s, handle, inode, true /* is_block */); @@ -1890,49 +1912,47 @@ ext4_xattr_block_set(handle_t *handle, struct inode *inode, if (error) goto cleanup; goto inserted; - } else { - int offset = (char *)s->here - bs->bh->b_data; + } +clone_block: + unlock_buffer(bs->bh); + ea_bdebug(bs->bh, "cloning"); + s->base = kmemdup(BHDR(bs->bh), bs->bh->b_size, GFP_NOFS); + error = -ENOMEM; + if (s->base == NULL) + goto cleanup; + s->first = ENTRY(header(s->base)+1); + header(s->base)->h_refcount = cpu_to_le32(1); + s->here = ENTRY(s->base + offset); + s->end = s->base + bs->bh->b_size; - unlock_buffer(bs->bh); - ea_bdebug(bs->bh, "cloning"); - s->base = kmemdup(BHDR(bs->bh), bs->bh->b_size, GFP_NOFS); - error = -ENOMEM; - if (s->base == NULL) + /* + * If existing entry points to an xattr inode, we need + * to prevent ext4_xattr_set_entry() from decrementing + * ref count on it because the reference belongs to the + * original block. In this case, make the entry look + * like it has an empty value. + */ + if (!s->not_found && s->here->e_value_inum) { + ea_ino = le32_to_cpu(s->here->e_value_inum); + error = ext4_xattr_inode_iget(inode, ea_ino, + le32_to_cpu(s->here->e_hash), + &tmp_inode); + if (error) goto cleanup; - s->first = ENTRY(header(s->base)+1); - header(s->base)->h_refcount = cpu_to_le32(1); - s->here = ENTRY(s->base + offset); - s->end = s->base + bs->bh->b_size; - /* - * If existing entry points to an xattr inode, we need - * to prevent ext4_xattr_set_entry() from decrementing - * ref count on it because the reference belongs to the - * original block. In this case, make the entry look - * like it has an empty value. - */ - if (!s->not_found && s->here->e_value_inum) { - ea_ino = le32_to_cpu(s->here->e_value_inum); - error = ext4_xattr_inode_iget(inode, ea_ino, - le32_to_cpu(s->here->e_hash), - &tmp_inode); - if (error) - goto cleanup; - - if (!ext4_test_inode_state(tmp_inode, - EXT4_STATE_LUSTRE_EA_INODE)) { - /* - * Defer quota free call for previous - * inode until success is guaranteed. - */ - old_ea_inode_quota = le32_to_cpu( - s->here->e_value_size); - } - iput(tmp_inode); - - s->here->e_value_inum = 0; - s->here->e_value_size = 0; + if (!ext4_test_inode_state(tmp_inode, + EXT4_STATE_LUSTRE_EA_INODE)) { + /* + * Defer quota free call for previous + * inode until success is guaranteed. + */ + old_ea_inode_quota = le32_to_cpu( + s->here->e_value_size); } + iput(tmp_inode); + + s->here->e_value_inum = 0; + s->here->e_value_size = 0; } } else { /* Allocate a buffer where we construct the new block. */ @@ -1999,18 +2019,13 @@ inserted: lock_buffer(new_bh); /* * We have to be careful about races with - * freeing, rehashing or adding references to - * xattr block. Once we hold buffer lock xattr - * block's state is stable so we can check - * whether the block got freed / rehashed or - * not. Since we unhash mbcache entry under - * buffer lock when freeing / rehashing xattr - * block, checking whether entry is still - * hashed is reliable. Same rules hold for - * e_reusable handling. + * adding references to xattr block. Once we + * hold buffer lock xattr block's state is + * stable so we can check the additional + * reference fits. */ - if (hlist_bl_unhashed(&ce->e_hash_list) || - !ce->e_reusable) { + ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1; + if (ref > EXT4_XATTR_REFCOUNT_MAX) { /* * Undo everything and check mbcache * again. @@ -2025,9 +2040,8 @@ inserted: new_bh = NULL; goto inserted; } - ref = le32_to_cpu(BHDR(new_bh)->h_refcount) + 1; BHDR(new_bh)->h_refcount = cpu_to_le32(ref); - if (ref >= EXT4_XATTR_REFCOUNT_MAX) + if (ref == EXT4_XATTR_REFCOUNT_MAX) ce->e_reusable = 0; ea_bdebug(new_bh, "reusing; refcount now=%d", ref); @@ -2175,8 +2189,9 @@ int ext4_xattr_ibody_find(struct inode *inode, struct ext4_xattr_info *i, struct ext4_inode *raw_inode; int error; - if (EXT4_I(inode)->i_extra_isize == 0) + if (!EXT4_INODE_HAS_XATTR_SPACE(inode)) return 0; + raw_inode = ext4_raw_inode(&is->iloc); header = IHDR(inode, raw_inode); is->s.base = is->s.first = IFIRST(header); @@ -2204,8 +2219,9 @@ int ext4_xattr_ibody_set(handle_t *handle, struct inode *inode, struct ext4_xattr_search *s = &is->s; int error; - if (EXT4_I(inode)->i_extra_isize == 0) + if (!EXT4_INODE_HAS_XATTR_SPACE(inode)) return -ENOSPC; + error = ext4_xattr_set_entry(i, s, handle, inode, false /* is_block */); if (error) return error; diff --git a/fs/ext4/xattr.h b/fs/ext4/xattr.h index 77efb9a627ad..824faf0b15a8 100644 --- a/fs/ext4/xattr.h +++ b/fs/ext4/xattr.h @@ -84,7 +84,7 @@ struct ext4_xattr_entry { /* * The minimum size of EA value when you start storing it in an external inode * size of block - size of header - size of 1 entry - 4 null bytes -*/ + */ #define EXT4_XATTR_MIN_LARGE_EA_SIZE(b) \ ((b) - EXT4_XATTR_LEN(3) - sizeof(struct ext4_xattr_header) - 4) @@ -95,6 +95,19 @@ struct ext4_xattr_entry { #define EXT4_ZERO_XATTR_VALUE ((void *)-1) +/* + * If we want to add an xattr to the inode, we should make sure that + * i_extra_isize is not 0 and that the inode size is not less than + * EXT4_GOOD_OLD_INODE_SIZE + extra_isize + pad. + * EXT4_GOOD_OLD_INODE_SIZE extra_isize header entry pad data + * |--------------------------|------------|------|---------|---|-------| + */ +#define EXT4_INODE_HAS_XATTR_SPACE(inode) \ + ((EXT4_I(inode)->i_extra_isize != 0) && \ + (EXT4_GOOD_OLD_INODE_SIZE + EXT4_I(inode)->i_extra_isize + \ + sizeof(struct ext4_xattr_ibody_header) + EXT4_XATTR_PAD <= \ + EXT4_INODE_SIZE((inode)->i_sb))) + struct ext4_xattr_info { const char *name; const void *value; @@ -178,6 +191,7 @@ extern void ext4_xattr_inode_array_free(struct ext4_xattr_inode_array *array); extern int ext4_expand_extra_isize_ea(struct inode *inode, int new_extra_isize, struct ext4_inode *raw_inode, handle_t *handle); +extern void ext4_evict_ea_inode(struct inode *inode); extern const struct xattr_handler *ext4_xattr_handlers[]; diff --git a/fs/jbd2/checkpoint.c b/fs/jbd2/checkpoint.c index 746132998c57..51bd38da21cd 100644 --- a/fs/jbd2/checkpoint.c +++ b/fs/jbd2/checkpoint.c @@ -203,7 +203,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) tid_t this_tid; int result, batch_count = 0; - jbd_debug(1, "Start checkpoint\n"); + jbd2_debug(1, "Start checkpoint\n"); /* * First thing: if there are any transactions in the log which @@ -212,7 +212,7 @@ int jbd2_log_do_checkpoint(journal_t *journal) */ result = jbd2_cleanup_journal_tail(journal); trace_jbd2_checkpoint(journal, result); - jbd_debug(1, "cleanup_journal_tail returned %d\n", result); + jbd2_debug(1, "cleanup_journal_tail returned %d\n", result); if (result <= 0) return result; @@ -804,5 +804,5 @@ void __jbd2_journal_drop_transaction(journal_t *journal, transaction_t *transact trace_jbd2_drop_transaction(journal, transaction); - jbd_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid); + jbd2_debug(1, "Dropping transaction %d, all done\n", transaction->t_tid); } diff --git a/fs/jbd2/commit.c b/fs/jbd2/commit.c index 890b5543a1c5..b2b2bc9b88d9 100644 --- a/fs/jbd2/commit.c +++ b/fs/jbd2/commit.c @@ -421,7 +421,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) /* Do we need to erase the effects of a prior jbd2_journal_flush? */ if (journal->j_flags & JBD2_FLUSHED) { - jbd_debug(3, "super block updated\n"); + jbd2_debug(3, "super block updated\n"); mutex_lock_io(&journal->j_checkpoint_mutex); /* * We hold j_checkpoint_mutex so tail cannot change under us. @@ -435,7 +435,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) REQ_SYNC); mutex_unlock(&journal->j_checkpoint_mutex); } else { - jbd_debug(3, "superblock not updated\n"); + jbd2_debug(3, "superblock not updated\n"); } J_ASSERT(journal->j_running_transaction != NULL); @@ -467,7 +467,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) commit_transaction = journal->j_running_transaction; trace_jbd2_start_commit(journal, commit_transaction); - jbd_debug(1, "JBD2: starting commit of transaction %d\n", + jbd2_debug(1, "JBD2: starting commit of transaction %d\n", commit_transaction->t_tid); write_lock(&journal->j_state_lock); @@ -540,7 +540,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) __jbd2_journal_clean_checkpoint_list(journal, false); spin_unlock(&journal->j_list_lock); - jbd_debug(3, "JBD2: commit phase 1\n"); + jbd2_debug(3, "JBD2: commit phase 1\n"); /* * Clear revoked flag to reflect there is no revoked buffers @@ -553,13 +553,13 @@ void jbd2_journal_commit_transaction(journal_t *journal) */ jbd2_journal_switch_revoke_table(journal); + write_lock(&journal->j_state_lock); /* * Reserved credits cannot be claimed anymore, free them */ atomic_sub(atomic_read(&journal->j_reserved_credits), &commit_transaction->t_outstanding_credits); - write_lock(&journal->j_state_lock); trace_jbd2_commit_flushing(journal, commit_transaction); stats.run.rs_flushing = jiffies; stats.run.rs_locked = jbd2_time_diff(stats.run.rs_locked, @@ -573,7 +573,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) wake_up(&journal->j_wait_transaction_locked); write_unlock(&journal->j_state_lock); - jbd_debug(3, "JBD2: commit phase 2a\n"); + jbd2_debug(3, "JBD2: commit phase 2a\n"); /* * Now start flushing things to disk, in the order they appear @@ -586,7 +586,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) blk_start_plug(&plug); jbd2_journal_write_revoke_records(commit_transaction, &log_bufs); - jbd_debug(3, "JBD2: commit phase 2b\n"); + jbd2_debug(3, "JBD2: commit phase 2b\n"); /* * Way to go: we have now written out all of the data for a @@ -642,7 +642,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) if (!descriptor) { J_ASSERT (bufs == 0); - jbd_debug(4, "JBD2: get descriptor\n"); + jbd2_debug(4, "JBD2: get descriptor\n"); descriptor = jbd2_journal_get_descriptor_buffer( commit_transaction, @@ -652,7 +652,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) continue; } - jbd_debug(4, "JBD2: got buffer %llu (%p)\n", + jbd2_debug(4, "JBD2: got buffer %llu (%p)\n", (unsigned long long)descriptor->b_blocknr, descriptor->b_data); tagp = &descriptor->b_data[sizeof(journal_header_t)]; @@ -737,7 +737,7 @@ void jbd2_journal_commit_transaction(journal_t *journal) commit_transaction->t_buffers == NULL || space_left < tag_bytes + 16 + csum_size) { - jbd_debug(4, "JBD2: Submit %d IOs\n", bufs); + jbd2_debug(4, "JBD2: Submit %d IOs\n", bufs); /* Write an end-of-descriptor marker before submitting the IOs. "tag" still points to @@ -839,7 +839,7 @@ start_journal_io: so we incur less scheduling load. */ - jbd_debug(3, "JBD2: commit phase 3\n"); + jbd2_debug(3, "JBD2: commit phase 3\n"); while (!list_empty(&io_bufs)) { struct buffer_head *bh = list_entry(io_bufs.prev, @@ -882,7 +882,7 @@ start_journal_io: J_ASSERT (commit_transaction->t_shadow_list == NULL); - jbd_debug(3, "JBD2: commit phase 4\n"); + jbd2_debug(3, "JBD2: commit phase 4\n"); /* Here we wait for the revoke record and descriptor record buffers */ while (!list_empty(&log_bufs)) { @@ -906,7 +906,7 @@ start_journal_io: if (err) jbd2_journal_abort(journal, err); - jbd_debug(3, "JBD2: commit phase 5\n"); + jbd2_debug(3, "JBD2: commit phase 5\n"); write_lock(&journal->j_state_lock); J_ASSERT(commit_transaction->t_state == T_COMMIT_DFLUSH); commit_transaction->t_state = T_COMMIT_JFLUSH; @@ -945,7 +945,7 @@ start_journal_io: transaction can be removed from any checkpoint list it was on before. */ - jbd_debug(3, "JBD2: commit phase 6\n"); + jbd2_debug(3, "JBD2: commit phase 6\n"); J_ASSERT(list_empty(&commit_transaction->t_inode_list)); J_ASSERT(commit_transaction->t_buffers == NULL); @@ -1122,7 +1122,7 @@ restart_loop: /* Done with this transaction! */ - jbd_debug(3, "JBD2: commit phase 7\n"); + jbd2_debug(3, "JBD2: commit phase 7\n"); J_ASSERT(commit_transaction->t_state == T_COMMIT_JFLUSH); @@ -1164,7 +1164,7 @@ restart_loop: journal->j_fc_cleanup_callback(journal, 1, commit_transaction->t_tid); trace_jbd2_end_commit(journal, commit_transaction); - jbd_debug(1, "JBD2: commit %d complete, head %d\n", + jbd2_debug(1, "JBD2: commit %d complete, head %d\n", journal->j_commit_sequence, journal->j_tail_sequence); write_lock(&journal->j_state_lock); diff --git a/fs/jbd2/journal.c b/fs/jbd2/journal.c index 2a1b9da7c3e3..b083961ea383 100644 --- a/fs/jbd2/journal.c +++ b/fs/jbd2/journal.c @@ -49,8 +49,7 @@ #include #ifdef CONFIG_JBD2_DEBUG -ushort jbd2_journal_enable_debug __read_mostly; -EXPORT_SYMBOL(jbd2_journal_enable_debug); +static ushort jbd2_journal_enable_debug __read_mostly; module_param_named(jbd2_debug, jbd2_journal_enable_debug, ushort, 0644); MODULE_PARM_DESC(jbd2_debug, "Debugging level for jbd2"); @@ -81,7 +80,6 @@ EXPORT_SYMBOL(jbd2_journal_errno); EXPORT_SYMBOL(jbd2_journal_ack_err); EXPORT_SYMBOL(jbd2_journal_clear_err); EXPORT_SYMBOL(jbd2_log_wait_commit); -EXPORT_SYMBOL(jbd2_log_start_commit); EXPORT_SYMBOL(jbd2_journal_start_commit); EXPORT_SYMBOL(jbd2_journal_force_commit_nested); EXPORT_SYMBOL(jbd2_journal_wipe); @@ -115,7 +113,6 @@ void __jbd2_debug(int level, const char *file, const char *func, printk(KERN_DEBUG "%s: (%s, %u): %pV", file, func, line, &vaf); va_end(args); } -EXPORT_SYMBOL(__jbd2_debug); #endif /* Checksumming functions */ @@ -203,11 +200,11 @@ loop: if (journal->j_flags & JBD2_UNMOUNT) goto end_loop; - jbd_debug(1, "commit_sequence=%u, commit_request=%u\n", + jbd2_debug(1, "commit_sequence=%u, commit_request=%u\n", journal->j_commit_sequence, journal->j_commit_request); if (journal->j_commit_sequence != journal->j_commit_request) { - jbd_debug(1, "OK, requests differ\n"); + jbd2_debug(1, "OK, requests differ\n"); write_unlock(&journal->j_state_lock); del_timer_sync(&journal->j_commit_timer); jbd2_journal_commit_transaction(journal); @@ -222,7 +219,7 @@ loop: * good idea, because that depends on threads that may * be already stopped. */ - jbd_debug(1, "Now suspending kjournald2\n"); + jbd2_debug(1, "Now suspending kjournald2\n"); write_unlock(&journal->j_state_lock); try_to_freeze(); write_lock(&journal->j_state_lock); @@ -252,7 +249,7 @@ loop: finish_wait(&journal->j_wait_commit, &wait); } - jbd_debug(1, "kjournald2 wakes\n"); + jbd2_debug(1, "kjournald2 wakes\n"); /* * Were we woken up by a commit wakeup event? @@ -260,7 +257,7 @@ loop: transaction = journal->j_running_transaction; if (transaction && time_after_eq(jiffies, transaction->t_expires)) { journal->j_commit_request = transaction->t_tid; - jbd_debug(1, "woke because of timeout\n"); + jbd2_debug(1, "woke because of timeout\n"); } goto loop; @@ -268,7 +265,7 @@ end_loop: del_timer_sync(&journal->j_commit_timer); journal->j_task = NULL; wake_up(&journal->j_wait_done_commit); - jbd_debug(1, "Journal thread exiting.\n"); + jbd2_debug(1, "Journal thread exiting.\n"); write_unlock(&journal->j_state_lock); return 0; } @@ -481,7 +478,7 @@ repeat: * Called with j_state_lock locked for writing. * Returns true if a transaction commit was started. */ -int __jbd2_log_start_commit(journal_t *journal, tid_t target) +static int __jbd2_log_start_commit(journal_t *journal, tid_t target) { /* Return if the txn has already requested to be committed */ if (journal->j_commit_request == target) @@ -500,7 +497,7 @@ int __jbd2_log_start_commit(journal_t *journal, tid_t target) */ journal->j_commit_request = target; - jbd_debug(1, "JBD2: requesting commit %u/%u\n", + jbd2_debug(1, "JBD2: requesting commit %u/%u\n", journal->j_commit_request, journal->j_commit_sequence); journal->j_running_transaction->t_requested = jiffies; @@ -705,7 +702,7 @@ int jbd2_log_wait_commit(journal_t *journal, tid_t tid) } #endif while (tid_gt(tid, journal->j_commit_sequence)) { - jbd_debug(1, "JBD2: want %u, j_commit_sequence=%u\n", + jbd2_debug(1, "JBD2: want %u, j_commit_sequence=%u\n", tid, journal->j_commit_sequence); read_unlock(&journal->j_state_lock); wake_up(&journal->j_wait_commit); @@ -1117,7 +1114,7 @@ int __jbd2_update_log_tail(journal_t *journal, tid_t tid, unsigned long block) freed += journal->j_last - journal->j_first; trace_jbd2_update_log_tail(journal, tid, block, freed); - jbd_debug(1, + jbd2_debug(1, "Cleaning journal tail from %u to %u (offset %lu), " "freeing %lu\n", journal->j_tail_sequence, tid, block, freed); @@ -1497,7 +1494,7 @@ journal_t *jbd2_journal_init_inode(struct inode *inode) return NULL; } - jbd_debug(1, "JBD2: inode %s/%ld, size %lld, bits %d, blksize %ld\n", + jbd2_debug(1, "JBD2: inode %s/%ld, size %lld, bits %d, blksize %ld\n", inode->i_sb->s_id, inode->i_ino, (long long) inode->i_size, inode->i_sb->s_blocksize_bits, inode->i_sb->s_blocksize); @@ -1577,7 +1574,7 @@ static int journal_reset(journal_t *journal) * attempting a write to a potential-readonly device. */ if (sb->s_start == 0) { - jbd_debug(1, "JBD2: Skipping superblock update on recovered sb " + jbd2_debug(1, "JBD2: Skipping superblock update on recovered sb " "(start %ld, seq %u, errno %d)\n", journal->j_tail, journal->j_tail_sequence, journal->j_errno); @@ -1681,7 +1678,7 @@ int jbd2_journal_update_sb_log_tail(journal_t *journal, tid_t tail_tid, } BUG_ON(!mutex_is_locked(&journal->j_checkpoint_mutex)); - jbd_debug(1, "JBD2: updating superblock (start %lu, seq %u)\n", + jbd2_debug(1, "JBD2: updating superblock (start %lu, seq %u)\n", tail_block, tail_tid); lock_buffer(journal->j_sb_buffer); @@ -1722,7 +1719,7 @@ static void jbd2_mark_journal_empty(journal_t *journal, blk_opf_t write_flags) return; } - jbd_debug(1, "JBD2: Marking journal as empty (seq %u)\n", + jbd2_debug(1, "JBD2: Marking journal as empty (seq %u)\n", journal->j_tail_sequence); sb->s_sequence = cpu_to_be32(journal->j_tail_sequence); @@ -1865,7 +1862,7 @@ void jbd2_journal_update_sb_errno(journal_t *journal) errcode = journal->j_errno; if (errcode == -ESHUTDOWN) errcode = 0; - jbd_debug(1, "JBD2: updating superblock error (errno %d)\n", errcode); + jbd2_debug(1, "JBD2: updating superblock error (errno %d)\n", errcode); sb->s_errno = cpu_to_be32(errcode); jbd2_write_superblock(journal, REQ_SYNC | REQ_FUA); @@ -2337,7 +2334,7 @@ int jbd2_journal_set_features(journal_t *journal, unsigned long compat, compat & JBD2_FEATURE_COMPAT_CHECKSUM) compat &= ~JBD2_FEATURE_COMPAT_CHECKSUM; - jbd_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n", + jbd2_debug(1, "Setting new features 0x%lx/0x%lx/0x%lx\n", compat, ro, incompat); sb = journal->j_superblock; @@ -2406,7 +2403,7 @@ void jbd2_journal_clear_features(journal_t *journal, unsigned long compat, { journal_superblock_t *sb; - jbd_debug(1, "Clear features 0x%lx/0x%lx/0x%lx\n", + jbd2_debug(1, "Clear features 0x%lx/0x%lx/0x%lx\n", compat, ro, incompat); sb = journal->j_superblock; @@ -2863,7 +2860,7 @@ static struct journal_head *journal_alloc_journal_head(void) #endif ret = kmem_cache_zalloc(jbd2_journal_head_cache, GFP_NOFS); if (!ret) { - jbd_debug(1, "out of memory for journal_head\n"); + jbd2_debug(1, "out of memory for journal_head\n"); pr_notice_ratelimited("ENOMEM in %s, retrying.\n", __func__); ret = kmem_cache_zalloc(jbd2_journal_head_cache, GFP_NOFS | __GFP_NOFAIL); diff --git a/fs/jbd2/recovery.c b/fs/jbd2/recovery.c index e699d6ab2c0e..f548479615c6 100644 --- a/fs/jbd2/recovery.c +++ b/fs/jbd2/recovery.c @@ -245,11 +245,11 @@ static int fc_do_one_pass(journal_t *journal, return 0; while (next_fc_block <= journal->j_fc_last) { - jbd_debug(3, "Fast commit replay: next block %ld\n", + jbd2_debug(3, "Fast commit replay: next block %ld\n", next_fc_block); err = jread(&bh, journal, next_fc_block); if (err) { - jbd_debug(3, "Fast commit replay: read error\n"); + jbd2_debug(3, "Fast commit replay: read error\n"); break; } @@ -263,7 +263,7 @@ static int fc_do_one_pass(journal_t *journal, } if (err) - jbd_debug(3, "Fast commit replay failed, err = %d\n", err); + jbd2_debug(3, "Fast commit replay failed, err = %d\n", err); return err; } @@ -297,7 +297,7 @@ int jbd2_journal_recover(journal_t *journal) */ if (!sb->s_start) { - jbd_debug(1, "No recovery required, last transaction %d\n", + jbd2_debug(1, "No recovery required, last transaction %d\n", be32_to_cpu(sb->s_sequence)); journal->j_transaction_sequence = be32_to_cpu(sb->s_sequence) + 1; return 0; @@ -309,10 +309,10 @@ int jbd2_journal_recover(journal_t *journal) if (!err) err = do_one_pass(journal, &info, PASS_REPLAY); - jbd_debug(1, "JBD2: recovery, exit status %d, " + jbd2_debug(1, "JBD2: recovery, exit status %d, " "recovered transactions %u to %u\n", err, info.start_transaction, info.end_transaction); - jbd_debug(1, "JBD2: Replayed %d and revoked %d/%d blocks\n", + jbd2_debug(1, "JBD2: Replayed %d and revoked %d/%d blocks\n", info.nr_replays, info.nr_revoke_hits, info.nr_revokes); /* Restart the log at the next transaction ID, thus invalidating @@ -362,7 +362,7 @@ int jbd2_journal_skip_recovery(journal_t *journal) #ifdef CONFIG_JBD2_DEBUG int dropped = info.end_transaction - be32_to_cpu(journal->j_superblock->s_sequence); - jbd_debug(1, + jbd2_debug(1, "JBD2: ignoring %d transaction%s from the journal.\n", dropped, (dropped == 1) ? "" : "s"); #endif @@ -484,7 +484,7 @@ static int do_one_pass(journal_t *journal, if (pass == PASS_SCAN) info->start_transaction = first_commit_ID; - jbd_debug(1, "Starting recovery pass %d\n", pass); + jbd2_debug(1, "Starting recovery pass %d\n", pass); /* * Now we walk through the log, transaction by transaction, @@ -510,7 +510,7 @@ static int do_one_pass(journal_t *journal, if (tid_geq(next_commit_ID, info->end_transaction)) break; - jbd_debug(2, "Scanning for sequence ID %u at %lu/%lu\n", + jbd2_debug(2, "Scanning for sequence ID %u at %lu/%lu\n", next_commit_ID, next_log_block, jbd2_has_feature_fast_commit(journal) ? journal->j_fc_last : journal->j_last); @@ -519,7 +519,7 @@ static int do_one_pass(journal_t *journal, * either the next descriptor block or the final commit * record. */ - jbd_debug(3, "JBD2: checking block %ld\n", next_log_block); + jbd2_debug(3, "JBD2: checking block %ld\n", next_log_block); err = jread(&bh, journal, next_log_block); if (err) goto failed; @@ -542,7 +542,7 @@ static int do_one_pass(journal_t *journal, blocktype = be32_to_cpu(tmp->h_blocktype); sequence = be32_to_cpu(tmp->h_sequence); - jbd_debug(3, "Found magic %d, sequence %d\n", + jbd2_debug(3, "Found magic %d, sequence %d\n", blocktype, sequence); if (sequence != next_commit_ID) { @@ -575,7 +575,7 @@ static int do_one_pass(journal_t *journal, goto failed; } need_check_commit_time = true; - jbd_debug(1, + jbd2_debug(1, "invalid descriptor block found in %lu\n", next_log_block); } @@ -758,7 +758,7 @@ static int do_one_pass(journal_t *journal, * It likely does not belong to same journal, * just end this recovery with success. */ - jbd_debug(1, "JBD2: Invalid checksum ignored in transaction %u, likely stale data\n", + jbd2_debug(1, "JBD2: Invalid checksum ignored in transaction %u, likely stale data\n", next_commit_ID); brelse(bh); goto done; @@ -826,7 +826,7 @@ static int do_one_pass(journal_t *journal, if (pass == PASS_SCAN && !jbd2_descriptor_block_csum_verify(journal, bh->b_data)) { - jbd_debug(1, "JBD2: invalid revoke block found in %lu\n", + jbd2_debug(1, "JBD2: invalid revoke block found in %lu\n", next_log_block); need_check_commit_time = true; } @@ -845,7 +845,7 @@ static int do_one_pass(journal_t *journal, continue; default: - jbd_debug(3, "Unrecognised magic %d, end of scan.\n", + jbd2_debug(3, "Unrecognised magic %d, end of scan.\n", blocktype); brelse(bh); goto done; diff --git a/fs/jbd2/revoke.c b/fs/jbd2/revoke.c index fa608788b93d..4556e4689024 100644 --- a/fs/jbd2/revoke.c +++ b/fs/jbd2/revoke.c @@ -398,7 +398,7 @@ int jbd2_journal_revoke(handle_t *handle, unsigned long long blocknr, } handle->h_revoke_credits--; - jbd_debug(2, "insert revoke for block %llu, bh_in=%p\n",blocknr, bh_in); + jbd2_debug(2, "insert revoke for block %llu, bh_in=%p\n",blocknr, bh_in); err = insert_revoke_hash(journal, blocknr, handle->h_transaction->t_tid); BUFFER_TRACE(bh_in, "exit"); @@ -428,7 +428,7 @@ int jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh) int did_revoke = 0; /* akpm: debug */ struct buffer_head *bh = jh2bh(jh); - jbd_debug(4, "journal_head %p, cancelling revoke\n", jh); + jbd2_debug(4, "journal_head %p, cancelling revoke\n", jh); /* Is the existing Revoke bit valid? If so, we trust it, and * only perform the full cancel if the revoke bit is set. If @@ -444,7 +444,7 @@ int jbd2_journal_cancel_revoke(handle_t *handle, struct journal_head *jh) if (need_cancel) { record = find_revoke_record(journal, bh->b_blocknr); if (record) { - jbd_debug(4, "cancelled existing revoke on " + jbd2_debug(4, "cancelled existing revoke on " "blocknr %llu\n", (unsigned long long)bh->b_blocknr); spin_lock(&journal->j_revoke_lock); list_del(&record->hash); @@ -560,7 +560,7 @@ void jbd2_journal_write_revoke_records(transaction_t *transaction, } if (descriptor) flush_descriptor(journal, descriptor, offset); - jbd_debug(1, "Wrote %d revoke records\n", count); + jbd2_debug(1, "Wrote %d revoke records\n", count); } /* diff --git a/fs/jbd2/transaction.c b/fs/jbd2/transaction.c index e9c308ae475f..e1be93ccd81c 100644 --- a/fs/jbd2/transaction.c +++ b/fs/jbd2/transaction.c @@ -373,7 +373,7 @@ alloc_transaction: return -ENOMEM; } - jbd_debug(3, "New handle %p going live.\n", handle); + jbd2_debug(3, "New handle %p going live.\n", handle); /* * We need to hold j_state_lock until t_updates has been incremented, @@ -453,7 +453,7 @@ repeat: handle->h_start_jiffies = jiffies; atomic_inc(&transaction->t_updates); atomic_inc(&transaction->t_handle_count); - jbd_debug(4, "Handle %p given %d credits (total %d, free %lu)\n", + jbd2_debug(4, "Handle %p given %d credits (total %d, free %lu)\n", handle, blocks, atomic_read(&transaction->t_outstanding_credits), jbd2_log_space_left(journal)); @@ -674,7 +674,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records) /* Don't extend a locked-down transaction! */ if (transaction->t_state != T_RUNNING) { - jbd_debug(3, "denied handle %p %d blocks: " + jbd2_debug(3, "denied handle %p %d blocks: " "transaction not running\n", handle, nblocks); goto error_out; } @@ -689,7 +689,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records) &transaction->t_outstanding_credits); if (wanted > journal->j_max_transaction_buffers) { - jbd_debug(3, "denied handle %p %d blocks: " + jbd2_debug(3, "denied handle %p %d blocks: " "transaction too large\n", handle, nblocks); atomic_sub(nblocks, &transaction->t_outstanding_credits); goto error_out; @@ -707,7 +707,7 @@ int jbd2_journal_extend(handle_t *handle, int nblocks, int revoke_records) handle->h_revoke_credits_requested += revoke_records; result = 0; - jbd_debug(3, "extended handle %p by %d\n", handle, nblocks); + jbd2_debug(3, "extended handle %p by %d\n", handle, nblocks); error_out: read_unlock(&journal->j_state_lock); return result; @@ -795,7 +795,7 @@ int jbd2__journal_restart(handle_t *handle, int nblocks, int revoke_records, * First unlink the handle from its current transaction, and start the * commit on that. */ - jbd_debug(2, "restarting handle %p\n", handle); + jbd2_debug(2, "restarting handle %p\n", handle); stop_this_handle(handle); handle->h_transaction = NULL; @@ -979,7 +979,7 @@ do_get_write_access(handle_t *handle, struct journal_head *jh, journal = transaction->t_journal; - jbd_debug(5, "journal_head %p, force_copy %d\n", jh, force_copy); + jbd2_debug(5, "journal_head %p, force_copy %d\n", jh, force_copy); JBUFFER_TRACE(jh, "entry"); repeat: @@ -1271,7 +1271,7 @@ int jbd2_journal_get_create_access(handle_t *handle, struct buffer_head *bh) struct journal_head *jh = jbd2_journal_add_journal_head(bh); int err; - jbd_debug(5, "journal_head %p\n", jh); + jbd2_debug(5, "journal_head %p\n", jh); err = -EROFS; if (is_handle_aborted(handle)) goto out; @@ -1486,8 +1486,6 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) struct journal_head *jh; int ret = 0; - if (is_handle_aborted(handle)) - return -EROFS; if (!buffer_jbd(bh)) return -EUCLEAN; @@ -1496,7 +1494,7 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) * of the running transaction. */ jh = bh2jh(bh); - jbd_debug(5, "journal_head %p\n", jh); + jbd2_debug(5, "journal_head %p\n", jh); JBUFFER_TRACE(jh, "entry"); /* @@ -1534,6 +1532,18 @@ int jbd2_journal_dirty_metadata(handle_t *handle, struct buffer_head *bh) journal = transaction->t_journal; spin_lock(&jh->b_state_lock); + if (is_handle_aborted(handle)) { + /* + * Check journal aborting with @jh->b_state_lock locked, + * since 'jh->b_transaction' could be replaced with + * 'jh->b_next_transaction' during old transaction + * committing if journal aborted, which may fail + * assertion on 'jh->b_frozen_data == NULL'. + */ + ret = -EROFS; + goto out_unlock_bh; + } + if (jh->b_modified == 0) { /* * This buffer's got modified and becoming part @@ -1818,7 +1828,7 @@ int jbd2_journal_stop(handle_t *handle) pid_t pid; if (--handle->h_ref > 0) { - jbd_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1, + jbd2_debug(4, "h_ref %d -> %d\n", handle->h_ref + 1, handle->h_ref); if (is_handle_aborted(handle)) return -EIO; @@ -1838,7 +1848,7 @@ int jbd2_journal_stop(handle_t *handle) if (is_handle_aborted(handle)) err = -EIO; - jbd_debug(4, "Handle %p going down\n", handle); + jbd2_debug(4, "Handle %p going down\n", handle); trace_jbd2_handle_stats(journal->j_fs_dev->bd_dev, tid, handle->h_type, handle->h_line_no, jiffies - handle->h_start_jiffies, @@ -1916,7 +1926,7 @@ int jbd2_journal_stop(handle_t *handle) * completes the commit thread, it just doesn't write * anything to disk. */ - jbd_debug(2, "transaction too old, requesting commit for " + jbd2_debug(2, "transaction too old, requesting commit for " "handle %p\n", handle); /* This is non-blocking */ jbd2_log_start_commit(journal, tid); @@ -2662,7 +2672,7 @@ static int jbd2_journal_file_inode(handle_t *handle, struct jbd2_inode *jinode, return -EROFS; journal = transaction->t_journal; - jbd_debug(4, "Adding inode %lu, tid:%d\n", jinode->i_vfs_inode->i_ino, + jbd2_debug(4, "Adding inode %lu, tid:%d\n", jinode->i_vfs_inode->i_ino, transaction->t_tid); spin_lock(&journal->j_list_lock); diff --git a/fs/kernfs/dir.c b/fs/kernfs/dir.c index 6eca72cfa1f2..1cc88ba6de90 100644 --- a/fs/kernfs/dir.c +++ b/fs/kernfs/dir.c @@ -1343,14 +1343,17 @@ static void __kernfs_remove(struct kernfs_node *kn) { struct kernfs_node *pos; + /* Short-circuit if non-root @kn has already finished removal. */ + if (!kn) + return; + lockdep_assert_held_write(&kernfs_root(kn)->kernfs_rwsem); /* - * Short-circuit if non-root @kn has already finished removal. * This is for kernfs_remove_self() which plays with active ref * after removal. */ - if (!kn || (kn->parent && RB_EMPTY_NODE(&kn->rb))) + if (kn->parent && RB_EMPTY_NODE(&kn->rb)) return; pr_debug("kernfs %s: removing\n", kn->name); diff --git a/fs/kernfs/file.c b/fs/kernfs/file.c index e3abfa843879..b3ec34386b43 100644 --- a/fs/kernfs/file.c +++ b/fs/kernfs/file.c @@ -18,21 +18,8 @@ #include "kernfs-internal.h" -/* - * There's one kernfs_open_file for each open file and one kernfs_open_node - * for each kernfs_node with one or more open files. - * - * kernfs_node->attr.open points to kernfs_open_node. attr.open is - * protected by kernfs_open_node_lock. - * - * filp->private_data points to seq_file whose ->private points to - * kernfs_open_file. kernfs_open_files are chained at - * kernfs_open_node->files, which is protected by kernfs_open_file_mutex. - */ -static DEFINE_SPINLOCK(kernfs_open_node_lock); -static DEFINE_MUTEX(kernfs_open_file_mutex); - struct kernfs_open_node { + struct rcu_head rcu_head; atomic_t event; wait_queue_head_t poll; struct list_head files; /* goes through kernfs_open_file.list */ @@ -51,6 +38,70 @@ struct kernfs_open_node { static DEFINE_SPINLOCK(kernfs_notify_lock); static struct kernfs_node *kernfs_notify_list = KERNFS_NOTIFY_EOL; +static inline struct mutex *kernfs_open_file_mutex_ptr(struct kernfs_node *kn) +{ + int idx = hash_ptr(kn, NR_KERNFS_LOCK_BITS); + + return &kernfs_locks->open_file_mutex[idx]; +} + +static inline struct mutex *kernfs_open_file_mutex_lock(struct kernfs_node *kn) +{ + struct mutex *lock; + + lock = kernfs_open_file_mutex_ptr(kn); + + mutex_lock(lock); + + return lock; +} + +/** + * kernfs_deref_open_node - Get kernfs_open_node corresponding to @kn. + * + * @of: associated kernfs_open_file instance. + * @kn: target kernfs_node. + * + * Fetch and return ->attr.open of @kn if @of->list is non empty. + * If @of->list is not empty we can safely assume that @of is on + * @kn->attr.open->files list and this guarantees that @kn->attr.open + * will not vanish i.e. dereferencing outside RCU read-side critical + * section is safe here. + * + * The caller needs to make sure that @of->list is not empty. + */ +static struct kernfs_open_node * +kernfs_deref_open_node(struct kernfs_open_file *of, struct kernfs_node *kn) +{ + struct kernfs_open_node *on; + + on = rcu_dereference_check(kn->attr.open, !list_empty(&of->list)); + + return on; +} + +/** + * kernfs_deref_open_node_protected - Get kernfs_open_node corresponding to @kn + * + * @kn: target kernfs_node. + * + * Fetch and return ->attr.open of @kn when caller holds the + * kernfs_open_file_mutex_ptr(kn). + * + * Update of ->attr.open happens under kernfs_open_file_mutex_ptr(kn). So when + * the caller guarantees that this mutex is being held, other updaters can't + * change ->attr.open and this means that we can safely deref ->attr.open + * outside RCU read-side critical section. + * + * The caller needs to make sure that kernfs_open_file_mutex is held. + */ +static struct kernfs_open_node * +kernfs_deref_open_node_protected(struct kernfs_node *kn) +{ + return rcu_dereference_protected(kn->attr.open, + lockdep_is_held(kernfs_open_file_mutex_ptr(kn))); +} + static struct kernfs_open_file *kernfs_of(struct file *file) { return ((struct seq_file *)file->private_data)->private; @@ -156,8 +207,12 @@ static void kernfs_seq_stop(struct seq_file *sf, void *v) static int kernfs_seq_show(struct seq_file *sf, void *v) { struct kernfs_open_file *of = sf->private; + struct kernfs_open_node *on = kernfs_deref_open_node(of, of->kn); - of->event = atomic_read(&of->kn->attr.open->event); + if (!on) + return -EINVAL; + + of->event = atomic_read(&on->event); return of->kn->attr.ops->seq_show(sf, v); } @@ -180,6 +235,7 @@ static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) struct kernfs_open_file *of = kernfs_of(iocb->ki_filp); ssize_t len = min_t(size_t, iov_iter_count(iter), PAGE_SIZE); const struct kernfs_ops *ops; + struct kernfs_open_node *on; char *buf; buf = of->prealloc_buf; @@ -201,7 +257,15 @@ static ssize_t kernfs_file_read_iter(struct kiocb *iocb, struct iov_iter *iter) goto out_free; } - of->event = atomic_read(&of->kn->attr.open->event); + on = kernfs_deref_open_node(of, of->kn); + if (!on) { + len = -EINVAL; + mutex_unlock(&of->mutex); + goto out_free; + } + + of->event = atomic_read(&on->event); + ops = kernfs_ops(of->kn); if (ops->read) len = ops->read(of, buf, len, iocb->ki_pos); @@ -243,7 +307,7 @@ static ssize_t kernfs_fop_read_iter(struct kiocb *iocb, struct iov_iter *iter) * There is no easy way for us to know if userspace is only doing a partial * write, so we don't support them. We expect the entire buffer to come on * the first write. Hint: if you're writing a value, first read the file, - * modify only the the value you're changing, then write entire buffer + * modify only the value you're changing, then write entire buffer * back. */ static ssize_t kernfs_fop_write_iter(struct kiocb *iocb, struct iov_iter *iter) @@ -484,7 +548,6 @@ static int kernfs_fop_mmap(struct file *file, struct vm_area_struct *vma) * It is not possible to successfully wrap close. * So error if someone is trying to use close. */ - rc = -EINVAL; if (vma->vm_ops && vma->vm_ops->close) goto out_put; @@ -518,37 +581,31 @@ static int kernfs_get_open_node(struct kernfs_node *kn, struct kernfs_open_file *of) { struct kernfs_open_node *on, *new_on = NULL; + struct mutex *mutex = NULL; - retry: - mutex_lock(&kernfs_open_file_mutex); - spin_lock_irq(&kernfs_open_node_lock); - - if (!kn->attr.open && new_on) { - kn->attr.open = new_on; - new_on = NULL; - } - - on = kn->attr.open; - if (on) - list_add_tail(&of->list, &on->files); - - spin_unlock_irq(&kernfs_open_node_lock); - mutex_unlock(&kernfs_open_file_mutex); + mutex = kernfs_open_file_mutex_lock(kn); + on = kernfs_deref_open_node_protected(kn); if (on) { - kfree(new_on); + list_add_tail(&of->list, &on->files); + mutex_unlock(mutex); return 0; + } else { + /* not there, initialize a new one */ + new_on = kmalloc(sizeof(*new_on), GFP_KERNEL); + if (!new_on) { + mutex_unlock(mutex); + return -ENOMEM; + } + atomic_set(&new_on->event, 1); + init_waitqueue_head(&new_on->poll); + INIT_LIST_HEAD(&new_on->files); + list_add_tail(&of->list, &new_on->files); + rcu_assign_pointer(kn->attr.open, new_on); } + mutex_unlock(mutex); - /* not there, initialize a new one and retry */ - new_on = kmalloc(sizeof(*new_on), GFP_KERNEL); - if (!new_on) - return -ENOMEM; - - atomic_set(&new_on->event, 1); - init_waitqueue_head(&new_on->poll); - INIT_LIST_HEAD(&new_on->files); - goto retry; + return 0; } /** @@ -567,24 +624,26 @@ static int kernfs_get_open_node(struct kernfs_node *kn, static void kernfs_unlink_open_file(struct kernfs_node *kn, struct kernfs_open_file *of) { - struct kernfs_open_node *on = kn->attr.open; - unsigned long flags; + struct kernfs_open_node *on; + struct mutex *mutex = NULL; - mutex_lock(&kernfs_open_file_mutex); - spin_lock_irqsave(&kernfs_open_node_lock, flags); + mutex = kernfs_open_file_mutex_lock(kn); + + on = kernfs_deref_open_node_protected(kn); + if (!on) { + mutex_unlock(mutex); + return; + } if (of) list_del(&of->list); - if (list_empty(&on->files)) - kn->attr.open = NULL; - else - on = NULL; + if (list_empty(&on->files)) { + rcu_assign_pointer(kn->attr.open, NULL); + kfree_rcu(on, rcu_head); + } - spin_unlock_irqrestore(&kernfs_open_node_lock, flags); - mutex_unlock(&kernfs_open_file_mutex); - - kfree(on); + mutex_unlock(mutex); } static int kernfs_fop_open(struct inode *inode, struct file *file) @@ -722,11 +781,11 @@ static void kernfs_release_file(struct kernfs_node *kn, /* * @of is guaranteed to have no other file operations in flight and * we just want to synchronize release and drain paths. - * @kernfs_open_file_mutex is enough. @of->mutex can't be used + * @kernfs_open_file_mutex_ptr(kn) is enough. @of->mutex can't be used * here because drain path may be called from places which can * cause circular dependency. */ - lockdep_assert_held(&kernfs_open_file_mutex); + lockdep_assert_held(kernfs_open_file_mutex_ptr(kn)); if (!of->released) { /* @@ -743,11 +802,12 @@ static int kernfs_fop_release(struct inode *inode, struct file *filp) { struct kernfs_node *kn = inode->i_private; struct kernfs_open_file *of = kernfs_of(filp); + struct mutex *mutex = NULL; if (kn->flags & KERNFS_HAS_RELEASE) { - mutex_lock(&kernfs_open_file_mutex); + mutex = kernfs_open_file_mutex_lock(kn); kernfs_release_file(kn, of); - mutex_unlock(&kernfs_open_file_mutex); + mutex_unlock(mutex); } kernfs_unlink_open_file(kn, of); @@ -762,6 +822,7 @@ void kernfs_drain_open_files(struct kernfs_node *kn) { struct kernfs_open_node *on; struct kernfs_open_file *of; + struct mutex *mutex = NULL; if (!(kn->flags & (KERNFS_HAS_MMAP | KERNFS_HAS_RELEASE))) return; @@ -771,20 +832,19 @@ void kernfs_drain_open_files(struct kernfs_node *kn) * ->attr.open at this point of time. This check allows early bail out * if ->attr.open is already NULL. kernfs_unlink_open_file makes * ->attr.open NULL only while holding kernfs_open_file_mutex so below - * check under kernfs_open_file_mutex will ensure bailing out if + * check under kernfs_open_file_mutex_ptr(kn) will ensure bailing out if * ->attr.open became NULL while waiting for the mutex. */ - if (!kn->attr.open) + if (!rcu_access_pointer(kn->attr.open)) return; - mutex_lock(&kernfs_open_file_mutex); - if (!kn->attr.open) { - mutex_unlock(&kernfs_open_file_mutex); + mutex = kernfs_open_file_mutex_lock(kn); + on = kernfs_deref_open_node_protected(kn); + if (!on) { + mutex_unlock(mutex); return; } - on = kn->attr.open; - list_for_each_entry(of, &on->files, list) { struct inode *inode = file_inode(of->file); @@ -795,7 +855,7 @@ void kernfs_drain_open_files(struct kernfs_node *kn) kernfs_release_file(kn, of); } - mutex_unlock(&kernfs_open_file_mutex); + mutex_unlock(mutex); } /* @@ -815,7 +875,10 @@ void kernfs_drain_open_files(struct kernfs_node *kn) __poll_t kernfs_generic_poll(struct kernfs_open_file *of, poll_table *wait) { struct kernfs_node *kn = kernfs_dentry_node(of->file->f_path.dentry); - struct kernfs_open_node *on = kn->attr.open; + struct kernfs_open_node *on = kernfs_deref_open_node(of, kn); + + if (!on) + return EPOLLERR; poll_wait(of->file, &on->poll, wait); @@ -922,13 +985,13 @@ void kernfs_notify(struct kernfs_node *kn) return; /* kick poll immediately */ - spin_lock_irqsave(&kernfs_open_node_lock, flags); - on = kn->attr.open; + rcu_read_lock(); + on = rcu_dereference(kn->attr.open); if (on) { atomic_inc(&on->event); wake_up_interruptible(&on->poll); } - spin_unlock_irqrestore(&kernfs_open_node_lock, flags); + rcu_read_unlock(); /* schedule work to kick fsnotify */ spin_lock_irqsave(&kernfs_notify_lock, flags); diff --git a/fs/kernfs/kernfs-internal.h b/fs/kernfs/kernfs-internal.h index eeaa779b929c..3ae214d02d44 100644 --- a/fs/kernfs/kernfs-internal.h +++ b/fs/kernfs/kernfs-internal.h @@ -164,4 +164,8 @@ void kernfs_drain_open_files(struct kernfs_node *kn); */ extern const struct inode_operations kernfs_symlink_iops; +/* + * kernfs locks + */ +extern struct kernfs_global_locks *kernfs_locks; #endif /* __KERNFS_INTERNAL_H */ diff --git a/fs/kernfs/mount.c b/fs/kernfs/mount.c index cfa79715fc1a..d0859f72d2d6 100644 --- a/fs/kernfs/mount.c +++ b/fs/kernfs/mount.c @@ -20,6 +20,7 @@ #include "kernfs-internal.h" struct kmem_cache *kernfs_node_cache, *kernfs_iattrs_cache; +struct kernfs_global_locks *kernfs_locks; static int kernfs_sop_show_options(struct seq_file *sf, struct dentry *dentry) { @@ -387,6 +388,22 @@ void kernfs_kill_sb(struct super_block *sb) kfree(info); } +static void __init kernfs_mutex_init(void) +{ + int count; + + for (count = 0; count < NR_KERNFS_LOCKS; count++) + mutex_init(&kernfs_locks->open_file_mutex[count]); +} + +static void __init kernfs_lock_init(void) +{ + kernfs_locks = kmalloc(sizeof(struct kernfs_global_locks), GFP_KERNEL); + WARN_ON(!kernfs_locks); + + kernfs_mutex_init(); +} + void __init kernfs_init(void) { kernfs_node_cache = kmem_cache_create("kernfs_node_cache", @@ -397,4 +414,6 @@ void __init kernfs_init(void) kernfs_iattrs_cache = kmem_cache_create("kernfs_iattrs_cache", sizeof(struct kernfs_iattrs), 0, SLAB_PANIC, NULL); + + kernfs_lock_init(); } diff --git a/fs/mbcache.c b/fs/mbcache.c index 97c54d3a2227..96f1d49d30a5 100644 --- a/fs/mbcache.c +++ b/fs/mbcache.c @@ -11,7 +11,7 @@ /* * Mbcache is a simple key-value store. Keys need not be unique, however * key-value pairs are expected to be unique (we use this fact in - * mb_cache_entry_delete()). + * mb_cache_entry_delete_or_get()). * * Ext2 and ext4 use this cache for deduplication of extended attribute blocks. * Ext4 also uses it for deduplication of xattr values stored in inodes. @@ -90,7 +90,7 @@ int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key, return -ENOMEM; INIT_LIST_HEAD(&entry->e_list); - /* One ref for hash, one ref returned */ + /* Initial hash reference */ atomic_set(&entry->e_refcnt, 1); entry->e_key = key; entry->e_value = value; @@ -106,25 +106,45 @@ int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key, } } hlist_bl_add_head(&entry->e_hash_list, head); - hlist_bl_unlock(head); - + /* + * Add entry to LRU list before it can be found by + * mb_cache_entry_delete() to avoid races + */ spin_lock(&cache->c_list_lock); list_add_tail(&entry->e_list, &cache->c_list); - /* Grab ref for LRU list */ - atomic_inc(&entry->e_refcnt); cache->c_entry_count++; spin_unlock(&cache->c_list_lock); + hlist_bl_unlock(head); return 0; } EXPORT_SYMBOL(mb_cache_entry_create); -void __mb_cache_entry_free(struct mb_cache_entry *entry) +void __mb_cache_entry_free(struct mb_cache *cache, struct mb_cache_entry *entry) { + struct hlist_bl_head *head; + + head = mb_cache_entry_head(cache, entry->e_key); + hlist_bl_lock(head); + hlist_bl_del(&entry->e_hash_list); + hlist_bl_unlock(head); kmem_cache_free(mb_entry_cache, entry); } EXPORT_SYMBOL(__mb_cache_entry_free); +/* + * mb_cache_entry_wait_unused - wait to be the last user of the entry + * + * @entry - entry to work on + * + * Wait to be the last user of the entry. + */ +void mb_cache_entry_wait_unused(struct mb_cache_entry *entry) +{ + wait_var_event(&entry->e_refcnt, atomic_read(&entry->e_refcnt) <= 2); +} +EXPORT_SYMBOL(mb_cache_entry_wait_unused); + static struct mb_cache_entry *__entry_find(struct mb_cache *cache, struct mb_cache_entry *entry, u32 key) @@ -142,10 +162,9 @@ static struct mb_cache_entry *__entry_find(struct mb_cache *cache, while (node) { entry = hlist_bl_entry(node, struct mb_cache_entry, e_hash_list); - if (entry->e_key == key && entry->e_reusable) { - atomic_inc(&entry->e_refcnt); + if (entry->e_key == key && entry->e_reusable && + atomic_inc_not_zero(&entry->e_refcnt)) goto out; - } node = node->next; } entry = NULL; @@ -205,10 +224,9 @@ struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key, head = mb_cache_entry_head(cache, key); hlist_bl_lock(head); hlist_bl_for_each_entry(entry, node, head, e_hash_list) { - if (entry->e_key == key && entry->e_value == value) { - atomic_inc(&entry->e_refcnt); + if (entry->e_key == key && entry->e_value == value && + atomic_inc_not_zero(&entry->e_refcnt)) goto out; - } } entry = NULL; out: @@ -217,42 +235,42 @@ out: } EXPORT_SYMBOL(mb_cache_entry_get); -/* mb_cache_entry_delete - remove a cache entry +/* mb_cache_entry_delete_or_get - remove a cache entry if it has no users * @cache - cache we work with * @key - key * @value - value * - * Remove entry from cache @cache with key @key and value @value. + * Remove entry from cache @cache with key @key and value @value. The removal + * happens only if the entry is unused. The function returns NULL in case the + * entry was successfully removed or there's no entry in cache. Otherwise the + * function grabs reference of the entry that we failed to delete because it + * still has users and return it. */ -void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value) +struct mb_cache_entry *mb_cache_entry_delete_or_get(struct mb_cache *cache, + u32 key, u64 value) { - struct hlist_bl_node *node; - struct hlist_bl_head *head; struct mb_cache_entry *entry; - head = mb_cache_entry_head(cache, key); - hlist_bl_lock(head); - hlist_bl_for_each_entry(entry, node, head, e_hash_list) { - if (entry->e_key == key && entry->e_value == value) { - /* We keep hash list reference to keep entry alive */ - hlist_bl_del_init(&entry->e_hash_list); - hlist_bl_unlock(head); - spin_lock(&cache->c_list_lock); - if (!list_empty(&entry->e_list)) { - list_del_init(&entry->e_list); - if (!WARN_ONCE(cache->c_entry_count == 0, - "mbcache: attempt to decrement c_entry_count past zero")) - cache->c_entry_count--; - atomic_dec(&entry->e_refcnt); - } - spin_unlock(&cache->c_list_lock); - mb_cache_entry_put(cache, entry); - return; - } - } - hlist_bl_unlock(head); + entry = mb_cache_entry_get(cache, key, value); + if (!entry) + return NULL; + + /* + * Drop the ref we got from mb_cache_entry_get() and the initial hash + * ref if we are the last user + */ + if (atomic_cmpxchg(&entry->e_refcnt, 2, 0) != 2) + return entry; + + spin_lock(&cache->c_list_lock); + if (!list_empty(&entry->e_list)) + list_del_init(&entry->e_list); + cache->c_entry_count--; + spin_unlock(&cache->c_list_lock); + __mb_cache_entry_free(cache, entry); + return NULL; } -EXPORT_SYMBOL(mb_cache_entry_delete); +EXPORT_SYMBOL(mb_cache_entry_delete_or_get); /* mb_cache_entry_touch - cache entry got used * @cache - cache the entry belongs to @@ -281,34 +299,24 @@ static unsigned long mb_cache_shrink(struct mb_cache *cache, unsigned long nr_to_scan) { struct mb_cache_entry *entry; - struct hlist_bl_head *head; unsigned long shrunk = 0; spin_lock(&cache->c_list_lock); while (nr_to_scan-- && !list_empty(&cache->c_list)) { entry = list_first_entry(&cache->c_list, struct mb_cache_entry, e_list); - if (entry->e_referenced) { + /* Drop initial hash reference if there is no user */ + if (entry->e_referenced || + atomic_cmpxchg(&entry->e_refcnt, 1, 0) != 1) { entry->e_referenced = 0; list_move_tail(&entry->e_list, &cache->c_list); continue; } list_del_init(&entry->e_list); cache->c_entry_count--; - /* - * We keep LRU list reference so that entry doesn't go away - * from under us. - */ spin_unlock(&cache->c_list_lock); - head = mb_cache_entry_head(cache, entry->e_key); - hlist_bl_lock(head); - if (!hlist_bl_unhashed(&entry->e_hash_list)) { - hlist_bl_del_init(&entry->e_hash_list); - atomic_dec(&entry->e_refcnt); - } - hlist_bl_unlock(head); - if (mb_cache_entry_put(cache, entry)) - shrunk++; + __mb_cache_entry_free(cache, entry); + shrunk++; cond_resched(); spin_lock(&cache->c_list_lock); } @@ -400,11 +408,6 @@ void mb_cache_destroy(struct mb_cache *cache) * point. */ list_for_each_entry_safe(entry, next, &cache->c_list, e_list) { - if (!hlist_bl_unhashed(&entry->e_hash_list)) { - hlist_bl_del_init(&entry->e_hash_list); - atomic_dec(&entry->e_refcnt); - } else - WARN_ON(1); list_del(&entry->e_list); WARN_ON(atomic_read(&entry->e_refcnt) != 1); mb_cache_entry_put(cache, entry); diff --git a/fs/xfs/Makefile b/fs/xfs/Makefile index b056cfc6398e..1131dd01e4fe 100644 --- a/fs/xfs/Makefile +++ b/fs/xfs/Makefile @@ -106,6 +106,7 @@ xfs-y += xfs_log.o \ xfs_icreate_item.o \ xfs_inode_item.o \ xfs_inode_item_recover.o \ + xfs_iunlink_item.o \ xfs_refcount_item.o \ xfs_rmap_item.o \ xfs_log_recover.o \ diff --git a/fs/xfs/libxfs/xfs_ag.c b/fs/xfs/libxfs/xfs_ag.c index 3e920cf1b454..bb0c700afe3c 100644 --- a/fs/xfs/libxfs/xfs_ag.c +++ b/fs/xfs/libxfs/xfs_ag.c @@ -120,18 +120,18 @@ xfs_initialize_perag_data( for (index = 0; index < agcount; index++) { /* - * read the agf, then the agi. This gets us - * all the information we need and populates the - * per-ag structures for us. + * Read the AGF and AGI buffers to populate the per-ag + * structures for us. */ - error = xfs_alloc_pagf_init(mp, NULL, index, 0); - if (error) - return error; - - error = xfs_ialloc_pagi_init(mp, NULL, index); - if (error) - return error; pag = xfs_perag_get(mp, index); + error = xfs_alloc_read_agf(pag, NULL, 0, NULL); + if (!error) + error = xfs_ialloc_read_agi(pag, NULL, NULL); + if (error) { + xfs_perag_put(pag); + return error; + } + ifree += pag->pagi_freecount; ialloc += pag->pagi_count; bfree += pag->pagf_freeblks; @@ -194,17 +194,76 @@ xfs_free_perag( XFS_IS_CORRUPT(pag->pag_mount, atomic_read(&pag->pag_ref) != 0); cancel_delayed_work_sync(&pag->pag_blockgc_work); - xfs_iunlink_destroy(pag); xfs_buf_hash_destroy(pag); call_rcu(&pag->rcu_head, __xfs_free_perag); } } +/* Find the size of the AG, in blocks. */ +static xfs_agblock_t +__xfs_ag_block_count( + struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agnumber_t agcount, + xfs_rfsblock_t dblocks) +{ + ASSERT(agno < agcount); + + if (agno < agcount - 1) + return mp->m_sb.sb_agblocks; + return dblocks - (agno * mp->m_sb.sb_agblocks); +} + +xfs_agblock_t +xfs_ag_block_count( + struct xfs_mount *mp, + xfs_agnumber_t agno) +{ + return __xfs_ag_block_count(mp, agno, mp->m_sb.sb_agcount, + mp->m_sb.sb_dblocks); +} + +/* Calculate the first and last possible inode number in an AG. */ +static void +__xfs_agino_range( + struct xfs_mount *mp, + xfs_agblock_t eoag, + xfs_agino_t *first, + xfs_agino_t *last) +{ + xfs_agblock_t bno; + + /* + * Calculate the first inode, which will be in the first + * cluster-aligned block after the AGFL. + */ + bno = round_up(XFS_AGFL_BLOCK(mp) + 1, M_IGEO(mp)->cluster_align); + *first = XFS_AGB_TO_AGINO(mp, bno); + + /* + * Calculate the last inode, which will be at the end of the + * last (aligned) cluster that can be allocated in the AG. + */ + bno = round_down(eoag, M_IGEO(mp)->cluster_align); + *last = XFS_AGB_TO_AGINO(mp, bno) - 1; +} + +void +xfs_agino_range( + struct xfs_mount *mp, + xfs_agnumber_t agno, + xfs_agino_t *first, + xfs_agino_t *last) +{ + return __xfs_agino_range(mp, xfs_ag_block_count(mp, agno), first, last); +} + int xfs_initialize_perag( struct xfs_mount *mp, xfs_agnumber_t agcount, + xfs_rfsblock_t dblocks, xfs_agnumber_t *maxagi) { struct xfs_perag *pag; @@ -263,13 +322,18 @@ xfs_initialize_perag( if (error) goto out_remove_pag; - error = xfs_iunlink_init(pag); - if (error) - goto out_hash_destroy; - /* first new pag is fully initialized */ if (first_initialised == NULLAGNUMBER) first_initialised = index; + + /* + * Pre-calculated geometry + */ + pag->block_count = __xfs_ag_block_count(mp, index, agcount, + dblocks); + pag->min_block = XFS_AGFL_BLOCK(mp); + __xfs_agino_range(mp, pag->block_count, &pag->agino_min, + &pag->agino_max); } index = xfs_set_inode_alloc(mp, agcount); @@ -280,8 +344,6 @@ xfs_initialize_perag( mp->m_ag_prealloc_blocks = xfs_prealloc_blocks(mp); return 0; -out_hash_destroy: - xfs_buf_hash_destroy(pag); out_remove_pag: radix_tree_delete(&mp->m_perag_tree, index); out_free_pag: @@ -293,7 +355,6 @@ out_unwind_new_pags: if (!pag) break; xfs_buf_hash_destroy(pag); - xfs_iunlink_destroy(pag); kmem_free(pag); } return error; @@ -321,12 +382,6 @@ xfs_get_aghdr_buf( return 0; } -static inline bool is_log_ag(struct xfs_mount *mp, struct aghdr_init_data *id) -{ - return mp->m_sb.sb_logstart > 0 && - id->agno == XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart); -} - /* * Generic btree root block init function */ @@ -352,7 +407,7 @@ xfs_freesp_init_recs( arec = XFS_ALLOC_REC_ADDR(mp, XFS_BUF_TO_BLOCK(bp), 1); arec->ar_startblock = cpu_to_be32(mp->m_ag_prealloc_blocks); - if (is_log_ag(mp, id)) { + if (xfs_ag_contains_log(mp, id->agno)) { struct xfs_alloc_rec *nrec; xfs_agblock_t start = XFS_FSB_TO_AGBNO(mp, mp->m_sb.sb_logstart); @@ -479,7 +534,7 @@ xfs_rmaproot_init( } /* account for the log space */ - if (is_log_ag(mp, id)) { + if (xfs_ag_contains_log(mp, id->agno)) { rrec = XFS_RMAP_REC_ADDR(block, be16_to_cpu(block->bb_numrecs) + 1); rrec->rm_startblock = cpu_to_be32( @@ -550,7 +605,7 @@ xfs_agfblock_init( agf->agf_refcount_blocks = cpu_to_be32(1); } - if (is_log_ag(mp, id)) { + if (xfs_ag_contains_log(mp, id->agno)) { int64_t logblocks = mp->m_sb.sb_logblocks; be32_add_cpu(&agf->agf_freeblks, -logblocks); @@ -761,11 +816,11 @@ xfs_ag_init_headers( int xfs_ag_shrink_space( - struct xfs_mount *mp, + struct xfs_perag *pag, struct xfs_trans **tpp, - xfs_agnumber_t agno, xfs_extlen_t delta) { + struct xfs_mount *mp = pag->pag_mount; struct xfs_alloc_arg args = { .tp = *tpp, .mp = mp, @@ -782,14 +837,14 @@ xfs_ag_shrink_space( xfs_agblock_t aglen; int error, err2; - ASSERT(agno == mp->m_sb.sb_agcount - 1); - error = xfs_ialloc_read_agi(mp, *tpp, agno, &agibp); + ASSERT(pag->pag_agno == mp->m_sb.sb_agcount - 1); + error = xfs_ialloc_read_agi(pag, *tpp, &agibp); if (error) return error; agi = agibp->b_addr; - error = xfs_alloc_read_agf(mp, *tpp, agno, 0, &agfbp); + error = xfs_alloc_read_agf(pag, *tpp, 0, &agfbp); if (error) return error; @@ -801,13 +856,14 @@ xfs_ag_shrink_space( if (delta >= aglen) return -EINVAL; - args.fsbno = XFS_AGB_TO_FSB(mp, agno, aglen - delta); + args.fsbno = XFS_AGB_TO_FSB(mp, pag->pag_agno, aglen - delta); /* * Make sure that the last inode cluster cannot overlap with the new * end of the AG, even if it's sparse. */ - error = xfs_ialloc_check_shrink(*tpp, agno, agibp, aglen - delta); + error = xfs_ialloc_check_shrink(*tpp, pag->pag_agno, agibp, + aglen - delta); if (error) return error; @@ -815,7 +871,7 @@ xfs_ag_shrink_space( * Disable perag reservations so it doesn't cause the allocation request * to fail. We'll reestablish reservation before we return. */ - error = xfs_ag_resv_free(agibp->b_pag); + error = xfs_ag_resv_free(pag); if (error) return error; @@ -844,7 +900,7 @@ xfs_ag_shrink_space( be32_add_cpu(&agi->agi_length, -delta); be32_add_cpu(&agf->agf_length, -delta); - err2 = xfs_ag_resv_init(agibp->b_pag, *tpp); + err2 = xfs_ag_resv_init(pag, *tpp); if (err2) { be32_add_cpu(&agi->agi_length, delta); be32_add_cpu(&agf->agf_length, delta); @@ -868,8 +924,9 @@ xfs_ag_shrink_space( xfs_ialloc_log_agi(*tpp, agibp, XFS_AGI_LENGTH); xfs_alloc_log_agf(*tpp, agfbp, XFS_AGF_LENGTH); return 0; + resv_init_out: - err2 = xfs_ag_resv_init(agibp->b_pag, *tpp); + err2 = xfs_ag_resv_init(pag, *tpp); if (!err2) return error; resv_err: @@ -883,9 +940,8 @@ resv_err: */ int xfs_ag_extend_space( - struct xfs_mount *mp, + struct xfs_perag *pag, struct xfs_trans *tp, - struct aghdr_init_data *id, xfs_extlen_t len) { struct xfs_buf *bp; @@ -893,23 +949,20 @@ xfs_ag_extend_space( struct xfs_agf *agf; int error; - /* - * Change the agi length. - */ - error = xfs_ialloc_read_agi(mp, tp, id->agno, &bp); + ASSERT(pag->pag_agno == pag->pag_mount->m_sb.sb_agcount - 1); + + error = xfs_ialloc_read_agi(pag, tp, &bp); if (error) return error; agi = bp->b_addr; be32_add_cpu(&agi->agi_length, len); - ASSERT(id->agno == mp->m_sb.sb_agcount - 1 || - be32_to_cpu(agi->agi_length) == mp->m_sb.sb_agblocks); xfs_ialloc_log_agi(tp, bp, XFS_AGI_LENGTH); /* * Change agf length. */ - error = xfs_alloc_read_agf(mp, tp, id->agno, 0, &bp); + error = xfs_alloc_read_agf(pag, tp, 0, &bp); if (error) return error; @@ -924,49 +977,49 @@ xfs_ag_extend_space( * XFS_RMAP_OINFO_SKIP_UPDATE is used here to tell the rmap btree that * this doesn't actually exist in the rmap btree. */ - error = xfs_rmap_free(tp, bp, bp->b_pag, - be32_to_cpu(agf->agf_length) - len, + error = xfs_rmap_free(tp, bp, pag, be32_to_cpu(agf->agf_length) - len, len, &XFS_RMAP_OINFO_SKIP_UPDATE); if (error) return error; - return xfs_free_extent(tp, XFS_AGB_TO_FSB(mp, id->agno, + error = xfs_free_extent(tp, XFS_AGB_TO_FSB(pag->pag_mount, pag->pag_agno, be32_to_cpu(agf->agf_length) - len), len, &XFS_RMAP_OINFO_SKIP_UPDATE, XFS_AG_RESV_NONE); + if (error) + return error; + + /* Update perag geometry */ + pag->block_count = be32_to_cpu(agf->agf_length); + __xfs_agino_range(pag->pag_mount, pag->block_count, &pag->agino_min, + &pag->agino_max); + return 0; } /* Retrieve AG geometry. */ int xfs_ag_get_geometry( - struct xfs_mount *mp, - xfs_agnumber_t agno, + struct xfs_perag *pag, struct xfs_ag_geometry *ageo) { struct xfs_buf *agi_bp; struct xfs_buf *agf_bp; struct xfs_agi *agi; struct xfs_agf *agf; - struct xfs_perag *pag; unsigned int freeblks; int error; - if (agno >= mp->m_sb.sb_agcount) - return -EINVAL; - /* Lock the AG headers. */ - error = xfs_ialloc_read_agi(mp, NULL, agno, &agi_bp); + error = xfs_ialloc_read_agi(pag, NULL, &agi_bp); if (error) return error; - error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agf_bp); + error = xfs_alloc_read_agf(pag, NULL, 0, &agf_bp); if (error) goto out_agi; - pag = agi_bp->b_pag; - /* Fill out form. */ memset(ageo, 0, sizeof(*ageo)); - ageo->ag_number = agno; + ageo->ag_number = pag->pag_agno; agi = agi_bp->b_addr; ageo->ag_icount = be32_to_cpu(agi->agi_count); diff --git a/fs/xfs/libxfs/xfs_ag.h b/fs/xfs/libxfs/xfs_ag.h index e411d51c2589..517a138faa66 100644 --- a/fs/xfs/libxfs/xfs_ag.h +++ b/fs/xfs/libxfs/xfs_ag.h @@ -67,6 +67,12 @@ struct xfs_perag { /* for rcu-safe freeing */ struct rcu_head rcu_head; + /* Precalculated geometry info */ + xfs_agblock_t block_count; + xfs_agblock_t min_block; + xfs_agino_t agino_min; + xfs_agino_t agino_max; + #ifdef __KERNEL__ /* -- kernel only structures below this line -- */ @@ -97,17 +103,11 @@ struct xfs_perag { /* background prealloc block trimming */ struct delayed_work pag_blockgc_work; - /* - * Unlinked inode information. This incore information reflects - * data stored in the AGI, so callers must hold the AGI buffer lock - * or have some other means to control concurrency. - */ - struct rhashtable pagi_unlinked_hash; #endif /* __KERNEL__ */ }; int xfs_initialize_perag(struct xfs_mount *mp, xfs_agnumber_t agcount, - xfs_agnumber_t *maxagi); + xfs_rfsblock_t dcount, xfs_agnumber_t *maxagi); int xfs_initialize_perag_data(struct xfs_mount *mp, xfs_agnumber_t agno); void xfs_free_perag(struct xfs_mount *mp); @@ -116,6 +116,56 @@ struct xfs_perag *xfs_perag_get_tag(struct xfs_mount *mp, xfs_agnumber_t agno, unsigned int tag); void xfs_perag_put(struct xfs_perag *pag); +/* + * Per-ag geometry infomation and validation + */ +xfs_agblock_t xfs_ag_block_count(struct xfs_mount *mp, xfs_agnumber_t agno); +void xfs_agino_range(struct xfs_mount *mp, xfs_agnumber_t agno, + xfs_agino_t *first, xfs_agino_t *last); + +static inline bool +xfs_verify_agbno(struct xfs_perag *pag, xfs_agblock_t agbno) +{ + if (agbno >= pag->block_count) + return false; + if (agbno <= pag->min_block) + return false; + return true; +} + +/* + * Verify that an AG inode number pointer neither points outside the AG + * nor points at static metadata. + */ +static inline bool +xfs_verify_agino(struct xfs_perag *pag, xfs_agino_t agino) +{ + if (agino < pag->agino_min) + return false; + if (agino > pag->agino_max) + return false; + return true; +} + +/* + * Verify that an AG inode number pointer neither points outside the AG + * nor points at static metadata, or is NULLAGINO. + */ +static inline bool +xfs_verify_agino_or_null(struct xfs_perag *pag, xfs_agino_t agino) +{ + if (agino == NULLAGINO) + return true; + return xfs_verify_agino(pag, agino); +} + +static inline bool +xfs_ag_contains_log(struct xfs_mount *mp, xfs_agnumber_t agno) +{ + return mp->m_sb.sb_logstart > 0 && + agno == XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart); +} + /* * Perag iteration APIs */ @@ -168,11 +218,10 @@ struct aghdr_init_data { }; int xfs_ag_init_headers(struct xfs_mount *mp, struct aghdr_init_data *id); -int xfs_ag_shrink_space(struct xfs_mount *mp, struct xfs_trans **tpp, - xfs_agnumber_t agno, xfs_extlen_t delta); -int xfs_ag_extend_space(struct xfs_mount *mp, struct xfs_trans *tp, - struct aghdr_init_data *id, xfs_extlen_t len); -int xfs_ag_get_geometry(struct xfs_mount *mp, xfs_agnumber_t agno, - struct xfs_ag_geometry *ageo); +int xfs_ag_shrink_space(struct xfs_perag *pag, struct xfs_trans **tpp, + xfs_extlen_t delta); +int xfs_ag_extend_space(struct xfs_perag *pag, struct xfs_trans *tp, + xfs_extlen_t len); +int xfs_ag_get_geometry(struct xfs_perag *pag, struct xfs_ag_geometry *ageo); #endif /* __LIBXFS_AG_H */ diff --git a/fs/xfs/libxfs/xfs_ag_resv.c b/fs/xfs/libxfs/xfs_ag_resv.c index fe94058d4e9e..5af123d13a63 100644 --- a/fs/xfs/libxfs/xfs_ag_resv.c +++ b/fs/xfs/libxfs/xfs_ag_resv.c @@ -322,7 +322,7 @@ out: * address. */ if (has_resv) { - error2 = xfs_alloc_pagf_init(mp, tp, pag->pag_agno, 0); + error2 = xfs_alloc_read_agf(pag, tp, 0, NULL); if (error2) return error2; diff --git a/fs/xfs/libxfs/xfs_alloc.c b/fs/xfs/libxfs/xfs_alloc.c index d3f2886fdc08..e2bdf089c0a3 100644 --- a/fs/xfs/libxfs/xfs_alloc.c +++ b/fs/xfs/libxfs/xfs_alloc.c @@ -84,7 +84,7 @@ xfs_prealloc_blocks( /* * The number of blocks per AG that we withhold from xfs_mod_fdblocks to * guarantee that we can refill the AGFL prior to allocating space in a nearly - * full AG. Although the the space described by the free space btrees, the + * full AG. Although the space described by the free space btrees, the * blocks used by the freesp btrees themselves, and the blocks owned by the * AGFL are counted in the ondisk fdblocks, it's a mistake to let the ondisk * free space in the AG drop so low that the free space btrees cannot refill an @@ -248,7 +248,7 @@ xfs_alloc_get_rec( int *stat) /* output: success/failure */ { struct xfs_mount *mp = cur->bc_mp; - xfs_agnumber_t agno = cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = cur->bc_ag.pag; union xfs_btree_rec *rec; int error; @@ -263,11 +263,11 @@ xfs_alloc_get_rec( goto out_bad_rec; /* check for valid extent range, including overflow */ - if (!xfs_verify_agbno(mp, agno, *bno)) + if (!xfs_verify_agbno(pag, *bno)) goto out_bad_rec; if (*bno > *bno + *len) goto out_bad_rec; - if (!xfs_verify_agbno(mp, agno, *bno + *len - 1)) + if (!xfs_verify_agbno(pag, *bno + *len - 1)) goto out_bad_rec; return 0; @@ -275,7 +275,8 @@ xfs_alloc_get_rec( out_bad_rec: xfs_warn(mp, "%s Freespace BTree record corruption in AG %d detected!", - cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size", agno); + cur->bc_btnum == XFS_BTNUM_BNO ? "Block" : "Size", + pag->pag_agno); xfs_warn(mp, "start block 0x%x block count 0x%x", *bno, *len); return -EFSCORRUPTED; @@ -703,20 +704,19 @@ const struct xfs_buf_ops xfs_agfl_buf_ops = { /* * Read in the allocation group free block array. */ -int /* error */ +int xfs_alloc_read_agfl( - xfs_mount_t *mp, /* mount point structure */ - xfs_trans_t *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - struct xfs_buf **bpp) /* buffer for the ag free block array */ + struct xfs_perag *pag, + struct xfs_trans *tp, + struct xfs_buf **bpp) { - struct xfs_buf *bp; /* return value */ - int error; + struct xfs_mount *mp = pag->pag_mount; + struct xfs_buf *bp; + int error; - ASSERT(agno != NULLAGNUMBER); error = xfs_trans_read_buf( mp, tp, mp->m_ddev_targp, - XFS_AG_DADDR(mp, agno, XFS_AGFL_DADDR(mp)), + XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGFL_DADDR(mp)), XFS_FSS_TO_BB(mp, 1), 0, &bp, &xfs_agfl_buf_ops); if (error) return error; @@ -1075,7 +1075,8 @@ xfs_alloc_ag_vextent_small( be32_to_cpu(agf->agf_flcount) <= args->minleft) goto out; - error = xfs_alloc_get_freelist(args->tp, args->agbp, &fbno, 0); + error = xfs_alloc_get_freelist(args->pag, args->tp, args->agbp, + &fbno, 0); if (error) goto error; if (fbno == NULLAGBLOCK) @@ -2609,7 +2610,7 @@ xfs_alloc_fix_freelist( ASSERT(tp->t_flags & XFS_TRANS_PERM_LOG_RES); if (!pag->pagf_init) { - error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp); + error = xfs_alloc_read_agf(pag, tp, flags, &agbp); if (error) { /* Couldn't lock the AGF so skip this AG. */ if (error == -EAGAIN) @@ -2639,7 +2640,7 @@ xfs_alloc_fix_freelist( * Can fail if we're not blocking on locks, and it's held. */ if (!agbp) { - error = xfs_alloc_read_agf(mp, tp, args->agno, flags, &agbp); + error = xfs_alloc_read_agf(pag, tp, flags, &agbp); if (error) { /* Couldn't lock the AGF so skip this AG. */ if (error == -EAGAIN) @@ -2697,7 +2698,7 @@ xfs_alloc_fix_freelist( else targs.oinfo = XFS_RMAP_OINFO_AG; while (!(flags & XFS_ALLOC_FLAG_NOSHRINK) && pag->pagf_flcount > need) { - error = xfs_alloc_get_freelist(tp, agbp, &bno, 0); + error = xfs_alloc_get_freelist(pag, tp, agbp, &bno, 0); if (error) goto out_agbp_relse; @@ -2712,7 +2713,7 @@ xfs_alloc_fix_freelist( targs.alignment = targs.minlen = targs.prod = 1; targs.type = XFS_ALLOCTYPE_THIS_AG; targs.pag = pag; - error = xfs_alloc_read_agfl(mp, tp, targs.agno, &agflbp); + error = xfs_alloc_read_agfl(pag, tp, &agflbp); if (error) goto out_agbp_relse; @@ -2741,7 +2742,7 @@ xfs_alloc_fix_freelist( * Put each allocated block on the list. */ for (bno = targs.agbno; bno < targs.agbno + targs.len; bno++) { - error = xfs_alloc_put_freelist(tp, agbp, + error = xfs_alloc_put_freelist(pag, tp, agbp, agflbp, bno, 0); if (error) goto out_agflbp_relse; @@ -2767,6 +2768,7 @@ out_no_agbp: */ int xfs_alloc_get_freelist( + struct xfs_perag *pag, struct xfs_trans *tp, struct xfs_buf *agbp, xfs_agblock_t *bnop, @@ -2779,7 +2781,6 @@ xfs_alloc_get_freelist( int error; uint32_t logflags; struct xfs_mount *mp = tp->t_mountp; - struct xfs_perag *pag; /* * Freelist is empty, give up. @@ -2791,8 +2792,7 @@ xfs_alloc_get_freelist( /* * Read the array of free blocks. */ - error = xfs_alloc_read_agfl(mp, tp, be32_to_cpu(agf->agf_seqno), - &agflbp); + error = xfs_alloc_read_agfl(pag, tp, &agflbp); if (error) return error; @@ -2807,7 +2807,6 @@ xfs_alloc_get_freelist( if (be32_to_cpu(agf->agf_flfirst) == xfs_agfl_size(mp)) agf->agf_flfirst = 0; - pag = agbp->b_pag; ASSERT(!pag->pagf_agflreset); be32_add_cpu(&agf->agf_flcount, -1); pag->pagf_flcount--; @@ -2867,30 +2866,12 @@ xfs_alloc_log_agf( xfs_trans_log_buf(tp, bp, (uint)first, (uint)last); } -/* - * Interface for inode allocation to force the pag data to be initialized. - */ -int /* error */ -xfs_alloc_pagf_init( - xfs_mount_t *mp, /* file system mount structure */ - xfs_trans_t *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - int flags) /* XFS_ALLOC_FLAGS_... */ -{ - struct xfs_buf *bp; - int error; - - error = xfs_alloc_read_agf(mp, tp, agno, flags, &bp); - if (!error) - xfs_trans_brelse(tp, bp); - return error; -} - /* * Put the block on the freelist for the allocation group. */ int xfs_alloc_put_freelist( + struct xfs_perag *pag, struct xfs_trans *tp, struct xfs_buf *agbp, struct xfs_buf *agflbp, @@ -2899,21 +2880,22 @@ xfs_alloc_put_freelist( { struct xfs_mount *mp = tp->t_mountp; struct xfs_agf *agf = agbp->b_addr; - struct xfs_perag *pag; __be32 *blockp; int error; uint32_t logflags; __be32 *agfl_bno; int startoff; - if (!agflbp && (error = xfs_alloc_read_agfl(mp, tp, - be32_to_cpu(agf->agf_seqno), &agflbp))) - return error; + if (!agflbp) { + error = xfs_alloc_read_agfl(pag, tp, &agflbp); + if (error) + return error; + } + be32_add_cpu(&agf->agf_fllast, 1); if (be32_to_cpu(agf->agf_fllast) == xfs_agfl_size(mp)) agf->agf_fllast = 0; - pag = agbp->b_pag; ASSERT(!pag->pagf_agflreset); be32_add_cpu(&agf->agf_flcount, 1); pag->pagf_flcount++; @@ -3070,61 +3052,57 @@ const struct xfs_buf_ops xfs_agf_buf_ops = { /* * Read in the allocation group header (free/alloc section). */ -int /* error */ +int xfs_read_agf( - struct xfs_mount *mp, /* mount point structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - int flags, /* XFS_BUF_ */ - struct xfs_buf **bpp) /* buffer for the ag freelist header */ + struct xfs_perag *pag, + struct xfs_trans *tp, + int flags, + struct xfs_buf **agfbpp) { - int error; + struct xfs_mount *mp = pag->pag_mount; + int error; - trace_xfs_read_agf(mp, agno); + trace_xfs_read_agf(pag->pag_mount, pag->pag_agno); - ASSERT(agno != NULLAGNUMBER); error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, - XFS_AG_DADDR(mp, agno, XFS_AGF_DADDR(mp)), - XFS_FSS_TO_BB(mp, 1), flags, bpp, &xfs_agf_buf_ops); + XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGF_DADDR(mp)), + XFS_FSS_TO_BB(mp, 1), flags, agfbpp, &xfs_agf_buf_ops); if (error) return error; - ASSERT(!(*bpp)->b_error); - xfs_buf_set_ref(*bpp, XFS_AGF_REF); + xfs_buf_set_ref(*agfbpp, XFS_AGF_REF); return 0; } /* - * Read in the allocation group header (free/alloc section). + * Read in the allocation group header (free/alloc section) and initialise the + * perag structure if necessary. If the caller provides @agfbpp, then return the + * locked buffer to the caller, otherwise free it. */ -int /* error */ +int xfs_alloc_read_agf( - struct xfs_mount *mp, /* mount point structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - int flags, /* XFS_ALLOC_FLAG_... */ - struct xfs_buf **bpp) /* buffer for the ag freelist header */ + struct xfs_perag *pag, + struct xfs_trans *tp, + int flags, + struct xfs_buf **agfbpp) { - struct xfs_agf *agf; /* ag freelist header */ - struct xfs_perag *pag; /* per allocation group data */ + struct xfs_buf *agfbp; + struct xfs_agf *agf; int error; int allocbt_blks; - trace_xfs_alloc_read_agf(mp, agno); + trace_xfs_alloc_read_agf(pag->pag_mount, pag->pag_agno); /* We don't support trylock when freeing. */ ASSERT((flags & (XFS_ALLOC_FLAG_FREEING | XFS_ALLOC_FLAG_TRYLOCK)) != (XFS_ALLOC_FLAG_FREEING | XFS_ALLOC_FLAG_TRYLOCK)); - ASSERT(agno != NULLAGNUMBER); - error = xfs_read_agf(mp, tp, agno, + error = xfs_read_agf(pag, tp, (flags & XFS_ALLOC_FLAG_TRYLOCK) ? XBF_TRYLOCK : 0, - bpp); + &agfbp); if (error) return error; - ASSERT(!(*bpp)->b_error); - agf = (*bpp)->b_addr; - pag = (*bpp)->b_pag; + agf = agfbp->b_addr; if (!pag->pagf_init) { pag->pagf_freeblks = be32_to_cpu(agf->agf_freeblks); pag->pagf_btreeblks = be32_to_cpu(agf->agf_btreeblks); @@ -3138,7 +3116,7 @@ xfs_alloc_read_agf( be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAPi]); pag->pagf_refcount_level = be32_to_cpu(agf->agf_refcount_level); pag->pagf_init = 1; - pag->pagf_agflreset = xfs_agfl_needs_reset(mp, agf); + pag->pagf_agflreset = xfs_agfl_needs_reset(pag->pag_mount, agf); /* * Update the in-core allocbt counter. Filter out the rmapbt @@ -3148,13 +3126,14 @@ xfs_alloc_read_agf( * counter only tracks non-root blocks. */ allocbt_blks = pag->pagf_btreeblks; - if (xfs_has_rmapbt(mp)) + if (xfs_has_rmapbt(pag->pag_mount)) allocbt_blks -= be32_to_cpu(agf->agf_rmap_blocks) - 1; if (allocbt_blks > 0) - atomic64_add(allocbt_blks, &mp->m_allocbt_blks); + atomic64_add(allocbt_blks, + &pag->pag_mount->m_allocbt_blks); } #ifdef DEBUG - else if (!xfs_is_shutdown(mp)) { + else if (!xfs_is_shutdown(pag->pag_mount)) { ASSERT(pag->pagf_freeblks == be32_to_cpu(agf->agf_freeblks)); ASSERT(pag->pagf_btreeblks == be32_to_cpu(agf->agf_btreeblks)); ASSERT(pag->pagf_flcount == be32_to_cpu(agf->agf_flcount)); @@ -3165,6 +3144,10 @@ xfs_alloc_read_agf( be32_to_cpu(agf->agf_levels[XFS_BTNUM_CNTi])); } #endif + if (agfbpp) + *agfbpp = agfbp; + else + xfs_trans_brelse(tp, agfbp); return 0; } diff --git a/fs/xfs/libxfs/xfs_alloc.h b/fs/xfs/libxfs/xfs_alloc.h index 84ca09b2223f..2c3f762dfb58 100644 --- a/fs/xfs/libxfs/xfs_alloc.h +++ b/fs/xfs/libxfs/xfs_alloc.h @@ -95,6 +95,11 @@ xfs_extlen_t xfs_alloc_longest_free_extent(struct xfs_perag *pag, xfs_extlen_t need, xfs_extlen_t reserved); unsigned int xfs_alloc_min_freelist(struct xfs_mount *mp, struct xfs_perag *pag); +int xfs_alloc_get_freelist(struct xfs_perag *pag, struct xfs_trans *tp, + struct xfs_buf *agfbp, xfs_agblock_t *bnop, int btreeblk); +int xfs_alloc_put_freelist(struct xfs_perag *pag, struct xfs_trans *tp, + struct xfs_buf *agfbp, struct xfs_buf *agflbp, + xfs_agblock_t bno, int btreeblk); /* * Compute and fill in value of m_alloc_maxlevels. @@ -103,17 +108,6 @@ void xfs_alloc_compute_maxlevels( struct xfs_mount *mp); /* file system mount structure */ -/* - * Get a block from the freelist. - * Returns with the buffer for the block gotten. - */ -int /* error */ -xfs_alloc_get_freelist( - struct xfs_trans *tp, /* transaction pointer */ - struct xfs_buf *agbp, /* buffer containing the agf structure */ - xfs_agblock_t *bnop, /* block address retrieved from freelist */ - int btreeblk); /* destination is a AGF btree */ - /* * Log the given fields from the agf structure. */ @@ -123,38 +117,6 @@ xfs_alloc_log_agf( struct xfs_buf *bp, /* buffer for a.g. freelist header */ uint32_t fields);/* mask of fields to be logged (XFS_AGF_...) */ -/* - * Interface for inode allocation to force the pag data to be initialized. - */ -int /* error */ -xfs_alloc_pagf_init( - struct xfs_mount *mp, /* file system mount structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - int flags); /* XFS_ALLOC_FLAGS_... */ - -/* - * Put the block on the freelist for the allocation group. - */ -int /* error */ -xfs_alloc_put_freelist( - struct xfs_trans *tp, /* transaction pointer */ - struct xfs_buf *agbp, /* buffer for a.g. freelist header */ - struct xfs_buf *agflbp,/* buffer for a.g. free block array */ - xfs_agblock_t bno, /* block being freed */ - int btreeblk); /* owner was a AGF btree */ - -/* - * Read in the allocation group header (free/alloc section). - */ -int /* error */ -xfs_alloc_read_agf( - struct xfs_mount *mp, /* mount point structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - int flags, /* XFS_ALLOC_FLAG_... */ - struct xfs_buf **bpp); /* buffer for the ag freelist header */ - /* * Allocate an extent (variable-size). */ @@ -206,10 +168,12 @@ xfs_alloc_get_rec( xfs_extlen_t *len, /* output: length of extent */ int *stat); /* output: success/failure */ -int xfs_read_agf(struct xfs_mount *mp, struct xfs_trans *tp, - xfs_agnumber_t agno, int flags, struct xfs_buf **bpp); -int xfs_alloc_read_agfl(struct xfs_mount *mp, struct xfs_trans *tp, - xfs_agnumber_t agno, struct xfs_buf **bpp); +int xfs_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags, + struct xfs_buf **agfbpp); +int xfs_alloc_read_agf(struct xfs_perag *pag, struct xfs_trans *tp, int flags, + struct xfs_buf **agfbpp); +int xfs_alloc_read_agfl(struct xfs_perag *pag, struct xfs_trans *tp, + struct xfs_buf **bpp); int xfs_free_agfl_block(struct xfs_trans *, xfs_agnumber_t, xfs_agblock_t, struct xfs_buf *, struct xfs_owner_info *); int xfs_alloc_fix_freelist(struct xfs_alloc_arg *args, int flags); diff --git a/fs/xfs/libxfs/xfs_alloc_btree.c b/fs/xfs/libxfs/xfs_alloc_btree.c index 8c9f73cc0bee..549a3cba0234 100644 --- a/fs/xfs/libxfs/xfs_alloc_btree.c +++ b/fs/xfs/libxfs/xfs_alloc_btree.c @@ -60,8 +60,8 @@ xfs_allocbt_alloc_block( xfs_agblock_t bno; /* Allocate the new block from the freelist. If we can't, give up. */ - error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_ag.agbp, - &bno, 1); + error = xfs_alloc_get_freelist(cur->bc_ag.pag, cur->bc_tp, + cur->bc_ag.agbp, &bno, 1); if (error) return error; @@ -71,7 +71,7 @@ xfs_allocbt_alloc_block( } atomic64_inc(&cur->bc_mp->m_allocbt_blks); - xfs_extent_busy_reuse(cur->bc_mp, cur->bc_ag.agbp->b_pag, bno, 1, false); + xfs_extent_busy_reuse(cur->bc_mp, cur->bc_ag.pag, bno, 1, false); new->s = cpu_to_be32(bno); @@ -89,7 +89,8 @@ xfs_allocbt_free_block( int error; bno = xfs_daddr_to_agbno(cur->bc_mp, xfs_buf_daddr(bp)); - error = xfs_alloc_put_freelist(cur->bc_tp, agbp, NULL, bno, 1); + error = xfs_alloc_put_freelist(cur->bc_ag.pag, cur->bc_tp, agbp, NULL, + bno, 1); if (error) return error; diff --git a/fs/xfs/libxfs/xfs_attr.c b/fs/xfs/libxfs/xfs_attr.c index 224649a76cbb..e28d93d232de 100644 --- a/fs/xfs/libxfs/xfs_attr.c +++ b/fs/xfs/libxfs/xfs_attr.c @@ -67,12 +67,10 @@ int xfs_inode_hasattr( struct xfs_inode *ip) { - if (!XFS_IFORK_Q(ip)) + if (!xfs_inode_has_attr_fork(ip)) return 0; - if (!ip->i_afp) - return 0; - if (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && - ip->i_afp->if_nextents == 0) + if (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS && + ip->i_af.if_nextents == 0) return 0; return 1; } @@ -85,7 +83,7 @@ bool xfs_attr_is_leaf( struct xfs_inode *ip) { - struct xfs_ifork *ifp = ip->i_afp; + struct xfs_ifork *ifp = &ip->i_af; struct xfs_iext_cursor icur; struct xfs_bmbt_irec imap; @@ -231,7 +229,7 @@ xfs_attr_get_ilocked( if (!xfs_inode_hasattr(args->dp)) return -ENOATTR; - if (args->dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) + if (args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) return xfs_attr_shortform_getvalue(args); if (xfs_attr_is_leaf(args->dp)) return xfs_attr_leaf_get(args); @@ -354,7 +352,7 @@ xfs_attr_try_sf_addname( /* * Build initial attribute list (if required). */ - if (dp->i_afp->if_format == XFS_DINODE_FMT_EXTENTS) + if (dp->i_af.if_format == XFS_DINODE_FMT_EXTENTS) xfs_attr_shortform_create(args); error = xfs_attr_shortform_addname(args); @@ -864,7 +862,7 @@ xfs_attr_lookup( if (!xfs_inode_hasattr(dp)) return -ENOATTR; - if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) + if (dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) return xfs_attr_sf_findname(args, NULL, NULL); if (xfs_attr_is_leaf(dp)) { @@ -1001,7 +999,7 @@ xfs_attr_set( * If the inode doesn't have an attribute fork, add one. * (inode must not be locked when we call this routine) */ - if (XFS_IFORK_Q(dp) == 0) { + if (xfs_inode_has_attr_fork(dp) == 0) { int sf_size = sizeof(struct xfs_attr_sf_hdr) + xfs_attr_sf_entsize_byname(args->namelen, args->valuelen); @@ -1101,7 +1099,7 @@ static inline int xfs_attr_sf_totsize(struct xfs_inode *dp) { struct xfs_attr_shortform *sf; - sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data; + sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data; return be16_to_cpu(sf->hdr.totsize); } @@ -1558,7 +1556,7 @@ xfs_attr_node_get( * If not in a transaction, we have to release all the buffers. */ out_release: - for (i = 0; state != NULL && i < state->path.active; i++) { + for (i = 0; i < state->path.active; i++) { xfs_trans_brelse(args->trans, state->path.blk[i].bp); state->path.blk[i].bp = NULL; } diff --git a/fs/xfs/libxfs/xfs_attr.h b/fs/xfs/libxfs/xfs_attr.h index dfb47fa63c6d..81be9b3e4004 100644 --- a/fs/xfs/libxfs/xfs_attr.h +++ b/fs/xfs/libxfs/xfs_attr.h @@ -560,9 +560,9 @@ static inline bool xfs_attr_is_shortform( struct xfs_inode *ip) { - return ip->i_afp->if_format == XFS_DINODE_FMT_LOCAL || - (ip->i_afp->if_format == XFS_DINODE_FMT_EXTENTS && - ip->i_afp->if_nextents == 0); + return ip->i_af.if_format == XFS_DINODE_FMT_LOCAL || + (ip->i_af.if_format == XFS_DINODE_FMT_EXTENTS && + ip->i_af.if_nextents == 0); } static inline enum xfs_delattr_state @@ -573,10 +573,10 @@ xfs_attr_init_add_state(struct xfs_da_args *args) * next state, the attribute fork may be null. This can occur only occur * on a pure remove, but we grab the next state before we check if a * replace operation is being performed. If we are called from any other - * context, i_afp is guaranteed to exist. Hence if the attr fork is + * context, i_af is guaranteed to exist. Hence if the attr fork is * null, we were called from a pure remove operation and so we are done. */ - if (!args->dp->i_afp) + if (!xfs_inode_has_attr_fork(args->dp)) return XFS_DAS_DONE; args->op_flags |= XFS_DA_OP_ADDNAME; diff --git a/fs/xfs/libxfs/xfs_attr_leaf.c b/fs/xfs/libxfs/xfs_attr_leaf.c index 8f47396f8dd2..beee51ad75ce 100644 --- a/fs/xfs/libxfs/xfs_attr_leaf.c +++ b/fs/xfs/libxfs/xfs_attr_leaf.c @@ -590,7 +590,7 @@ xfs_attr_shortform_bytesfit( * to real extents, or the delalloc conversion will take care of the * literal area rebalancing. */ - if (bytes <= XFS_IFORK_ASIZE(dp)) + if (bytes <= xfs_inode_attr_fork_size(dp)) return dp->i_forkoff; /* @@ -682,7 +682,7 @@ xfs_attr_shortform_create( struct xfs_da_args *args) { struct xfs_inode *dp = args->dp; - struct xfs_ifork *ifp = dp->i_afp; + struct xfs_ifork *ifp = &dp->i_af; struct xfs_attr_sf_hdr *hdr; trace_xfs_attr_sf_create(args); @@ -719,7 +719,7 @@ xfs_attr_sf_findname( int end; int i; - sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data; + sf = (struct xfs_attr_shortform *)args->dp->i_af.if_u1.if_data; sfe = &sf->list[0]; end = sf->hdr.count; for (i = 0; i < end; sfe = xfs_attr_sf_nextentry(sfe), @@ -764,7 +764,7 @@ xfs_attr_shortform_add( mp = dp->i_mount; dp->i_forkoff = forkoff; - ifp = dp->i_afp; + ifp = &dp->i_af; ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; if (xfs_attr_sf_findname(args, &sfe, NULL) == -EEXIST) @@ -797,11 +797,9 @@ xfs_attr_fork_remove( struct xfs_inode *ip, struct xfs_trans *tp) { - ASSERT(ip->i_afp->if_nextents == 0); + ASSERT(ip->i_af.if_nextents == 0); - xfs_idestroy_fork(ip->i_afp); - kmem_cache_free(xfs_ifork_cache, ip->i_afp); - ip->i_afp = NULL; + xfs_ifork_zap_attr(ip); ip->i_forkoff = 0; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); } @@ -825,7 +823,7 @@ xfs_attr_sf_removename( dp = args->dp; mp = dp->i_mount; - sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data; + sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data; error = xfs_attr_sf_findname(args, &sfe, &base); @@ -889,7 +887,7 @@ xfs_attr_shortform_lookup(xfs_da_args_t *args) trace_xfs_attr_sf_lookup(args); - ifp = args->dp->i_afp; + ifp = &args->dp->i_af; ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; sfe = &sf->list[0]; @@ -917,8 +915,8 @@ xfs_attr_shortform_getvalue( struct xfs_attr_sf_entry *sfe; int i; - ASSERT(args->dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL); - sf = (struct xfs_attr_shortform *)args->dp->i_afp->if_u1.if_data; + ASSERT(args->dp->i_af.if_format == XFS_DINODE_FMT_LOCAL); + sf = (struct xfs_attr_shortform *)args->dp->i_af.if_u1.if_data; sfe = &sf->list[0]; for (i = 0; i < sf->hdr.count; sfe = xfs_attr_sf_nextentry(sfe), i++) { @@ -948,7 +946,7 @@ xfs_attr_shortform_to_leaf( trace_xfs_attr_sf_to_leaf(args); dp = args->dp; - ifp = dp->i_afp; + ifp = &dp->i_af; sf = (struct xfs_attr_shortform *)ifp->if_u1.if_data; size = be16_to_cpu(sf->hdr.totsize); tmpbuffer = kmem_alloc(size, 0); @@ -1055,8 +1053,8 @@ xfs_attr_shortform_verify( int i; int64_t size; - ASSERT(ip->i_afp->if_format == XFS_DINODE_FMT_LOCAL); - ifp = XFS_IFORK_PTR(ip, XFS_ATTR_FORK); + ASSERT(ip->i_af.if_format == XFS_DINODE_FMT_LOCAL); + ifp = xfs_ifork_ptr(ip, XFS_ATTR_FORK); sfp = (struct xfs_attr_shortform *)ifp->if_u1.if_data; size = ifp->if_bytes; diff --git a/fs/xfs/libxfs/xfs_attr_remote.c b/fs/xfs/libxfs/xfs_attr_remote.c index 7298c148f848..d440393b40eb 100644 --- a/fs/xfs/libxfs/xfs_attr_remote.c +++ b/fs/xfs/libxfs/xfs_attr_remote.c @@ -543,6 +543,7 @@ xfs_attr_rmtval_stale( { struct xfs_mount *mp = ip->i_mount; struct xfs_buf *bp; + int error; ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); @@ -550,14 +551,18 @@ xfs_attr_rmtval_stale( XFS_IS_CORRUPT(mp, map->br_startblock == HOLESTARTBLOCK)) return -EFSCORRUPTED; - bp = xfs_buf_incore(mp->m_ddev_targp, + error = xfs_buf_incore(mp->m_ddev_targp, XFS_FSB_TO_DADDR(mp, map->br_startblock), - XFS_FSB_TO_BB(mp, map->br_blockcount), incore_flags); - if (bp) { - xfs_buf_stale(bp); - xfs_buf_relse(bp); + XFS_FSB_TO_BB(mp, map->br_blockcount), + incore_flags, &bp); + if (error) { + if (error == -ENOENT) + return 0; + return error; } + xfs_buf_stale(bp); + xfs_buf_relse(bp); return 0; } diff --git a/fs/xfs/libxfs/xfs_bmap.c b/fs/xfs/libxfs/xfs_bmap.c index 6833110d1bd4..e56723dc9cd5 100644 --- a/fs/xfs/libxfs/xfs_bmap.c +++ b/fs/xfs/libxfs/xfs_bmap.c @@ -128,7 +128,7 @@ xfs_bmbt_lookup_first( */ static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); return whichfork != XFS_COW_FORK && ifp->if_format == XFS_DINODE_FMT_EXTENTS && @@ -140,7 +140,7 @@ static inline bool xfs_bmap_needs_btree(struct xfs_inode *ip, int whichfork) */ static inline bool xfs_bmap_wants_extents(struct xfs_inode *ip, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); return whichfork != XFS_COW_FORK && ifp->if_format == XFS_DINODE_FMT_BTREE && @@ -319,7 +319,7 @@ xfs_bmap_check_leaf_extents( int whichfork) /* data or attr fork */ { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_block *block; /* current btree block */ xfs_fsblock_t bno; /* block # of "block" */ struct xfs_buf *bp; /* buffer for "block" */ @@ -538,7 +538,7 @@ xfs_bmap_btree_to_extents( int *logflagsp, /* inode logging flags */ int whichfork) /* data or attr fork */ { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_mount *mp = ip->i_mount; struct xfs_btree_block *rblock = ifp->if_broot; struct xfs_btree_block *cblock;/* child btree block */ @@ -616,7 +616,7 @@ xfs_bmap_extents_to_btree( mp = ip->i_mount; ASSERT(whichfork != XFS_COW_FORK); - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(ifp->if_format == XFS_DINODE_FMT_EXTENTS); /* @@ -745,7 +745,7 @@ xfs_bmap_local_to_extents_empty( struct xfs_inode *ip, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(whichfork != XFS_COW_FORK); ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); @@ -785,7 +785,7 @@ xfs_bmap_local_to_extents( * So sending the data fork of a regular inode is invalid. */ ASSERT(!(S_ISREG(VFS_I(ip)->i_mode) && whichfork == XFS_DATA_FORK)); - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(ifp->if_format == XFS_DINODE_FMT_LOCAL); if (!ifp->if_bytes) { @@ -880,7 +880,7 @@ xfs_bmap_add_attrfork_btree( mp = ip->i_mount; - if (XFS_BMAP_BMDR_SPACE(block) <= XFS_IFORK_DSIZE(ip)) + if (XFS_BMAP_BMDR_SPACE(block) <= xfs_inode_data_fork_size(ip)) *flags |= XFS_ILOG_DBROOT; else { cur = xfs_bmbt_init_cursor(mp, tp, ip, XFS_DATA_FORK); @@ -920,7 +920,7 @@ xfs_bmap_add_attrfork_extents( int error; /* error return value */ if (ip->i_df.if_nextents * sizeof(struct xfs_bmbt_rec) <= - XFS_IFORK_DSIZE(ip)) + xfs_inode_data_fork_size(ip)) return 0; cur = NULL; error = xfs_bmap_extents_to_btree(tp, ip, &cur, 0, flags, @@ -951,7 +951,7 @@ xfs_bmap_add_attrfork_local( { struct xfs_da_args dargs; /* args for dir/attr code */ - if (ip->i_df.if_bytes <= XFS_IFORK_DSIZE(ip)) + if (ip->i_df.if_bytes <= xfs_inode_data_fork_size(ip)) return 0; if (S_ISDIR(VFS_I(ip)->i_mode)) { @@ -1023,7 +1023,7 @@ xfs_bmap_add_attrfork( int logflags; /* logging flags */ int error; /* error return value */ - ASSERT(XFS_IFORK_Q(ip) == 0); + ASSERT(xfs_inode_has_attr_fork(ip) == 0); mp = ip->i_mount; ASSERT(!XFS_NOT_DQATTACHED(mp, ip)); @@ -1034,16 +1034,15 @@ xfs_bmap_add_attrfork( rsvd, &tp); if (error) return error; - if (XFS_IFORK_Q(ip)) + if (xfs_inode_has_attr_fork(ip)) goto trans_cancel; xfs_trans_log_inode(tp, ip, XFS_ILOG_CORE); error = xfs_bmap_set_attrforkoff(ip, size, &version); if (error) goto trans_cancel; - ASSERT(ip->i_afp == NULL); - ip->i_afp = xfs_ifork_alloc(XFS_DINODE_FMT_EXTENTS, 0); + xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0); logflags = 0; switch (ip->i_df.if_format) { case XFS_DINODE_FMT_LOCAL: @@ -1116,7 +1115,7 @@ xfs_iread_bmbt_block( xfs_extnum_t num_recs; xfs_extnum_t j; int whichfork = cur->bc_ino.whichfork; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); block = xfs_btree_get_block(cur, level, &bp); @@ -1164,7 +1163,7 @@ xfs_iread_extents( int whichfork) { struct xfs_iread_state ir; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_mount *mp = ip->i_mount; struct xfs_btree_cur *cur; int error; @@ -1208,7 +1207,7 @@ xfs_bmap_first_unused( xfs_fileoff_t *first_unused, /* unused block */ int whichfork) /* data or attr fork */ { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec got; struct xfs_iext_cursor icur; xfs_fileoff_t lastaddr = 0; @@ -1255,7 +1254,7 @@ xfs_bmap_last_before( xfs_fileoff_t *last_block, /* last block */ int whichfork) /* data or attr fork */ { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec got; struct xfs_iext_cursor icur; int error; @@ -1289,7 +1288,7 @@ xfs_bmap_last_extent( struct xfs_bmbt_irec *rec, int *is_empty) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_iext_cursor icur; int error; @@ -1355,7 +1354,7 @@ xfs_bmap_last_offset( xfs_fileoff_t *last_block, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec rec; int is_empty; int error; @@ -1389,7 +1388,7 @@ xfs_bmap_add_extent_delay_real( int whichfork) { struct xfs_mount *mp = bma->ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(bma->ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); struct xfs_bmbt_irec *new = &bma->got; int error; /* error return value */ int i; /* temp state */ @@ -1955,7 +1954,7 @@ xfs_bmap_add_extent_unwritten_real( *logflagsp = 0; cur = *curp; - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(!isnullstartblock(new->br_startblock)); @@ -2480,7 +2479,7 @@ xfs_bmap_add_extent_hole_delay( uint32_t state = xfs_bmap_fork_to_state(whichfork); xfs_filblks_t temp; /* temp for indirect calculations */ - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(isnullstartblock(new->br_startblock)); /* @@ -2616,7 +2615,7 @@ xfs_bmap_add_extent_hole_real( int *logflagsp, uint32_t flags) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_mount *mp = ip->i_mount; struct xfs_btree_cur *cur = *curp; int error; /* error return value */ @@ -3185,7 +3184,8 @@ xfs_bmap_longest_free_extent( pag = xfs_perag_get(mp, ag); if (!pag->pagf_init) { - error = xfs_alloc_pagf_init(mp, tp, ag, XFS_ALLOC_FLAG_TRYLOCK); + error = xfs_alloc_read_agf(pag, tp, XFS_ALLOC_FLAG_TRYLOCK, + NULL); if (error) { /* Couldn't lock the AGF, so skip this AG. */ if (error == -EAGAIN) { @@ -3866,7 +3866,7 @@ xfs_bmapi_read( { struct xfs_mount *mp = ip->i_mount; int whichfork = xfs_bmapi_whichfork(flags); - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec got; xfs_fileoff_t obno; xfs_fileoff_t end; @@ -3959,7 +3959,7 @@ xfs_bmapi_reserve_delalloc( int eof) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); xfs_extlen_t alen; xfs_extlen_t indlen; int error; @@ -4086,7 +4086,7 @@ xfs_bmapi_allocate( { struct xfs_mount *mp = bma->ip->i_mount; int whichfork = xfs_bmapi_whichfork(bma->flags); - struct xfs_ifork *ifp = XFS_IFORK_PTR(bma->ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); int tmp_logflags = 0; int error; @@ -4185,7 +4185,7 @@ xfs_bmapi_convert_unwritten( uint32_t flags) { int whichfork = xfs_bmapi_whichfork(flags); - struct xfs_ifork *ifp = XFS_IFORK_PTR(bma->ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); int tmp_logflags = 0; int error; @@ -4262,7 +4262,7 @@ xfs_bmapi_minleft( struct xfs_inode *ip, int fork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, fork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, fork); if (tp && tp->t_firstblock != NULLFSBLOCK) return 0; @@ -4283,7 +4283,7 @@ xfs_bmapi_finish( int whichfork, int error) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(bma->ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(bma->ip, whichfork); if ((bma->logflags & xfs_ilog_fext(whichfork)) && ifp->if_format != XFS_DINODE_FMT_EXTENTS) @@ -4322,7 +4322,7 @@ xfs_bmapi_write( }; struct xfs_mount *mp = ip->i_mount; int whichfork = xfs_bmapi_whichfork(flags); - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); xfs_fileoff_t end; /* end of mapped file region */ bool eof = false; /* after the end of extents */ int error; /* error return */ @@ -4503,7 +4503,7 @@ xfs_bmapi_convert_delalloc( struct iomap *iomap, unsigned int *seq) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_mount *mp = ip->i_mount; xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); struct xfs_bmalloca bma = { NULL }; @@ -4640,7 +4640,7 @@ xfs_bmapi_remap( int whichfork = xfs_bmapi_whichfork(flags); int logflags = 0, error; - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(len > 0); ASSERT(len <= (xfs_filblks_t)XFS_MAX_BMBT_EXTLEN); ASSERT(xfs_isilocked(ip, XFS_ILOCK_EXCL)); @@ -4797,7 +4797,7 @@ xfs_bmap_del_extent_delay( struct xfs_bmbt_irec *del) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec new; int64_t da_old, da_new, da_diff = 0; xfs_fileoff_t del_endoff, got_endoff; @@ -4924,7 +4924,7 @@ xfs_bmap_del_extent_cow( struct xfs_bmbt_irec *del) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); struct xfs_bmbt_irec new; xfs_fileoff_t del_endoff, got_endoff; uint32_t state = BMAP_COWFORK; @@ -5022,7 +5022,7 @@ xfs_bmap_del_extent_real( mp = ip->i_mount; XFS_STATS_INC(mp, xs_del_exlist); - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); ASSERT(del->br_blockcount > 0); xfs_iext_get_extent(ifp, icur, &got); ASSERT(got.br_startoff <= del->br_startoff); @@ -5288,7 +5288,7 @@ __xfs_bunmapi( whichfork = xfs_bmapi_whichfork(flags); ASSERT(whichfork != XFS_COW_FORK); - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); if (XFS_IS_CORRUPT(mp, !xfs_ifork_has_extents(ifp))) return -EFSCORRUPTED; if (xfs_is_shutdown(mp)) @@ -5629,7 +5629,7 @@ xfs_bmse_merge( struct xfs_btree_cur *cur, int *logflags) /* output */ { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec new; xfs_filblks_t blockcount; int error, i; @@ -5750,7 +5750,7 @@ xfs_bmap_collapse_extents( { int whichfork = XFS_DATA_FORK; struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_cur *cur = NULL; struct xfs_bmbt_irec got, prev; struct xfs_iext_cursor icur; @@ -5865,7 +5865,7 @@ xfs_bmap_insert_extents( { int whichfork = XFS_DATA_FORK; struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_cur *cur = NULL; struct xfs_bmbt_irec got, next; struct xfs_iext_cursor icur; @@ -5965,7 +5965,7 @@ xfs_bmap_split_extent( xfs_fileoff_t split_fsb) { int whichfork = XFS_DATA_FORK; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_cur *cur = NULL; struct xfs_bmbt_irec got; struct xfs_bmbt_irec new; /* split extent */ diff --git a/fs/xfs/libxfs/xfs_bmap_btree.c b/fs/xfs/libxfs/xfs_bmap_btree.c index 2b77d45c215f..cfa052d40105 100644 --- a/fs/xfs/libxfs/xfs_bmap_btree.c +++ b/fs/xfs/libxfs/xfs_bmap_btree.c @@ -304,7 +304,7 @@ xfs_bmbt_get_minrecs( if (level == cur->bc_nlevels - 1) { struct xfs_ifork *ifp; - ifp = XFS_IFORK_PTR(cur->bc_ino.ip, + ifp = xfs_ifork_ptr(cur->bc_ino.ip, cur->bc_ino.whichfork); return xfs_bmbt_maxrecs(cur->bc_mp, @@ -322,7 +322,7 @@ xfs_bmbt_get_maxrecs( if (level == cur->bc_nlevels - 1) { struct xfs_ifork *ifp; - ifp = XFS_IFORK_PTR(cur->bc_ino.ip, + ifp = xfs_ifork_ptr(cur->bc_ino.ip, cur->bc_ino.whichfork); return xfs_bmbt_maxrecs(cur->bc_mp, @@ -550,7 +550,7 @@ xfs_bmbt_init_cursor( struct xfs_inode *ip, /* inode owning the btree */ int whichfork) /* data or attr fork */ { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_cur *cur; ASSERT(whichfork != XFS_COW_FORK); @@ -564,7 +564,7 @@ xfs_bmbt_init_cursor( if (xfs_has_crc(mp)) cur->bc_flags |= XFS_BTREE_CRC_BLOCKS; - cur->bc_ino.forksize = XFS_IFORK_SIZE(ip, whichfork); + cur->bc_ino.forksize = xfs_inode_fork_size(ip, whichfork); cur->bc_ino.ip = ip; cur->bc_ino.allocated = 0; cur->bc_ino.flags = 0; @@ -664,7 +664,7 @@ xfs_bmbt_change_owner( ASSERT(tp || buffer_list); ASSERT(!(tp && buffer_list)); - ASSERT(XFS_IFORK_PTR(ip, whichfork)->if_format == XFS_DINODE_FMT_BTREE); + ASSERT(xfs_ifork_ptr(ip, whichfork)->if_format == XFS_DINODE_FMT_BTREE); cur = xfs_bmbt_init_cursor(ip->i_mount, tp, ip, whichfork); cur->bc_ino.flags |= XFS_BTCUR_BMBT_INVALID_OWNER; diff --git a/fs/xfs/libxfs/xfs_btree.c b/fs/xfs/libxfs/xfs_btree.c index 2eecc49fc1b2..4c16c8c31fcb 100644 --- a/fs/xfs/libxfs/xfs_btree.c +++ b/fs/xfs/libxfs/xfs_btree.c @@ -91,10 +91,9 @@ xfs_btree_check_lblock_siblings( static inline xfs_failaddr_t xfs_btree_check_sblock_siblings( - struct xfs_mount *mp, + struct xfs_perag *pag, struct xfs_btree_cur *cur, int level, - xfs_agnumber_t agno, xfs_agblock_t agbno, __be32 dsibling) { @@ -110,7 +109,7 @@ xfs_btree_check_sblock_siblings( if (!xfs_btree_check_sptr(cur, sibling, level + 1)) return __this_address; } else { - if (!xfs_verify_agbno(mp, agno, sibling)) + if (!xfs_verify_agbno(pag, sibling)) return __this_address; } return NULL; @@ -195,11 +194,11 @@ __xfs_btree_check_sblock( struct xfs_buf *bp) { struct xfs_mount *mp = cur->bc_mp; + struct xfs_perag *pag = cur->bc_ag.pag; xfs_btnum_t btnum = cur->bc_btnum; int crc = xfs_has_crc(mp); xfs_failaddr_t fa; xfs_agblock_t agbno = NULLAGBLOCK; - xfs_agnumber_t agno = NULLAGNUMBER; if (crc) { if (!uuid_equal(&block->bb_u.s.bb_uuid, &mp->m_sb.sb_meta_uuid)) @@ -217,16 +216,14 @@ __xfs_btree_check_sblock( cur->bc_ops->get_maxrecs(cur, level)) return __this_address; - if (bp) { + if (bp) agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp)); - agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp)); - } - fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, agbno, + fa = xfs_btree_check_sblock_siblings(pag, cur, level, agbno, block->bb_u.s.bb_leftsib); if (!fa) - fa = xfs_btree_check_sblock_siblings(mp, cur, level, agno, - agbno, block->bb_u.s.bb_rightsib); + fa = xfs_btree_check_sblock_siblings(pag, cur, level, agbno, + block->bb_u.s.bb_rightsib); return fa; } @@ -288,7 +285,7 @@ xfs_btree_check_sptr( { if (level <= 0) return false; - return xfs_verify_agbno(cur->bc_mp, cur->bc_ag.pag->pag_agno, agbno); + return xfs_verify_agbno(cur->bc_ag.pag, agbno); } /* @@ -725,7 +722,7 @@ xfs_btree_ifork_ptr( if (cur->bc_flags & XFS_BTREE_STAGING) return cur->bc_ino.ifake->if_fork; - return XFS_IFORK_PTR(cur->bc_ino.ip, cur->bc_ino.whichfork); + return xfs_ifork_ptr(cur->bc_ino.ip, cur->bc_ino.whichfork); } /* @@ -3559,7 +3556,7 @@ xfs_btree_kill_iroot( { int whichfork = cur->bc_ino.whichfork; struct xfs_inode *ip = cur->bc_ino.ip; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_block *block; struct xfs_btree_block *cblock; union xfs_btree_key *kp; @@ -4595,7 +4592,6 @@ xfs_btree_sblock_verify( { struct xfs_mount *mp = bp->b_mount; struct xfs_btree_block *block = XFS_BUF_TO_BLOCK(bp); - xfs_agnumber_t agno; xfs_agblock_t agbno; xfs_failaddr_t fa; @@ -4604,12 +4600,11 @@ xfs_btree_sblock_verify( return __this_address; /* sibling pointer verification */ - agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp)); agbno = xfs_daddr_to_agbno(mp, xfs_buf_daddr(bp)); - fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno, + fa = xfs_btree_check_sblock_siblings(bp->b_pag, NULL, -1, agbno, block->bb_u.s.bb_leftsib); if (!fa) - fa = xfs_btree_check_sblock_siblings(mp, NULL, -1, agno, agbno, + fa = xfs_btree_check_sblock_siblings(bp->b_pag, NULL, -1, agbno, block->bb_u.s.bb_rightsib); return fa; } diff --git a/fs/xfs/libxfs/xfs_dir2.c b/fs/xfs/libxfs/xfs_dir2.c index 3cd51fa3837b..76eedc2756b3 100644 --- a/fs/xfs/libxfs/xfs_dir2.c +++ b/fs/xfs/libxfs/xfs_dir2.c @@ -193,7 +193,7 @@ xfs_dir_isempty( ASSERT(S_ISDIR(VFS_I(dp)->i_mode)); if (dp->i_disk_size == 0) /* might happen during shutdown. */ return 1; - if (dp->i_disk_size > XFS_IFORK_DSIZE(dp)) + if (dp->i_disk_size > xfs_inode_data_fork_size(dp)) return 0; sfp = (xfs_dir2_sf_hdr_t *)dp->i_df.if_u1.if_data; return !sfp->count; diff --git a/fs/xfs/libxfs/xfs_dir2_block.c b/fs/xfs/libxfs/xfs_dir2_block.c index df0869bba275..00f960a703b2 100644 --- a/fs/xfs/libxfs/xfs_dir2_block.c +++ b/fs/xfs/libxfs/xfs_dir2_block.c @@ -842,7 +842,7 @@ xfs_dir2_block_removename( * See if the size as a shortform is good enough. */ size = xfs_dir2_block_sfsize(dp, hdr, &sfh); - if (size > XFS_IFORK_DSIZE(dp)) + if (size > xfs_inode_data_fork_size(dp)) return 0; /* @@ -1055,7 +1055,7 @@ xfs_dir2_leaf_to_block( * Now see if the resulting block can be shrunken to shortform. */ size = xfs_dir2_block_sfsize(dp, hdr, &sfh); - if (size > XFS_IFORK_DSIZE(dp)) + if (size > xfs_inode_data_fork_size(dp)) return 0; return xfs_dir2_block_to_sf(args, dbp, size, &sfh); @@ -1071,7 +1071,7 @@ xfs_dir2_sf_to_block( struct xfs_trans *tp = args->trans; struct xfs_inode *dp = args->dp; struct xfs_mount *mp = dp->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(dp, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK); struct xfs_da_geometry *geo = args->geo; xfs_dir2_db_t blkno; /* dir-relative block # (0) */ xfs_dir2_data_hdr_t *hdr; /* block header */ diff --git a/fs/xfs/libxfs/xfs_dir2_sf.c b/fs/xfs/libxfs/xfs_dir2_sf.c index 5a97a87eaa20..003812fd7d35 100644 --- a/fs/xfs/libxfs/xfs_dir2_sf.c +++ b/fs/xfs/libxfs/xfs_dir2_sf.c @@ -237,7 +237,7 @@ xfs_dir2_block_sfsize( (i8count ? /* inumber */ count * XFS_INO64_SIZE : count * XFS_INO32_SIZE); - if (size > XFS_IFORK_DSIZE(dp)) + if (size > xfs_inode_data_fork_size(dp)) return size; /* size value is a failure */ } /* @@ -406,7 +406,7 @@ xfs_dir2_sf_addname( * Won't fit as shortform any more (due to size), * or the pick routine says it won't (due to offset values). */ - if (new_isize > XFS_IFORK_DSIZE(dp) || + if (new_isize > xfs_inode_data_fork_size(dp) || (pick = xfs_dir2_sf_addname_pick(args, objchange, &sfep, &offset)) == 0) { /* @@ -710,7 +710,7 @@ xfs_dir2_sf_verify( struct xfs_inode *ip) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); struct xfs_dir2_sf_hdr *sfp; struct xfs_dir2_sf_entry *sfep; struct xfs_dir2_sf_entry *next_sfep; @@ -1031,7 +1031,7 @@ xfs_dir2_sf_replace_needblock( newsize = dp->i_df.if_bytes + (sfp->count + 1) * XFS_INO64_DIFF; return inum > XFS_DIR2_MAX_SHORT_INUM && - sfp->i8count == 0 && newsize > XFS_IFORK_DSIZE(dp); + sfp->i8count == 0 && newsize > xfs_inode_data_fork_size(dp); } /* diff --git a/fs/xfs/libxfs/xfs_format.h b/fs/xfs/libxfs/xfs_format.h index afdfc8108c5f..b55bdfa9c8a8 100644 --- a/fs/xfs/libxfs/xfs_format.h +++ b/fs/xfs/libxfs/xfs_format.h @@ -704,7 +704,7 @@ struct xfs_agfl { * When the bigtime feature is enabled, ondisk inode timestamps become an * unsigned 64-bit nanoseconds counter. This means that the bigtime inode * timestamp epoch is the start of the classic timestamp range, which is - * Dec 31 20:45:52 UTC 1901. Because the epochs are not the same, callers + * Dec 13 20:45:52 UTC 1901. Because the epochs are not the same, callers * /must/ use the bigtime conversion functions when encoding and decoding raw * timestamps. */ diff --git a/fs/xfs/libxfs/xfs_ialloc.c b/fs/xfs/libxfs/xfs_ialloc.c index bf2f4bc89193..6cdfd64bc56b 100644 --- a/fs/xfs/libxfs/xfs_ialloc.c +++ b/fs/xfs/libxfs/xfs_ialloc.c @@ -105,7 +105,6 @@ xfs_inobt_get_rec( int *stat) { struct xfs_mount *mp = cur->bc_mp; - xfs_agnumber_t agno = cur->bc_ag.pag->pag_agno; union xfs_btree_rec *rec; int error; uint64_t realfree; @@ -116,7 +115,7 @@ xfs_inobt_get_rec( xfs_inobt_btrec_to_irec(mp, rec, irec); - if (!xfs_verify_agino(mp, agno, irec->ir_startino)) + if (!xfs_verify_agino(cur->bc_ag.pag, irec->ir_startino)) goto out_bad_rec; if (irec->ir_count < XFS_INODES_PER_HOLEMASK_BIT || irec->ir_count > XFS_INODES_PER_CHUNK) @@ -137,7 +136,8 @@ xfs_inobt_get_rec( out_bad_rec: xfs_warn(mp, "%s Inode BTree record corruption in AG %d detected!", - cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free", agno); + cur->bc_btnum == XFS_BTNUM_INO ? "Used" : "Free", + cur->bc_ag.pag->pag_agno); xfs_warn(mp, "start inode 0x%x, count 0x%x, free 0x%x freemask 0x%llx, holemask 0x%x", irec->ir_startino, irec->ir_count, irec->ir_freecount, @@ -1610,7 +1610,7 @@ xfs_dialloc_good_ag( return false; if (!pag->pagi_init) { - error = xfs_ialloc_pagi_init(mp, tp, pag->pag_agno); + error = xfs_ialloc_read_agi(pag, tp, NULL); if (error) return false; } @@ -1621,7 +1621,7 @@ xfs_dialloc_good_ag( return false; if (!pag->pagf_init) { - error = xfs_alloc_pagf_init(mp, tp, pag->pag_agno, flags); + error = xfs_alloc_read_agf(pag, tp, flags, NULL); if (error) return false; } @@ -1679,7 +1679,7 @@ xfs_dialloc_try_ag( * Then read in the AGI buffer and recheck with the AGI buffer * lock held. */ - error = xfs_ialloc_read_agi(pag->pag_mount, *tpp, pag->pag_agno, &agbp); + error = xfs_ialloc_read_agi(pag, *tpp, &agbp); if (error) return error; @@ -2169,7 +2169,7 @@ xfs_difree( /* * Get the allocation group header. */ - error = xfs_ialloc_read_agi(mp, tp, pag->pag_agno, &agbp); + error = xfs_ialloc_read_agi(pag, tp, &agbp); if (error) { xfs_warn(mp, "%s: xfs_ialloc_read_agi() returned error %d.", __func__, error); @@ -2215,7 +2215,7 @@ xfs_imap_lookup( int error; int i; - error = xfs_ialloc_read_agi(mp, tp, pag->pag_agno, &agbp); + error = xfs_ialloc_read_agi(pag, tp, &agbp); if (error) { xfs_alert(mp, "%s: xfs_ialloc_read_agi() returned error %d, agno %d", @@ -2571,47 +2571,48 @@ const struct xfs_buf_ops xfs_agi_buf_ops = { */ int xfs_read_agi( - struct xfs_mount *mp, /* file system mount structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - struct xfs_buf **bpp) /* allocation group hdr buf */ + struct xfs_perag *pag, + struct xfs_trans *tp, + struct xfs_buf **agibpp) { + struct xfs_mount *mp = pag->pag_mount; int error; - trace_xfs_read_agi(mp, agno); + trace_xfs_read_agi(pag->pag_mount, pag->pag_agno); - ASSERT(agno != NULLAGNUMBER); error = xfs_trans_read_buf(mp, tp, mp->m_ddev_targp, - XFS_AG_DADDR(mp, agno, XFS_AGI_DADDR(mp)), - XFS_FSS_TO_BB(mp, 1), 0, bpp, &xfs_agi_buf_ops); + XFS_AG_DADDR(mp, pag->pag_agno, XFS_AGI_DADDR(mp)), + XFS_FSS_TO_BB(mp, 1), 0, agibpp, &xfs_agi_buf_ops); if (error) return error; if (tp) - xfs_trans_buf_set_type(tp, *bpp, XFS_BLFT_AGI_BUF); + xfs_trans_buf_set_type(tp, *agibpp, XFS_BLFT_AGI_BUF); - xfs_buf_set_ref(*bpp, XFS_AGI_REF); + xfs_buf_set_ref(*agibpp, XFS_AGI_REF); return 0; } +/* + * Read in the agi and initialise the per-ag data. If the caller supplies a + * @agibpp, return the locked AGI buffer to them, otherwise release it. + */ int xfs_ialloc_read_agi( - struct xfs_mount *mp, /* file system mount structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - struct xfs_buf **bpp) /* allocation group hdr buf */ + struct xfs_perag *pag, + struct xfs_trans *tp, + struct xfs_buf **agibpp) { - struct xfs_agi *agi; /* allocation group header */ - struct xfs_perag *pag; /* per allocation group data */ + struct xfs_buf *agibp; + struct xfs_agi *agi; int error; - trace_xfs_ialloc_read_agi(mp, agno); + trace_xfs_ialloc_read_agi(pag->pag_mount, pag->pag_agno); - error = xfs_read_agi(mp, tp, agno, bpp); + error = xfs_read_agi(pag, tp, &agibp); if (error) return error; - agi = (*bpp)->b_addr; - pag = (*bpp)->b_pag; + agi = agibp->b_addr; if (!pag->pagi_init) { pag->pagi_freecount = be32_to_cpu(agi->agi_freecount); pag->pagi_count = be32_to_cpu(agi->agi_count); @@ -2623,27 +2624,11 @@ xfs_ialloc_read_agi( * we are in the middle of a forced shutdown. */ ASSERT(pag->pagi_freecount == be32_to_cpu(agi->agi_freecount) || - xfs_is_shutdown(mp)); - return 0; -} - -/* - * Read in the agi to initialise the per-ag data in the mount structure - */ -int -xfs_ialloc_pagi_init( - xfs_mount_t *mp, /* file system mount structure */ - xfs_trans_t *tp, /* transaction pointer */ - xfs_agnumber_t agno) /* allocation group number */ -{ - struct xfs_buf *bp = NULL; - int error; - - error = xfs_ialloc_read_agi(mp, tp, agno, &bp); - if (error) - return error; - if (bp) - xfs_trans_brelse(tp, bp); + xfs_is_shutdown(pag->pag_mount)); + if (agibpp) + *agibpp = agibp; + else + xfs_trans_brelse(tp, agibp); return 0; } @@ -2912,8 +2897,7 @@ xfs_ialloc_calc_rootino( * allocation group, or very odd geometries created by old mkfs * versions on very small filesystems. */ - if (mp->m_sb.sb_logstart && - XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart) == 0) + if (xfs_ag_contains_log(mp, 0)) first_bno += mp->m_sb.sb_logblocks; /* diff --git a/fs/xfs/libxfs/xfs_ialloc.h b/fs/xfs/libxfs/xfs_ialloc.h index a7705b6a1fd3..9bbbca6ac4ed 100644 --- a/fs/xfs/libxfs/xfs_ialloc.h +++ b/fs/xfs/libxfs/xfs_ialloc.h @@ -62,25 +62,10 @@ xfs_ialloc_log_agi( struct xfs_buf *bp, /* allocation group header buffer */ uint32_t fields); /* bitmask of fields to log */ -/* - * Read in the allocation group header (inode allocation section) - */ -int /* error */ -xfs_ialloc_read_agi( - struct xfs_mount *mp, /* file system mount structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno, /* allocation group number */ - struct xfs_buf **bpp); /* allocation group hdr buf */ - -/* - * Read in the allocation group header to initialise the per-ag data - * in the mount structure - */ -int -xfs_ialloc_pagi_init( - struct xfs_mount *mp, /* file system mount structure */ - struct xfs_trans *tp, /* transaction pointer */ - xfs_agnumber_t agno); /* allocation group number */ +int xfs_read_agi(struct xfs_perag *pag, struct xfs_trans *tp, + struct xfs_buf **agibpp); +int xfs_ialloc_read_agi(struct xfs_perag *pag, struct xfs_trans *tp, + struct xfs_buf **agibpp); /* * Lookup a record by ino in the btree given by cur. @@ -102,8 +87,6 @@ int xfs_ialloc_inode_init(struct xfs_mount *mp, struct xfs_trans *tp, xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_agblock_t length, unsigned int gen); -int xfs_read_agi(struct xfs_mount *mp, struct xfs_trans *tp, - xfs_agnumber_t agno, struct xfs_buf **bpp); union xfs_btree_rec; void xfs_inobt_btrec_to_irec(struct xfs_mount *mp, diff --git a/fs/xfs/libxfs/xfs_ialloc_btree.c b/fs/xfs/libxfs/xfs_ialloc_btree.c index b2ad2fdc40f5..8c83e265770c 100644 --- a/fs/xfs/libxfs/xfs_ialloc_btree.c +++ b/fs/xfs/libxfs/xfs_ialloc_btree.c @@ -683,10 +683,10 @@ xfs_inobt_rec_check_count( static xfs_extlen_t xfs_inobt_max_size( - struct xfs_mount *mp, - xfs_agnumber_t agno) + struct xfs_perag *pag) { - xfs_agblock_t agblocks = xfs_ag_block_count(mp, agno); + struct xfs_mount *mp = pag->pag_mount; + xfs_agblock_t agblocks = pag->block_count; /* Bail out if we're uninitialized, which can happen in mkfs. */ if (M_IGEO(mp)->inobt_mxr[0] == 0) @@ -697,8 +697,7 @@ xfs_inobt_max_size( * never be available for the kinds of things that would require btree * expansion. We therefore can pretend the space isn't there. */ - if (mp->m_sb.sb_logstart && - XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart) == agno) + if (xfs_ag_contains_log(mp, pag->pag_agno)) agblocks -= mp->m_sb.sb_logblocks; return xfs_btree_calc_size(M_IGEO(mp)->inobt_mnr, @@ -722,7 +721,7 @@ xfs_inobt_cur( ASSERT(*agi_bpp == NULL); ASSERT(*curpp == NULL); - error = xfs_ialloc_read_agi(mp, tp, pag->pag_agno, agi_bpp); + error = xfs_ialloc_read_agi(pag, tp, agi_bpp); if (error) return error; @@ -757,16 +756,15 @@ xfs_inobt_count_blocks( /* Read finobt block count from AGI header. */ static int xfs_finobt_read_blocks( - struct xfs_mount *mp, - struct xfs_trans *tp, struct xfs_perag *pag, + struct xfs_trans *tp, xfs_extlen_t *tree_blocks) { struct xfs_buf *agbp; struct xfs_agi *agi; int error; - error = xfs_ialloc_read_agi(mp, tp, pag->pag_agno, &agbp); + error = xfs_ialloc_read_agi(pag, tp, &agbp); if (error) return error; @@ -794,14 +792,14 @@ xfs_finobt_calc_reserves( return 0; if (xfs_has_inobtcounts(mp)) - error = xfs_finobt_read_blocks(mp, tp, pag, &tree_len); + error = xfs_finobt_read_blocks(pag, tp, &tree_len); else error = xfs_inobt_count_blocks(mp, tp, pag, XFS_BTNUM_FINO, &tree_len); if (error) return error; - *ask += xfs_inobt_max_size(mp, pag->pag_agno); + *ask += xfs_inobt_max_size(pag); *used += tree_len; return 0; } diff --git a/fs/xfs/libxfs/xfs_inode_buf.c b/fs/xfs/libxfs/xfs_inode_buf.c index 3b1b63f9d886..758aacd8166b 100644 --- a/fs/xfs/libxfs/xfs_inode_buf.c +++ b/fs/xfs/libxfs/xfs_inode_buf.c @@ -10,6 +10,7 @@ #include "xfs_log_format.h" #include "xfs_trans_resv.h" #include "xfs_mount.h" +#include "xfs_ag.h" #include "xfs_inode.h" #include "xfs_errortag.h" #include "xfs_error.h" @@ -41,14 +42,12 @@ xfs_inode_buf_verify( bool readahead) { struct xfs_mount *mp = bp->b_mount; - xfs_agnumber_t agno; int i; int ni; /* * Validate the magic number and version of every inode in the buffer */ - agno = xfs_daddr_to_agno(mp, xfs_buf_daddr(bp)); ni = XFS_BB_TO_FSB(mp, bp->b_length) * mp->m_sb.sb_inopblock; for (i = 0; i < ni; i++) { struct xfs_dinode *dip; @@ -59,7 +58,7 @@ xfs_inode_buf_verify( unlinked_ino = be32_to_cpu(dip->di_next_unlinked); di_ok = xfs_verify_magic16(bp, dip->di_magic) && xfs_dinode_good_version(mp, dip->di_version) && - xfs_verify_agino_or_null(mp, agno, unlinked_ino); + xfs_verify_agino_or_null(bp->b_pag, unlinked_ino); if (unlikely(XFS_TEST_ERROR(!di_ok, mp, XFS_ERRTAG_ITOBP_INOTOBP))) { if (readahead) { @@ -178,7 +177,6 @@ xfs_inode_from_disk( xfs_failaddr_t fa; ASSERT(ip->i_cowfp == NULL); - ASSERT(ip->i_afp == NULL); fa = xfs_dinode_verify(ip->i_mount, ip->i_ino, from); if (fa) { @@ -230,7 +228,8 @@ xfs_inode_from_disk( ip->i_nblocks = be64_to_cpu(from->di_nblocks); ip->i_extsize = be32_to_cpu(from->di_extsize); ip->i_forkoff = from->di_forkoff; - ip->i_diflags = be16_to_cpu(from->di_flags); + ip->i_diflags = be16_to_cpu(from->di_flags); + ip->i_next_unlinked = be32_to_cpu(from->di_next_unlinked); if (from->di_dmevmask || from->di_dmstate) xfs_iflags_set(ip, XFS_IPRESERVE_DM_FIELDS); @@ -286,7 +285,7 @@ xfs_inode_to_disk_iext_counters( { if (xfs_inode_has_large_extent_counts(ip)) { to->di_big_nextents = cpu_to_be64(xfs_ifork_nextents(&ip->i_df)); - to->di_big_anextents = cpu_to_be32(xfs_ifork_nextents(ip->i_afp)); + to->di_big_anextents = cpu_to_be32(xfs_ifork_nextents(&ip->i_af)); /* * We might be upgrading the inode to use larger extent counters * than was previously used. Hence zero the unused field. @@ -294,7 +293,7 @@ xfs_inode_to_disk_iext_counters( to->di_nrext64_pad = cpu_to_be16(0); } else { to->di_nextents = cpu_to_be32(xfs_ifork_nextents(&ip->i_df)); - to->di_anextents = cpu_to_be16(xfs_ifork_nextents(ip->i_afp)); + to->di_anextents = cpu_to_be16(xfs_ifork_nextents(&ip->i_af)); } } @@ -326,7 +325,7 @@ xfs_inode_to_disk( to->di_nblocks = cpu_to_be64(ip->i_nblocks); to->di_extsize = cpu_to_be32(ip->i_extsize); to->di_forkoff = ip->i_forkoff; - to->di_aformat = xfs_ifork_format(ip->i_afp); + to->di_aformat = xfs_ifork_format(&ip->i_af); to->di_flags = cpu_to_be16(ip->i_diflags); if (xfs_has_v3inodes(ip->i_mount)) { diff --git a/fs/xfs/libxfs/xfs_inode_fork.c b/fs/xfs/libxfs/xfs_inode_fork.c index 1a4cdf550f6d..9327a4f39206 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.c +++ b/fs/xfs/libxfs/xfs_inode_fork.c @@ -35,7 +35,7 @@ xfs_init_local_fork( const void *data, int64_t size) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); int mem_size = size; bool zero_terminate; @@ -102,7 +102,7 @@ xfs_iformat_extents( int whichfork) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); int state = xfs_bmap_fork_to_state(whichfork); xfs_extnum_t nex = xfs_dfork_nextents(dip, whichfork); int size = nex * sizeof(xfs_bmbt_rec_t); @@ -173,7 +173,7 @@ xfs_iformat_btree( int size; int level; - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); dfp = (xfs_bmdr_block_t *)XFS_DFORK_PTR(dip, whichfork); size = XFS_BMAP_BROOT_SPACE(mp, dfp); nrecs = be16_to_cpu(dfp->bb_numrecs); @@ -276,17 +276,23 @@ xfs_dfork_attr_shortform_size( return be16_to_cpu(atp->hdr.totsize); } -struct xfs_ifork * -xfs_ifork_alloc( +void +xfs_ifork_init_attr( + struct xfs_inode *ip, enum xfs_dinode_fmt format, xfs_extnum_t nextents) { - struct xfs_ifork *ifp; + ip->i_af.if_format = format; + ip->i_af.if_nextents = nextents; +} - ifp = kmem_cache_zalloc(xfs_ifork_cache, GFP_NOFS | __GFP_NOFAIL); - ifp->if_format = format; - ifp->if_nextents = nextents; - return ifp; +void +xfs_ifork_zap_attr( + struct xfs_inode *ip) +{ + xfs_idestroy_fork(&ip->i_af); + memset(&ip->i_af, 0, sizeof(struct xfs_ifork)); + ip->i_af.if_format = XFS_DINODE_FMT_EXTENTS; } int @@ -301,9 +307,9 @@ xfs_iformat_attr_fork( * Initialize the extent count early, as the per-format routines may * depend on it. */ - ip->i_afp = xfs_ifork_alloc(dip->di_aformat, naextents); + xfs_ifork_init_attr(ip, dip->di_aformat, naextents); - switch (ip->i_afp->if_format) { + switch (ip->i_af.if_format) { case XFS_DINODE_FMT_LOCAL: error = xfs_iformat_local(ip, dip, XFS_ATTR_FORK, xfs_dfork_attr_shortform_size(dip)); @@ -323,10 +329,8 @@ xfs_iformat_attr_fork( break; } - if (error) { - kmem_cache_free(xfs_ifork_cache, ip->i_afp); - ip->i_afp = NULL; - } + if (error) + xfs_ifork_zap_attr(ip); return error; } @@ -370,7 +374,7 @@ xfs_iroot_realloc( return; } - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); if (rec_diff > 0) { /* * If there wasn't any memory allocated before, just @@ -400,7 +404,7 @@ xfs_iroot_realloc( (int)new_size); ifp->if_broot_bytes = (int)new_size; ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <= - XFS_IFORK_SIZE(ip, whichfork)); + xfs_inode_fork_size(ip, whichfork)); memmove(np, op, cur_max * (uint)sizeof(xfs_fsblock_t)); return; } @@ -454,7 +458,7 @@ xfs_iroot_realloc( ifp->if_broot_bytes = (int)new_size; if (ifp->if_broot) ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <= - XFS_IFORK_SIZE(ip, whichfork)); + xfs_inode_fork_size(ip, whichfork)); return; } @@ -480,11 +484,11 @@ xfs_idata_realloc( int64_t byte_diff, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); int64_t new_size = ifp->if_bytes + byte_diff; ASSERT(new_size >= 0); - ASSERT(new_size <= XFS_IFORK_SIZE(ip, whichfork)); + ASSERT(new_size <= xfs_inode_fork_size(ip, whichfork)); if (byte_diff == 0) return; @@ -539,7 +543,7 @@ xfs_iextents_copy( int whichfork) { int state = xfs_bmap_fork_to_state(whichfork); - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_iext_cursor icur; struct xfs_bmbt_irec rec; int64_t copied = 0; @@ -591,7 +595,7 @@ xfs_iflush_fork( if (!iip) return; - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); /* * This can happen if we gave up in iformat in an error path, * for the attribute fork. @@ -607,7 +611,7 @@ xfs_iflush_fork( if ((iip->ili_fields & dataflag[whichfork]) && (ifp->if_bytes > 0)) { ASSERT(ifp->if_u1.if_data != NULL); - ASSERT(ifp->if_bytes <= XFS_IFORK_SIZE(ip, whichfork)); + ASSERT(ifp->if_bytes <= xfs_inode_fork_size(ip, whichfork)); memcpy(cp, ifp->if_u1.if_data, ifp->if_bytes); } break; @@ -626,7 +630,7 @@ xfs_iflush_fork( (ifp->if_broot_bytes > 0)) { ASSERT(ifp->if_broot != NULL); ASSERT(XFS_BMAP_BMDR_SPACE(ifp->if_broot) <= - XFS_IFORK_SIZE(ip, whichfork)); + xfs_inode_fork_size(ip, whichfork)); xfs_bmbt_to_bmdr(mp, ifp->if_broot, ifp->if_broot_bytes, (xfs_bmdr_block_t *)cp, XFS_DFORK_SIZE(dip, mp, whichfork)); @@ -656,7 +660,7 @@ xfs_iext_state_to_fork( if (state & BMAP_COWFORK) return ip->i_cowfp; else if (state & BMAP_ATTRFORK) - return ip->i_afp; + return &ip->i_af; return &ip->i_df; } @@ -707,18 +711,17 @@ int xfs_ifork_verify_local_attr( struct xfs_inode *ip) { - struct xfs_ifork *ifp = ip->i_afp; + struct xfs_ifork *ifp = &ip->i_af; xfs_failaddr_t fa; - if (!ifp) + if (!xfs_inode_has_attr_fork(ip)) fa = __this_address; else fa = xfs_attr_shortform_verify(ip); if (fa) { xfs_inode_verifier_error(ip, -EFSCORRUPTED, "attr fork", - ifp ? ifp->if_u1.if_data : NULL, - ifp ? ifp->if_bytes : 0, fa); + ifp->if_u1.if_data, ifp->if_bytes, fa); return -EFSCORRUPTED; } @@ -731,7 +734,7 @@ xfs_iext_count_may_overflow( int whichfork, int nr_to_add) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); uint64_t max_exts; uint64_t nr_exts; diff --git a/fs/xfs/libxfs/xfs_inode_fork.h b/fs/xfs/libxfs/xfs_inode_fork.h index 4f68c1f20beb..d3943d6ad0b9 100644 --- a/fs/xfs/libxfs/xfs_inode_fork.h +++ b/fs/xfs/libxfs/xfs_inode_fork.h @@ -77,28 +77,8 @@ struct xfs_ifork { /* * Fork handling. */ - -#define XFS_IFORK_Q(ip) ((ip)->i_forkoff != 0) -#define XFS_IFORK_BOFF(ip) ((int)((ip)->i_forkoff << 3)) - -#define XFS_IFORK_PTR(ip,w) \ - ((w) == XFS_DATA_FORK ? \ - &(ip)->i_df : \ - ((w) == XFS_ATTR_FORK ? \ - (ip)->i_afp : \ - (ip)->i_cowfp)) -#define XFS_IFORK_DSIZE(ip) \ - (XFS_IFORK_Q(ip) ? XFS_IFORK_BOFF(ip) : XFS_LITINO((ip)->i_mount)) -#define XFS_IFORK_ASIZE(ip) \ - (XFS_IFORK_Q(ip) ? XFS_LITINO((ip)->i_mount) - XFS_IFORK_BOFF(ip) : 0) -#define XFS_IFORK_SIZE(ip,w) \ - ((w) == XFS_DATA_FORK ? \ - XFS_IFORK_DSIZE(ip) : \ - ((w) == XFS_ATTR_FORK ? \ - XFS_IFORK_ASIZE(ip) : \ - 0)) #define XFS_IFORK_MAXEXT(ip, w) \ - (XFS_IFORK_SIZE(ip, w) / sizeof(xfs_bmbt_rec_t)) + (xfs_inode_fork_size(ip, w) / sizeof(xfs_bmbt_rec_t)) static inline bool xfs_ifork_has_extents(struct xfs_ifork *ifp) { @@ -179,8 +159,9 @@ xfs_dfork_nextents( return 0; } -struct xfs_ifork *xfs_ifork_alloc(enum xfs_dinode_fmt format, - xfs_extnum_t nextents); +void xfs_ifork_zap_attr(struct xfs_inode *ip); +void xfs_ifork_init_attr(struct xfs_inode *ip, enum xfs_dinode_fmt format, + xfs_extnum_t nextents); struct xfs_ifork *xfs_iext_state_to_fork(struct xfs_inode *ip, int state); int xfs_iformat_data_fork(struct xfs_inode *, struct xfs_dinode *); diff --git a/fs/xfs/libxfs/xfs_refcount.c b/fs/xfs/libxfs/xfs_refcount.c index 97e9e6020596..64b910caafaa 100644 --- a/fs/xfs/libxfs/xfs_refcount.c +++ b/fs/xfs/libxfs/xfs_refcount.c @@ -111,7 +111,7 @@ xfs_refcount_get_rec( int *stat) { struct xfs_mount *mp = cur->bc_mp; - xfs_agnumber_t agno = cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = cur->bc_ag.pag; union xfs_btree_rec *rec; int error; xfs_agblock_t realstart; @@ -121,8 +121,6 @@ xfs_refcount_get_rec( return error; xfs_refcount_btrec_to_irec(rec, irec); - - agno = cur->bc_ag.pag->pag_agno; if (irec->rc_blockcount == 0 || irec->rc_blockcount > MAXREFCEXTLEN) goto out_bad_rec; @@ -137,22 +135,23 @@ xfs_refcount_get_rec( } /* check for valid extent range, including overflow */ - if (!xfs_verify_agbno(mp, agno, realstart)) + if (!xfs_verify_agbno(pag, realstart)) goto out_bad_rec; if (realstart > realstart + irec->rc_blockcount) goto out_bad_rec; - if (!xfs_verify_agbno(mp, agno, realstart + irec->rc_blockcount - 1)) + if (!xfs_verify_agbno(pag, realstart + irec->rc_blockcount - 1)) goto out_bad_rec; if (irec->rc_refcount == 0 || irec->rc_refcount > MAXREFCOUNT) goto out_bad_rec; - trace_xfs_refcount_get(cur->bc_mp, cur->bc_ag.pag->pag_agno, irec); + trace_xfs_refcount_get(cur->bc_mp, pag->pag_agno, irec); return 0; out_bad_rec: xfs_warn(mp, - "Refcount BTree record corruption in AG %d detected!", agno); + "Refcount BTree record corruption in AG %d detected!", + pag->pag_agno); xfs_warn(mp, "Start block 0x%x, block count 0x%x, references 0x%x", irec->rc_startblock, irec->rc_blockcount, irec->rc_refcount); @@ -1177,8 +1176,8 @@ xfs_refcount_finish_one( *pcur = NULL; } if (rcur == NULL) { - error = xfs_alloc_read_agf(tp->t_mountp, tp, pag->pag_agno, - XFS_ALLOC_FLAG_FREEING, &agbp); + error = xfs_alloc_read_agf(pag, tp, XFS_ALLOC_FLAG_FREEING, + &agbp); if (error) goto out_drop; @@ -1710,7 +1709,7 @@ xfs_refcount_recover_cow_leftovers( if (error) return error; - error = xfs_alloc_read_agf(mp, tp, pag->pag_agno, 0, &agbp); + error = xfs_alloc_read_agf(pag, tp, 0, &agbp); if (error) goto out_trans; cur = xfs_refcountbt_init_cursor(mp, tp, agbp, pag); diff --git a/fs/xfs/libxfs/xfs_refcount_btree.c b/fs/xfs/libxfs/xfs_refcount_btree.c index d14c1720b0fb..316c1ec0c3c2 100644 --- a/fs/xfs/libxfs/xfs_refcount_btree.c +++ b/fs/xfs/libxfs/xfs_refcount_btree.c @@ -493,7 +493,7 @@ xfs_refcountbt_calc_reserves( if (!xfs_has_reflink(mp)) return 0; - error = xfs_alloc_read_agf(mp, tp, pag->pag_agno, 0, &agbp); + error = xfs_alloc_read_agf(pag, tp, 0, &agbp); if (error) return error; @@ -507,8 +507,7 @@ xfs_refcountbt_calc_reserves( * never be available for the kinds of things that would require btree * expansion. We therefore can pretend the space isn't there. */ - if (mp->m_sb.sb_logstart && - XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart) == pag->pag_agno) + if (xfs_ag_contains_log(mp, pag->pag_agno)) agblocks -= mp->m_sb.sb_logblocks; *ask += xfs_refcountbt_max_size(mp, agblocks); diff --git a/fs/xfs/libxfs/xfs_rmap.c b/fs/xfs/libxfs/xfs_rmap.c index 2845019d31da..094dfc897ebc 100644 --- a/fs/xfs/libxfs/xfs_rmap.c +++ b/fs/xfs/libxfs/xfs_rmap.c @@ -215,7 +215,7 @@ xfs_rmap_get_rec( int *stat) { struct xfs_mount *mp = cur->bc_mp; - xfs_agnumber_t agno = cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = cur->bc_ag.pag; union xfs_btree_rec *rec; int error; @@ -235,12 +235,12 @@ xfs_rmap_get_rec( goto out_bad_rec; } else { /* check for valid extent range, including overflow */ - if (!xfs_verify_agbno(mp, agno, irec->rm_startblock)) + if (!xfs_verify_agbno(pag, irec->rm_startblock)) goto out_bad_rec; if (irec->rm_startblock > irec->rm_startblock + irec->rm_blockcount) goto out_bad_rec; - if (!xfs_verify_agbno(mp, agno, + if (!xfs_verify_agbno(pag, irec->rm_startblock + irec->rm_blockcount - 1)) goto out_bad_rec; } @@ -254,7 +254,7 @@ xfs_rmap_get_rec( out_bad_rec: xfs_warn(mp, "Reverse Mapping BTree record corruption in AG %d detected!", - agno); + pag->pag_agno); xfs_warn(mp, "Owner 0x%llx, flags 0x%x, start block 0x%x block count 0x%x", irec->rm_owner, irec->rm_flags, irec->rm_startblock, diff --git a/fs/xfs/libxfs/xfs_rmap_btree.c b/fs/xfs/libxfs/xfs_rmap_btree.c index 69e104d0277f..7f83f62e51e0 100644 --- a/fs/xfs/libxfs/xfs_rmap_btree.c +++ b/fs/xfs/libxfs/xfs_rmap_btree.c @@ -90,7 +90,7 @@ xfs_rmapbt_alloc_block( xfs_agblock_t bno; /* Allocate the new block from the freelist. If we can't, give up. */ - error = xfs_alloc_get_freelist(cur->bc_tp, cur->bc_ag.agbp, + error = xfs_alloc_get_freelist(pag, cur->bc_tp, cur->bc_ag.agbp, &bno, 1); if (error) return error; @@ -129,7 +129,7 @@ xfs_rmapbt_free_block( bno, 1); be32_add_cpu(&agf->agf_rmap_blocks, -1); xfs_alloc_log_agf(cur->bc_tp, agbp, XFS_AGF_RMAP_BLOCKS); - error = xfs_alloc_put_freelist(cur->bc_tp, agbp, NULL, bno, 1); + error = xfs_alloc_put_freelist(pag, cur->bc_tp, agbp, NULL, bno, 1); if (error) return error; @@ -652,7 +652,7 @@ xfs_rmapbt_calc_reserves( if (!xfs_has_rmapbt(mp)) return 0; - error = xfs_alloc_read_agf(mp, tp, pag->pag_agno, 0, &agbp); + error = xfs_alloc_read_agf(pag, tp, 0, &agbp); if (error) return error; @@ -666,8 +666,7 @@ xfs_rmapbt_calc_reserves( * never be available for the kinds of things that would require btree * expansion. We therefore can pretend the space isn't there. */ - if (mp->m_sb.sb_logstart && - XFS_FSB_TO_AGNO(mp, mp->m_sb.sb_logstart) == pag->pag_agno) + if (xfs_ag_contains_log(mp, pag->pag_agno)) agblocks -= mp->m_sb.sb_logblocks; /* Reserve 1% of the AG or enough for 1 block per record. */ diff --git a/fs/xfs/libxfs/xfs_symlink_remote.c b/fs/xfs/libxfs/xfs_symlink_remote.c index 8b9bd178a487..bdc777b9ec4a 100644 --- a/fs/xfs/libxfs/xfs_symlink_remote.c +++ b/fs/xfs/libxfs/xfs_symlink_remote.c @@ -204,7 +204,7 @@ xfs_failaddr_t xfs_symlink_shortform_verify( struct xfs_inode *ip) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); char *sfp = (char *)ifp->if_u1.if_data; int size = ifp->if_bytes; char *endp = sfp + size; diff --git a/fs/xfs/libxfs/xfs_types.c b/fs/xfs/libxfs/xfs_types.c index e810d23f2d97..5c2765934732 100644 --- a/fs/xfs/libxfs/xfs_types.c +++ b/fs/xfs/libxfs/xfs_types.c @@ -13,25 +13,13 @@ #include "xfs_mount.h" #include "xfs_ag.h" -/* Find the size of the AG, in blocks. */ -inline xfs_agblock_t -xfs_ag_block_count( - struct xfs_mount *mp, - xfs_agnumber_t agno) -{ - ASSERT(agno < mp->m_sb.sb_agcount); - - if (agno < mp->m_sb.sb_agcount - 1) - return mp->m_sb.sb_agblocks; - return mp->m_sb.sb_dblocks - (agno * mp->m_sb.sb_agblocks); -} /* * Verify that an AG block number pointer neither points outside the AG * nor points at static metadata. */ -inline bool -xfs_verify_agbno( +static inline bool +xfs_verify_agno_agbno( struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agblock_t agbno) @@ -59,7 +47,7 @@ xfs_verify_fsbno( if (agno >= mp->m_sb.sb_agcount) return false; - return xfs_verify_agbno(mp, agno, XFS_FSB_TO_AGBNO(mp, fsbno)); + return xfs_verify_agno_agbno(mp, agno, XFS_FSB_TO_AGBNO(mp, fsbno)); } /* @@ -85,40 +73,12 @@ xfs_verify_fsbext( XFS_FSB_TO_AGNO(mp, fsbno + len - 1); } -/* Calculate the first and last possible inode number in an AG. */ -inline void -xfs_agino_range( - struct xfs_mount *mp, - xfs_agnumber_t agno, - xfs_agino_t *first, - xfs_agino_t *last) -{ - xfs_agblock_t bno; - xfs_agblock_t eoag; - - eoag = xfs_ag_block_count(mp, agno); - - /* - * Calculate the first inode, which will be in the first - * cluster-aligned block after the AGFL. - */ - bno = round_up(XFS_AGFL_BLOCK(mp) + 1, M_IGEO(mp)->cluster_align); - *first = XFS_AGB_TO_AGINO(mp, bno); - - /* - * Calculate the last inode, which will be at the end of the - * last (aligned) cluster that can be allocated in the AG. - */ - bno = round_down(eoag, M_IGEO(mp)->cluster_align); - *last = XFS_AGB_TO_AGINO(mp, bno) - 1; -} - /* * Verify that an AG inode number pointer neither points outside the AG * nor points at static metadata. */ -inline bool -xfs_verify_agino( +static inline bool +xfs_verify_agno_agino( struct xfs_mount *mp, xfs_agnumber_t agno, xfs_agino_t agino) @@ -130,19 +90,6 @@ xfs_verify_agino( return agino >= first && agino <= last; } -/* - * Verify that an AG inode number pointer neither points outside the AG - * nor points at static metadata, or is NULLAGINO. - */ -bool -xfs_verify_agino_or_null( - struct xfs_mount *mp, - xfs_agnumber_t agno, - xfs_agino_t agino) -{ - return agino == NULLAGINO || xfs_verify_agino(mp, agno, agino); -} - /* * Verify that an FS inode number pointer neither points outside the * filesystem nor points at static AG metadata. @@ -159,7 +106,7 @@ xfs_verify_ino( return false; if (XFS_AGINO_TO_INO(mp, agno, agino) != ino) return false; - return xfs_verify_agino(mp, agno, agino); + return xfs_verify_agno_agino(mp, agno, agino); } /* Is this an internal inode number? */ @@ -229,12 +176,8 @@ xfs_icount_range( /* root, rtbitmap, rtsum all live in the first chunk */ *min = XFS_INODES_PER_CHUNK; - for_each_perag(mp, agno, pag) { - xfs_agino_t first, last; - - xfs_agino_range(mp, agno, &first, &last); - nr_inos += last - first + 1; - } + for_each_perag(mp, agno, pag) + nr_inos += pag->agino_max - pag->agino_min + 1; *max = nr_inos; } diff --git a/fs/xfs/libxfs/xfs_types.h b/fs/xfs/libxfs/xfs_types.h index 373f64a492a4..a6b7d98cf68f 100644 --- a/fs/xfs/libxfs/xfs_types.h +++ b/fs/xfs/libxfs/xfs_types.h @@ -179,19 +179,10 @@ enum xfs_ag_resv_type { */ struct xfs_mount; -xfs_agblock_t xfs_ag_block_count(struct xfs_mount *mp, xfs_agnumber_t agno); -bool xfs_verify_agbno(struct xfs_mount *mp, xfs_agnumber_t agno, - xfs_agblock_t agbno); bool xfs_verify_fsbno(struct xfs_mount *mp, xfs_fsblock_t fsbno); bool xfs_verify_fsbext(struct xfs_mount *mp, xfs_fsblock_t fsbno, xfs_fsblock_t len); -void xfs_agino_range(struct xfs_mount *mp, xfs_agnumber_t agno, - xfs_agino_t *first, xfs_agino_t *last); -bool xfs_verify_agino(struct xfs_mount *mp, xfs_agnumber_t agno, - xfs_agino_t agino); -bool xfs_verify_agino_or_null(struct xfs_mount *mp, xfs_agnumber_t agno, - xfs_agino_t agino); bool xfs_verify_ino(struct xfs_mount *mp, xfs_ino_t ino); bool xfs_internal_inum(struct xfs_mount *mp, xfs_ino_t ino); bool xfs_verify_dir_ino(struct xfs_mount *mp, xfs_ino_t ino); diff --git a/fs/xfs/scrub/agheader.c b/fs/xfs/scrub/agheader.c index 90aebfe9dc5f..b7b838bd4ba4 100644 --- a/fs/xfs/scrub/agheader.c +++ b/fs/xfs/scrub/agheader.c @@ -541,16 +541,16 @@ xchk_agf( /* Check the AG length */ eoag = be32_to_cpu(agf->agf_length); - if (eoag != xfs_ag_block_count(mp, agno)) + if (eoag != pag->block_count) xchk_block_set_corrupt(sc, sc->sa.agf_bp); /* Check the AGF btree roots and levels */ agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_BNO]); - if (!xfs_verify_agbno(mp, agno, agbno)) + if (!xfs_verify_agbno(pag, agbno)) xchk_block_set_corrupt(sc, sc->sa.agf_bp); agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_CNT]); - if (!xfs_verify_agbno(mp, agno, agbno)) + if (!xfs_verify_agbno(pag, agbno)) xchk_block_set_corrupt(sc, sc->sa.agf_bp); level = be32_to_cpu(agf->agf_levels[XFS_BTNUM_BNO]); @@ -563,7 +563,7 @@ xchk_agf( if (xfs_has_rmapbt(mp)) { agbno = be32_to_cpu(agf->agf_roots[XFS_BTNUM_RMAP]); - if (!xfs_verify_agbno(mp, agno, agbno)) + if (!xfs_verify_agbno(pag, agbno)) xchk_block_set_corrupt(sc, sc->sa.agf_bp); level = be32_to_cpu(agf->agf_levels[XFS_BTNUM_RMAP]); @@ -573,7 +573,7 @@ xchk_agf( if (xfs_has_reflink(mp)) { agbno = be32_to_cpu(agf->agf_refcount_root); - if (!xfs_verify_agbno(mp, agno, agbno)) + if (!xfs_verify_agbno(pag, agbno)) xchk_block_set_corrupt(sc, sc->sa.agf_bp); level = be32_to_cpu(agf->agf_refcount_level); @@ -639,9 +639,8 @@ xchk_agfl_block( { struct xchk_agfl_info *sai = priv; struct xfs_scrub *sc = sai->sc; - xfs_agnumber_t agno = sc->sa.pag->pag_agno; - if (xfs_verify_agbno(mp, agno, agbno) && + if (xfs_verify_agbno(sc->sa.pag, agbno) && sai->nr_entries < sai->sz_entries) sai->entries[sai->nr_entries++] = agbno; else @@ -871,12 +870,12 @@ xchk_agi( /* Check the AG length */ eoag = be32_to_cpu(agi->agi_length); - if (eoag != xfs_ag_block_count(mp, agno)) + if (eoag != pag->block_count) xchk_block_set_corrupt(sc, sc->sa.agi_bp); /* Check btree roots and levels */ agbno = be32_to_cpu(agi->agi_root); - if (!xfs_verify_agbno(mp, agno, agbno)) + if (!xfs_verify_agbno(pag, agbno)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); level = be32_to_cpu(agi->agi_level); @@ -885,7 +884,7 @@ xchk_agi( if (xfs_has_finobt(mp)) { agbno = be32_to_cpu(agi->agi_free_root); - if (!xfs_verify_agbno(mp, agno, agbno)) + if (!xfs_verify_agbno(pag, agbno)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); level = be32_to_cpu(agi->agi_free_level); @@ -902,17 +901,17 @@ xchk_agi( /* Check inode pointers */ agino = be32_to_cpu(agi->agi_newino); - if (!xfs_verify_agino_or_null(mp, agno, agino)) + if (!xfs_verify_agino_or_null(pag, agino)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); agino = be32_to_cpu(agi->agi_dirino); - if (!xfs_verify_agino_or_null(mp, agno, agino)) + if (!xfs_verify_agino_or_null(pag, agino)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); /* Check unlinked inode buckets */ for (i = 0; i < XFS_AGI_UNLINKED_BUCKETS; i++) { agino = be32_to_cpu(agi->agi_unlinked[i]); - if (!xfs_verify_agino_or_null(mp, agno, agino)) + if (!xfs_verify_agino_or_null(pag, agino)) xchk_block_set_corrupt(sc, sc->sa.agi_bp); } diff --git a/fs/xfs/scrub/agheader_repair.c b/fs/xfs/scrub/agheader_repair.c index 6da7f2ca77de..1b0b4e243f77 100644 --- a/fs/xfs/scrub/agheader_repair.c +++ b/fs/xfs/scrub/agheader_repair.c @@ -106,7 +106,7 @@ xrep_agf_check_agfl_block( { struct xfs_scrub *sc = priv; - if (!xfs_verify_agbno(mp, sc->sa.pag->pag_agno, agbno)) + if (!xfs_verify_agbno(sc->sa.pag, agbno)) return -EFSCORRUPTED; return 0; } @@ -130,10 +130,7 @@ xrep_check_btree_root( struct xfs_scrub *sc, struct xrep_find_ag_btree *fab) { - struct xfs_mount *mp = sc->mp; - xfs_agnumber_t agno = sc->sm->sm_agno; - - return xfs_verify_agbno(mp, agno, fab->root) && + return xfs_verify_agbno(sc->sa.pag, fab->root) && fab->height <= fab->maxlevels; } @@ -201,8 +198,7 @@ xrep_agf_init_header( agf->agf_magicnum = cpu_to_be32(XFS_AGF_MAGIC); agf->agf_versionnum = cpu_to_be32(XFS_AGF_VERSION); agf->agf_seqno = cpu_to_be32(sc->sa.pag->pag_agno); - agf->agf_length = cpu_to_be32(xfs_ag_block_count(mp, - sc->sa.pag->pag_agno)); + agf->agf_length = cpu_to_be32(sc->sa.pag->block_count); agf->agf_flfirst = old_agf->agf_flfirst; agf->agf_fllast = old_agf->agf_fllast; agf->agf_flcount = old_agf->agf_flcount; @@ -405,7 +401,7 @@ xrep_agf( * btrees rooted in the AGF. If the AGFL contents are obviously bad * then we'll bail out. */ - error = xfs_alloc_read_agfl(mp, sc->tp, sc->sa.pag->pag_agno, &agfl_bp); + error = xfs_alloc_read_agfl(sc->sa.pag, sc->tp, &agfl_bp); if (error) return error; @@ -666,8 +662,7 @@ xrep_agfl( * nothing wrong with the AGF, but all the AG header repair functions * have this chicken-and-egg problem. */ - error = xfs_alloc_read_agf(mp, sc->tp, sc->sa.pag->pag_agno, 0, - &agf_bp); + error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp); if (error) return error; @@ -742,8 +737,7 @@ xrep_agi_find_btrees( int error; /* Read the AGF. */ - error = xfs_alloc_read_agf(mp, sc->tp, sc->sa.pag->pag_agno, 0, - &agf_bp); + error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp); if (error) return error; @@ -782,8 +776,7 @@ xrep_agi_init_header( agi->agi_magicnum = cpu_to_be32(XFS_AGI_MAGIC); agi->agi_versionnum = cpu_to_be32(XFS_AGI_VERSION); agi->agi_seqno = cpu_to_be32(sc->sa.pag->pag_agno); - agi->agi_length = cpu_to_be32(xfs_ag_block_count(mp, - sc->sa.pag->pag_agno)); + agi->agi_length = cpu_to_be32(sc->sa.pag->block_count); agi->agi_newino = cpu_to_be32(NULLAGINO); agi->agi_dirino = cpu_to_be32(NULLAGINO); if (xfs_has_crc(mp)) diff --git a/fs/xfs/scrub/alloc.c b/fs/xfs/scrub/alloc.c index 87518e1292f8..ab427b4d7fe0 100644 --- a/fs/xfs/scrub/alloc.c +++ b/fs/xfs/scrub/alloc.c @@ -93,8 +93,7 @@ xchk_allocbt_rec( struct xchk_btree *bs, const union xfs_btree_rec *rec) { - struct xfs_mount *mp = bs->cur->bc_mp; - xfs_agnumber_t agno = bs->cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = bs->cur->bc_ag.pag; xfs_agblock_t bno; xfs_extlen_t len; @@ -102,8 +101,8 @@ xchk_allocbt_rec( len = be32_to_cpu(rec->alloc.ar_blockcount); if (bno + len <= bno || - !xfs_verify_agbno(mp, agno, bno) || - !xfs_verify_agbno(mp, agno, bno + len - 1)) + !xfs_verify_agbno(pag, bno) || + !xfs_verify_agbno(pag, bno + len - 1)) xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_allocbt_xref(bs->sc, bno, len); diff --git a/fs/xfs/scrub/bmap.c b/fs/xfs/scrub/bmap.c index 285995ba3947..f0b9cb6506fd 100644 --- a/fs/xfs/scrub/bmap.c +++ b/fs/xfs/scrub/bmap.c @@ -377,7 +377,7 @@ xchk_bmapbt_rec( struct xfs_inode *ip = bs->cur->bc_ino.ip; struct xfs_buf *bp = NULL; struct xfs_btree_block *block; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, info->whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, info->whichfork); uint64_t owner; int i; @@ -426,7 +426,7 @@ xchk_bmap_btree( struct xchk_bmap_info *info) { struct xfs_owner_info oinfo; - struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, whichfork); struct xfs_mount *mp = sc->mp; struct xfs_inode *ip = sc->ip; struct xfs_btree_cur *cur; @@ -478,7 +478,7 @@ xchk_bmap_check_rmap( return 0; /* Now look up the bmbt record. */ - ifp = XFS_IFORK_PTR(sc->ip, sbcri->whichfork); + ifp = xfs_ifork_ptr(sc->ip, sbcri->whichfork); if (!ifp) { xchk_fblock_set_corrupt(sc, sbcri->whichfork, rec->rm_offset); @@ -540,7 +540,7 @@ xchk_bmap_check_ag_rmaps( struct xfs_buf *agf; int error; - error = xfs_alloc_read_agf(sc->mp, sc->tp, pag->pag_agno, 0, &agf); + error = xfs_alloc_read_agf(pag, sc->tp, 0, &agf); if (error) return error; @@ -563,7 +563,7 @@ xchk_bmap_check_rmaps( struct xfs_scrub *sc, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, whichfork); struct xfs_perag *pag; xfs_agnumber_t agno; bool zero_size; @@ -578,7 +578,7 @@ xchk_bmap_check_rmaps( if (XFS_IS_REALTIME_INODE(sc->ip) && whichfork == XFS_DATA_FORK) return 0; - ASSERT(XFS_IFORK_PTR(sc->ip, whichfork) != NULL); + ASSERT(xfs_ifork_ptr(sc->ip, whichfork) != NULL); /* * Only do this for complex maps that are in btree format, or for @@ -624,7 +624,7 @@ xchk_bmap( struct xchk_bmap_info info = { NULL }; struct xfs_mount *mp = sc->mp; struct xfs_inode *ip = sc->ip; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); xfs_fileoff_t endoff; struct xfs_iext_cursor icur; int error = 0; @@ -689,7 +689,7 @@ xchk_bmap( /* Scrub extent records. */ info.lastoff = 0; - ifp = XFS_IFORK_PTR(ip, whichfork); + ifp = xfs_ifork_ptr(ip, whichfork); for_each_xfs_iext(ifp, &icur, &irec) { if (xchk_should_terminate(sc, &error) || (sc->sm->sm_flags & XFS_SCRUB_OFLAG_CORRUPT)) diff --git a/fs/xfs/scrub/btree.c b/fs/xfs/scrub/btree.c index 39dd46f038fe..2f4519590dc1 100644 --- a/fs/xfs/scrub/btree.c +++ b/fs/xfs/scrub/btree.c @@ -462,7 +462,7 @@ xchk_btree_check_iroot_minrecs( */ if (bs->cur->bc_btnum == XFS_BTNUM_BMAP && bs->cur->bc_ino.whichfork == XFS_DATA_FORK && - XFS_IFORK_Q(bs->sc->ip)) + xfs_inode_has_attr_fork(bs->sc->ip)) return false; return true; diff --git a/fs/xfs/scrub/common.c b/fs/xfs/scrub/common.c index 97b54ac3075f..9bbbf20f401b 100644 --- a/fs/xfs/scrub/common.c +++ b/fs/xfs/scrub/common.c @@ -416,15 +416,15 @@ xchk_ag_read_headers( if (!sa->pag) return -ENOENT; - error = xfs_ialloc_read_agi(mp, sc->tp, agno, &sa->agi_bp); + error = xfs_ialloc_read_agi(sa->pag, sc->tp, &sa->agi_bp); if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGI)) return error; - error = xfs_alloc_read_agf(mp, sc->tp, agno, 0, &sa->agf_bp); + error = xfs_alloc_read_agf(sa->pag, sc->tp, 0, &sa->agf_bp); if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGF)) return error; - error = xfs_alloc_read_agfl(mp, sc->tp, agno, &sa->agfl_bp); + error = xfs_alloc_read_agfl(sa->pag, sc->tp, &sa->agfl_bp); if (error && want_ag_read_header_failure(sc, XFS_SCRUB_TYPE_AGFL)) return error; diff --git a/fs/xfs/scrub/dabtree.c b/fs/xfs/scrub/dabtree.c index b962cfbbd92b..84fe3d33d699 100644 --- a/fs/xfs/scrub/dabtree.c +++ b/fs/xfs/scrub/dabtree.c @@ -482,7 +482,7 @@ xchk_da_btree( int error; /* Skip short format data structures; no btree to scan. */ - if (!xfs_ifork_has_extents(XFS_IFORK_PTR(sc->ip, whichfork))) + if (!xfs_ifork_has_extents(xfs_ifork_ptr(sc->ip, whichfork))) return 0; /* Set up initial da state. */ diff --git a/fs/xfs/scrub/dir.c b/fs/xfs/scrub/dir.c index 38897adde7b5..5abb5fdb71d9 100644 --- a/fs/xfs/scrub/dir.c +++ b/fs/xfs/scrub/dir.c @@ -667,7 +667,7 @@ xchk_directory_blocks( { struct xfs_bmbt_irec got; struct xfs_da_args args; - struct xfs_ifork *ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); struct xfs_mount *mp = sc->mp; xfs_fileoff_t leaf_lblk; xfs_fileoff_t free_lblk; diff --git a/fs/xfs/scrub/fscounters.c b/fs/xfs/scrub/fscounters.c index 48a6cbdf95d0..6a6f8fe7f87c 100644 --- a/fs/xfs/scrub/fscounters.c +++ b/fs/xfs/scrub/fscounters.c @@ -78,10 +78,10 @@ xchk_fscount_warmup( continue; /* Lock both AG headers. */ - error = xfs_ialloc_read_agi(mp, sc->tp, agno, &agi_bp); + error = xfs_ialloc_read_agi(pag, sc->tp, &agi_bp); if (error) break; - error = xfs_alloc_read_agf(mp, sc->tp, agno, 0, &agf_bp); + error = xfs_alloc_read_agf(pag, sc->tp, 0, &agf_bp); if (error) break; diff --git a/fs/xfs/scrub/health.c b/fs/xfs/scrub/health.c index 2e61df3bca83..aa65ec88a0c0 100644 --- a/fs/xfs/scrub/health.c +++ b/fs/xfs/scrub/health.c @@ -8,6 +8,8 @@ #include "xfs_shared.h" #include "xfs_format.h" #include "xfs_btree.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" #include "xfs_ag.h" #include "xfs_health.h" #include "scrub/scrub.h" diff --git a/fs/xfs/scrub/ialloc.c b/fs/xfs/scrub/ialloc.c index 00848ee542fb..e1026e07bf94 100644 --- a/fs/xfs/scrub/ialloc.c +++ b/fs/xfs/scrub/ialloc.c @@ -104,13 +104,13 @@ xchk_iallocbt_chunk( xfs_extlen_t len) { struct xfs_mount *mp = bs->cur->bc_mp; - xfs_agnumber_t agno = bs->cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = bs->cur->bc_ag.pag; xfs_agblock_t bno; bno = XFS_AGINO_TO_AGBNO(mp, agino); if (bno + len <= bno || - !xfs_verify_agbno(mp, agno, bno) || - !xfs_verify_agbno(mp, agno, bno + len - 1)) + !xfs_verify_agbno(pag, bno) || + !xfs_verify_agbno(pag, bno + len - 1)) xchk_btree_set_corrupt(bs->sc, bs->cur, 0); xchk_iallocbt_chunk_xref(bs->sc, irec, agino, bno, len); @@ -421,10 +421,10 @@ xchk_iallocbt_rec( const union xfs_btree_rec *rec) { struct xfs_mount *mp = bs->cur->bc_mp; + struct xfs_perag *pag = bs->cur->bc_ag.pag; struct xchk_iallocbt *iabt = bs->private; struct xfs_inobt_rec_incore irec; uint64_t holes; - xfs_agnumber_t agno = bs->cur->bc_ag.pag->pag_agno; xfs_agino_t agino; xfs_extlen_t len; int holecount; @@ -446,8 +446,8 @@ xchk_iallocbt_rec( agino = irec.ir_startino; /* Record has to be properly aligned within the AG. */ - if (!xfs_verify_agino(mp, agno, agino) || - !xfs_verify_agino(mp, agno, agino + XFS_INODES_PER_CHUNK - 1)) { + if (!xfs_verify_agino(pag, agino) || + !xfs_verify_agino(pag, agino + XFS_INODES_PER_CHUNK - 1)) { xchk_btree_set_corrupt(bs->sc, bs->cur, 0); goto out; } diff --git a/fs/xfs/scrub/quota.c b/fs/xfs/scrub/quota.c index 3c7506c7553c..21b4c9006859 100644 --- a/fs/xfs/scrub/quota.c +++ b/fs/xfs/scrub/quota.c @@ -185,7 +185,7 @@ xchk_quota_data_fork( /* Check for data fork problems that apply only to quota files. */ max_dqid_off = ((xfs_dqid_t)-1) / qi->qi_dqperchunk; - ifp = XFS_IFORK_PTR(sc->ip, XFS_DATA_FORK); + ifp = xfs_ifork_ptr(sc->ip, XFS_DATA_FORK); for_each_xfs_iext(ifp, &icur, &irec) { if (xchk_should_terminate(sc, &error)) break; diff --git a/fs/xfs/scrub/refcount.c b/fs/xfs/scrub/refcount.c index 2744eecdbaf0..c68b767dc08f 100644 --- a/fs/xfs/scrub/refcount.c +++ b/fs/xfs/scrub/refcount.c @@ -13,6 +13,8 @@ #include "scrub/scrub.h" #include "scrub/common.h" #include "scrub/btree.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" #include "xfs_ag.h" /* @@ -332,9 +334,8 @@ xchk_refcountbt_rec( struct xchk_btree *bs, const union xfs_btree_rec *rec) { - struct xfs_mount *mp = bs->cur->bc_mp; xfs_agblock_t *cow_blocks = bs->private; - xfs_agnumber_t agno = bs->cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = bs->cur->bc_ag.pag; xfs_agblock_t bno; xfs_extlen_t len; xfs_nlink_t refcount; @@ -354,8 +355,8 @@ xchk_refcountbt_rec( /* Check the extent. */ bno &= ~XFS_REFC_COW_START; if (bno + len <= bno || - !xfs_verify_agbno(mp, agno, bno) || - !xfs_verify_agbno(mp, agno, bno + len - 1)) + !xfs_verify_agbno(pag, bno) || + !xfs_verify_agbno(pag, bno + len - 1)) xchk_btree_set_corrupt(bs->sc, bs->cur, 0); if (refcount == 0) diff --git a/fs/xfs/scrub/repair.c b/fs/xfs/scrub/repair.c index 1e7b6b209ee8..c18bd039fce9 100644 --- a/fs/xfs/scrub/repair.c +++ b/fs/xfs/scrub/repair.c @@ -199,7 +199,7 @@ xrep_calc_ag_resblks( icount = pag->pagi_count; } else { /* Try to get the actual counters from disk. */ - error = xfs_ialloc_read_agi(mp, NULL, sm->sm_agno, &bp); + error = xfs_ialloc_read_agi(pag, NULL, &bp); if (!error) { icount = pag->pagi_count; xfs_buf_relse(bp); @@ -207,9 +207,9 @@ xrep_calc_ag_resblks( } /* Now grab the block counters from the AGF. */ - error = xfs_alloc_read_agf(mp, NULL, sm->sm_agno, 0, &bp); + error = xfs_alloc_read_agf(pag, NULL, 0, &bp); if (error) { - aglen = xfs_ag_block_count(mp, sm->sm_agno); + aglen = pag->block_count; freelen = aglen; usedlen = aglen; } else { @@ -220,25 +220,22 @@ xrep_calc_ag_resblks( usedlen = aglen - freelen; xfs_buf_relse(bp); } - xfs_perag_put(pag); /* If the icount is impossible, make some worst-case assumptions. */ if (icount == NULLAGINO || - !xfs_verify_agino(mp, sm->sm_agno, icount)) { - xfs_agino_t first, last; - - xfs_agino_range(mp, sm->sm_agno, &first, &last); - icount = last - first + 1; + !xfs_verify_agino(pag, icount)) { + icount = pag->agino_max - pag->agino_min + 1; } /* If the block counts are impossible, make worst-case assumptions. */ if (aglen == NULLAGBLOCK || - aglen != xfs_ag_block_count(mp, sm->sm_agno) || + aglen != pag->block_count || freelen >= aglen) { - aglen = xfs_ag_block_count(mp, sm->sm_agno); + aglen = pag->block_count; freelen = aglen; usedlen = aglen; } + xfs_perag_put(pag); trace_xrep_calc_ag_resblks(mp, sm->sm_agno, icount, aglen, freelen, usedlen); @@ -300,13 +297,13 @@ xrep_alloc_ag_block( switch (resv) { case XFS_AG_RESV_AGFL: case XFS_AG_RESV_RMAPBT: - error = xfs_alloc_get_freelist(sc->tp, sc->sa.agf_bp, &bno, 1); + error = xfs_alloc_get_freelist(sc->sa.pag, sc->tp, + sc->sa.agf_bp, &bno, 1); if (error) return error; if (bno == NULLAGBLOCK) return -ENOSPC; - xfs_extent_busy_reuse(sc->mp, sc->sa.pag, bno, - 1, false); + xfs_extent_busy_reuse(sc->mp, sc->sa.pag, bno, 1, false); *fsbno = XFS_AGB_TO_FSB(sc->mp, sc->sa.pag->pag_agno, bno); if (resv == XFS_AG_RESV_RMAPBT) xfs_ag_resv_rmapbt_alloc(sc->mp, sc->sa.pag->pag_agno); @@ -457,16 +454,19 @@ xrep_invalidate_blocks( * assume it's owned by someone else. */ for_each_xbitmap_block(fsbno, bmr, n, bitmap) { + int error; + /* Skip AG headers and post-EOFS blocks */ if (!xfs_verify_fsbno(sc->mp, fsbno)) continue; - bp = xfs_buf_incore(sc->mp->m_ddev_targp, + error = xfs_buf_incore(sc->mp->m_ddev_targp, XFS_FSB_TO_DADDR(sc->mp, fsbno), - XFS_FSB_TO_BB(sc->mp, 1), XBF_TRYLOCK); - if (bp) { - xfs_trans_bjoin(sc->tp, bp); - xfs_trans_binval(sc->tp, bp); - } + XFS_FSB_TO_BB(sc->mp, 1), XBF_TRYLOCK, &bp); + if (error) + continue; + + xfs_trans_bjoin(sc->tp, bp); + xfs_trans_binval(sc->tp, bp); } return 0; @@ -516,8 +516,8 @@ xrep_put_freelist( return error; /* Put the block on the AGFL. */ - error = xfs_alloc_put_freelist(sc->tp, sc->sa.agf_bp, sc->sa.agfl_bp, - agbno, 0); + error = xfs_alloc_put_freelist(sc->sa.pag, sc->tp, sc->sa.agf_bp, + sc->sa.agfl_bp, agbno, 0); if (error) return error; xfs_extent_busy_insert(sc->tp, sc->sa.pag, agbno, 1, @@ -536,13 +536,12 @@ xrep_reap_block( { struct xfs_btree_cur *cur; struct xfs_buf *agf_bp = NULL; - xfs_agnumber_t agno; xfs_agblock_t agbno; bool has_other_rmap; int error; - agno = XFS_FSB_TO_AGNO(sc->mp, fsbno); agbno = XFS_FSB_TO_AGBNO(sc->mp, fsbno); + ASSERT(XFS_FSB_TO_AGNO(sc->mp, fsbno) == sc->sa.pag->pag_agno); /* * If we are repairing per-inode metadata, we need to read in the AGF @@ -550,7 +549,7 @@ xrep_reap_block( * the AGF buffer that the setup functions already grabbed. */ if (sc->ip) { - error = xfs_alloc_read_agf(sc->mp, sc->tp, agno, 0, &agf_bp); + error = xfs_alloc_read_agf(sc->sa.pag, sc->tp, 0, &agf_bp); if (error) return error; } else { diff --git a/fs/xfs/scrub/rmap.c b/fs/xfs/scrub/rmap.c index 8dae0345c7df..229826b2e1c0 100644 --- a/fs/xfs/scrub/rmap.c +++ b/fs/xfs/scrub/rmap.c @@ -92,7 +92,7 @@ xchk_rmapbt_rec( { struct xfs_mount *mp = bs->cur->bc_mp; struct xfs_rmap_irec irec; - xfs_agnumber_t agno = bs->cur->bc_ag.pag->pag_agno; + struct xfs_perag *pag = bs->cur->bc_ag.pag; bool non_inode; bool is_unwritten; bool is_bmbt; @@ -121,8 +121,8 @@ xchk_rmapbt_rec( * Otherwise we must point somewhere past the static metadata * but before the end of the FS. Run the regular check. */ - if (!xfs_verify_agbno(mp, agno, irec.rm_startblock) || - !xfs_verify_agbno(mp, agno, irec.rm_startblock + + if (!xfs_verify_agbno(pag, irec.rm_startblock) || + !xfs_verify_agbno(pag, irec.rm_startblock + irec.rm_blockcount - 1)) xchk_btree_set_corrupt(bs->sc, bs->cur, 0); } diff --git a/fs/xfs/scrub/symlink.c b/fs/xfs/scrub/symlink.c index 599ee277bba2..75311f8daeeb 100644 --- a/fs/xfs/scrub/symlink.c +++ b/fs/xfs/scrub/symlink.c @@ -41,7 +41,7 @@ xchk_symlink( if (!S_ISLNK(VFS_I(ip)->i_mode)) return -ENOENT; - ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); len = ip->i_disk_size; /* Plausible size? */ @@ -52,8 +52,8 @@ xchk_symlink( /* Inline symlink? */ if (ifp->if_format == XFS_DINODE_FMT_LOCAL) { - if (len > XFS_IFORK_DSIZE(ip) || - len > strnlen(ifp->if_u1.if_data, XFS_IFORK_DSIZE(ip))) + if (len > xfs_inode_data_fork_size(ip) || + len > strnlen(ifp->if_u1.if_data, xfs_inode_data_fork_size(ip))) xchk_fblock_set_corrupt(sc, XFS_DATA_FORK, 0); goto out; } diff --git a/fs/xfs/xfs_attr_inactive.c b/fs/xfs/xfs_attr_inactive.c index 27265771f247..5db87b34fb6e 100644 --- a/fs/xfs/xfs_attr_inactive.c +++ b/fs/xfs/xfs_attr_inactive.c @@ -158,6 +158,7 @@ xfs_attr3_node_inactive( } child_fsb = be32_to_cpu(ichdr.btree[0].before); xfs_trans_brelse(*trans, bp); /* no locks for later trans */ + bp = NULL; /* * If this is the node level just above the leaves, simply loop @@ -211,12 +212,8 @@ xfs_attr3_node_inactive( &child_bp); if (error) return error; - error = bp->b_error; - if (error) { - xfs_trans_brelse(*trans, child_bp); - return error; - } xfs_trans_binval(*trans, child_bp); + child_bp = NULL; /* * If we're not done, re-read the parent to get the next @@ -233,6 +230,7 @@ xfs_attr3_node_inactive( bp->b_addr); child_fsb = be32_to_cpu(phdr.btree[i + 1].before); xfs_trans_brelse(*trans, bp); + bp = NULL; } /* * Atomically commit the whole invalidate stuff. @@ -338,7 +336,7 @@ xfs_attr_inactive( ASSERT(! XFS_NOT_DQATTACHED(mp, dp)); xfs_ilock(dp, lock_mode); - if (!XFS_IFORK_Q(dp)) + if (!xfs_inode_has_attr_fork(dp)) goto out_destroy_fork; xfs_iunlock(dp, lock_mode); @@ -351,7 +349,7 @@ xfs_attr_inactive( lock_mode = XFS_ILOCK_EXCL; xfs_ilock(dp, lock_mode); - if (!XFS_IFORK_Q(dp)) + if (!xfs_inode_has_attr_fork(dp)) goto out_cancel; /* @@ -362,12 +360,11 @@ xfs_attr_inactive( /* * Invalidate and truncate the attribute fork extents. Make sure the - * fork actually has attributes as otherwise the invalidation has no + * fork actually has xattr blocks as otherwise the invalidation has no * blocks to read and returns an error. In this case, just do the fork * removal below. */ - if (xfs_inode_hasattr(dp) && - dp->i_afp->if_format != XFS_DINODE_FMT_LOCAL) { + if (dp->i_af.if_nextents > 0) { error = xfs_attr3_root_inactive(&trans, dp); if (error) goto out_cancel; @@ -388,11 +385,7 @@ out_cancel: xfs_trans_cancel(trans); out_destroy_fork: /* kill the in-core attr fork before we drop the inode lock */ - if (dp->i_afp) { - xfs_idestroy_fork(dp->i_afp); - kmem_cache_free(xfs_ifork_cache, dp->i_afp); - dp->i_afp = NULL; - } + xfs_ifork_zap_attr(dp); if (lock_mode) xfs_iunlock(dp, lock_mode); return error; diff --git a/fs/xfs/xfs_attr_list.c b/fs/xfs/xfs_attr_list.c index 90a14e85e76d..99bbbe1a0e44 100644 --- a/fs/xfs/xfs_attr_list.c +++ b/fs/xfs/xfs_attr_list.c @@ -61,8 +61,7 @@ xfs_attr_shortform_list( int sbsize, nsbuf, count, i; int error = 0; - ASSERT(dp->i_afp != NULL); - sf = (struct xfs_attr_shortform *)dp->i_afp->if_u1.if_data; + sf = (struct xfs_attr_shortform *)dp->i_af.if_u1.if_data; ASSERT(sf != NULL); if (!sf->hdr.count) return 0; @@ -80,7 +79,7 @@ xfs_attr_shortform_list( */ if (context->bufsize == 0 || (XFS_ISRESET_CURSOR(cursor) && - (dp->i_afp->if_bytes + sf->hdr.count * 16) < context->bufsize)) { + (dp->i_af.if_bytes + sf->hdr.count * 16) < context->bufsize)) { for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) { if (XFS_IS_CORRUPT(context->dp->i_mount, !xfs_attr_namecheck(sfe->nameval, @@ -121,7 +120,7 @@ xfs_attr_shortform_list( for (i = 0, sfe = &sf->list[0]; i < sf->hdr.count; i++) { if (unlikely( ((char *)sfe < (char *)sf) || - ((char *)sfe >= ((char *)sf + dp->i_afp->if_bytes)))) { + ((char *)sfe >= ((char *)sf + dp->i_af.if_bytes)))) { XFS_CORRUPTION_ERROR("xfs_attr_shortform_list", XFS_ERRLEVEL_LOW, context->dp->i_mount, sfe, @@ -513,7 +512,7 @@ xfs_attr_list_ilocked( */ if (!xfs_inode_hasattr(dp)) return 0; - if (dp->i_afp->if_format == XFS_DINODE_FMT_LOCAL) + if (dp->i_af.if_format == XFS_DINODE_FMT_LOCAL) return xfs_attr_shortform_list(context); if (xfs_attr_is_leaf(dp)) return xfs_attr_leaf_list(context); diff --git a/fs/xfs/xfs_bmap_util.c b/fs/xfs/xfs_bmap_util.c index 85e1a26c92e8..04d0c2bff67c 100644 --- a/fs/xfs/xfs_bmap_util.c +++ b/fs/xfs/xfs_bmap_util.c @@ -256,7 +256,7 @@ xfs_bmap_count_blocks( xfs_filblks_t *count) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_btree_cur *cur; xfs_extlen_t btblocks = 0; int error; @@ -439,29 +439,28 @@ xfs_getbmap( whichfork = XFS_COW_FORK; else whichfork = XFS_DATA_FORK; - ifp = XFS_IFORK_PTR(ip, whichfork); xfs_ilock(ip, XFS_IOLOCK_SHARED); switch (whichfork) { case XFS_ATTR_FORK: - if (!XFS_IFORK_Q(ip)) - goto out_unlock_iolock; + lock = xfs_ilock_attr_map_shared(ip); + if (!xfs_inode_has_attr_fork(ip)) + goto out_unlock_ilock; max_len = 1LL << 32; - lock = xfs_ilock_attr_map_shared(ip); break; case XFS_COW_FORK: + lock = XFS_ILOCK_SHARED; + xfs_ilock(ip, lock); + /* No CoW fork? Just return */ - if (!ifp) - goto out_unlock_iolock; + if (!xfs_ifork_ptr(ip, whichfork)) + goto out_unlock_ilock; if (xfs_get_cowextsz_hint(ip)) max_len = mp->m_super->s_maxbytes; else max_len = XFS_ISIZE(ip); - - lock = XFS_ILOCK_SHARED; - xfs_ilock(ip, lock); break; case XFS_DATA_FORK: if (!(iflags & BMV_IF_DELALLOC) && @@ -491,6 +490,8 @@ xfs_getbmap( break; } + ifp = xfs_ifork_ptr(ip, whichfork); + switch (ifp->if_format) { case XFS_DINODE_FMT_EXTENTS: case XFS_DINODE_FMT_BTREE: @@ -1320,8 +1321,8 @@ xfs_swap_extents_check_format( * extent format... */ if (tifp->if_format == XFS_DINODE_FMT_BTREE) { - if (XFS_IFORK_Q(ip) && - XFS_BMAP_BMDR_SPACE(tifp->if_broot) > XFS_IFORK_BOFF(ip)) + if (xfs_inode_has_attr_fork(ip) && + XFS_BMAP_BMDR_SPACE(tifp->if_broot) > xfs_inode_fork_boff(ip)) return -EINVAL; if (tifp->if_nextents <= XFS_IFORK_MAXEXT(ip, XFS_DATA_FORK)) return -EINVAL; @@ -1329,8 +1330,8 @@ xfs_swap_extents_check_format( /* Reciprocal target->temp btree format checks */ if (ifp->if_format == XFS_DINODE_FMT_BTREE) { - if (XFS_IFORK_Q(tip) && - XFS_BMAP_BMDR_SPACE(ip->i_df.if_broot) > XFS_IFORK_BOFF(tip)) + if (xfs_inode_has_attr_fork(tip) && + XFS_BMAP_BMDR_SPACE(ip->i_df.if_broot) > xfs_inode_fork_boff(tip)) return -EINVAL; if (ifp->if_nextents <= XFS_IFORK_MAXEXT(tip, XFS_DATA_FORK)) return -EINVAL; @@ -1506,15 +1507,15 @@ xfs_swap_extent_forks( /* * Count the number of extended attribute blocks */ - if (XFS_IFORK_Q(ip) && ip->i_afp->if_nextents > 0 && - ip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) { + if (xfs_inode_has_attr_fork(ip) && ip->i_af.if_nextents > 0 && + ip->i_af.if_format != XFS_DINODE_FMT_LOCAL) { error = xfs_bmap_count_blocks(tp, ip, XFS_ATTR_FORK, &junk, &aforkblks); if (error) return error; } - if (XFS_IFORK_Q(tip) && tip->i_afp->if_nextents > 0 && - tip->i_afp->if_format != XFS_DINODE_FMT_LOCAL) { + if (xfs_inode_has_attr_fork(tip) && tip->i_af.if_nextents > 0 && + tip->i_af.if_format != XFS_DINODE_FMT_LOCAL) { error = xfs_bmap_count_blocks(tp, tip, XFS_ATTR_FORK, &junk, &taforkblks); if (error) diff --git a/fs/xfs/xfs_buf.c b/fs/xfs/xfs_buf.c index 5e8f40d8c052..584afe076923 100644 --- a/fs/xfs/xfs_buf.c +++ b/fs/xfs/xfs_buf.c @@ -21,7 +21,7 @@ #include "xfs_error.h" #include "xfs_ag.h" -static struct kmem_cache *xfs_buf_cache; +struct kmem_cache *xfs_buf_cache; /* * Locking orders @@ -294,6 +294,16 @@ xfs_buf_free_pages( bp->b_flags &= ~_XBF_PAGES; } +static void +xfs_buf_free_callback( + struct callback_head *cb) +{ + struct xfs_buf *bp = container_of(cb, struct xfs_buf, b_rcu); + + xfs_buf_free_maps(bp); + kmem_cache_free(xfs_buf_cache, bp); +} + static void xfs_buf_free( struct xfs_buf *bp) @@ -307,8 +317,7 @@ xfs_buf_free( else if (bp->b_flags & _XBF_KMEM) kmem_free(bp->b_addr); - xfs_buf_free_maps(bp); - kmem_cache_free(xfs_buf_cache, bp); + call_rcu(&bp->b_rcu, xfs_buf_free_callback); } static int @@ -503,100 +512,45 @@ xfs_buf_hash_destroy( rhashtable_destroy(&pag->pag_buf_hash); } -/* - * Look up a buffer in the buffer cache and return it referenced and locked - * in @found_bp. - * - * If @new_bp is supplied and we have a lookup miss, insert @new_bp into the - * cache. - * - * If XBF_TRYLOCK is set in @flags, only try to lock the buffer and return - * -EAGAIN if we fail to lock it. - * - * Return values are: - * -EFSCORRUPTED if have been supplied with an invalid address - * -EAGAIN on trylock failure - * -ENOENT if we fail to find a match and @new_bp was NULL - * 0, with @found_bp: - * - @new_bp if we inserted it into the cache - * - the buffer we found and locked. - */ static int -xfs_buf_find( +xfs_buf_map_verify( struct xfs_buftarg *btp, - struct xfs_buf_map *map, - int nmaps, - xfs_buf_flags_t flags, - struct xfs_buf *new_bp, - struct xfs_buf **found_bp) + struct xfs_buf_map *map) { - struct xfs_perag *pag; - struct xfs_buf *bp; - struct xfs_buf_map cmap = { .bm_bn = map[0].bm_bn }; xfs_daddr_t eofs; - int i; - - *found_bp = NULL; - - for (i = 0; i < nmaps; i++) - cmap.bm_len += map[i].bm_len; /* Check for IOs smaller than the sector size / not sector aligned */ - ASSERT(!(BBTOB(cmap.bm_len) < btp->bt_meta_sectorsize)); - ASSERT(!(BBTOB(cmap.bm_bn) & (xfs_off_t)btp->bt_meta_sectormask)); + ASSERT(!(BBTOB(map->bm_len) < btp->bt_meta_sectorsize)); + ASSERT(!(BBTOB(map->bm_bn) & (xfs_off_t)btp->bt_meta_sectormask)); /* * Corrupted block numbers can get through to here, unfortunately, so we * have to check that the buffer falls within the filesystem bounds. */ eofs = XFS_FSB_TO_BB(btp->bt_mount, btp->bt_mount->m_sb.sb_dblocks); - if (cmap.bm_bn < 0 || cmap.bm_bn >= eofs) { + if (map->bm_bn < 0 || map->bm_bn >= eofs) { xfs_alert(btp->bt_mount, "%s: daddr 0x%llx out of range, EOFS 0x%llx", - __func__, cmap.bm_bn, eofs); + __func__, map->bm_bn, eofs); WARN_ON(1); return -EFSCORRUPTED; } - - pag = xfs_perag_get(btp->bt_mount, - xfs_daddr_to_agno(btp->bt_mount, cmap.bm_bn)); - - spin_lock(&pag->pag_buf_lock); - bp = rhashtable_lookup_fast(&pag->pag_buf_hash, &cmap, - xfs_buf_hash_params); - if (bp) { - atomic_inc(&bp->b_hold); - goto found; - } - - /* No match found */ - if (!new_bp) { - XFS_STATS_INC(btp->bt_mount, xb_miss_locked); - spin_unlock(&pag->pag_buf_lock); - xfs_perag_put(pag); - return -ENOENT; - } - - /* the buffer keeps the perag reference until it is freed */ - new_bp->b_pag = pag; - rhashtable_insert_fast(&pag->pag_buf_hash, &new_bp->b_rhash_head, - xfs_buf_hash_params); - spin_unlock(&pag->pag_buf_lock); - *found_bp = new_bp; return 0; +} -found: - spin_unlock(&pag->pag_buf_lock); - xfs_perag_put(pag); - - if (!xfs_buf_trylock(bp)) { - if (flags & XBF_TRYLOCK) { - xfs_buf_rele(bp); - XFS_STATS_INC(btp->bt_mount, xb_busy_locked); +static int +xfs_buf_find_lock( + struct xfs_buf *bp, + xfs_buf_flags_t flags) +{ + if (flags & XBF_TRYLOCK) { + if (!xfs_buf_trylock(bp)) { + XFS_STATS_INC(bp->b_mount, xb_busy_locked); return -EAGAIN; } + } else { xfs_buf_lock(bp); - XFS_STATS_INC(btp->bt_mount, xb_get_locked_waited); + XFS_STATS_INC(bp->b_mount, xb_get_locked_waited); } /* @@ -609,57 +563,59 @@ found: bp->b_flags &= _XBF_KMEM | _XBF_PAGES; bp->b_ops = NULL; } - - trace_xfs_buf_find(bp, flags, _RET_IP_); - XFS_STATS_INC(btp->bt_mount, xb_get_locked); - *found_bp = bp; return 0; } -struct xfs_buf * -xfs_buf_incore( - struct xfs_buftarg *target, - xfs_daddr_t blkno, - size_t numblks, - xfs_buf_flags_t flags) +static inline int +xfs_buf_lookup( + struct xfs_perag *pag, + struct xfs_buf_map *map, + xfs_buf_flags_t flags, + struct xfs_buf **bpp) { - struct xfs_buf *bp; + struct xfs_buf *bp; int error; - DEFINE_SINGLE_BUF_MAP(map, blkno, numblks); - error = xfs_buf_find(target, &map, 1, flags, NULL, &bp); - if (error) - return NULL; - return bp; + rcu_read_lock(); + bp = rhashtable_lookup(&pag->pag_buf_hash, map, xfs_buf_hash_params); + if (!bp || !atomic_inc_not_zero(&bp->b_hold)) { + rcu_read_unlock(); + return -ENOENT; + } + rcu_read_unlock(); + + error = xfs_buf_find_lock(bp, flags); + if (error) { + xfs_buf_rele(bp); + return error; + } + + trace_xfs_buf_find(bp, flags, _RET_IP_); + *bpp = bp; + return 0; } /* - * Assembles a buffer covering the specified range. The code is optimised for - * cache hits, as metadata intensive workloads will see 3 orders of magnitude - * more hits than misses. + * Insert the new_bp into the hash table. This consumes the perag reference + * taken for the lookup regardless of the result of the insert. */ -int -xfs_buf_get_map( - struct xfs_buftarg *target, +static int +xfs_buf_find_insert( + struct xfs_buftarg *btp, + struct xfs_perag *pag, + struct xfs_buf_map *cmap, struct xfs_buf_map *map, int nmaps, xfs_buf_flags_t flags, struct xfs_buf **bpp) { - struct xfs_buf *bp; struct xfs_buf *new_bp; + struct xfs_buf *bp; int error; - *bpp = NULL; - error = xfs_buf_find(target, map, nmaps, flags, NULL, &bp); - if (!error) - goto found; - if (error != -ENOENT) - return error; - - error = _xfs_buf_alloc(target, map, nmaps, flags, &new_bp); + error = _xfs_buf_alloc(btp, map, nmaps, flags, &new_bp); if (error) - return error; + goto out_drop_pag; /* * For buffers that fit entirely within a single page, first attempt to @@ -674,18 +630,94 @@ xfs_buf_get_map( goto out_free_buf; } - error = xfs_buf_find(target, map, nmaps, flags, new_bp, &bp); - if (error) + spin_lock(&pag->pag_buf_lock); + bp = rhashtable_lookup_get_insert_fast(&pag->pag_buf_hash, + &new_bp->b_rhash_head, xfs_buf_hash_params); + if (IS_ERR(bp)) { + error = PTR_ERR(bp); + spin_unlock(&pag->pag_buf_lock); goto out_free_buf; + } + if (bp) { + /* found an existing buffer */ + atomic_inc(&bp->b_hold); + spin_unlock(&pag->pag_buf_lock); + error = xfs_buf_find_lock(bp, flags); + if (error) + xfs_buf_rele(bp); + else + *bpp = bp; + goto out_free_buf; + } - if (bp != new_bp) - xfs_buf_free(new_bp); + /* The new buffer keeps the perag reference until it is freed. */ + new_bp->b_pag = pag; + spin_unlock(&pag->pag_buf_lock); + *bpp = new_bp; + return 0; -found: +out_free_buf: + xfs_buf_free(new_bp); +out_drop_pag: + xfs_perag_put(pag); + return error; +} + +/* + * Assembles a buffer covering the specified range. The code is optimised for + * cache hits, as metadata intensive workloads will see 3 orders of magnitude + * more hits than misses. + */ +int +xfs_buf_get_map( + struct xfs_buftarg *btp, + struct xfs_buf_map *map, + int nmaps, + xfs_buf_flags_t flags, + struct xfs_buf **bpp) +{ + struct xfs_perag *pag; + struct xfs_buf *bp = NULL; + struct xfs_buf_map cmap = { .bm_bn = map[0].bm_bn }; + int error; + int i; + + for (i = 0; i < nmaps; i++) + cmap.bm_len += map[i].bm_len; + + error = xfs_buf_map_verify(btp, &cmap); + if (error) + return error; + + pag = xfs_perag_get(btp->bt_mount, + xfs_daddr_to_agno(btp->bt_mount, cmap.bm_bn)); + + error = xfs_buf_lookup(pag, &cmap, flags, &bp); + if (error && error != -ENOENT) + goto out_put_perag; + + /* cache hits always outnumber misses by at least 10:1 */ + if (unlikely(!bp)) { + XFS_STATS_INC(btp->bt_mount, xb_miss_locked); + + if (flags & XBF_INCORE) + goto out_put_perag; + + /* xfs_buf_find_insert() consumes the perag reference. */ + error = xfs_buf_find_insert(btp, pag, &cmap, map, nmaps, + flags, &bp); + if (error) + return error; + } else { + XFS_STATS_INC(btp->bt_mount, xb_get_locked); + xfs_perag_put(pag); + } + + /* We do not hold a perag reference anymore. */ if (!bp->b_addr) { error = _xfs_buf_map_pages(bp, flags); if (unlikely(error)) { - xfs_warn_ratelimited(target->bt_mount, + xfs_warn_ratelimited(btp->bt_mount, "%s: failed to map %u pages", __func__, bp->b_page_count); xfs_buf_relse(bp); @@ -700,12 +732,13 @@ found: if (!(flags & XBF_READ)) xfs_buf_ioerror(bp, 0); - XFS_STATS_INC(target->bt_mount, xb_get); + XFS_STATS_INC(btp->bt_mount, xb_get); trace_xfs_buf_get(bp, flags, _RET_IP_); *bpp = bp; return 0; -out_free_buf: - xfs_buf_free(new_bp); + +out_put_perag: + xfs_perag_put(pag); return error; } @@ -2275,29 +2308,6 @@ xfs_buf_delwri_pushbuf( return error; } -int __init -xfs_buf_init(void) -{ - xfs_buf_cache = kmem_cache_create("xfs_buf", sizeof(struct xfs_buf), 0, - SLAB_HWCACHE_ALIGN | - SLAB_RECLAIM_ACCOUNT | - SLAB_MEM_SPREAD, - NULL); - if (!xfs_buf_cache) - goto out; - - return 0; - - out: - return -ENOMEM; -} - -void -xfs_buf_terminate(void) -{ - kmem_cache_destroy(xfs_buf_cache); -} - void xfs_buf_set_ref(struct xfs_buf *bp, int lru_ref) { /* diff --git a/fs/xfs/xfs_buf.h b/fs/xfs/xfs_buf.h index 1ee3056ff9cf..549c60942208 100644 --- a/fs/xfs/xfs_buf.h +++ b/fs/xfs/xfs_buf.h @@ -15,6 +15,8 @@ #include #include +extern struct kmem_cache *xfs_buf_cache; + /* * Base types */ @@ -42,9 +44,11 @@ struct xfs_buf; #define _XBF_DELWRI_Q (1u << 22)/* buffer on a delwri queue */ /* flags used only as arguments to access routines */ +#define XBF_INCORE (1u << 29)/* lookup only, return if found in cache */ #define XBF_TRYLOCK (1u << 30)/* lock requested, but do not wait */ #define XBF_UNMAPPED (1u << 31)/* do not map the buffer */ + typedef unsigned int xfs_buf_flags_t; #define XFS_BUF_FLAGS \ @@ -63,6 +67,7 @@ typedef unsigned int xfs_buf_flags_t; { _XBF_KMEM, "KMEM" }, \ { _XBF_DELWRI_Q, "DELWRI_Q" }, \ /* The following interface flags should never be set */ \ + { XBF_INCORE, "INCORE" }, \ { XBF_TRYLOCK, "TRYLOCK" }, \ { XBF_UNMAPPED, "UNMAPPED" } @@ -193,13 +198,10 @@ struct xfs_buf { int b_last_error; const struct xfs_buf_ops *b_ops; + struct rcu_head b_rcu; }; /* Finding and Reading Buffers */ -struct xfs_buf *xfs_buf_incore(struct xfs_buftarg *target, - xfs_daddr_t blkno, size_t numblks, - xfs_buf_flags_t flags); - int xfs_buf_get_map(struct xfs_buftarg *target, struct xfs_buf_map *map, int nmaps, xfs_buf_flags_t flags, struct xfs_buf **bpp); int xfs_buf_read_map(struct xfs_buftarg *target, struct xfs_buf_map *map, @@ -209,6 +211,19 @@ void xfs_buf_readahead_map(struct xfs_buftarg *target, struct xfs_buf_map *map, int nmaps, const struct xfs_buf_ops *ops); +static inline int +xfs_buf_incore( + struct xfs_buftarg *target, + xfs_daddr_t blkno, + size_t numblks, + xfs_buf_flags_t flags, + struct xfs_buf **bpp) +{ + DEFINE_SINGLE_BUF_MAP(map, blkno, numblks); + + return xfs_buf_get_map(target, &map, 1, XBF_INCORE | flags, bpp); +} + static inline int xfs_buf_get( struct xfs_buftarg *target, @@ -294,10 +309,6 @@ extern int xfs_buf_delwri_submit(struct list_head *); extern int xfs_buf_delwri_submit_nowait(struct list_head *); extern int xfs_buf_delwri_pushbuf(struct xfs_buf *, struct list_head *); -/* Buffer Daemon Setup Routines */ -extern int xfs_buf_init(void); -extern void xfs_buf_terminate(void); - static inline xfs_daddr_t xfs_buf_daddr(struct xfs_buf *bp) { return bp->b_maps[0].bm_bn; diff --git a/fs/xfs/xfs_dir2_readdir.c b/fs/xfs/xfs_dir2_readdir.c index a7174a5b3203..e295fc8062d8 100644 --- a/fs/xfs/xfs_dir2_readdir.c +++ b/fs/xfs/xfs_dir2_readdir.c @@ -248,7 +248,7 @@ xfs_dir2_leaf_readbuf( struct xfs_inode *dp = args->dp; struct xfs_buf *bp = NULL; struct xfs_da_geometry *geo = args->geo; - struct xfs_ifork *ifp = XFS_IFORK_PTR(dp, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(dp, XFS_DATA_FORK); struct xfs_bmbt_irec map; struct blk_plug plug; xfs_dir2_off_t new_off; diff --git a/fs/xfs/xfs_discard.c b/fs/xfs/xfs_discard.c index c6fe3f6ebb6b..bfc829c07f03 100644 --- a/fs/xfs/xfs_discard.c +++ b/fs/xfs/xfs_discard.c @@ -45,7 +45,7 @@ xfs_trim_extents( */ xfs_log_force(mp, XFS_LOG_SYNC); - error = xfs_alloc_read_agf(mp, NULL, agno, 0, &agbp); + error = xfs_alloc_read_agf(pag, NULL, 0, &agbp); if (error) goto out_put_perag; agf = agbp->b_addr; diff --git a/fs/xfs/xfs_dquot.c b/fs/xfs/xfs_dquot.c index 5a6c3c3c4de2..8fb90da89787 100644 --- a/fs/xfs/xfs_dquot.c +++ b/fs/xfs/xfs_dquot.c @@ -549,7 +549,7 @@ xfs_dquot_check_type( * at the same time. The non-user quota file can be switched between * group and project quota uses depending on the mount options, which * means that we can encounter the other type when we try to load quota - * defaults. Quotacheck will soon reset the the entire quota file + * defaults. Quotacheck will soon reset the entire quota file * (including the root dquot) anyway, but don't log scary corruption * reports to dmesg. */ diff --git a/fs/xfs/xfs_extfree_item.c b/fs/xfs/xfs_extfree_item.c index 765be054dffe..27ccfcd82f04 100644 --- a/fs/xfs/xfs_extfree_item.c +++ b/fs/xfs/xfs_extfree_item.c @@ -11,6 +11,7 @@ #include "xfs_bit.h" #include "xfs_shared.h" #include "xfs_mount.h" +#include "xfs_ag.h" #include "xfs_defer.h" #include "xfs_trans.h" #include "xfs_trans_priv.h" @@ -187,12 +188,12 @@ xfs_efi_copy_format(xfs_log_iovec_t *buf, xfs_efi_log_format_t *dst_efi_fmt) { xfs_efi_log_format_t *src_efi_fmt = buf->i_addr; uint i; - uint len = sizeof(xfs_efi_log_format_t) + - (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t); - uint len32 = sizeof(xfs_efi_log_format_32_t) + - (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t); - uint len64 = sizeof(xfs_efi_log_format_64_t) + - (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t); + uint len = sizeof(xfs_efi_log_format_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_t); + uint len32 = sizeof(xfs_efi_log_format_32_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_32_t); + uint len64 = sizeof(xfs_efi_log_format_64_t) + + (src_efi_fmt->efi_nextents - 1) * sizeof(xfs_extent_64_t); if (buf->i_len == len) { memcpy((char *)dst_efi_fmt, (char*)src_efi_fmt, len); @@ -551,6 +552,7 @@ xfs_agfl_free_finish_item( xfs_agnumber_t agno; xfs_agblock_t agbno; uint next_extent; + struct xfs_perag *pag; free = container_of(item, struct xfs_extent_free_item, xefi_list); ASSERT(free->xefi_blockcount == 1); @@ -560,9 +562,11 @@ xfs_agfl_free_finish_item( trace_xfs_agfl_free_deferred(mp, agno, 0, agbno, free->xefi_blockcount); - error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp); + pag = xfs_perag_get(mp, agno); + error = xfs_alloc_read_agf(pag, tp, 0, &agbp); if (!error) error = xfs_free_agfl_block(tp, agno, agbno, agbp, &oinfo); + xfs_perag_put(pag); /* * Mark the transaction dirty, even on error. This ensures the diff --git a/fs/xfs/xfs_filestream.c b/fs/xfs/xfs_filestream.c index be9bcf8a1f99..34b21a29c39b 100644 --- a/fs/xfs/xfs_filestream.c +++ b/fs/xfs/xfs_filestream.c @@ -126,7 +126,7 @@ xfs_filestream_pick_ag( pag = xfs_perag_get(mp, ag); if (!pag->pagf_init) { - err = xfs_alloc_pagf_init(mp, NULL, ag, trylock); + err = xfs_alloc_read_agf(pag, NULL, trylock, NULL); if (err) { if (err != -EAGAIN) { xfs_perag_put(pag); @@ -181,7 +181,7 @@ next_ag: if (ag != startag) continue; - /* Allow sleeping in xfs_alloc_pagf_init() on the 2nd pass. */ + /* Allow sleeping in xfs_alloc_read_agf() on the 2nd pass. */ if (trylock != 0) { trylock = 0; continue; diff --git a/fs/xfs/xfs_fsmap.c b/fs/xfs/xfs_fsmap.c index bb23199f65c3..d8337274c74d 100644 --- a/fs/xfs/xfs_fsmap.c +++ b/fs/xfs/xfs_fsmap.c @@ -642,8 +642,7 @@ __xfs_getfsmap_datadev( info->agf_bp = NULL; } - error = xfs_alloc_read_agf(mp, tp, pag->pag_agno, 0, - &info->agf_bp); + error = xfs_alloc_read_agf(pag, tp, 0, &info->agf_bp); if (error) break; diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c index d4a77c53f94b..5fe9af24dfcd 100644 --- a/fs/xfs/xfs_fsops.c +++ b/fs/xfs/xfs_fsops.c @@ -41,6 +41,7 @@ xfs_resizefs_init_new_ags( xfs_agnumber_t oagcount, xfs_agnumber_t nagcount, xfs_rfsblock_t delta, + struct xfs_perag *last_pag, bool *lastag_extended) { struct xfs_mount *mp = tp->t_mountp; @@ -73,7 +74,7 @@ xfs_resizefs_init_new_ags( if (delta) { *lastag_extended = true; - error = xfs_ag_extend_space(mp, tp, id, delta); + error = xfs_ag_extend_space(last_pag, tp, delta); } return error; } @@ -96,6 +97,7 @@ xfs_growfs_data_private( xfs_agnumber_t oagcount; struct xfs_trans *tp; struct aghdr_init_data id = {}; + struct xfs_perag *last_pag; nb = in->newblocks; error = xfs_sb_validate_fsb_count(&mp->m_sb, nb); @@ -128,10 +130,9 @@ xfs_growfs_data_private( return -EINVAL; oagcount = mp->m_sb.sb_agcount; - /* allocate the new per-ag structures */ if (nagcount > oagcount) { - error = xfs_initialize_perag(mp, nagcount, &nagimax); + error = xfs_initialize_perag(mp, nagcount, nb, &nagimax); if (error) return error; } else if (nagcount < oagcount) { @@ -145,15 +146,17 @@ xfs_growfs_data_private( if (error) return error; + last_pag = xfs_perag_get(mp, oagcount - 1); if (delta > 0) { error = xfs_resizefs_init_new_ags(tp, &id, oagcount, nagcount, - delta, &lastag_extended); + delta, last_pag, &lastag_extended); } else { xfs_warn_mount(mp, XFS_OPSTATE_WARNED_SHRINK, "EXPERIMENTAL online shrink feature in use. Use at your own risk!"); - error = xfs_ag_shrink_space(mp, &tp, nagcount - 1, -delta); + error = xfs_ag_shrink_space(last_pag, &tp, -delta); } + xfs_perag_put(last_pag); if (error) goto out_trans_cancel; diff --git a/fs/xfs/xfs_icache.c b/fs/xfs/xfs_icache.c index 2609825d53ee..e3b2304bb4d2 100644 --- a/fs/xfs/xfs_icache.c +++ b/fs/xfs/xfs_icache.c @@ -98,8 +98,9 @@ xfs_inode_alloc( ip->i_ino = ino; ip->i_mount = mp; memset(&ip->i_imap, 0, sizeof(struct xfs_imap)); - ip->i_afp = NULL; ip->i_cowfp = NULL; + memset(&ip->i_af, 0, sizeof(ip->i_af)); + ip->i_af.if_format = XFS_DINODE_FMT_EXTENTS; memset(&ip->i_df, 0, sizeof(ip->i_df)); ip->i_flags = 0; ip->i_delayed_blks = 0; @@ -111,6 +112,8 @@ xfs_inode_alloc( INIT_WORK(&ip->i_ioend_work, xfs_end_io); INIT_LIST_HEAD(&ip->i_ioend_list); spin_lock_init(&ip->i_ioend_lock); + ip->i_next_unlinked = NULLAGINO; + ip->i_prev_unlinked = NULLAGINO; return ip; } @@ -130,10 +133,8 @@ xfs_inode_free_callback( break; } - if (ip->i_afp) { - xfs_idestroy_fork(ip->i_afp); - kmem_cache_free(xfs_ifork_cache, ip->i_afp); - } + xfs_ifork_zap_attr(ip); + if (ip->i_cowfp) { xfs_idestroy_fork(ip->i_cowfp); kmem_cache_free(xfs_ifork_cache, ip->i_cowfp); @@ -912,6 +913,7 @@ reclaim: ip->i_checked = 0; spin_unlock(&ip->i_flags_lock); + ASSERT(!ip->i_itemp || ip->i_itemp->ili_item.li_buf == NULL); xfs_iunlock(ip, XFS_ILOCK_EXCL); XFS_STATS_INC(ip->i_mount, xs_ig_reclaims); @@ -1774,7 +1776,7 @@ xfs_check_delalloc( struct xfs_inode *ip, int whichfork) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); struct xfs_bmbt_irec got; struct xfs_iext_cursor icur; diff --git a/fs/xfs/xfs_inode.c b/fs/xfs/xfs_inode.c index 3e1c62ffa4f7..3022918bf96a 100644 --- a/fs/xfs/xfs_inode.c +++ b/fs/xfs/xfs_inode.c @@ -20,6 +20,7 @@ #include "xfs_trans.h" #include "xfs_buf_item.h" #include "xfs_inode_item.h" +#include "xfs_iunlink_item.h" #include "xfs_ialloc.h" #include "xfs_bmap.h" #include "xfs_bmap_util.h" @@ -125,7 +126,7 @@ xfs_ilock_attr_map_shared( { uint lock_mode = XFS_ILOCK_SHARED; - if (ip->i_afp && xfs_need_iread_extents(ip->i_afp)) + if (xfs_inode_has_attr_fork(ip) && xfs_need_iread_extents(&ip->i_af)) lock_mode = XFS_ILOCK_EXCL; xfs_ilock(ip, lock_mode); return lock_mode; @@ -635,7 +636,7 @@ xfs_ip2xflags( flags |= FS_XFLAG_COWEXTSIZE; } - if (XFS_IFORK_Q(ip)) + if (xfs_inode_has_attr_fork(ip)) flags |= FS_XFLAG_HASATTR; return flags; } @@ -893,7 +894,7 @@ xfs_init_new_inode( */ if (init_xattrs && xfs_has_attr(mp)) { ip->i_forkoff = xfs_default_attroffset(ip) >> 3; - ip->i_afp = xfs_ifork_alloc(XFS_DINODE_FMT_EXTENTS, 0); + xfs_ifork_init_attr(ip, XFS_DINODE_FMT_EXTENTS, 0); } /* @@ -1293,8 +1294,8 @@ xfs_itruncate_clear_reflink_flags( if (!xfs_is_reflink_inode(ip)) return; - dfork = XFS_IFORK_PTR(ip, XFS_DATA_FORK); - cfork = XFS_IFORK_PTR(ip, XFS_COW_FORK); + dfork = xfs_ifork_ptr(ip, XFS_DATA_FORK); + cfork = xfs_ifork_ptr(ip, XFS_COW_FORK); if (dfork->if_bytes == 0 && cfork->if_bytes == 0) ip->i_diflags2 &= ~XFS_DIFLAG2_REFLINK; if (cfork->if_bytes == 0) @@ -1643,7 +1644,7 @@ xfs_inode_needs_inactive( struct xfs_inode *ip) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *cow_ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_ifork *cow_ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); /* * If the inode is already free, then there can be nothing @@ -1762,13 +1763,12 @@ xfs_inactive( * now. The code calls a routine that recursively deconstructs the * attribute fork. If also blows away the in-core attribute fork. */ - if (XFS_IFORK_Q(ip)) { + if (xfs_inode_has_attr_fork(ip)) { error = xfs_attr_inactive(ip); if (error) goto out; } - ASSERT(!ip->i_afp); ASSERT(ip->i_forkoff == 0); /* @@ -1801,195 +1801,69 @@ out: * because we must walk that list to find the inode that points to the inode * being removed from the unlinked hash bucket list. * - * What if we modelled the unlinked list as a collection of records capturing - * "X.next_unlinked = Y" relations? If we indexed those records on Y, we'd - * have a fast way to look up unlinked list predecessors, which avoids the - * slow list walk. That's exactly what we do here (in-core) with a per-AG - * rhashtable. + * Hence we keep an in-memory double linked list to link each inode on an + * unlinked list. Because there are 64 unlinked lists per AGI, keeping pointer + * based lists would require having 64 list heads in the perag, one for each + * list. This is expensive in terms of memory (think millions of AGs) and cache + * misses on lookups. Instead, use the fact that inodes on the unlinked list + * must be referenced at the VFS level to keep them on the list and hence we + * have an existence guarantee for inodes on the unlinked list. * - * Because this is a backref cache, we ignore operational failures since the - * iunlink code can fall back to the slow bucket walk. The only errors that - * should bubble out are for obviously incorrect situations. - * - * All users of the backref cache MUST hold the AGI buffer lock to serialize - * access or have otherwise provided for concurrency control. + * Given we have an existence guarantee, we can use lockless inode cache lookups + * to resolve aginos to xfs inodes. This means we only need 8 bytes per inode + * for the double linked unlinked list, and we don't need any extra locking to + * keep the list safe as all manipulations are done under the AGI buffer lock. + * Keeping the list up to date does not require memory allocation, just finding + * the XFS inode and updating the next/prev unlinked list aginos. */ -/* Capture a "X.next_unlinked = Y" relationship. */ -struct xfs_iunlink { - struct rhash_head iu_rhash_head; - xfs_agino_t iu_agino; /* X */ - xfs_agino_t iu_next_unlinked; /* Y */ -}; - -/* Unlinked list predecessor lookup hashtable construction */ -static int -xfs_iunlink_obj_cmpfn( - struct rhashtable_compare_arg *arg, - const void *obj) -{ - const xfs_agino_t *key = arg->key; - const struct xfs_iunlink *iu = obj; - - if (iu->iu_next_unlinked != *key) - return 1; - return 0; -} - -static const struct rhashtable_params xfs_iunlink_hash_params = { - .min_size = XFS_AGI_UNLINKED_BUCKETS, - .key_len = sizeof(xfs_agino_t), - .key_offset = offsetof(struct xfs_iunlink, - iu_next_unlinked), - .head_offset = offsetof(struct xfs_iunlink, iu_rhash_head), - .automatic_shrinking = true, - .obj_cmpfn = xfs_iunlink_obj_cmpfn, -}; - /* - * Return X, where X.next_unlinked == @agino. Returns NULLAGINO if no such - * relation is found. + * Find an inode on the unlinked list. This does not take references to the + * inode as we have existence guarantees by holding the AGI buffer lock and that + * only unlinked, referenced inodes can be on the unlinked inode list. If we + * don't find the inode in cache, then let the caller handle the situation. */ -static xfs_agino_t -xfs_iunlink_lookup_backref( +static struct xfs_inode * +xfs_iunlink_lookup( struct xfs_perag *pag, xfs_agino_t agino) { - struct xfs_iunlink *iu; + struct xfs_inode *ip; - iu = rhashtable_lookup_fast(&pag->pagi_unlinked_hash, &agino, - xfs_iunlink_hash_params); - return iu ? iu->iu_agino : NULLAGINO; -} + rcu_read_lock(); + ip = radix_tree_lookup(&pag->pag_ici_root, agino); -/* - * Take ownership of an iunlink cache entry and insert it into the hash table. - * If successful, the entry will be owned by the cache; if not, it is freed. - * Either way, the caller does not own @iu after this call. - */ -static int -xfs_iunlink_insert_backref( - struct xfs_perag *pag, - struct xfs_iunlink *iu) -{ - int error; - - error = rhashtable_insert_fast(&pag->pagi_unlinked_hash, - &iu->iu_rhash_head, xfs_iunlink_hash_params); /* - * Fail loudly if there already was an entry because that's a sign of - * corruption of in-memory data. Also fail loudly if we see an error - * code we didn't anticipate from the rhashtable code. Currently we - * only anticipate ENOMEM. + * Inode not in memory or in RCU freeing limbo should not happen. + * Warn about this and let the caller handle the failure. */ - if (error) { - WARN(error != -ENOMEM, "iunlink cache insert error %d", error); - kmem_free(iu); + if (WARN_ON_ONCE(!ip || !ip->i_ino)) { + rcu_read_unlock(); + return NULL; } - /* - * Absorb any runtime errors that aren't a result of corruption because - * this is a cache and we can always fall back to bucket list scanning. - */ - if (error != 0 && error != -EEXIST) - error = 0; - return error; + ASSERT(!xfs_iflags_test(ip, XFS_IRECLAIMABLE | XFS_IRECLAIM)); + rcu_read_unlock(); + return ip; } -/* Remember that @prev_agino.next_unlinked = @this_agino. */ +/* Update the prev pointer of the next agino. */ static int -xfs_iunlink_add_backref( +xfs_iunlink_update_backref( struct xfs_perag *pag, xfs_agino_t prev_agino, - xfs_agino_t this_agino) + xfs_agino_t next_agino) { - struct xfs_iunlink *iu; + struct xfs_inode *ip; - if (XFS_TEST_ERROR(false, pag->pag_mount, XFS_ERRTAG_IUNLINK_FALLBACK)) + /* No update necessary if we are at the end of the list. */ + if (next_agino == NULLAGINO) return 0; - iu = kmem_zalloc(sizeof(*iu), KM_NOFS); - iu->iu_agino = prev_agino; - iu->iu_next_unlinked = this_agino; - - return xfs_iunlink_insert_backref(pag, iu); -} - -/* - * Replace X.next_unlinked = @agino with X.next_unlinked = @next_unlinked. - * If @next_unlinked is NULLAGINO, we drop the backref and exit. If there - * wasn't any such entry then we don't bother. - */ -static int -xfs_iunlink_change_backref( - struct xfs_perag *pag, - xfs_agino_t agino, - xfs_agino_t next_unlinked) -{ - struct xfs_iunlink *iu; - int error; - - /* Look up the old entry; if there wasn't one then exit. */ - iu = rhashtable_lookup_fast(&pag->pagi_unlinked_hash, &agino, - xfs_iunlink_hash_params); - if (!iu) - return 0; - - /* - * Remove the entry. This shouldn't ever return an error, but if we - * couldn't remove the old entry we don't want to add it again to the - * hash table, and if the entry disappeared on us then someone's - * violated the locking rules and we need to fail loudly. Either way - * we cannot remove the inode because internal state is or would have - * been corrupt. - */ - error = rhashtable_remove_fast(&pag->pagi_unlinked_hash, - &iu->iu_rhash_head, xfs_iunlink_hash_params); - if (error) - return error; - - /* If there is no new next entry just free our item and return. */ - if (next_unlinked == NULLAGINO) { - kmem_free(iu); - return 0; - } - - /* Update the entry and re-add it to the hash table. */ - iu->iu_next_unlinked = next_unlinked; - return xfs_iunlink_insert_backref(pag, iu); -} - -/* Set up the in-core predecessor structures. */ -int -xfs_iunlink_init( - struct xfs_perag *pag) -{ - return rhashtable_init(&pag->pagi_unlinked_hash, - &xfs_iunlink_hash_params); -} - -/* Free the in-core predecessor structures. */ -static void -xfs_iunlink_free_item( - void *ptr, - void *arg) -{ - struct xfs_iunlink *iu = ptr; - bool *freed_anything = arg; - - *freed_anything = true; - kmem_free(iu); -} - -void -xfs_iunlink_destroy( - struct xfs_perag *pag) -{ - bool freed_anything = false; - - rhashtable_free_and_destroy(&pag->pagi_unlinked_hash, - xfs_iunlink_free_item, &freed_anything); - - ASSERT(freed_anything == false || xfs_is_shutdown(pag->pag_mount)); + ip = xfs_iunlink_lookup(pag, next_agino); + if (!ip) + return -EFSCORRUPTED; + ip->i_prev_unlinked = prev_agino; + return 0; } /* @@ -2008,7 +1882,7 @@ xfs_iunlink_update_bucket( xfs_agino_t old_value; int offset; - ASSERT(xfs_verify_agino_or_null(tp->t_mountp, pag->pag_agno, new_agino)); + ASSERT(xfs_verify_agino_or_null(pag, new_agino)); old_value = be32_to_cpu(agi->agi_unlinked[bucket_index]); trace_xfs_iunlink_update_bucket(tp->t_mountp, pag->pag_agno, bucket_index, @@ -2031,88 +1905,53 @@ xfs_iunlink_update_bucket( return 0; } -/* Set an on-disk inode's next_unlinked pointer. */ -STATIC void -xfs_iunlink_update_dinode( +static int +xfs_iunlink_insert_inode( struct xfs_trans *tp, struct xfs_perag *pag, - xfs_agino_t agino, - struct xfs_buf *ibp, - struct xfs_dinode *dip, - struct xfs_imap *imap, - xfs_agino_t next_agino) + struct xfs_buf *agibp, + struct xfs_inode *ip) { struct xfs_mount *mp = tp->t_mountp; - int offset; - - ASSERT(xfs_verify_agino_or_null(mp, pag->pag_agno, next_agino)); - - trace_xfs_iunlink_update_dinode(mp, pag->pag_agno, agino, - be32_to_cpu(dip->di_next_unlinked), next_agino); - - dip->di_next_unlinked = cpu_to_be32(next_agino); - offset = imap->im_boffset + - offsetof(struct xfs_dinode, di_next_unlinked); - - /* need to recalc the inode CRC if appropriate */ - xfs_dinode_calc_crc(mp, dip); - xfs_trans_inode_buf(tp, ibp); - xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1); -} - -/* Set an in-core inode's unlinked pointer and return the old value. */ -STATIC int -xfs_iunlink_update_inode( - struct xfs_trans *tp, - struct xfs_inode *ip, - struct xfs_perag *pag, - xfs_agino_t next_agino, - xfs_agino_t *old_next_agino) -{ - struct xfs_mount *mp = tp->t_mountp; - struct xfs_dinode *dip; - struct xfs_buf *ibp; - xfs_agino_t old_value; + struct xfs_agi *agi = agibp->b_addr; + xfs_agino_t next_agino; + xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); + short bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; int error; - ASSERT(xfs_verify_agino_or_null(mp, pag->pag_agno, next_agino)); - - error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp); - if (error) - return error; - dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset); - - /* Make sure the old pointer isn't garbage. */ - old_value = be32_to_cpu(dip->di_next_unlinked); - if (!xfs_verify_agino_or_null(mp, pag->pag_agno, old_value)) { - xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip, - sizeof(*dip), __this_address); - error = -EFSCORRUPTED; - goto out; + /* + * Get the index into the agi hash table for the list this inode will + * go on. Make sure the pointer isn't garbage and that this inode + * isn't already on the list. + */ + next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); + if (next_agino == agino || + !xfs_verify_agino_or_null(pag, next_agino)) { + xfs_buf_mark_corrupt(agibp); + return -EFSCORRUPTED; } /* - * Since we're updating a linked list, we should never find that the - * current pointer is the same as the new value, unless we're - * terminating the list. + * Update the prev pointer in the next inode to point back to this + * inode. */ - *old_next_agino = old_value; - if (old_value == next_agino) { - if (next_agino != NULLAGINO) { - xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, - dip, sizeof(*dip), __this_address); - error = -EFSCORRUPTED; - } - goto out; + error = xfs_iunlink_update_backref(pag, agino, next_agino); + if (error) + return error; + + if (next_agino != NULLAGINO) { + /* + * There is already another inode in the bucket, so point this + * inode to the current head of the list. + */ + error = xfs_iunlink_log_inode(tp, ip, pag, next_agino); + if (error) + return error; + ip->i_next_unlinked = next_agino; } - /* Ok, update the new pointer. */ - xfs_iunlink_update_dinode(tp, pag, XFS_INO_TO_AGINO(mp, ip->i_ino), - ibp, dip, &ip->i_imap, next_agino); - return 0; -out: - xfs_trans_brelse(tp, ibp); - return error; + /* Point the head of the list to point to this inode. */ + return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino); } /* @@ -2129,11 +1968,7 @@ xfs_iunlink( { struct xfs_mount *mp = tp->t_mountp; struct xfs_perag *pag; - struct xfs_agi *agi; struct xfs_buf *agibp; - xfs_agino_t next_agino; - xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); - short bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; int error; ASSERT(VFS_I(ip)->i_nlink == 0); @@ -2143,166 +1978,80 @@ xfs_iunlink( pag = xfs_perag_get(mp, XFS_INO_TO_AGNO(mp, ip->i_ino)); /* Get the agi buffer first. It ensures lock ordering on the list. */ - error = xfs_read_agi(mp, tp, pag->pag_agno, &agibp); + error = xfs_read_agi(pag, tp, &agibp); if (error) goto out; - agi = agibp->b_addr; - /* - * Get the index into the agi hash table for the list this inode will - * go on. Make sure the pointer isn't garbage and that this inode - * isn't already on the list. - */ - next_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); - if (next_agino == agino || - !xfs_verify_agino_or_null(mp, pag->pag_agno, next_agino)) { - xfs_buf_mark_corrupt(agibp); - error = -EFSCORRUPTED; - goto out; - } - - if (next_agino != NULLAGINO) { - xfs_agino_t old_agino; - - /* - * There is already another inode in the bucket, so point this - * inode to the current head of the list. - */ - error = xfs_iunlink_update_inode(tp, ip, pag, next_agino, - &old_agino); - if (error) - goto out; - ASSERT(old_agino == NULLAGINO); - - /* - * agino has been unlinked, add a backref from the next inode - * back to agino. - */ - error = xfs_iunlink_add_backref(pag, agino, next_agino); - if (error) - goto out; - } - - /* Point the head of the list to point to this inode. */ - error = xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, agino); + error = xfs_iunlink_insert_inode(tp, pag, agibp, ip); out: xfs_perag_put(pag); return error; } -/* Return the imap, dinode pointer, and buffer for an inode. */ -STATIC int -xfs_iunlink_map_ino( - struct xfs_trans *tp, - xfs_agnumber_t agno, - xfs_agino_t agino, - struct xfs_imap *imap, - struct xfs_dinode **dipp, - struct xfs_buf **bpp) -{ - struct xfs_mount *mp = tp->t_mountp; - int error; - - imap->im_blkno = 0; - error = xfs_imap(mp, tp, XFS_AGINO_TO_INO(mp, agno, agino), imap, 0); - if (error) { - xfs_warn(mp, "%s: xfs_imap returned error %d.", - __func__, error); - return error; - } - - error = xfs_imap_to_bp(mp, tp, imap, bpp); - if (error) { - xfs_warn(mp, "%s: xfs_imap_to_bp returned error %d.", - __func__, error); - return error; - } - - *dipp = xfs_buf_offset(*bpp, imap->im_boffset); - return 0; -} - -/* - * Walk the unlinked chain from @head_agino until we find the inode that - * points to @target_agino. Return the inode number, map, dinode pointer, - * and inode cluster buffer of that inode as @agino, @imap, @dipp, and @bpp. - * - * @tp, @pag, @head_agino, and @target_agino are input parameters. - * @agino, @imap, @dipp, and @bpp are all output parameters. - * - * Do not call this function if @target_agino is the head of the list. - */ -STATIC int -xfs_iunlink_map_prev( +static int +xfs_iunlink_remove_inode( struct xfs_trans *tp, struct xfs_perag *pag, - xfs_agino_t head_agino, - xfs_agino_t target_agino, - xfs_agino_t *agino, - struct xfs_imap *imap, - struct xfs_dinode **dipp, - struct xfs_buf **bpp) + struct xfs_buf *agibp, + struct xfs_inode *ip) { struct xfs_mount *mp = tp->t_mountp; - xfs_agino_t next_agino; + struct xfs_agi *agi = agibp->b_addr; + xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); + xfs_agino_t head_agino; + short bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; int error; - ASSERT(head_agino != target_agino); - *bpp = NULL; + trace_xfs_iunlink_remove(ip); - /* See if our backref cache can find it faster. */ - *agino = xfs_iunlink_lookup_backref(pag, target_agino); - if (*agino != NULLAGINO) { - error = xfs_iunlink_map_ino(tp, pag->pag_agno, *agino, imap, - dipp, bpp); - if (error) - return error; - - if (be32_to_cpu((*dipp)->di_next_unlinked) == target_agino) - return 0; - - /* - * If we get here the cache contents were corrupt, so drop the - * buffer and fall back to walking the bucket list. - */ - xfs_trans_brelse(tp, *bpp); - *bpp = NULL; - WARN_ON_ONCE(1); + /* + * Get the index into the agi hash table for the list this inode will + * go on. Make sure the head pointer isn't garbage. + */ + head_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); + if (!xfs_verify_agino(pag, head_agino)) { + XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, + agi, sizeof(*agi)); + return -EFSCORRUPTED; } - trace_xfs_iunlink_map_prev_fallback(mp, pag->pag_agno); + /* + * Set our inode's next_unlinked pointer to NULL and then return + * the old pointer value so that we can update whatever was previous + * to us in the list to point to whatever was next in the list. + */ + error = xfs_iunlink_log_inode(tp, ip, pag, NULLAGINO); + if (error) + return error; - /* Otherwise, walk the entire bucket until we find it. */ - next_agino = head_agino; - while (next_agino != target_agino) { - xfs_agino_t unlinked_agino; + /* + * Update the prev pointer in the next inode to point back to previous + * inode in the chain. + */ + error = xfs_iunlink_update_backref(pag, ip->i_prev_unlinked, + ip->i_next_unlinked); + if (error) + return error; - if (*bpp) - xfs_trans_brelse(tp, *bpp); + if (head_agino != agino) { + struct xfs_inode *prev_ip; - *agino = next_agino; - error = xfs_iunlink_map_ino(tp, pag->pag_agno, next_agino, imap, - dipp, bpp); - if (error) - return error; + prev_ip = xfs_iunlink_lookup(pag, ip->i_prev_unlinked); + if (!prev_ip) + return -EFSCORRUPTED; - unlinked_agino = be32_to_cpu((*dipp)->di_next_unlinked); - /* - * Make sure this pointer is valid and isn't an obvious - * infinite loop. - */ - if (!xfs_verify_agino(mp, pag->pag_agno, unlinked_agino) || - next_agino == unlinked_agino) { - XFS_CORRUPTION_ERROR(__func__, - XFS_ERRLEVEL_LOW, mp, - *dipp, sizeof(**dipp)); - error = -EFSCORRUPTED; - return error; - } - next_agino = unlinked_agino; + error = xfs_iunlink_log_inode(tp, prev_ip, pag, + ip->i_next_unlinked); + prev_ip->i_next_unlinked = ip->i_next_unlinked; + } else { + /* Point the head of the list to the next unlinked inode. */ + error = xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, + ip->i_next_unlinked); } - return 0; + ip->i_next_unlinked = NULLAGINO; + ip->i_prev_unlinked = NULLAGINO; + return error; } /* @@ -2314,87 +2063,17 @@ xfs_iunlink_remove( struct xfs_perag *pag, struct xfs_inode *ip) { - struct xfs_mount *mp = tp->t_mountp; - struct xfs_agi *agi; struct xfs_buf *agibp; - struct xfs_buf *last_ibp; - struct xfs_dinode *last_dip = NULL; - xfs_agino_t agino = XFS_INO_TO_AGINO(mp, ip->i_ino); - xfs_agino_t next_agino; - xfs_agino_t head_agino; - short bucket_index = agino % XFS_AGI_UNLINKED_BUCKETS; int error; trace_xfs_iunlink_remove(ip); /* Get the agi buffer first. It ensures lock ordering on the list. */ - error = xfs_read_agi(mp, tp, pag->pag_agno, &agibp); - if (error) - return error; - agi = agibp->b_addr; - - /* - * Get the index into the agi hash table for the list this inode will - * go on. Make sure the head pointer isn't garbage. - */ - head_agino = be32_to_cpu(agi->agi_unlinked[bucket_index]); - if (!xfs_verify_agino(mp, pag->pag_agno, head_agino)) { - XFS_CORRUPTION_ERROR(__func__, XFS_ERRLEVEL_LOW, mp, - agi, sizeof(*agi)); - return -EFSCORRUPTED; - } - - /* - * Set our inode's next_unlinked pointer to NULL and then return - * the old pointer value so that we can update whatever was previous - * to us in the list to point to whatever was next in the list. - */ - error = xfs_iunlink_update_inode(tp, ip, pag, NULLAGINO, &next_agino); + error = xfs_read_agi(pag, tp, &agibp); if (error) return error; - /* - * If there was a backref pointing from the next inode back to this - * one, remove it because we've removed this inode from the list. - * - * Later, if this inode was in the middle of the list we'll update - * this inode's backref to point from the next inode. - */ - if (next_agino != NULLAGINO) { - error = xfs_iunlink_change_backref(pag, next_agino, NULLAGINO); - if (error) - return error; - } - - if (head_agino != agino) { - struct xfs_imap imap; - xfs_agino_t prev_agino; - - /* We need to search the list for the inode being freed. */ - error = xfs_iunlink_map_prev(tp, pag, head_agino, agino, - &prev_agino, &imap, &last_dip, &last_ibp); - if (error) - return error; - - /* Point the previous inode on the list to the next inode. */ - xfs_iunlink_update_dinode(tp, pag, prev_agino, last_ibp, - last_dip, &imap, next_agino); - - /* - * Now we deal with the backref for this inode. If this inode - * pointed at a real inode, change the backref that pointed to - * us to point to our old next. If this inode was the end of - * the list, delete the backref that pointed to us. Note that - * change_backref takes care of deleting the backref if - * next_agino is NULLAGINO. - */ - return xfs_iunlink_change_backref(agibp->b_pag, agino, - next_agino); - } - - /* Point the head of the list to the next unlinked inode. */ - return xfs_iunlink_update_bucket(tp, pag, agibp, bucket_index, - next_agino); + return xfs_iunlink_remove_inode(tp, pag, agibp, ip); } /* @@ -3032,10 +2711,12 @@ out_trans_abort: static int xfs_rename_alloc_whiteout( struct user_namespace *mnt_userns, + struct xfs_name *src_name, struct xfs_inode *dp, struct xfs_inode **wip) { struct xfs_inode *tmpfile; + struct qstr name; int error; error = xfs_create_tmpfile(mnt_userns, dp, S_IFCHR | WHITEOUT_MODE, @@ -3043,6 +2724,15 @@ xfs_rename_alloc_whiteout( if (error) return error; + name.name = src_name->name; + name.len = src_name->len; + error = xfs_inode_init_security(VFS_I(tmpfile), VFS_I(dp), &name); + if (error) { + xfs_finish_inode_setup(tmpfile); + xfs_irele(tmpfile); + return error; + } + /* * Prepare the tmpfile inode as if it were created through the VFS. * Complete the inode setup and flag it as linkable. nlink is already @@ -3093,7 +2783,8 @@ xfs_rename( * appropriately. */ if (flags & RENAME_WHITEOUT) { - error = xfs_rename_alloc_whiteout(mnt_userns, target_dp, &wip); + error = xfs_rename_alloc_whiteout(mnt_userns, src_name, + target_dp, &wip); if (error) return error; @@ -3229,11 +2920,13 @@ retry: if (inodes[i] == wip || (inodes[i] == target_ip && (VFS_I(target_ip)->i_nlink == 1 || src_is_directory))) { - struct xfs_buf *bp; - xfs_agnumber_t agno; + struct xfs_perag *pag; + struct xfs_buf *bp; - agno = XFS_INO_TO_AGNO(mp, inodes[i]->i_ino); - error = xfs_read_agi(mp, tp, agno, &bp); + pag = xfs_perag_get(mp, + XFS_INO_TO_AGNO(mp, inodes[i]->i_ino)); + error = xfs_read_agi(pag, tp, &bp); + xfs_perag_put(pag); if (error) goto out_trans_cancel; } @@ -3452,13 +3145,13 @@ xfs_iflush( goto flush_out; } } - if (XFS_TEST_ERROR(ip->i_df.if_nextents + xfs_ifork_nextents(ip->i_afp) > + if (XFS_TEST_ERROR(ip->i_df.if_nextents + xfs_ifork_nextents(&ip->i_af) > ip->i_nblocks, mp, XFS_ERRTAG_IFLUSH_5)) { xfs_alert_tag(mp, XFS_PTAG_IFLUSH, "%s: detected corrupt incore inode %llu, " "total extents = %llu nblocks = %lld, ptr "PTR_FMT, __func__, ip->i_ino, - ip->i_df.if_nextents + xfs_ifork_nextents(ip->i_afp), + ip->i_df.if_nextents + xfs_ifork_nextents(&ip->i_af), ip->i_nblocks, ip); goto flush_out; } @@ -3488,7 +3181,8 @@ xfs_iflush( if (ip->i_df.if_format == XFS_DINODE_FMT_LOCAL && xfs_ifork_verify_local_data(ip)) goto flush_out; - if (ip->i_afp && ip->i_afp->if_format == XFS_DINODE_FMT_LOCAL && + if (xfs_inode_has_attr_fork(ip) && + ip->i_af.if_format == XFS_DINODE_FMT_LOCAL && xfs_ifork_verify_local_attr(ip)) goto flush_out; @@ -3506,7 +3200,7 @@ xfs_iflush( } xfs_iflush_fork(ip, dip, iip, XFS_DATA_FORK); - if (XFS_IFORK_Q(ip)) + if (xfs_inode_has_attr_fork(ip)) xfs_iflush_fork(ip, dip, iip, XFS_ATTR_FORK); /* diff --git a/fs/xfs/xfs_inode.h b/fs/xfs/xfs_inode.h index 7be6f8e705ab..4d626f4321bc 100644 --- a/fs/xfs/xfs_inode.h +++ b/fs/xfs/xfs_inode.h @@ -33,9 +33,9 @@ typedef struct xfs_inode { struct xfs_imap i_imap; /* location for xfs_imap() */ /* Extent information. */ - struct xfs_ifork *i_afp; /* attribute fork pointer */ struct xfs_ifork *i_cowfp; /* copy on write extents */ struct xfs_ifork i_df; /* data fork */ + struct xfs_ifork i_af; /* attribute fork */ /* Transaction and locking information. */ struct xfs_inode_log_item *i_itemp; /* logging information */ @@ -68,6 +68,10 @@ typedef struct xfs_inode { uint64_t i_diflags2; /* XFS_DIFLAG2_... */ struct timespec64 i_crtime; /* time created */ + /* unlinked list pointers */ + xfs_agino_t i_next_unlinked; + xfs_agino_t i_prev_unlinked; + /* VFS inode */ struct inode i_vnode; /* embedded VFS inode */ @@ -77,6 +81,66 @@ typedef struct xfs_inode { struct list_head i_ioend_list; } xfs_inode_t; +static inline bool xfs_inode_has_attr_fork(struct xfs_inode *ip) +{ + return ip->i_forkoff > 0; +} + +static inline struct xfs_ifork * +xfs_ifork_ptr( + struct xfs_inode *ip, + int whichfork) +{ + switch (whichfork) { + case XFS_DATA_FORK: + return &ip->i_df; + case XFS_ATTR_FORK: + if (!xfs_inode_has_attr_fork(ip)) + return NULL; + return &ip->i_af; + case XFS_COW_FORK: + return ip->i_cowfp; + default: + ASSERT(0); + return NULL; + } +} + +static inline unsigned int xfs_inode_fork_boff(struct xfs_inode *ip) +{ + return ip->i_forkoff << 3; +} + +static inline unsigned int xfs_inode_data_fork_size(struct xfs_inode *ip) +{ + if (xfs_inode_has_attr_fork(ip)) + return xfs_inode_fork_boff(ip); + + return XFS_LITINO(ip->i_mount); +} + +static inline unsigned int xfs_inode_attr_fork_size(struct xfs_inode *ip) +{ + if (xfs_inode_has_attr_fork(ip)) + return XFS_LITINO(ip->i_mount) - xfs_inode_fork_boff(ip); + return 0; +} + +static inline unsigned int +xfs_inode_fork_size( + struct xfs_inode *ip, + int whichfork) +{ + switch (whichfork) { + case XFS_DATA_FORK: + return xfs_inode_data_fork_size(ip); + case XFS_ATTR_FORK: + return xfs_inode_attr_fork_size(ip); + default: + return 0; + } +} + /* Convert from vfs inode to xfs inode */ static inline struct xfs_inode *XFS_I(struct inode *inode) { @@ -505,9 +569,6 @@ extern struct kmem_cache *xfs_inode_cache; bool xfs_inode_needs_inactive(struct xfs_inode *ip); -int xfs_iunlink_init(struct xfs_perag *pag); -void xfs_iunlink_destroy(struct xfs_perag *pag); - void xfs_end_io(struct work_struct *work); int xfs_ilock2_io_mmap(struct xfs_inode *ip1, struct xfs_inode *ip2); diff --git a/fs/xfs/xfs_inode_item.c b/fs/xfs/xfs_inode_item.c index 721def0639fd..6e19ece916bf 100644 --- a/fs/xfs/xfs_inode_item.c +++ b/fs/xfs/xfs_inode_item.c @@ -57,7 +57,7 @@ xfs_inode_item_data_fork_size( ip->i_df.if_nextents > 0 && ip->i_df.if_bytes > 0) { /* worst case, doesn't subtract delalloc extents */ - *nbytes += XFS_IFORK_DSIZE(ip); + *nbytes += xfs_inode_data_fork_size(ip); *nvecs += 1; } break; @@ -92,27 +92,27 @@ xfs_inode_item_attr_fork_size( { struct xfs_inode *ip = iip->ili_inode; - switch (ip->i_afp->if_format) { + switch (ip->i_af.if_format) { case XFS_DINODE_FMT_EXTENTS: if ((iip->ili_fields & XFS_ILOG_AEXT) && - ip->i_afp->if_nextents > 0 && - ip->i_afp->if_bytes > 0) { + ip->i_af.if_nextents > 0 && + ip->i_af.if_bytes > 0) { /* worst case, doesn't subtract unused space */ - *nbytes += XFS_IFORK_ASIZE(ip); + *nbytes += xfs_inode_attr_fork_size(ip); *nvecs += 1; } break; case XFS_DINODE_FMT_BTREE: if ((iip->ili_fields & XFS_ILOG_ABROOT) && - ip->i_afp->if_broot_bytes > 0) { - *nbytes += ip->i_afp->if_broot_bytes; + ip->i_af.if_broot_bytes > 0) { + *nbytes += ip->i_af.if_broot_bytes; *nvecs += 1; } break; case XFS_DINODE_FMT_LOCAL: if ((iip->ili_fields & XFS_ILOG_ADATA) && - ip->i_afp->if_bytes > 0) { - *nbytes += xlog_calc_iovec_len(ip->i_afp->if_bytes); + ip->i_af.if_bytes > 0) { + *nbytes += xlog_calc_iovec_len(ip->i_af.if_bytes); *nvecs += 1; } break; @@ -143,7 +143,7 @@ xfs_inode_item_size( xfs_log_dinode_size(ip->i_mount); xfs_inode_item_data_fork_size(iip, nvecs, nbytes); - if (XFS_IFORK_Q(ip)) + if (xfs_inode_has_attr_fork(ip)) xfs_inode_item_attr_fork_size(iip, nvecs, nbytes); } @@ -237,18 +237,18 @@ xfs_inode_item_format_attr_fork( struct xfs_inode *ip = iip->ili_inode; size_t data_bytes; - switch (ip->i_afp->if_format) { + switch (ip->i_af.if_format) { case XFS_DINODE_FMT_EXTENTS: iip->ili_fields &= ~(XFS_ILOG_ADATA | XFS_ILOG_ABROOT); if ((iip->ili_fields & XFS_ILOG_AEXT) && - ip->i_afp->if_nextents > 0 && - ip->i_afp->if_bytes > 0) { + ip->i_af.if_nextents > 0 && + ip->i_af.if_bytes > 0) { struct xfs_bmbt_rec *p; - ASSERT(xfs_iext_count(ip->i_afp) == - ip->i_afp->if_nextents); + ASSERT(xfs_iext_count(&ip->i_af) == + ip->i_af.if_nextents); p = xlog_prepare_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_EXT); data_bytes = xfs_iextents_copy(ip, p, XFS_ATTR_FORK); @@ -265,13 +265,13 @@ xfs_inode_item_format_attr_fork( ~(XFS_ILOG_ADATA | XFS_ILOG_AEXT); if ((iip->ili_fields & XFS_ILOG_ABROOT) && - ip->i_afp->if_broot_bytes > 0) { - ASSERT(ip->i_afp->if_broot != NULL); + ip->i_af.if_broot_bytes > 0) { + ASSERT(ip->i_af.if_broot != NULL); xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_BROOT, - ip->i_afp->if_broot, - ip->i_afp->if_broot_bytes); - ilf->ilf_asize = ip->i_afp->if_broot_bytes; + ip->i_af.if_broot, + ip->i_af.if_broot_bytes); + ilf->ilf_asize = ip->i_af.if_broot_bytes; ilf->ilf_size++; } else { iip->ili_fields &= ~XFS_ILOG_ABROOT; @@ -282,12 +282,12 @@ xfs_inode_item_format_attr_fork( ~(XFS_ILOG_AEXT | XFS_ILOG_ABROOT); if ((iip->ili_fields & XFS_ILOG_ADATA) && - ip->i_afp->if_bytes > 0) { - ASSERT(ip->i_afp->if_u1.if_data != NULL); + ip->i_af.if_bytes > 0) { + ASSERT(ip->i_af.if_u1.if_data != NULL); xlog_copy_iovec(lv, vecp, XLOG_REG_TYPE_IATTR_LOCAL, - ip->i_afp->if_u1.if_data, - ip->i_afp->if_bytes); - ilf->ilf_asize = (unsigned)ip->i_afp->if_bytes; + ip->i_af.if_u1.if_data, + ip->i_af.if_bytes); + ilf->ilf_asize = (unsigned)ip->i_af.if_bytes; ilf->ilf_size++; } else { iip->ili_fields &= ~XFS_ILOG_ADATA; @@ -355,11 +355,11 @@ xfs_inode_to_log_dinode_iext_counters( { if (xfs_inode_has_large_extent_counts(ip)) { to->di_big_nextents = xfs_ifork_nextents(&ip->i_df); - to->di_big_anextents = xfs_ifork_nextents(ip->i_afp); + to->di_big_anextents = xfs_ifork_nextents(&ip->i_af); to->di_nrext64_pad = 0; } else { to->di_nextents = xfs_ifork_nextents(&ip->i_df); - to->di_anextents = xfs_ifork_nextents(ip->i_afp); + to->di_anextents = xfs_ifork_nextents(&ip->i_af); } } @@ -390,7 +390,7 @@ xfs_inode_to_log_dinode( to->di_nblocks = ip->i_nblocks; to->di_extsize = ip->i_extsize; to->di_forkoff = ip->i_forkoff; - to->di_aformat = xfs_ifork_format(ip->i_afp); + to->di_aformat = xfs_ifork_format(&ip->i_af); to->di_flags = ip->i_diflags; xfs_copy_dm_fields_to_log_dinode(ip, to); @@ -480,7 +480,7 @@ xfs_inode_item_format( xfs_inode_item_format_core(ip, lv, &vecp); xfs_inode_item_format_data_fork(iip, ilf, lv, &vecp); - if (XFS_IFORK_Q(ip)) { + if (xfs_inode_has_attr_fork(ip)) { xfs_inode_item_format_attr_fork(iip, ilf, lv, &vecp); } else { iip->ili_fields &= diff --git a/fs/xfs/xfs_ioctl.c b/fs/xfs/xfs_ioctl.c index 0d67ff8a8961..1f783e979629 100644 --- a/fs/xfs/xfs_ioctl.c +++ b/fs/xfs/xfs_ioctl.c @@ -955,6 +955,7 @@ xfs_ioc_ag_geometry( struct xfs_mount *mp, void __user *arg) { + struct xfs_perag *pag; struct xfs_ag_geometry ageo; int error; @@ -965,7 +966,12 @@ xfs_ioc_ag_geometry( if (memchr_inv(&ageo.ag_reserved, 0, sizeof(ageo.ag_reserved))) return -EINVAL; - error = xfs_ag_get_geometry(mp, ageo.ag_number, &ageo); + pag = xfs_perag_get(mp, ageo.ag_number); + if (!pag) + return -EINVAL; + + error = xfs_ag_get_geometry(pag, &ageo); + xfs_perag_put(pag); if (error) return error; @@ -985,7 +991,7 @@ xfs_fill_fsxattr( struct fileattr *fa) { struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); fileattr_fill_xflags(fa, xfs_ip2xflags(ip)); diff --git a/fs/xfs/xfs_iomap.c b/fs/xfs/xfs_iomap.c index 5d50fed291b4..2817d3dd25eb 100644 --- a/fs/xfs/xfs_iomap.c +++ b/fs/xfs/xfs_iomap.c @@ -159,7 +159,7 @@ xfs_iomap_eof_align_last_fsb( struct xfs_inode *ip, xfs_fileoff_t end_fsb) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); xfs_extlen_t extsz = xfs_get_extsz_hint(ip); xfs_extlen_t align = xfs_eof_alignment(ip); struct xfs_bmbt_irec irec; @@ -370,7 +370,7 @@ xfs_iomap_prealloc_size( struct xfs_iext_cursor ncur = *icur; struct xfs_bmbt_irec prev, got; struct xfs_mount *mp = ip->i_mount; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, whichfork); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, whichfork); xfs_fileoff_t offset_fsb = XFS_B_TO_FSBT(mp, offset); int64_t freesp; xfs_fsblock_t qblocks; @@ -1310,12 +1310,12 @@ xfs_xattr_iomap_begin( lockmode = xfs_ilock_attr_map_shared(ip); /* if there are no attribute fork or extents, return ENOENT */ - if (!XFS_IFORK_Q(ip) || !ip->i_afp->if_nextents) { + if (!xfs_inode_has_attr_fork(ip) || !ip->i_af.if_nextents) { error = -ENOENT; goto out_unlock; } - ASSERT(ip->i_afp->if_format != XFS_DINODE_FMT_LOCAL); + ASSERT(ip->i_af.if_format != XFS_DINODE_FMT_LOCAL); error = xfs_bmapi_read(ip, offset_fsb, end_fsb - offset_fsb, &imap, &nimaps, XFS_BMAPI_ATTRFORK); out_unlock: diff --git a/fs/xfs/xfs_iops.c b/fs/xfs/xfs_iops.c index a7402f6ea510..45518b8c613c 100644 --- a/fs/xfs/xfs_iops.c +++ b/fs/xfs/xfs_iops.c @@ -75,9 +75,8 @@ xfs_initxattrs( * these attrs can be journalled at inode creation time (along with the * inode, of course, such that log replay can't cause these to be lost). */ - -STATIC int -xfs_init_security( +int +xfs_inode_init_security( struct inode *inode, struct inode *dir, const struct qstr *qstr) @@ -122,7 +121,7 @@ xfs_cleanup_inode( /* Oh, the horror. * If we can't add the ACL or we fail in - * xfs_init_security we must back out. + * xfs_inode_init_security we must back out. * ENOSPC can hit here, among other things. */ xfs_dentry_to_name(&teardown, dentry); @@ -208,7 +207,7 @@ xfs_generic_create( inode = VFS_I(ip); - error = xfs_init_security(inode, dir, &dentry->d_name); + error = xfs_inode_init_security(inode, dir, &dentry->d_name); if (unlikely(error)) goto out_cleanup_inode; @@ -424,7 +423,7 @@ xfs_vn_symlink( inode = VFS_I(cip); - error = xfs_init_security(inode, dir, &dentry->d_name); + error = xfs_inode_init_security(inode, dir, &dentry->d_name); if (unlikely(error)) goto out_cleanup_inode; @@ -1282,7 +1281,7 @@ xfs_setup_inode( * If there is no attribute fork no ACL can exist on this inode, * and it can't have any file capabilities attached to it either. */ - if (!XFS_IFORK_Q(ip)) { + if (!xfs_inode_has_attr_fork(ip)) { inode_has_no_xattr(inode); cache_no_acl(inode); } diff --git a/fs/xfs/xfs_iops.h b/fs/xfs/xfs_iops.h index 278949056048..cb5fc68c9ea0 100644 --- a/fs/xfs/xfs_iops.h +++ b/fs/xfs/xfs_iops.h @@ -17,4 +17,7 @@ extern void xfs_setattr_time(struct xfs_inode *ip, struct iattr *iattr); int xfs_vn_setattr_size(struct user_namespace *mnt_userns, struct dentry *dentry, struct iattr *vap); +int xfs_inode_init_security(struct inode *inode, struct inode *dir, + const struct qstr *qstr); + #endif /* __XFS_IOPS_H__ */ diff --git a/fs/xfs/xfs_itable.c b/fs/xfs/xfs_itable.c index f74c9fff72bb..36312b00b164 100644 --- a/fs/xfs/xfs_itable.c +++ b/fs/xfs/xfs_itable.c @@ -111,8 +111,8 @@ xfs_bulkstat_one_int( buf->bs_extents64 = nextents; xfs_bulkstat_health(ip, buf); - buf->bs_aextents = xfs_ifork_nextents(ip->i_afp); - buf->bs_forkoff = XFS_IFORK_BOFF(ip); + buf->bs_aextents = xfs_ifork_nextents(&ip->i_af); + buf->bs_forkoff = xfs_inode_fork_boff(ip); buf->bs_version = XFS_BULKSTAT_VERSION_V5; if (xfs_has_v3inodes(mp)) { diff --git a/fs/xfs/xfs_iunlink_item.c b/fs/xfs/xfs_iunlink_item.c new file mode 100644 index 000000000000..43005ce8bd48 --- /dev/null +++ b/fs/xfs/xfs_iunlink_item.c @@ -0,0 +1,180 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020-2022, Red Hat, Inc. + * All Rights Reserved. + */ +#include "xfs.h" +#include "xfs_fs.h" +#include "xfs_shared.h" +#include "xfs_format.h" +#include "xfs_log_format.h" +#include "xfs_trans_resv.h" +#include "xfs_mount.h" +#include "xfs_inode.h" +#include "xfs_trans.h" +#include "xfs_trans_priv.h" +#include "xfs_ag.h" +#include "xfs_iunlink_item.h" +#include "xfs_trace.h" +#include "xfs_error.h" + +struct kmem_cache *xfs_iunlink_cache; + +static inline struct xfs_iunlink_item *IUL_ITEM(struct xfs_log_item *lip) +{ + return container_of(lip, struct xfs_iunlink_item, item); +} + +static void +xfs_iunlink_item_release( + struct xfs_log_item *lip) +{ + struct xfs_iunlink_item *iup = IUL_ITEM(lip); + + xfs_perag_put(iup->pag); + kmem_cache_free(xfs_iunlink_cache, IUL_ITEM(lip)); +} + + +static uint64_t +xfs_iunlink_item_sort( + struct xfs_log_item *lip) +{ + return IUL_ITEM(lip)->ip->i_ino; +} + +/* + * Look up the inode cluster buffer and log the on-disk unlinked inode change + * we need to make. + */ +static int +xfs_iunlink_log_dinode( + struct xfs_trans *tp, + struct xfs_iunlink_item *iup) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_inode *ip = iup->ip; + struct xfs_dinode *dip; + struct xfs_buf *ibp; + int offset; + int error; + + error = xfs_imap_to_bp(mp, tp, &ip->i_imap, &ibp); + if (error) + return error; + /* + * Don't log the unlinked field on stale buffers as this may be the + * transaction that frees the inode cluster and relogging the buffer + * here will incorrectly remove the stale state. + */ + if (ibp->b_flags & XBF_STALE) + goto out; + + dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset); + + /* Make sure the old pointer isn't garbage. */ + if (be32_to_cpu(dip->di_next_unlinked) != iup->old_agino) { + xfs_inode_verifier_error(ip, -EFSCORRUPTED, __func__, dip, + sizeof(*dip), __this_address); + error = -EFSCORRUPTED; + goto out; + } + + trace_xfs_iunlink_update_dinode(mp, iup->pag->pag_agno, + XFS_INO_TO_AGINO(mp, ip->i_ino), + be32_to_cpu(dip->di_next_unlinked), iup->next_agino); + + dip->di_next_unlinked = cpu_to_be32(iup->next_agino); + offset = ip->i_imap.im_boffset + + offsetof(struct xfs_dinode, di_next_unlinked); + + xfs_dinode_calc_crc(mp, dip); + xfs_trans_inode_buf(tp, ibp); + xfs_trans_log_buf(tp, ibp, offset, offset + sizeof(xfs_agino_t) - 1); + return 0; +out: + xfs_trans_brelse(tp, ibp); + return error; +} + +/* + * On precommit, we grab the inode cluster buffer for the inode number we were + * passed, then update the next unlinked field for that inode in the buffer and + * log the buffer. This ensures that the inode cluster buffer was logged in the + * correct order w.r.t. other inode cluster buffers. We can then remove the + * iunlink item from the transaction and release it as it is has now served it's + * purpose. + */ +static int +xfs_iunlink_item_precommit( + struct xfs_trans *tp, + struct xfs_log_item *lip) +{ + struct xfs_iunlink_item *iup = IUL_ITEM(lip); + int error; + + error = xfs_iunlink_log_dinode(tp, iup); + list_del(&lip->li_trans); + xfs_iunlink_item_release(lip); + return error; +} + +static const struct xfs_item_ops xfs_iunlink_item_ops = { + .iop_release = xfs_iunlink_item_release, + .iop_sort = xfs_iunlink_item_sort, + .iop_precommit = xfs_iunlink_item_precommit, +}; + + +/* + * Initialize the inode log item for a newly allocated (in-core) inode. + * + * Inode extents can only reside within an AG. Hence specify the starting + * block for the inode chunk by offset within an AG as well as the + * length of the allocated extent. + * + * This joins the item to the transaction and marks it dirty so + * that we don't need a separate call to do this, nor does the + * caller need to know anything about the iunlink item. + */ +int +xfs_iunlink_log_inode( + struct xfs_trans *tp, + struct xfs_inode *ip, + struct xfs_perag *pag, + xfs_agino_t next_agino) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_iunlink_item *iup; + + ASSERT(xfs_verify_agino_or_null(pag, next_agino)); + ASSERT(xfs_verify_agino_or_null(pag, ip->i_next_unlinked)); + + /* + * Since we're updating a linked list, we should never find that the + * current pointer is the same as the new value, unless we're + * terminating the list. + */ + if (ip->i_next_unlinked == next_agino) { + if (next_agino != NULLAGINO) + return -EFSCORRUPTED; + return 0; + } + + iup = kmem_cache_zalloc(xfs_iunlink_cache, GFP_KERNEL | __GFP_NOFAIL); + xfs_log_item_init(mp, &iup->item, XFS_LI_IUNLINK, + &xfs_iunlink_item_ops); + + iup->ip = ip; + iup->next_agino = next_agino; + iup->old_agino = ip->i_next_unlinked; + + atomic_inc(&pag->pag_ref); + iup->pag = pag; + + xfs_trans_add_item(tp, &iup->item); + tp->t_flags |= XFS_TRANS_DIRTY; + set_bit(XFS_LI_DIRTY, &iup->item.li_flags); + return 0; +} + diff --git a/fs/xfs/xfs_iunlink_item.h b/fs/xfs/xfs_iunlink_item.h new file mode 100644 index 000000000000..c793cdcaccde --- /dev/null +++ b/fs/xfs/xfs_iunlink_item.h @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2020-2022, Red Hat, Inc. + * All Rights Reserved. + */ +#ifndef XFS_IUNLINK_ITEM_H +#define XFS_IUNLINK_ITEM_H 1 + +struct xfs_trans; +struct xfs_inode; +struct xfs_perag; + +/* in memory log item structure */ +struct xfs_iunlink_item { + struct xfs_log_item item; + struct xfs_inode *ip; + struct xfs_perag *pag; + xfs_agino_t next_agino; + xfs_agino_t old_agino; +}; + +extern struct kmem_cache *xfs_iunlink_cache; + +int xfs_iunlink_log_inode(struct xfs_trans *tp, struct xfs_inode *ip, + struct xfs_perag *pag, xfs_agino_t next_agino); + +#endif /* XFS_IUNLINK_ITEM_H */ diff --git a/fs/xfs/xfs_log.c b/fs/xfs/xfs_log.c index ae904b21e9cc..4b1c0a9c6368 100644 --- a/fs/xfs/xfs_log.c +++ b/fs/xfs/xfs_log.c @@ -57,7 +57,8 @@ xlog_grant_push_ail( STATIC void xlog_sync( struct xlog *log, - struct xlog_in_core *iclog); + struct xlog_in_core *iclog, + struct xlog_ticket *ticket); #if defined(DEBUG) STATIC void xlog_verify_grant_tail( @@ -567,7 +568,8 @@ xlog_state_shutdown_callbacks( int xlog_state_release_iclog( struct xlog *log, - struct xlog_in_core *iclog) + struct xlog_in_core *iclog, + struct xlog_ticket *ticket) { xfs_lsn_t tail_lsn; bool last_ref; @@ -614,7 +616,7 @@ xlog_state_release_iclog( trace_xlog_iclog_syncing(iclog, _RET_IP_); spin_unlock(&log->l_icloglock); - xlog_sync(log, iclog); + xlog_sync(log, iclog, ticket); spin_lock(&log->l_icloglock); return 0; } @@ -881,7 +883,7 @@ xlog_force_iclog( iclog->ic_flags |= XLOG_ICL_NEED_FLUSH | XLOG_ICL_NEED_FUA; if (iclog->ic_state == XLOG_STATE_ACTIVE) xlog_state_switch_iclogs(iclog->ic_log, iclog, 0); - return xlog_state_release_iclog(iclog->ic_log, iclog); + return xlog_state_release_iclog(iclog->ic_log, iclog, NULL); } /* @@ -944,6 +946,8 @@ xlog_write_unmount_record( .lv_niovecs = 1, .lv_iovecp = ®, }; + LIST_HEAD(lv_chain); + list_add(&vec.lv_list, &lv_chain); BUILD_BUG_ON((sizeof(struct xlog_op_header) + sizeof(struct xfs_unmount_log_format)) != @@ -952,7 +956,7 @@ xlog_write_unmount_record( /* account for space used by record data */ ticket->t_curr_res -= sizeof(unmount_rec); - return xlog_write(log, NULL, &vec, ticket, reg.i_len); + return xlog_write(log, NULL, &lv_chain, ticket, reg.i_len); } /* @@ -2000,7 +2004,7 @@ xlog_calc_iclog_size( } /* - * Flush out the in-core log (iclog) to the on-disk log in an asynchronous + * Flush out the in-core log (iclog) to the on-disk log in an asynchronous * fashion. Previously, we should have moved the current iclog * ptr in the log to point to the next available iclog. This allows further * write to continue while this code syncs out an iclog ready to go. @@ -2025,7 +2029,8 @@ xlog_calc_iclog_size( STATIC void xlog_sync( struct xlog *log, - struct xlog_in_core *iclog) + struct xlog_in_core *iclog, + struct xlog_ticket *ticket) { unsigned int count; /* byte count of bwrite */ unsigned int roundoff; /* roundoff to BB or stripe */ @@ -2037,12 +2042,20 @@ xlog_sync( count = xlog_calc_iclog_size(log, iclog, &roundoff); - /* move grant heads by roundoff in sync */ - xlog_grant_add_space(log, &log->l_reserve_head.grant, roundoff); - xlog_grant_add_space(log, &log->l_write_head.grant, roundoff); + /* + * If we have a ticket, account for the roundoff via the ticket + * reservation to avoid touching the hot grant heads needlessly. + * Otherwise, we have to move grant heads directly. + */ + if (ticket) { + ticket->t_curr_res -= roundoff; + } else { + xlog_grant_add_space(log, &log->l_reserve_head.grant, roundoff); + xlog_grant_add_space(log, &log->l_write_head.grant, roundoff); + } /* put cycle number in every block */ - xlog_pack_data(log, iclog, roundoff); + xlog_pack_data(log, iclog, roundoff); /* real byte length */ size = iclog->ic_offset; @@ -2275,7 +2288,7 @@ xlog_write_get_more_iclog_space( spin_lock(&log->l_icloglock); ASSERT(iclog->ic_state == XLOG_STATE_WANT_SYNC); xlog_state_finish_copy(log, iclog, *record_cnt, *data_cnt); - error = xlog_state_release_iclog(log, iclog); + error = xlog_state_release_iclog(log, iclog, ticket); spin_unlock(&log->l_icloglock); if (error) return error; @@ -2471,13 +2484,13 @@ int xlog_write( struct xlog *log, struct xfs_cil_ctx *ctx, - struct xfs_log_vec *log_vector, + struct list_head *lv_chain, struct xlog_ticket *ticket, uint32_t len) { struct xlog_in_core *iclog = NULL; - struct xfs_log_vec *lv = log_vector; + struct xfs_log_vec *lv; uint32_t record_cnt = 0; uint32_t data_cnt = 0; int error = 0; @@ -2505,7 +2518,7 @@ xlog_write( if (ctx) xlog_cil_set_ctx_write_state(ctx, iclog); - while (lv) { + list_for_each_entry(lv, lv_chain, lv_list) { /* * If the entire log vec does not fit in the iclog, punt it to * the partial copy loop which can handle this case. @@ -2526,7 +2539,6 @@ xlog_write( xlog_write_full(lv, ticket, iclog, &log_offset, &len, &record_cnt, &data_cnt); } - lv = lv->lv_next; } ASSERT(len == 0); @@ -2538,7 +2550,7 @@ xlog_write( */ spin_lock(&log->l_icloglock); xlog_state_finish_copy(log, iclog, record_cnt, 0); - error = xlog_state_release_iclog(log, iclog); + error = xlog_state_release_iclog(log, iclog, ticket); spin_unlock(&log->l_icloglock); return error; @@ -2958,7 +2970,7 @@ restart: * reference to the iclog. */ if (!atomic_add_unless(&iclog->ic_refcnt, -1, 1)) - error = xlog_state_release_iclog(log, iclog); + error = xlog_state_release_iclog(log, iclog, ticket); spin_unlock(&log->l_icloglock); if (error) return error; @@ -3406,7 +3418,8 @@ xfs_log_ticket_get( static int xlog_calc_unit_res( struct xlog *log, - int unit_bytes) + int unit_bytes, + int *niclogs) { int iclog_space; uint num_headers; @@ -3486,6 +3499,8 @@ xlog_calc_unit_res( /* roundoff padding for transaction data and one for commit record */ unit_bytes += 2 * log->l_iclog_roundoff; + if (niclogs) + *niclogs = num_headers; return unit_bytes; } @@ -3494,7 +3509,7 @@ xfs_log_calc_unit_res( struct xfs_mount *mp, int unit_bytes) { - return xlog_calc_unit_res(mp->m_log, unit_bytes); + return xlog_calc_unit_res(mp->m_log, unit_bytes, NULL); } /* @@ -3512,7 +3527,7 @@ xlog_ticket_alloc( tic = kmem_cache_zalloc(xfs_log_ticket_cache, GFP_NOFS | __GFP_NOFAIL); - unit_res = xlog_calc_unit_res(log, unit_bytes); + unit_res = xlog_calc_unit_res(log, unit_bytes, &tic->t_iclog_hdrs); atomic_set(&tic->t_ref, 1); tic->t_task = current; diff --git a/fs/xfs/xfs_log.h b/fs/xfs/xfs_log.h index f3ce046a7d45..2728886c2963 100644 --- a/fs/xfs/xfs_log.h +++ b/fs/xfs/xfs_log.h @@ -9,7 +9,8 @@ struct xfs_cil_ctx; struct xfs_log_vec { - struct xfs_log_vec *lv_next; /* next lv in build list */ + struct list_head lv_list; /* CIL lv chain ptrs */ + uint32_t lv_order_id; /* chain ordering info */ int lv_niovecs; /* number of iovecs in lv */ struct xfs_log_iovec *lv_iovecp; /* iovec array */ struct xfs_log_item *lv_item; /* owner */ diff --git a/fs/xfs/xfs_log_cil.c b/fs/xfs/xfs_log_cil.c index db6cb7800251..eccbfb99e894 100644 --- a/fs/xfs/xfs_log_cil.c +++ b/fs/xfs/xfs_log_cil.c @@ -44,9 +44,20 @@ xlog_cil_ticket_alloc( * transaction overhead reservation from the first transaction commit. */ tic->t_curr_res = 0; + tic->t_iclog_hdrs = 0; return tic; } +static inline void +xlog_cil_set_iclog_hdr_count(struct xfs_cil *cil) +{ + struct xlog *log = cil->xc_log; + + atomic_set(&cil->xc_iclog_hdrs, + (XLOG_CIL_BLOCKING_SPACE_LIMIT(log) / + (log->l_iclog_size - log->l_iclog_hsize))); +} + /* * Check if the current log item was first committed in this sequence. * We can't rely on just the log item being in the CIL, we have to check @@ -61,7 +72,7 @@ xlog_item_in_current_chkpt( struct xfs_cil *cil, struct xfs_log_item *lip) { - if (list_empty(&lip->li_cil)) + if (test_bit(XLOG_CIL_EMPTY, &cil->xc_flags)) return false; /* @@ -93,15 +104,88 @@ xlog_cil_ctx_alloc(void) ctx = kmem_zalloc(sizeof(*ctx), KM_NOFS); INIT_LIST_HEAD(&ctx->committing); INIT_LIST_HEAD(&ctx->busy_extents); + INIT_LIST_HEAD(&ctx->log_items); + INIT_LIST_HEAD(&ctx->lv_chain); INIT_WORK(&ctx->push_work, xlog_cil_push_work); return ctx; } +/* + * Aggregate the CIL per cpu structures into global counts, lists, etc and + * clear the percpu state ready for the next context to use. This is called + * from the push code with the context lock held exclusively, hence nothing else + * will be accessing or modifying the per-cpu counters. + */ +static void +xlog_cil_push_pcp_aggregate( + struct xfs_cil *cil, + struct xfs_cil_ctx *ctx) +{ + struct xlog_cil_pcp *cilpcp; + int cpu; + + for_each_online_cpu(cpu) { + cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); + + ctx->ticket->t_curr_res += cilpcp->space_reserved; + cilpcp->space_reserved = 0; + + if (!list_empty(&cilpcp->busy_extents)) { + list_splice_init(&cilpcp->busy_extents, + &ctx->busy_extents); + } + if (!list_empty(&cilpcp->log_items)) + list_splice_init(&cilpcp->log_items, &ctx->log_items); + + /* + * We're in the middle of switching cil contexts. Reset the + * counter we use to detect when the current context is nearing + * full. + */ + cilpcp->space_used = 0; + } +} + +/* + * Aggregate the CIL per-cpu space used counters into the global atomic value. + * This is called when the per-cpu counter aggregation will first pass the soft + * limit threshold so we can switch to atomic counter aggregation for accurate + * detection of hard limit traversal. + */ +static void +xlog_cil_insert_pcp_aggregate( + struct xfs_cil *cil, + struct xfs_cil_ctx *ctx) +{ + struct xlog_cil_pcp *cilpcp; + int cpu; + int count = 0; + + /* Trigger atomic updates then aggregate only for the first caller */ + if (!test_and_clear_bit(XLOG_CIL_PCP_SPACE, &cil->xc_flags)) + return; + + for_each_online_cpu(cpu) { + int old, prev; + + cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); + do { + old = cilpcp->space_used; + prev = cmpxchg(&cilpcp->space_used, old, 0); + } while (old != prev); + count += old; + } + atomic_add(count, &ctx->space_used); +} + static void xlog_cil_ctx_switch( struct xfs_cil *cil, struct xfs_cil_ctx *ctx) { + xlog_cil_set_iclog_hdr_count(cil); + set_bit(XLOG_CIL_EMPTY, &cil->xc_flags); + set_bit(XLOG_CIL_PCP_SPACE, &cil->xc_flags); ctx->sequence = ++cil->xc_current_sequence; ctx->cil = cil; cil->xc_ctx = ctx; @@ -123,6 +207,7 @@ xlog_cil_init_post_recovery( { log->l_cilp->xc_ctx->ticket = xlog_cil_ticket_alloc(log); log->l_cilp->xc_ctx->sequence = 1; + xlog_cil_set_iclog_hdr_count(log->l_cilp); } static inline int @@ -254,6 +339,7 @@ xlog_cil_alloc_shadow_bufs( memset(lv, 0, xlog_cil_iovec_space(niovecs)); + INIT_LIST_HEAD(&lv->lv_list); lv->lv_item = lip; lv->lv_size = buf_size; if (ordered) @@ -269,7 +355,6 @@ xlog_cil_alloc_shadow_bufs( else lv->lv_buf_len = 0; lv->lv_bytes = 0; - lv->lv_next = NULL; } /* Ensure the lv is set up according to ->iop_size */ @@ -396,7 +481,6 @@ xlog_cil_insert_format_items( if (lip->li_lv && shadow->lv_size <= lip->li_lv->lv_size) { /* same or smaller, optimise common overwrite case */ lv = lip->li_lv; - lv->lv_next = NULL; if (ordered) goto insert; @@ -433,6 +517,23 @@ insert: } } +/* + * The use of lockless waitqueue_active() requires that the caller has + * serialised itself against the wakeup call in xlog_cil_push_work(). That + * can be done by either holding the push lock or the context lock. + */ +static inline bool +xlog_cil_over_hard_limit( + struct xlog *log, + int32_t space_used) +{ + if (waitqueue_active(&log->l_cilp->xc_push_wait)) + return true; + if (space_used >= XLOG_CIL_BLOCKING_SPACE_LIMIT(log)) + return true; + return false; +} + /* * Insert the log items into the CIL and calculate the difference in space * consumed by the item. Add the space to the checkpoint ticket and calculate @@ -450,8 +551,10 @@ xlog_cil_insert_items( struct xfs_cil_ctx *ctx = cil->xc_ctx; struct xfs_log_item *lip; int len = 0; - int iclog_space; int iovhdr_res = 0, split_res = 0, ctx_res = 0; + int space_used; + int order; + struct xlog_cil_pcp *cilpcp; ASSERT(tp); @@ -461,46 +564,112 @@ xlog_cil_insert_items( */ xlog_cil_insert_format_items(log, tp, &len); - spin_lock(&cil->xc_cil_lock); - - /* attach the transaction to the CIL if it has any busy extents */ - if (!list_empty(&tp->t_busy)) - list_splice_init(&tp->t_busy, &ctx->busy_extents); + /* + * Subtract the space released by intent cancelation from the space we + * consumed so that we remove it from the CIL space and add it back to + * the current transaction reservation context. + */ + len -= released_space; /* - * Now transfer enough transaction reservation to the context ticket - * for the checkpoint. The context ticket is special - the unit - * reservation has to grow as well as the current reservation as we - * steal from tickets so we can correctly determine the space used - * during the transaction commit. + * Grab the per-cpu pointer for the CIL before we start any accounting. + * That ensures that we are running with pre-emption disabled and so we + * can't be scheduled away between split sample/update operations that + * are done without outside locking to serialise them. */ - if (ctx->ticket->t_curr_res == 0) { - ctx_res = ctx->ticket->t_unit_res; - ctx->ticket->t_curr_res = ctx_res; - tp->t_ticket->t_curr_res -= ctx_res; - } + cilpcp = get_cpu_ptr(cil->xc_pcp); - /* do we need space for more log record headers? */ - iclog_space = log->l_iclog_size - log->l_iclog_hsize; - if (len > 0 && (ctx->space_used / iclog_space != - (ctx->space_used + len) / iclog_space)) { - split_res = (len + iclog_space - 1) / iclog_space; - /* need to take into account split region headers, too */ - split_res *= log->l_iclog_hsize + sizeof(struct xlog_op_header); - ctx->ticket->t_unit_res += split_res; - ctx->ticket->t_curr_res += split_res; - tp->t_ticket->t_curr_res -= split_res; - ASSERT(tp->t_ticket->t_curr_res >= len); + /* + * We need to take the CIL checkpoint unit reservation on the first + * commit into the CIL. Test the XLOG_CIL_EMPTY bit first so we don't + * unnecessarily do an atomic op in the fast path here. We can clear the + * XLOG_CIL_EMPTY bit as we are under the xc_ctx_lock here and that + * needs to be held exclusively to reset the XLOG_CIL_EMPTY bit. + */ + if (test_bit(XLOG_CIL_EMPTY, &cil->xc_flags) && + test_and_clear_bit(XLOG_CIL_EMPTY, &cil->xc_flags)) + ctx_res = ctx->ticket->t_unit_res; + + /* + * Check if we need to steal iclog headers. atomic_read() is not a + * locked atomic operation, so we can check the value before we do any + * real atomic ops in the fast path. If we've already taken the CIL unit + * reservation from this commit, we've already got one iclog header + * space reserved so we have to account for that otherwise we risk + * overrunning the reservation on this ticket. + * + * If the CIL is already at the hard limit, we might need more header + * space that originally reserved. So steal more header space from every + * commit that occurs once we are over the hard limit to ensure the CIL + * push won't run out of reservation space. + * + * This can steal more than we need, but that's OK. + * + * The cil->xc_ctx_lock provides the serialisation necessary for safely + * calling xlog_cil_over_hard_limit() in this context. + */ + space_used = atomic_read(&ctx->space_used) + cilpcp->space_used + len; + if (atomic_read(&cil->xc_iclog_hdrs) > 0 || + xlog_cil_over_hard_limit(log, space_used)) { + split_res = log->l_iclog_hsize + + sizeof(struct xlog_op_header); + if (ctx_res) + ctx_res += split_res * (tp->t_ticket->t_iclog_hdrs - 1); + else + ctx_res = split_res * tp->t_ticket->t_iclog_hdrs; + atomic_sub(tp->t_ticket->t_iclog_hdrs, &cil->xc_iclog_hdrs); } - tp->t_ticket->t_curr_res -= len; - tp->t_ticket->t_curr_res += released_space; - ctx->space_used += len; - ctx->space_used -= released_space; + cilpcp->space_reserved += ctx_res; + + /* + * Accurately account when over the soft limit, otherwise fold the + * percpu count into the global count if over the per-cpu threshold. + */ + if (!test_bit(XLOG_CIL_PCP_SPACE, &cil->xc_flags)) { + atomic_add(len, &ctx->space_used); + } else if (cilpcp->space_used + len > + (XLOG_CIL_SPACE_LIMIT(log) / num_online_cpus())) { + space_used = atomic_add_return(cilpcp->space_used + len, + &ctx->space_used); + cilpcp->space_used = 0; + + /* + * If we just transitioned over the soft limit, we need to + * transition to the global atomic counter. + */ + if (space_used >= XLOG_CIL_SPACE_LIMIT(log)) + xlog_cil_insert_pcp_aggregate(cil, ctx); + } else { + cilpcp->space_used += len; + } + /* attach the transaction to the CIL if it has any busy extents */ + if (!list_empty(&tp->t_busy)) + list_splice_init(&tp->t_busy, &cilpcp->busy_extents); + + /* + * Now update the order of everything modified in the transaction + * and insert items into the CIL if they aren't already there. + * We do this here so we only need to take the CIL lock once during + * the transaction commit. + */ + order = atomic_inc_return(&ctx->order_id); + list_for_each_entry(lip, &tp->t_items, li_trans) { + /* Skip items which aren't dirty in this transaction. */ + if (!test_bit(XFS_LI_DIRTY, &lip->li_flags)) + continue; + + lip->li_order_id = order; + if (!list_empty(&lip->li_cil)) + continue; + list_add_tail(&lip->li_cil, &cilpcp->log_items); + } + put_cpu_ptr(cilpcp); /* * If we've overrun the reservation, dump the tx details before we move * the log items. Shutdown is imminent... */ + tp->t_ticket->t_curr_res -= ctx_res + len; if (WARN_ON(tp->t_ticket->t_curr_res < 0)) { xfs_warn(log->l_mp, "Transaction log reservation overrun:"); xfs_warn(log->l_mp, @@ -510,44 +679,20 @@ xlog_cil_insert_items( split_res); xfs_warn(log->l_mp, " ctx ticket: %d bytes", ctx_res); xlog_print_trans(tp); - } - - /* - * Now (re-)position everything modified at the tail of the CIL. - * We do this here so we only need to take the CIL lock once during - * the transaction commit. - */ - list_for_each_entry(lip, &tp->t_items, li_trans) { - - /* Skip items which aren't dirty in this transaction. */ - if (!test_bit(XFS_LI_DIRTY, &lip->li_flags)) - continue; - - /* - * Only move the item if it isn't already at the tail. This is - * to prevent a transient list_empty() state when reinserting - * an item that is already the only item in the CIL. - */ - if (!list_is_last(&lip->li_cil, &cil->xc_cil)) - list_move_tail(&lip->li_cil, &cil->xc_cil); - } - - spin_unlock(&cil->xc_cil_lock); - - if (tp->t_ticket->t_curr_res < 0) xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); + } } static void xlog_cil_free_logvec( - struct xfs_log_vec *log_vector) + struct list_head *lv_chain) { struct xfs_log_vec *lv; - for (lv = log_vector; lv; ) { - struct xfs_log_vec *next = lv->lv_next; + while (!list_empty(lv_chain)) { + lv = list_first_entry(lv_chain, struct xfs_log_vec, lv_list); + list_del_init(&lv->lv_list); kmem_free(lv); - lv = next; } } @@ -647,7 +792,7 @@ xlog_cil_committed( spin_unlock(&ctx->cil->xc_push_lock); } - xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, ctx->lv_chain, + xfs_trans_committed_bulk(ctx->cil->xc_log->l_ailp, &ctx->lv_chain, ctx->start_lsn, abort); xfs_extent_busy_sort(&ctx->busy_extents); @@ -658,7 +803,7 @@ xlog_cil_committed( list_del(&ctx->committing); spin_unlock(&ctx->cil->xc_push_lock); - xlog_cil_free_logvec(ctx->lv_chain); + xlog_cil_free_logvec(&ctx->lv_chain); if (!list_empty(&ctx->busy_extents)) xlog_discard_busy_extents(mp, ctx); @@ -817,7 +962,6 @@ restart: static int xlog_cil_write_chain( struct xfs_cil_ctx *ctx, - struct xfs_log_vec *chain, uint32_t chain_len) { struct xlog *log = ctx->cil->xc_log; @@ -826,7 +970,7 @@ xlog_cil_write_chain( error = xlog_cil_order_write(ctx->cil, ctx->sequence, _START_RECORD); if (error) return error; - return xlog_write(log, ctx, chain, ctx->ticket, chain_len); + return xlog_write(log, ctx, &ctx->lv_chain, ctx->ticket, chain_len); } /* @@ -855,6 +999,8 @@ xlog_cil_write_commit_record( .lv_iovecp = ®, }; int error; + LIST_HEAD(lv_chain); + list_add(&vec.lv_list, &lv_chain); if (xlog_is_shutdown(log)) return -EIO; @@ -865,7 +1011,7 @@ xlog_cil_write_commit_record( /* account for space used by record data */ ctx->ticket->t_curr_res -= reg.i_len; - error = xlog_write(log, ctx, &vec, ctx->ticket, reg.i_len); + error = xlog_write(log, ctx, &lv_chain, ctx->ticket, reg.i_len); if (error) xlog_force_shutdown(log, SHUTDOWN_LOG_IO_ERROR); return error; @@ -931,11 +1077,30 @@ xlog_cil_build_trans_hdr( lvhdr->lv_niovecs = 2; lvhdr->lv_iovecp = &hdr->lhdr[0]; lvhdr->lv_bytes = hdr->lhdr[0].i_len + hdr->lhdr[1].i_len; - lvhdr->lv_next = ctx->lv_chain; tic->t_curr_res -= lvhdr->lv_bytes; } +/* + * CIL item reordering compare function. We want to order in ascending ID order, + * but we want to leave items with the same ID in the order they were added to + * the list. This is important for operations like reflink where we log 4 order + * dependent intents in a single transaction when we overwrite an existing + * shared extent with a new shared extent. i.e. BUI(unmap), CUI(drop), + * CUI (inc), BUI(remap)... + */ +static int +xlog_cil_order_cmp( + void *priv, + const struct list_head *a, + const struct list_head *b) +{ + struct xfs_log_vec *l1 = container_of(a, struct xfs_log_vec, lv_list); + struct xfs_log_vec *l2 = container_of(b, struct xfs_log_vec, lv_list); + + return l1->lv_order_id > l2->lv_order_id; +} + /* * Pull all the log vectors off the items in the CIL, and remove the items from * the CIL. We don't need the CIL lock here because it's only needed on the @@ -947,18 +1112,16 @@ xlog_cil_build_trans_hdr( */ static void xlog_cil_build_lv_chain( - struct xfs_cil *cil, struct xfs_cil_ctx *ctx, struct list_head *whiteouts, uint32_t *num_iovecs, uint32_t *num_bytes) { - struct xfs_log_vec *lv = NULL; - - while (!list_empty(&cil->xc_cil)) { + while (!list_empty(&ctx->log_items)) { struct xfs_log_item *item; + struct xfs_log_vec *lv; - item = list_first_entry(&cil->xc_cil, + item = list_first_entry(&ctx->log_items, struct xfs_log_item, li_cil); if (test_bit(XFS_LI_WHITEOUT, &item->li_flags)) { @@ -967,18 +1130,18 @@ xlog_cil_build_lv_chain( continue; } - list_del_init(&item->li_cil); - if (!ctx->lv_chain) - ctx->lv_chain = item->li_lv; - else - lv->lv_next = item->li_lv; lv = item->li_lv; - item->li_lv = NULL; - *num_iovecs += lv->lv_niovecs; + lv->lv_order_id = item->li_order_id; /* we don't write ordered log vectors */ if (lv->lv_buf_len != XFS_LOG_VEC_ORDERED) *num_bytes += lv->lv_bytes; + *num_iovecs += lv->lv_niovecs; + list_add_tail(&lv->lv_list, &ctx->lv_chain); + + list_del_init(&item->li_cil); + item->li_order_id = 0; + item->li_lv = NULL; } } @@ -1022,10 +1185,11 @@ xlog_cil_push_work( int num_bytes = 0; int error = 0; struct xlog_cil_trans_hdr thdr; - struct xfs_log_vec lvhdr = { NULL }; + struct xfs_log_vec lvhdr = {}; xfs_csn_t push_seq; bool push_commit_stable; LIST_HEAD (whiteouts); + struct xlog_ticket *ticket; new_ctx = xlog_cil_ctx_alloc(); new_ctx->ticket = xlog_cil_ticket_alloc(log); @@ -1049,12 +1213,14 @@ xlog_cil_push_work( if (waitqueue_active(&cil->xc_push_wait)) wake_up_all(&cil->xc_push_wait); + xlog_cil_push_pcp_aggregate(cil, ctx); + /* * Check if we've anything to push. If there is nothing, then we don't * move on to a new sequence number and so we have to be able to push * this sequence again later. */ - if (list_empty(&cil->xc_cil)) { + if (test_bit(XLOG_CIL_EMPTY, &cil->xc_flags)) { cil->xc_push_seq = 0; spin_unlock(&cil->xc_push_lock); goto out_skip; @@ -1094,7 +1260,7 @@ xlog_cil_push_work( list_add(&ctx->committing, &cil->xc_committing); spin_unlock(&cil->xc_push_lock); - xlog_cil_build_lv_chain(cil, ctx, &whiteouts, &num_iovecs, &num_bytes); + xlog_cil_build_lv_chain(ctx, &whiteouts, &num_iovecs, &num_bytes); /* * Switch the contexts so we can drop the context lock and move out @@ -1126,15 +1292,31 @@ xlog_cil_push_work( spin_unlock(&cil->xc_push_lock); up_write(&cil->xc_ctx_lock); + /* + * Sort the log vector chain before we add the transaction headers. + * This ensures we always have the transaction headers at the start + * of the chain. + */ + list_sort(NULL, &ctx->lv_chain, xlog_cil_order_cmp); + /* * Build a checkpoint transaction header and write it to the log to * begin the transaction. We need to account for the space used by the * transaction header here as it is not accounted for in xlog_write(). + * Add the lvhdr to the head of the lv chain we pass to xlog_write() so + * it gets written into the iclog first. */ xlog_cil_build_trans_hdr(ctx, &thdr, &lvhdr, num_iovecs); num_bytes += lvhdr.lv_bytes; + list_add(&lvhdr.lv_list, &ctx->lv_chain); - error = xlog_cil_write_chain(ctx, &lvhdr, num_bytes); + /* + * Take the lvhdr back off the lv_chain immediately after calling + * xlog_cil_write_chain() as it should not be passed to log IO + * completion. + */ + error = xlog_cil_write_chain(ctx, num_bytes); + list_del(&lvhdr.lv_list); if (error) goto out_abort_free_ticket; @@ -1142,7 +1324,14 @@ xlog_cil_push_work( if (error) goto out_abort_free_ticket; - xfs_log_ticket_ungrant(log, ctx->ticket); + /* + * Grab the ticket from the ctx so we can ungrant it after releasing the + * commit_iclog. The ctx may be freed by the time we return from + * releasing the commit_iclog (i.e. checkpoint has been completed and + * callback run) so we can't reference the ctx after the call to + * xlog_state_release_iclog(). + */ + ticket = ctx->ticket; /* * If the checkpoint spans multiple iclogs, wait for all previous iclogs @@ -1192,12 +1381,14 @@ xlog_cil_push_work( if (push_commit_stable && ctx->commit_iclog->ic_state == XLOG_STATE_ACTIVE) xlog_state_switch_iclogs(log, ctx->commit_iclog, 0); - xlog_state_release_iclog(log, ctx->commit_iclog); + ticket = ctx->ticket; + xlog_state_release_iclog(log, ctx->commit_iclog, ticket); /* Not safe to reference ctx now! */ spin_unlock(&log->l_icloglock); xlog_cil_cleanup_whiteouts(&whiteouts); + xfs_log_ticket_ungrant(log, ticket); return; out_skip: @@ -1207,17 +1398,19 @@ out_skip: return; out_abort_free_ticket: - xfs_log_ticket_ungrant(log, ctx->ticket); ASSERT(xlog_is_shutdown(log)); xlog_cil_cleanup_whiteouts(&whiteouts); if (!ctx->commit_iclog) { + xfs_log_ticket_ungrant(log, ctx->ticket); xlog_cil_committed(ctx); return; } spin_lock(&log->l_icloglock); - xlog_state_release_iclog(log, ctx->commit_iclog); + ticket = ctx->ticket; + xlog_state_release_iclog(log, ctx->commit_iclog, ticket); /* Not safe to reference ctx now! */ spin_unlock(&log->l_icloglock); + xfs_log_ticket_ungrant(log, ticket); } /* @@ -1232,18 +1425,27 @@ xlog_cil_push_background( struct xlog *log) __releases(cil->xc_ctx_lock) { struct xfs_cil *cil = log->l_cilp; + int space_used = atomic_read(&cil->xc_ctx->space_used); /* * The cil won't be empty because we are called while holding the - * context lock so whatever we added to the CIL will still be there + * context lock so whatever we added to the CIL will still be there. */ - ASSERT(!list_empty(&cil->xc_cil)); + ASSERT(!test_bit(XLOG_CIL_EMPTY, &cil->xc_flags)); /* - * Don't do a background push if we haven't used up all the - * space available yet. + * We are done if: + * - we haven't used up all the space available yet; or + * - we've already queued up a push; and + * - we're not over the hard limit; and + * - nothing has been over the hard limit. + * + * If so, we don't need to take the push lock as there's nothing to do. */ - if (cil->xc_ctx->space_used < XLOG_CIL_SPACE_LIMIT(log)) { + if (space_used < XLOG_CIL_SPACE_LIMIT(log) || + (cil->xc_push_seq == cil->xc_current_sequence && + space_used < XLOG_CIL_BLOCKING_SPACE_LIMIT(log) && + !waitqueue_active(&cil->xc_push_wait))) { up_read(&cil->xc_ctx_lock); return; } @@ -1270,12 +1472,11 @@ xlog_cil_push_background( * dipping back down under the hard limit. * * The ctx->xc_push_lock provides the serialisation necessary for safely - * using the lockless waitqueue_active() check in this context. + * calling xlog_cil_over_hard_limit() in this context. */ - if (cil->xc_ctx->space_used >= XLOG_CIL_BLOCKING_SPACE_LIMIT(log) || - waitqueue_active(&cil->xc_push_wait)) { + if (xlog_cil_over_hard_limit(log, space_used)) { trace_xfs_log_cil_wait(log, cil->xc_ctx->ticket); - ASSERT(cil->xc_ctx->space_used < log->l_logsize); + ASSERT(space_used < log->l_logsize); xlog_wait(&cil->xc_push_wait, &cil->xc_push_lock); return; } @@ -1334,7 +1535,8 @@ xlog_cil_push_now( * If the CIL is empty or we've already pushed the sequence then * there's no more work that we need to do. */ - if (list_empty(&cil->xc_cil) || push_seq <= cil->xc_push_seq) { + if (test_bit(XLOG_CIL_EMPTY, &cil->xc_flags) || + push_seq <= cil->xc_push_seq) { spin_unlock(&cil->xc_push_lock); return; } @@ -1352,7 +1554,7 @@ xlog_cil_empty( bool empty = false; spin_lock(&cil->xc_push_lock); - if (list_empty(&cil->xc_cil)) + if (test_bit(XLOG_CIL_EMPTY, &cil->xc_flags)) empty = true; spin_unlock(&cil->xc_push_lock); return empty; @@ -1483,7 +1685,7 @@ xlog_cil_flush( * If the CIL is empty, make sure that any previous checkpoint that may * still be in an active iclog is pushed to stable storage. */ - if (list_empty(&log->l_cilp->xc_cil)) + if (test_bit(XLOG_CIL_EMPTY, &log->l_cilp->xc_flags)) xfs_log_force(log->l_mp, 0); } @@ -1568,7 +1770,7 @@ restart: * we would have found the context on the committing list. */ if (sequence == cil->xc_current_sequence && - !list_empty(&cil->xc_cil)) { + !test_bit(XLOG_CIL_EMPTY, &cil->xc_flags)) { spin_unlock(&cil->xc_push_lock); goto restart; } @@ -1588,15 +1790,49 @@ out_shutdown: return 0; } +/* + * Move dead percpu state to the relevant CIL context structures. + * + * We have to lock the CIL context here to ensure that nothing is modifying + * the percpu state, either addition or removal. Both of these are done under + * the CIL context lock, so grabbing that exclusively here will ensure we can + * safely drain the cilpcp for the CPU that is dying. + */ +void +xlog_cil_pcp_dead( + struct xlog *log, + unsigned int cpu) +{ + struct xfs_cil *cil = log->l_cilp; + struct xlog_cil_pcp *cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); + struct xfs_cil_ctx *ctx; + + down_write(&cil->xc_ctx_lock); + ctx = cil->xc_ctx; + if (ctx->ticket) + ctx->ticket->t_curr_res += cilpcp->space_reserved; + cilpcp->space_reserved = 0; + + if (!list_empty(&cilpcp->log_items)) + list_splice_init(&cilpcp->log_items, &ctx->log_items); + if (!list_empty(&cilpcp->busy_extents)) + list_splice_init(&cilpcp->busy_extents, &ctx->busy_extents); + atomic_add(cilpcp->space_used, &ctx->space_used); + cilpcp->space_used = 0; + up_write(&cil->xc_ctx_lock); +} + /* * Perform initial CIL structure initialisation. */ int xlog_cil_init( - struct xlog *log) + struct xlog *log) { - struct xfs_cil *cil; - struct xfs_cil_ctx *ctx; + struct xfs_cil *cil; + struct xfs_cil_ctx *ctx; + struct xlog_cil_pcp *cilpcp; + int cpu; cil = kmem_zalloc(sizeof(*cil), KM_MAYFAIL); if (!cil) @@ -1611,22 +1847,31 @@ xlog_cil_init( if (!cil->xc_push_wq) goto out_destroy_cil; - INIT_LIST_HEAD(&cil->xc_cil); + cil->xc_log = log; + cil->xc_pcp = alloc_percpu(struct xlog_cil_pcp); + if (!cil->xc_pcp) + goto out_destroy_wq; + + for_each_possible_cpu(cpu) { + cilpcp = per_cpu_ptr(cil->xc_pcp, cpu); + INIT_LIST_HEAD(&cilpcp->busy_extents); + INIT_LIST_HEAD(&cilpcp->log_items); + } + INIT_LIST_HEAD(&cil->xc_committing); - spin_lock_init(&cil->xc_cil_lock); spin_lock_init(&cil->xc_push_lock); init_waitqueue_head(&cil->xc_push_wait); init_rwsem(&cil->xc_ctx_lock); init_waitqueue_head(&cil->xc_start_wait); init_waitqueue_head(&cil->xc_commit_wait); - cil->xc_log = log; log->l_cilp = cil; ctx = xlog_cil_ctx_alloc(); xlog_cil_ctx_switch(cil, ctx); - return 0; +out_destroy_wq: + destroy_workqueue(cil->xc_push_wq); out_destroy_cil: kmem_free(cil); return -ENOMEM; @@ -1636,14 +1881,17 @@ void xlog_cil_destroy( struct xlog *log) { - if (log->l_cilp->xc_ctx) { - if (log->l_cilp->xc_ctx->ticket) - xfs_log_ticket_put(log->l_cilp->xc_ctx->ticket); - kmem_free(log->l_cilp->xc_ctx); + struct xfs_cil *cil = log->l_cilp; + + if (cil->xc_ctx) { + if (cil->xc_ctx->ticket) + xfs_log_ticket_put(cil->xc_ctx->ticket); + kmem_free(cil->xc_ctx); } - ASSERT(list_empty(&log->l_cilp->xc_cil)); - destroy_workqueue(log->l_cilp->xc_push_wq); - kmem_free(log->l_cilp); + ASSERT(test_bit(XLOG_CIL_EMPTY, &cil->xc_flags)); + free_percpu(cil->xc_pcp); + destroy_workqueue(cil->xc_push_wq); + kmem_free(cil); } diff --git a/fs/xfs/xfs_log_priv.h b/fs/xfs/xfs_log_priv.h index 686c01eb3661..1bd2963e8fbd 100644 --- a/fs/xfs/xfs_log_priv.h +++ b/fs/xfs/xfs_log_priv.h @@ -143,15 +143,16 @@ enum xlog_iclog_state { #define XLOG_COVER_OPS 5 typedef struct xlog_ticket { - struct list_head t_queue; /* reserve/write queue */ - struct task_struct *t_task; /* task that owns this ticket */ - xlog_tid_t t_tid; /* transaction identifier : 4 */ - atomic_t t_ref; /* ticket reference count : 4 */ - int t_curr_res; /* current reservation in bytes : 4 */ - int t_unit_res; /* unit reservation in bytes : 4 */ - char t_ocnt; /* original count : 1 */ - char t_cnt; /* current count : 1 */ - uint8_t t_flags; /* properties of reservation : 1 */ + struct list_head t_queue; /* reserve/write queue */ + struct task_struct *t_task; /* task that owns this ticket */ + xlog_tid_t t_tid; /* transaction identifier */ + atomic_t t_ref; /* ticket reference count */ + int t_curr_res; /* current reservation */ + int t_unit_res; /* unit reservation */ + char t_ocnt; /* original unit count */ + char t_cnt; /* current unit count */ + uint8_t t_flags; /* properties of reservation */ + int t_iclog_hdrs; /* iclog hdrs in t_curr_res */ } xlog_ticket_t; /* @@ -221,13 +222,25 @@ struct xfs_cil_ctx { xfs_lsn_t commit_lsn; /* chkpt commit record lsn */ struct xlog_in_core *commit_iclog; struct xlog_ticket *ticket; /* chkpt ticket */ - int space_used; /* aggregate size of regions */ + atomic_t space_used; /* aggregate size of regions */ struct list_head busy_extents; /* busy extents in chkpt */ - struct xfs_log_vec *lv_chain; /* logvecs being pushed */ + struct list_head log_items; /* log items in chkpt */ + struct list_head lv_chain; /* logvecs being pushed */ struct list_head iclog_entry; struct list_head committing; /* ctx committing list */ struct work_struct discard_endio_work; struct work_struct push_work; + atomic_t order_id; +}; + +/* + * Per-cpu CIL tracking items + */ +struct xlog_cil_pcp { + int32_t space_used; + uint32_t space_reserved; + struct list_head busy_extents; + struct list_head log_items; }; /* @@ -248,8 +261,8 @@ struct xfs_cil_ctx { */ struct xfs_cil { struct xlog *xc_log; - struct list_head xc_cil; - spinlock_t xc_cil_lock; + unsigned long xc_flags; + atomic_t xc_iclog_hdrs; struct workqueue_struct *xc_push_wq; struct rw_semaphore xc_ctx_lock ____cacheline_aligned_in_smp; @@ -263,8 +276,17 @@ struct xfs_cil { wait_queue_head_t xc_start_wait; xfs_csn_t xc_current_sequence; wait_queue_head_t xc_push_wait; /* background push throttle */ + + void __percpu *xc_pcp; /* percpu CIL structures */ +#ifdef CONFIG_HOTPLUG_CPU + struct list_head xc_pcp_list; +#endif } ____cacheline_aligned_in_smp; +/* xc_flags bit values */ +#define XLOG_CIL_EMPTY 1 +#define XLOG_CIL_PCP_SPACE 2 + /* * The amount of log space we allow the CIL to aggregate is difficult to size. * Whatever we choose, we have to make sure we can get a reservation for the @@ -486,14 +508,15 @@ struct xlog_ticket *xlog_ticket_alloc(struct xlog *log, int unit_bytes, void xlog_print_tic_res(struct xfs_mount *mp, struct xlog_ticket *ticket); void xlog_print_trans(struct xfs_trans *); int xlog_write(struct xlog *log, struct xfs_cil_ctx *ctx, - struct xfs_log_vec *log_vector, struct xlog_ticket *tic, + struct list_head *lv_chain, struct xlog_ticket *tic, uint32_t len); void xfs_log_ticket_ungrant(struct xlog *log, struct xlog_ticket *ticket); void xfs_log_ticket_regrant(struct xlog *log, struct xlog_ticket *ticket); void xlog_state_switch_iclogs(struct xlog *log, struct xlog_in_core *iclog, int eventual_size); -int xlog_state_release_iclog(struct xlog *log, struct xlog_in_core *iclog); +int xlog_state_release_iclog(struct xlog *log, struct xlog_in_core *iclog, + struct xlog_ticket *ticket); /* * When we crack an atomic LSN, we sample it first so that the value will not @@ -682,4 +705,9 @@ xlog_kvmalloc( return p; } +/* + * CIL CPU dead notifier + */ +void xlog_cil_pcp_dead(struct xlog *log, unsigned int cpu); + #endif /* __XFS_LOG_PRIV_H__ */ diff --git a/fs/xfs/xfs_log_recover.c b/fs/xfs/xfs_log_recover.c index 940c8107cbd4..17e923b9c5fa 100644 --- a/fs/xfs/xfs_log_recover.c +++ b/fs/xfs/xfs_log_recover.c @@ -2629,21 +2629,21 @@ xlog_recover_cancel_intents( */ STATIC void xlog_recover_clear_agi_bucket( - xfs_mount_t *mp, - xfs_agnumber_t agno, - int bucket) + struct xfs_perag *pag, + int bucket) { - xfs_trans_t *tp; - xfs_agi_t *agi; - struct xfs_buf *agibp; - int offset; - int error; + struct xfs_mount *mp = pag->pag_mount; + struct xfs_trans *tp; + struct xfs_agi *agi; + struct xfs_buf *agibp; + int offset; + int error; error = xfs_trans_alloc(mp, &M_RES(mp)->tr_clearagi, 0, 0, 0, &tp); if (error) goto out_error; - error = xfs_read_agi(mp, tp, agno, &agibp); + error = xfs_read_agi(pag, tp, &agibp); if (error) goto out_abort; @@ -2662,60 +2662,62 @@ xlog_recover_clear_agi_bucket( out_abort: xfs_trans_cancel(tp); out_error: - xfs_warn(mp, "%s: failed to clear agi %d. Continuing.", __func__, agno); + xfs_warn(mp, "%s: failed to clear agi %d. Continuing.", __func__, + pag->pag_agno); return; } -STATIC xfs_agino_t -xlog_recover_process_one_iunlink( - struct xfs_mount *mp, - xfs_agnumber_t agno, - xfs_agino_t agino, - int bucket) +static int +xlog_recover_iunlink_bucket( + struct xfs_perag *pag, + struct xfs_agi *agi, + int bucket) { - struct xfs_buf *ibp; - struct xfs_dinode *dip; - struct xfs_inode *ip; - xfs_ino_t ino; - int error; + struct xfs_mount *mp = pag->pag_mount; + struct xfs_inode *prev_ip = NULL; + struct xfs_inode *ip; + xfs_agino_t prev_agino, agino; + int error = 0; - ino = XFS_AGINO_TO_INO(mp, agno, agino); - error = xfs_iget(mp, NULL, ino, 0, 0, &ip); - if (error) - goto fail; + agino = be32_to_cpu(agi->agi_unlinked[bucket]); + while (agino != NULLAGINO) { + error = xfs_iget(mp, NULL, + XFS_AGINO_TO_INO(mp, pag->pag_agno, agino), + 0, 0, &ip); + if (error) + break; - /* - * Get the on disk inode to find the next inode in the bucket. - */ - error = xfs_imap_to_bp(mp, NULL, &ip->i_imap, &ibp); - if (error) - goto fail_iput; - dip = xfs_buf_offset(ibp, ip->i_imap.im_boffset); + ASSERT(VFS_I(ip)->i_nlink == 0); + ASSERT(VFS_I(ip)->i_mode != 0); + xfs_iflags_clear(ip, XFS_IRECOVERY); + agino = ip->i_next_unlinked; - xfs_iflags_clear(ip, XFS_IRECOVERY); - ASSERT(VFS_I(ip)->i_nlink == 0); - ASSERT(VFS_I(ip)->i_mode != 0); + if (prev_ip) { + ip->i_prev_unlinked = prev_agino; + xfs_irele(prev_ip); - /* setup for the next pass */ - agino = be32_to_cpu(dip->di_next_unlinked); - xfs_buf_relse(ibp); + /* + * Ensure the inode is removed from the unlinked list + * before we continue so that it won't race with + * building the in-memory list here. This could be + * serialised with the agibp lock, but that just + * serialises via lockstepping and it's much simpler + * just to flush the inodegc queue and wait for it to + * complete. + */ + xfs_inodegc_flush(mp); + } - xfs_irele(ip); - return agino; + prev_agino = agino; + prev_ip = ip; + } - fail_iput: - xfs_irele(ip); - fail: - /* - * We can't read in the inode this bucket points to, or this inode - * is messed up. Just ditch this bucket of inodes. We will lose - * some inodes and space, but at least we won't hang. - * - * Call xlog_recover_clear_agi_bucket() to perform a transaction to - * clear the inode pointer in the bucket. - */ - xlog_recover_clear_agi_bucket(mp, agno, bucket); - return NULLAGINO; + if (prev_ip) { + ip->i_prev_unlinked = prev_agino; + xfs_irele(prev_ip); + } + xfs_inodegc_flush(mp); + return error; } /* @@ -2741,59 +2743,70 @@ xlog_recover_process_one_iunlink( * scheduled on this CPU to ensure other scheduled work can run without undue * latency. */ -STATIC void -xlog_recover_process_iunlinks( - struct xlog *log) +static void +xlog_recover_iunlink_ag( + struct xfs_perag *pag) { - struct xfs_mount *mp = log->l_mp; - struct xfs_perag *pag; - xfs_agnumber_t agno; struct xfs_agi *agi; struct xfs_buf *agibp; - xfs_agino_t agino; int bucket; int error; - for_each_perag(mp, agno, pag) { - error = xfs_read_agi(mp, NULL, pag->pag_agno, &agibp); + error = xfs_read_agi(pag, NULL, &agibp); + if (error) { + /* + * AGI is b0rked. Don't process it. + * + * We should probably mark the filesystem as corrupt after we've + * recovered all the ag's we can.... + */ + return; + } + + /* + * Unlock the buffer so that it can be acquired in the normal course of + * the transaction to truncate and free each inode. Because we are not + * racing with anyone else here for the AGI buffer, we don't even need + * to hold it locked to read the initial unlinked bucket entries out of + * the buffer. We keep buffer reference though, so that it stays pinned + * in memory while we need the buffer. + */ + agi = agibp->b_addr; + xfs_buf_unlock(agibp); + + for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) { + error = xlog_recover_iunlink_bucket(pag, agi, bucket); if (error) { /* - * AGI is b0rked. Don't process it. - * - * We should probably mark the filesystem as corrupt - * after we've recovered all the ag's we can.... + * Bucket is unrecoverable, so only a repair scan can + * free the remaining unlinked inodes. Just empty the + * bucket and remaining inodes on it unreferenced and + * unfreeable. */ - continue; + xfs_inodegc_flush(pag->pag_mount); + xlog_recover_clear_agi_bucket(pag, bucket); } - /* - * Unlock the buffer so that it can be acquired in the normal - * course of the transaction to truncate and free each inode. - * Because we are not racing with anyone else here for the AGI - * buffer, we don't even need to hold it locked to read the - * initial unlinked bucket entries out of the buffer. We keep - * buffer reference though, so that it stays pinned in memory - * while we need the buffer. - */ - agi = agibp->b_addr; - xfs_buf_unlock(agibp); - - for (bucket = 0; bucket < XFS_AGI_UNLINKED_BUCKETS; bucket++) { - agino = be32_to_cpu(agi->agi_unlinked[bucket]); - while (agino != NULLAGINO) { - agino = xlog_recover_process_one_iunlink(mp, - pag->pag_agno, agino, bucket); - cond_resched(); - } - } - xfs_buf_rele(agibp); } + xfs_buf_rele(agibp); +} + +static void +xlog_recover_process_iunlinks( + struct xlog *log) +{ + struct xfs_perag *pag; + xfs_agnumber_t agno; + + for_each_perag(log->l_mp, agno, pag) + xlog_recover_iunlink_ag(pag); + /* * Flush the pending unlinked inodes to ensure that the inactivations * are fully completed on disk and the incore inodes can be reclaimed * before we signal that recovery is complete. */ - xfs_inodegc_flush(mp); + xfs_inodegc_flush(log->l_mp); } STATIC void @@ -3313,7 +3326,8 @@ xlog_do_recover( /* re-initialise in-core superblock and geometry structures */ mp->m_features |= xfs_sb_version_to_features(sbp); xfs_reinit_percpu_counters(mp); - error = xfs_initialize_perag(mp, sbp->sb_agcount, &mp->m_maxagi); + error = xfs_initialize_perag(mp, sbp->sb_agcount, sbp->sb_dblocks, + &mp->m_maxagi); if (error) { xfs_warn(mp, "Failed post-recovery per-ag init: %d", error); return error; diff --git a/fs/xfs/xfs_mount.c b/fs/xfs/xfs_mount.c index daa8d29c46b4..f10c88cee116 100644 --- a/fs/xfs/xfs_mount.c +++ b/fs/xfs/xfs_mount.c @@ -778,7 +778,8 @@ xfs_mountfs( /* * Allocate and initialize the per-ag data. */ - error = xfs_initialize_perag(mp, sbp->sb_agcount, &mp->m_maxagi); + error = xfs_initialize_perag(mp, sbp->sb_agcount, mp->m_sb.sb_dblocks, + &mp->m_maxagi); if (error) { xfs_warn(mp, "Failed per-ag init: %d", error); goto out_free_dir; diff --git a/fs/xfs/xfs_qm.c b/fs/xfs/xfs_qm.c index abf08bbf34a9..57dd3b722265 100644 --- a/fs/xfs/xfs_qm.c +++ b/fs/xfs/xfs_qm.c @@ -1154,7 +1154,7 @@ xfs_qm_dqusage_adjust( ASSERT(ip->i_delayed_blks == 0); if (XFS_IS_REALTIME_INODE(ip)) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); error = xfs_iread_extents(tp, ip, XFS_DATA_FORK); if (error) @@ -1229,12 +1229,11 @@ xfs_qm_flush_one( */ if (!xfs_dqflock_nowait(dqp)) { /* buf is pinned in-core by delwri list */ - bp = xfs_buf_incore(mp->m_ddev_targp, dqp->q_blkno, - mp->m_quotainfo->qi_dqchunklen, 0); - if (!bp) { - error = -EINVAL; + error = xfs_buf_incore(mp->m_ddev_targp, dqp->q_blkno, + mp->m_quotainfo->qi_dqchunklen, 0, &bp); + if (error) 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 e7a7c00d93be..724806c7ce3e 100644 --- a/fs/xfs/xfs_reflink.c +++ b/fs/xfs/xfs_reflink.c @@ -125,11 +125,10 @@ * shared blocks. If there are no shared extents, fbno and flen will * be set to NULLAGBLOCK and 0, respectively. */ -int +static int xfs_reflink_find_shared( - struct xfs_mount *mp, + struct xfs_perag *pag, struct xfs_trans *tp, - xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_extlen_t aglen, xfs_agblock_t *fbno, @@ -140,11 +139,11 @@ xfs_reflink_find_shared( struct xfs_btree_cur *cur; int error; - error = xfs_alloc_read_agf(mp, tp, agno, 0, &agbp); + error = xfs_alloc_read_agf(pag, tp, 0, &agbp); if (error) return error; - cur = xfs_refcountbt_init_cursor(mp, tp, agbp, agbp->b_pag); + cur = xfs_refcountbt_init_cursor(pag->pag_mount, tp, agbp, pag); error = xfs_refcount_find_shared(cur, agbno, aglen, fbno, flen, find_end_of_shared); @@ -171,7 +170,8 @@ xfs_reflink_trim_around_shared( struct xfs_bmbt_irec *irec, bool *shared) { - xfs_agnumber_t agno; + struct xfs_mount *mp = ip->i_mount; + struct xfs_perag *pag; xfs_agblock_t agbno; xfs_extlen_t aglen; xfs_agblock_t fbno; @@ -186,12 +186,13 @@ xfs_reflink_trim_around_shared( trace_xfs_reflink_trim_around_shared(ip, irec); - agno = XFS_FSB_TO_AGNO(ip->i_mount, irec->br_startblock); - agbno = XFS_FSB_TO_AGBNO(ip->i_mount, irec->br_startblock); + pag = xfs_perag_get(mp, XFS_FSB_TO_AGNO(mp, irec->br_startblock)); + agbno = XFS_FSB_TO_AGBNO(mp, irec->br_startblock); aglen = irec->br_blockcount; - error = xfs_reflink_find_shared(ip->i_mount, NULL, agno, agbno, - aglen, &fbno, &flen, true); + error = xfs_reflink_find_shared(pag, NULL, agbno, aglen, &fbno, &flen, + true); + xfs_perag_put(pag); if (error) return error; @@ -452,7 +453,7 @@ xfs_reflink_cancel_cow_blocks( xfs_fileoff_t end_fsb, bool cancel_real) { - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); struct xfs_bmbt_irec got, del; struct xfs_iext_cursor icur; int error = 0; @@ -593,7 +594,7 @@ xfs_reflink_end_cow_extent( struct xfs_bmbt_irec got, del, data; struct xfs_mount *mp = ip->i_mount; struct xfs_trans *tp; - struct xfs_ifork *ifp = XFS_IFORK_PTR(ip, XFS_COW_FORK); + struct xfs_ifork *ifp = xfs_ifork_ptr(ip, XFS_COW_FORK); unsigned int resblks; int nmaps; int error; @@ -1420,16 +1421,11 @@ xfs_reflink_inode_has_shared_extents( struct xfs_bmbt_irec got; struct xfs_mount *mp = ip->i_mount; struct xfs_ifork *ifp; - xfs_agnumber_t agno; - xfs_agblock_t agbno; - xfs_extlen_t aglen; - xfs_agblock_t rbno; - xfs_extlen_t rlen; struct xfs_iext_cursor icur; bool found; int error; - ifp = XFS_IFORK_PTR(ip, XFS_DATA_FORK); + ifp = xfs_ifork_ptr(ip, XFS_DATA_FORK); error = xfs_iread_extents(tp, ip, XFS_DATA_FORK); if (error) return error; @@ -1437,17 +1433,25 @@ xfs_reflink_inode_has_shared_extents( *has_shared = false; found = xfs_iext_lookup_extent(ip, ifp, 0, &icur, &got); while (found) { + struct xfs_perag *pag; + xfs_agblock_t agbno; + xfs_extlen_t aglen; + xfs_agblock_t rbno; + xfs_extlen_t rlen; + if (isnullstartblock(got.br_startblock) || got.br_state != XFS_EXT_NORM) goto next; - agno = XFS_FSB_TO_AGNO(mp, got.br_startblock); + + pag = xfs_perag_get(mp, XFS_FSB_TO_AGNO(mp, got.br_startblock)); agbno = XFS_FSB_TO_AGBNO(mp, got.br_startblock); aglen = got.br_blockcount; - - error = xfs_reflink_find_shared(mp, tp, agno, agbno, aglen, + error = xfs_reflink_find_shared(pag, tp, agbno, aglen, &rbno, &rlen, false); + xfs_perag_put(pag); if (error) return error; + /* Is there still a shared block here? */ if (rbno != NULLAGBLOCK) { *has_shared = true; diff --git a/fs/xfs/xfs_reflink.h b/fs/xfs/xfs_reflink.h index bea65f2fe657..65c5dfe17ecf 100644 --- a/fs/xfs/xfs_reflink.h +++ b/fs/xfs/xfs_reflink.h @@ -16,9 +16,6 @@ static inline bool xfs_is_cow_inode(struct xfs_inode *ip) return xfs_is_reflink_inode(ip) || xfs_is_always_cow_inode(ip); } -extern int xfs_reflink_find_shared(struct xfs_mount *mp, struct xfs_trans *tp, - xfs_agnumber_t agno, xfs_agblock_t agbno, xfs_extlen_t aglen, - xfs_agblock_t *fbno, xfs_extlen_t *flen, bool find_maximal); extern int xfs_reflink_trim_around_shared(struct xfs_inode *ip, struct xfs_bmbt_irec *irec, bool *shared); int xfs_bmap_trim_cow(struct xfs_inode *ip, struct xfs_bmbt_irec *imap, diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c index aa977c7ea370..3d27ba1295c9 100644 --- a/fs/xfs/xfs_super.c +++ b/fs/xfs/xfs_super.c @@ -40,6 +40,7 @@ #include "xfs_defer.h" #include "xfs_attr_item.h" #include "xfs_xattr.h" +#include "xfs_iunlink_item.h" #include #include @@ -1966,11 +1967,19 @@ xfs_init_caches(void) { int error; + xfs_buf_cache = kmem_cache_create("xfs_buf", sizeof(struct xfs_buf), 0, + SLAB_HWCACHE_ALIGN | + SLAB_RECLAIM_ACCOUNT | + SLAB_MEM_SPREAD, + NULL); + if (!xfs_buf_cache) + goto out; + xfs_log_ticket_cache = kmem_cache_create("xfs_log_ticket", sizeof(struct xlog_ticket), 0, 0, NULL); if (!xfs_log_ticket_cache) - goto out; + goto out_destroy_buf_cache; error = xfs_btree_init_cur_caches(); if (error) @@ -2096,8 +2105,16 @@ xfs_init_caches(void) if (!xfs_attri_cache) goto out_destroy_attrd_cache; + xfs_iunlink_cache = kmem_cache_create("xfs_iul_item", + sizeof(struct xfs_iunlink_item), + 0, 0, NULL); + if (!xfs_iunlink_cache) + goto out_destroy_attri_cache; + return 0; + out_destroy_attri_cache: + kmem_cache_destroy(xfs_attri_cache); out_destroy_attrd_cache: kmem_cache_destroy(xfs_attrd_cache); out_destroy_bui_cache: @@ -2136,6 +2153,8 @@ xfs_init_caches(void) xfs_btree_destroy_cur_caches(); out_destroy_log_ticket_cache: kmem_cache_destroy(xfs_log_ticket_cache); + out_destroy_buf_cache: + kmem_cache_destroy(xfs_buf_cache); out: return -ENOMEM; } @@ -2148,6 +2167,7 @@ xfs_destroy_caches(void) * destroy caches. */ rcu_barrier(); + kmem_cache_destroy(xfs_iunlink_cache); kmem_cache_destroy(xfs_attri_cache); kmem_cache_destroy(xfs_attrd_cache); kmem_cache_destroy(xfs_bui_cache); @@ -2168,6 +2188,7 @@ xfs_destroy_caches(void) xfs_defer_destroy_item_caches(); xfs_btree_destroy_cur_caches(); kmem_cache_destroy(xfs_log_ticket_cache); + kmem_cache_destroy(xfs_buf_cache); } STATIC int __init @@ -2213,6 +2234,7 @@ xfs_cpu_dead( list_for_each_entry_safe(mp, n, &xfs_mount_list, m_mount_list) { spin_unlock(&xfs_mount_list_lock); xfs_inodegc_cpu_dead(mp, cpu); + xlog_cil_pcp_dead(mp->m_log, cpu); spin_lock(&xfs_mount_list_lock); } spin_unlock(&xfs_mount_list_lock); @@ -2272,13 +2294,9 @@ init_xfs_fs(void) if (error) goto out_destroy_wq; - error = xfs_buf_init(); - if (error) - goto out_mru_cache_uninit; - error = xfs_init_procfs(); if (error) - goto out_buf_terminate; + goto out_mru_cache_uninit; error = xfs_sysctl_register(); if (error) @@ -2335,8 +2353,6 @@ init_xfs_fs(void) xfs_sysctl_unregister(); out_cleanup_procfs: xfs_cleanup_procfs(); - out_buf_terminate: - xfs_buf_terminate(); out_mru_cache_uninit: xfs_mru_cache_uninit(); out_destroy_wq: @@ -2362,7 +2378,6 @@ exit_xfs_fs(void) kset_unregister(xfs_kset); xfs_sysctl_unregister(); xfs_cleanup_procfs(); - xfs_buf_terminate(); xfs_mru_cache_uninit(); xfs_destroy_workqueues(); xfs_destroy_caches(); diff --git a/fs/xfs/xfs_symlink.c b/fs/xfs/xfs_symlink.c index 4145ba872547..8389f3ef88ef 100644 --- a/fs/xfs/xfs_symlink.c +++ b/fs/xfs/xfs_symlink.c @@ -256,7 +256,7 @@ xfs_symlink( /* * If the symlink will fit into the inode, write it inline. */ - if (pathlen <= XFS_IFORK_DSIZE(ip)) { + if (pathlen <= xfs_inode_data_fork_size(ip)) { xfs_init_local_fork(ip, XFS_DATA_FORK, target_path, pathlen); ip->i_disk_size = pathlen; diff --git a/fs/xfs/xfs_trace.h b/fs/xfs/xfs_trace.h index 0fa1b7a2918c..f9057af6e0c8 100644 --- a/fs/xfs/xfs_trace.h +++ b/fs/xfs/xfs_trace.h @@ -2171,7 +2171,7 @@ DECLARE_EVENT_CLASS(xfs_swap_extent_class, __entry->format = ip->i_df.if_format; __entry->nex = ip->i_df.if_nextents; __entry->broot_size = ip->i_df.if_broot_bytes; - __entry->fork_off = XFS_IFORK_BOFF(ip); + __entry->fork_off = xfs_inode_fork_boff(ip); ), TP_printk("dev %d:%d ino 0x%llx (%s), %s format, num_extents %llu, " "broot size %d, forkoff 0x%x", @@ -3672,7 +3672,6 @@ DEFINE_EVENT(xfs_ag_inode_class, name, \ TP_ARGS(ip)) DEFINE_AGINODE_EVENT(xfs_iunlink); DEFINE_AGINODE_EVENT(xfs_iunlink_remove); -DEFINE_AG_EVENT(xfs_iunlink_map_prev_fallback); DECLARE_EVENT_CLASS(xfs_fs_corrupt_class, TP_PROTO(struct xfs_mount *mp, unsigned int flags), diff --git a/fs/xfs/xfs_trans.c b/fs/xfs/xfs_trans.c index 82cf0189c0db..7bd16fbff534 100644 --- a/fs/xfs/xfs_trans.c +++ b/fs/xfs/xfs_trans.c @@ -760,7 +760,7 @@ xfs_log_item_batch_insert( void xfs_trans_committed_bulk( struct xfs_ail *ailp, - struct xfs_log_vec *log_vector, + struct list_head *lv_chain, xfs_lsn_t commit_lsn, bool aborted) { @@ -775,7 +775,7 @@ xfs_trans_committed_bulk( spin_unlock(&ailp->ail_lock); /* unpin all the log items */ - for (lv = log_vector; lv; lv = lv->lv_next ) { + list_for_each_entry(lv, lv_chain, lv_list) { struct xfs_log_item *lip = lv->lv_item; xfs_lsn_t item_lsn; @@ -844,6 +844,90 @@ xfs_trans_committed_bulk( spin_unlock(&ailp->ail_lock); } +/* + * Sort transaction items prior to running precommit operations. This will + * attempt to order the items such that they will always be locked in the same + * order. Items that have no sort function are moved to the end of the list + * and so are locked last. + * + * This may need refinement as different types of objects add sort functions. + * + * Function is more complex than it needs to be because we are comparing 64 bit + * values and the function only returns 32 bit values. + */ +static int +xfs_trans_precommit_sort( + void *unused_arg, + const struct list_head *a, + const struct list_head *b) +{ + struct xfs_log_item *lia = container_of(a, + struct xfs_log_item, li_trans); + struct xfs_log_item *lib = container_of(b, + struct xfs_log_item, li_trans); + int64_t diff; + + /* + * If both items are non-sortable, leave them alone. If only one is + * sortable, move the non-sortable item towards the end of the list. + */ + if (!lia->li_ops->iop_sort && !lib->li_ops->iop_sort) + return 0; + if (!lia->li_ops->iop_sort) + return 1; + if (!lib->li_ops->iop_sort) + return -1; + + diff = lia->li_ops->iop_sort(lia) - lib->li_ops->iop_sort(lib); + if (diff < 0) + return -1; + if (diff > 0) + return 1; + return 0; +} + +/* + * Run transaction precommit functions. + * + * If there is an error in any of the callouts, then stop immediately and + * trigger a shutdown to abort the transaction. There is no recovery possible + * from errors at this point as the transaction is dirty.... + */ +static int +xfs_trans_run_precommits( + struct xfs_trans *tp) +{ + struct xfs_mount *mp = tp->t_mountp; + struct xfs_log_item *lip, *n; + int error = 0; + + /* + * Sort the item list to avoid ABBA deadlocks with other transactions + * running precommit operations that lock multiple shared items such as + * inode cluster buffers. + */ + list_sort(NULL, &tp->t_items, xfs_trans_precommit_sort); + + /* + * Precommit operations can remove the log item from the transaction + * if the log item exists purely to delay modifications until they + * can be ordered against other operations. Hence we have to use + * list_for_each_entry_safe() here. + */ + list_for_each_entry_safe(lip, n, &tp->t_items, li_trans) { + if (!test_bit(XFS_LI_DIRTY, &lip->li_flags)) + continue; + if (lip->li_ops->iop_precommit) { + error = lip->li_ops->iop_precommit(tp, lip); + if (error) + break; + } + } + if (error) + xfs_force_shutdown(mp, SHUTDOWN_CORRUPT_INCORE); + return error; +} + /* * Commit the given transaction to the log. * @@ -869,6 +953,13 @@ __xfs_trans_commit( trace_xfs_trans_commit(tp, _RET_IP_); + error = xfs_trans_run_precommits(tp); + if (error) { + if (tp->t_flags & XFS_TRANS_PERM_LOG_RES) + xfs_defer_cancel(tp); + goto out_unreserve; + } + /* * Finish deferred items on final commit. Only permanent transactions * should ever have deferred ops. diff --git a/fs/xfs/xfs_trans.h b/fs/xfs/xfs_trans.h index 9561f193e7e1..55819785941c 100644 --- a/fs/xfs/xfs_trans.h +++ b/fs/xfs/xfs_trans.h @@ -45,6 +45,7 @@ struct xfs_log_item { struct xfs_log_vec *li_lv; /* active log vector */ struct xfs_log_vec *li_lv_shadow; /* standby vector */ xfs_csn_t li_seq; /* CIL commit seq */ + uint32_t li_order_id; /* CIL commit order */ }; /* @@ -71,10 +72,12 @@ struct xfs_item_ops { void (*iop_format)(struct xfs_log_item *, struct xfs_log_vec *); void (*iop_pin)(struct xfs_log_item *); void (*iop_unpin)(struct xfs_log_item *, int remove); - uint (*iop_push)(struct xfs_log_item *, struct list_head *); + uint64_t (*iop_sort)(struct xfs_log_item *lip); + int (*iop_precommit)(struct xfs_trans *tp, struct xfs_log_item *lip); void (*iop_committing)(struct xfs_log_item *lip, xfs_csn_t seq); - void (*iop_release)(struct xfs_log_item *); xfs_lsn_t (*iop_committed)(struct xfs_log_item *, xfs_lsn_t); + uint (*iop_push)(struct xfs_log_item *, struct list_head *); + void (*iop_release)(struct xfs_log_item *); int (*iop_recover)(struct xfs_log_item *lip, struct list_head *capture_list); bool (*iop_match)(struct xfs_log_item *item, uint64_t id); diff --git a/fs/xfs/xfs_trans_priv.h b/fs/xfs/xfs_trans_priv.h index f0d79a9050ba..d5400150358e 100644 --- a/fs/xfs/xfs_trans_priv.h +++ b/fs/xfs/xfs_trans_priv.h @@ -19,7 +19,8 @@ void xfs_trans_add_item(struct xfs_trans *, struct xfs_log_item *); void xfs_trans_del_item(struct xfs_log_item *); void xfs_trans_unreserve_and_mod_sb(struct xfs_trans *tp); -void xfs_trans_committed_bulk(struct xfs_ail *ailp, struct xfs_log_vec *lv, +void xfs_trans_committed_bulk(struct xfs_ail *ailp, + struct list_head *lv_chain, xfs_lsn_t commit_lsn, bool aborted); /* * AIL traversal cursor. diff --git a/include/asm-generic/Kbuild b/include/asm-generic/Kbuild index 36db8b9eb68a..941be574bbe0 100644 --- a/include/asm-generic/Kbuild +++ b/include/asm-generic/Kbuild @@ -45,7 +45,6 @@ mandatory-y += msi.h mandatory-y += pci.h mandatory-y += percpu.h mandatory-y += pgalloc.h -mandatory-y += platform-feature.h mandatory-y += preempt.h mandatory-y += rwonce.h mandatory-y += sections.h diff --git a/include/asm-generic/platform-feature.h b/include/asm-generic/platform-feature.h deleted file mode 100644 index 4b0af3d51588..000000000000 --- a/include/asm-generic/platform-feature.h +++ /dev/null @@ -1,8 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _ASM_GENERIC_PLATFORM_FEATURE_H -#define _ASM_GENERIC_PLATFORM_FEATURE_H - -/* Number of arch specific feature flags. */ -#define PLATFORM_ARCH_FEAT_N 0 - -#endif /* _ASM_GENERIC_PLATFORM_FEATURE_H */ diff --git a/include/crypto/hash.h b/include/crypto/hash.h index f140e4643949..f5841992dc9b 100644 --- a/include/crypto/hash.h +++ b/include/crypto/hash.h @@ -718,6 +718,8 @@ static inline void ahash_request_set_crypt(struct ahash_request *req, struct crypto_shash *crypto_alloc_shash(const char *alg_name, u32 type, u32 mask); +int crypto_has_shash(const char *alg_name, u32 type, u32 mask); + static inline struct crypto_tfm *crypto_shash_tfm(struct crypto_shash *tfm) { return &tfm->base; diff --git a/include/crypto/kpp.h b/include/crypto/kpp.h index cccceadc164b..24d01e9877c1 100644 --- a/include/crypto/kpp.h +++ b/include/crypto/kpp.h @@ -104,6 +104,8 @@ struct kpp_alg { */ struct crypto_kpp *crypto_alloc_kpp(const char *alg_name, u32 type, u32 mask); +int crypto_has_kpp(const char *alg_name, u32 type, u32 mask); + static inline struct crypto_tfm *crypto_kpp_tfm(struct crypto_kpp *tfm) { return &tfm->base; diff --git a/include/dt-bindings/clock/bcm21664.h b/include/dt-bindings/clock/bcm21664.h index 5a7f0e4750a8..7c7492742f3d 100644 --- a/include/dt-bindings/clock/bcm21664.h +++ b/include/dt-bindings/clock/bcm21664.h @@ -1,15 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _CLOCK_BCM21664_H diff --git a/include/dt-bindings/clock/bcm281xx.h b/include/dt-bindings/clock/bcm281xx.h index a763460cf1af..d74ca42112e7 100644 --- a/include/dt-bindings/clock/bcm281xx.h +++ b/include/dt-bindings/clock/bcm281xx.h @@ -1,15 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2013 Broadcom Corporation * Copyright 2013 Linaro Limited - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _CLOCK_BCM281XX_H diff --git a/include/dt-bindings/clock/efm32-cmu.h b/include/dt-bindings/clock/efm32-cmu.h deleted file mode 100644 index 4b48d15fe194..000000000000 --- a/include/dt-bindings/clock/efm32-cmu.h +++ /dev/null @@ -1,43 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __DT_BINDINGS_CLOCK_EFM32_CMU_H -#define __DT_BINDINGS_CLOCK_EFM32_CMU_H - -#define clk_HFXO 0 -#define clk_HFRCO 1 -#define clk_LFXO 2 -#define clk_LFRCO 3 -#define clk_ULFRCO 4 -#define clk_AUXHFRCO 5 -#define clk_HFCLKNODIV 6 -#define clk_HFCLK 7 -#define clk_HFPERCLK 8 -#define clk_HFCORECLK 9 -#define clk_LFACLK 10 -#define clk_LFBCLK 11 -#define clk_WDOGCLK 12 -#define clk_HFCORECLKDMA 13 -#define clk_HFCORECLKAES 14 -#define clk_HFCORECLKUSBC 15 -#define clk_HFCORECLKUSB 16 -#define clk_HFCORECLKLE 17 -#define clk_HFCORECLKEBI 18 -#define clk_HFPERCLKUSART0 19 -#define clk_HFPERCLKUSART1 20 -#define clk_HFPERCLKUSART2 21 -#define clk_HFPERCLKUART0 22 -#define clk_HFPERCLKUART1 23 -#define clk_HFPERCLKTIMER0 24 -#define clk_HFPERCLKTIMER1 25 -#define clk_HFPERCLKTIMER2 26 -#define clk_HFPERCLKTIMER3 27 -#define clk_HFPERCLKACMP0 28 -#define clk_HFPERCLKACMP1 29 -#define clk_HFPERCLKI2C0 30 -#define clk_HFPERCLKI2C1 31 -#define clk_HFPERCLKGPIO 32 -#define clk_HFPERCLKVCMP 33 -#define clk_HFPERCLKPRS 34 -#define clk_HFPERCLKADC0 35 -#define clk_HFPERCLKDAC0 36 - -#endif /* __DT_BINDINGS_CLOCK_EFM32_CMU_H */ diff --git a/include/dt-bindings/clock/qcom,gcc-ipq8074.h b/include/dt-bindings/clock/qcom,gcc-ipq8074.h index 55f8322a1e50..e4991d303708 100644 --- a/include/dt-bindings/clock/qcom,gcc-ipq8074.h +++ b/include/dt-bindings/clock/qcom,gcc-ipq8074.h @@ -233,6 +233,7 @@ #define GCC_PCIE0_AXI_S_BRIDGE_CLK 224 #define GCC_PCIE0_RCHNG_CLK_SRC 225 #define GCC_PCIE0_RCHNG_CLK 226 +#define GCC_CRYPTO_PPE_CLK 227 #define GCC_BLSP1_BCR 0 #define GCC_BLSP1_QUP1_BCR 1 diff --git a/include/dt-bindings/clock/qcom,gcc-msm8939.h b/include/dt-bindings/clock/qcom,gcc-msm8939.h index 0634467c4ce5..2d545ed0d35a 100644 --- a/include/dt-bindings/clock/qcom,gcc-msm8939.h +++ b/include/dt-bindings/clock/qcom,gcc-msm8939.h @@ -192,6 +192,7 @@ #define GCC_VENUS0_CORE0_VCODEC0_CLK 183 #define GCC_VENUS0_CORE1_VCODEC0_CLK 184 #define GCC_OXILI_TIMER_CLK 185 +#define SYSTEM_MM_NOC_BFDCD_CLK_SRC 186 /* Indexes for GDSCs */ #define BIMC_GDSC 0 diff --git a/include/dt-bindings/clock/r9a07g043-cpg.h b/include/dt-bindings/clock/r9a07g043-cpg.h index 27e232733096..77cde8effdc7 100644 --- a/include/dt-bindings/clock/r9a07g043-cpg.h +++ b/include/dt-bindings/clock/r9a07g043-cpg.h @@ -108,6 +108,15 @@ #define R9A07G043_ADC_ADCLK 76 #define R9A07G043_ADC_PCLK 77 #define R9A07G043_TSU_PCLK 78 +#define R9A07G043_NCEPLDM_DM_CLK 79 /* RZ/Five Only */ +#define R9A07G043_NCEPLDM_ACLK 80 /* RZ/Five Only */ +#define R9A07G043_NCEPLDM_TCK 81 /* RZ/Five Only */ +#define R9A07G043_NCEPLMT_ACLK 82 /* RZ/Five Only */ +#define R9A07G043_NCEPLIC_ACLK 83 /* RZ/Five Only */ +#define R9A07G043_AX45MP_CORE0_CLK 84 /* RZ/Five Only */ +#define R9A07G043_AX45MP_ACLK 85 /* RZ/Five Only */ +#define R9A07G043_IAX45_CLK 86 /* RZ/Five Only */ +#define R9A07G043_IAX45_PCLK 87 /* RZ/Five Only */ /* R9A07G043 Resets */ #define R9A07G043_CA55_RST_1_0 0 /* RZ/G2UL Only */ @@ -180,5 +189,16 @@ #define R9A07G043_ADC_PRESETN 67 #define R9A07G043_ADC_ADRST_N 68 #define R9A07G043_TSU_PRESETN 69 +#define R9A07G043_NCEPLDM_DTM_PWR_RST_N 70 /* RZ/Five Only */ +#define R9A07G043_NCEPLDM_ARESETN 71 /* RZ/Five Only */ +#define R9A07G043_NCEPLMT_POR_RSTN 72 /* RZ/Five Only */ +#define R9A07G043_NCEPLMT_ARESETN 73 /* RZ/Five Only */ +#define R9A07G043_NCEPLIC_ARESETN 74 /* RZ/Five Only */ +#define R9A07G043_AX45MP_ARESETNM 75 /* RZ/Five Only */ +#define R9A07G043_AX45MP_ARESETNS 76 /* RZ/Five Only */ +#define R9A07G043_AX45MP_L2_RESETN 77 /* RZ/Five Only */ +#define R9A07G043_AX45MP_CORE0_RESETN 78 /* RZ/Five Only */ +#define R9A07G043_IAX45_RESETN 79 /* RZ/Five Only */ + #endif /* __DT_BINDINGS_CLOCK_R9A07G043_CPG_H__ */ diff --git a/include/dt-bindings/clock/sprd,ums512-clk.h b/include/dt-bindings/clock/sprd,ums512-clk.h new file mode 100644 index 000000000000..4f1d90849944 --- /dev/null +++ b/include/dt-bindings/clock/sprd,ums512-clk.h @@ -0,0 +1,397 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Unisoc UMS512 SoC DTS file + * + * Copyright (C) 2022, Unisoc Inc. + */ + +#ifndef _DT_BINDINGS_CLK_UMS512_H_ +#define _DT_BINDINGS_CLK_UMS512_H_ + +#define CLK_26M_AUD 0 +#define CLK_13M 1 +#define CLK_6M5 2 +#define CLK_4M3 3 +#define CLK_2M 4 +#define CLK_1M 5 +#define CLK_250K 6 +#define CLK_RCO_25M 7 +#define CLK_RCO_4M 8 +#define CLK_RCO_2M 9 +#define CLK_ISPPLL_GATE 10 +#define CLK_DPLL0_GATE 11 +#define CLK_DPLL1_GATE 12 +#define CLK_LPLL_GATE 13 +#define CLK_TWPLL_GATE 14 +#define CLK_GPLL_GATE 15 +#define CLK_RPLL_GATE 16 +#define CLK_CPPLL_GATE 17 +#define CLK_MPLL0_GATE 18 +#define CLK_MPLL1_GATE 19 +#define CLK_MPLL2_GATE 20 +#define CLK_PMU_GATE_NUM (CLK_MPLL2_GATE + 1) + +#define CLK_DPLL0 0 +#define CLK_DPLL0_58M31 1 +#define CLK_ANLG_PHY_G0_NUM (CLK_DPLL0_58M31 + 1) + +#define CLK_MPLL1 0 +#define CLK_MPLL1_63M38 1 +#define CLK_ANLG_PHY_G2_NUM (CLK_MPLL1_63M38 + 1) + +#define CLK_RPLL 0 +#define CLK_AUDIO_GATE 1 +#define CLK_MPLL0 2 +#define CLK_MPLL0_56M88 3 +#define CLK_MPLL2 4 +#define CLK_MPLL2_47M13 5 +#define CLK_ANLG_PHY_G3_NUM (CLK_MPLL2_47M13 + 1) + +#define CLK_TWPLL 0 +#define CLK_TWPLL_768M 1 +#define CLK_TWPLL_384M 2 +#define CLK_TWPLL_192M 3 +#define CLK_TWPLL_96M 4 +#define CLK_TWPLL_48M 5 +#define CLK_TWPLL_24M 6 +#define CLK_TWPLL_12M 7 +#define CLK_TWPLL_512M 8 +#define CLK_TWPLL_256M 9 +#define CLK_TWPLL_128M 10 +#define CLK_TWPLL_64M 11 +#define CLK_TWPLL_307M2 12 +#define CLK_TWPLL_219M4 13 +#define CLK_TWPLL_170M6 14 +#define CLK_TWPLL_153M6 15 +#define CLK_TWPLL_76M8 16 +#define CLK_TWPLL_51M2 17 +#define CLK_TWPLL_38M4 18 +#define CLK_TWPLL_19M2 19 +#define CLK_TWPLL_12M29 20 +#define CLK_LPLL 21 +#define CLK_LPLL_614M4 22 +#define CLK_LPLL_409M6 23 +#define CLK_LPLL_245M76 24 +#define CLK_LPLL_30M72 25 +#define CLK_ISPPLL 26 +#define CLK_ISPPLL_468M 27 +#define CLK_ISPPLL_78M 28 +#define CLK_GPLL 29 +#define CLK_GPLL_40M 30 +#define CLK_CPPLL 31 +#define CLK_CPPLL_39M32 32 +#define CLK_ANLG_PHY_GC_NUM (CLK_CPPLL_39M32 + 1) + +#define CLK_AP_APB 0 +#define CLK_IPI 1 +#define CLK_AP_UART0 2 +#define CLK_AP_UART1 3 +#define CLK_AP_UART2 4 +#define CLK_AP_I2C0 5 +#define CLK_AP_I2C1 6 +#define CLK_AP_I2C2 7 +#define CLK_AP_I2C3 8 +#define CLK_AP_I2C4 9 +#define CLK_AP_SPI0 10 +#define CLK_AP_SPI1 11 +#define CLK_AP_SPI2 12 +#define CLK_AP_SPI3 13 +#define CLK_AP_IIS0 14 +#define CLK_AP_IIS1 15 +#define CLK_AP_IIS2 16 +#define CLK_AP_SIM 17 +#define CLK_AP_CE 18 +#define CLK_SDIO0_2X 19 +#define CLK_SDIO1_2X 20 +#define CLK_EMMC_2X 21 +#define CLK_VSP 22 +#define CLK_DISPC0 23 +#define CLK_DISPC0_DPI 24 +#define CLK_DSI_APB 25 +#define CLK_DSI_RXESC 26 +#define CLK_DSI_LANEBYTE 27 +#define CLK_VDSP 28 +#define CLK_VDSP_M 29 +#define CLK_AP_CLK_NUM (CLK_VDSP_M + 1) + +#define CLK_DSI_EB 0 +#define CLK_DISPC_EB 1 +#define CLK_VSP_EB 2 +#define CLK_VDMA_EB 3 +#define CLK_DMA_PUB_EB 4 +#define CLK_DMA_SEC_EB 5 +#define CLK_IPI_EB 6 +#define CLK_AHB_CKG_EB 7 +#define CLK_BM_CLK_EB 8 +#define CLK_AP_AHB_GATE_NUM (CLK_BM_CLK_EB + 1) + +#define CLK_AON_APB 0 +#define CLK_ADI 1 +#define CLK_AUX0 2 +#define CLK_AUX1 3 +#define CLK_AUX2 4 +#define CLK_PROBE 5 +#define CLK_PWM0 6 +#define CLK_PWM1 7 +#define CLK_PWM2 8 +#define CLK_PWM3 9 +#define CLK_EFUSE 10 +#define CLK_UART0 11 +#define CLK_UART1 12 +#define CLK_THM0 13 +#define CLK_THM1 14 +#define CLK_THM2 15 +#define CLK_THM3 16 +#define CLK_AON_I2C 17 +#define CLK_AON_IIS 18 +#define CLK_SCC 19 +#define CLK_APCPU_DAP 20 +#define CLK_APCPU_DAP_MTCK 21 +#define CLK_APCPU_TS 22 +#define CLK_DEBUG_TS 23 +#define CLK_DSI_TEST_S 24 +#define CLK_DJTAG_TCK 25 +#define CLK_DJTAG_TCK_HW 26 +#define CLK_AON_TMR 27 +#define CLK_AON_PMU 28 +#define CLK_DEBOUNCE 29 +#define CLK_APCPU_PMU 30 +#define CLK_TOP_DVFS 31 +#define CLK_OTG_UTMI 32 +#define CLK_OTG_REF 33 +#define CLK_CSSYS 34 +#define CLK_CSSYS_PUB 35 +#define CLK_CSSYS_APB 36 +#define CLK_AP_AXI 37 +#define CLK_AP_MM 38 +#define CLK_SDIO2_2X 39 +#define CLK_ANALOG_IO_APB 40 +#define CLK_DMC_REF_CLK 41 +#define CLK_EMC 42 +#define CLK_USB 43 +#define CLK_26M_PMU 44 +#define CLK_AON_APB_NUM (CLK_26M_PMU + 1) + +#define CLK_MM_AHB 0 +#define CLK_MM_MTX 1 +#define CLK_SENSOR0 2 +#define CLK_SENSOR1 3 +#define CLK_SENSOR2 4 +#define CLK_CPP 5 +#define CLK_JPG 6 +#define CLK_FD 7 +#define CLK_DCAM_IF 8 +#define CLK_DCAM_AXI 9 +#define CLK_ISP 10 +#define CLK_MIPI_CSI0 11 +#define CLK_MIPI_CSI1 12 +#define CLK_MIPI_CSI2 13 +#define CLK_MM_CLK_NUM (CLK_MIPI_CSI2 + 1) + +#define CLK_RC100M_CAL_EB 0 +#define CLK_DJTAG_TCK_EB 1 +#define CLK_DJTAG_EB 2 +#define CLK_AUX0_EB 3 +#define CLK_AUX1_EB 4 +#define CLK_AUX2_EB 5 +#define CLK_PROBE_EB 6 +#define CLK_MM_EB 7 +#define CLK_GPU_EB 8 +#define CLK_MSPI_EB 9 +#define CLK_APCPU_DAP_EB 10 +#define CLK_AON_CSSYS_EB 11 +#define CLK_CSSYS_APB_EB 12 +#define CLK_CSSYS_PUB_EB 13 +#define CLK_SDPHY_CFG_EB 14 +#define CLK_SDPHY_REF_EB 15 +#define CLK_EFUSE_EB 16 +#define CLK_GPIO_EB 17 +#define CLK_MBOX_EB 18 +#define CLK_KPD_EB 19 +#define CLK_AON_SYST_EB 20 +#define CLK_AP_SYST_EB 21 +#define CLK_AON_TMR_EB 22 +#define CLK_OTG_UTMI_EB 23 +#define CLK_OTG_PHY_EB 24 +#define CLK_SPLK_EB 25 +#define CLK_PIN_EB 26 +#define CLK_ANA_EB 27 +#define CLK_APCPU_TS0_EB 28 +#define CLK_APB_BUSMON_EB 29 +#define CLK_AON_IIS_EB 30 +#define CLK_SCC_EB 31 +#define CLK_THM0_EB 32 +#define CLK_THM1_EB 33 +#define CLK_THM2_EB 34 +#define CLK_ASIM_TOP_EB 35 +#define CLK_I2C_EB 36 +#define CLK_PMU_EB 37 +#define CLK_ADI_EB 38 +#define CLK_EIC_EB 39 +#define CLK_AP_INTC0_EB 40 +#define CLK_AP_INTC1_EB 41 +#define CLK_AP_INTC2_EB 42 +#define CLK_AP_INTC3_EB 43 +#define CLK_AP_INTC4_EB 44 +#define CLK_AP_INTC5_EB 45 +#define CLK_AUDCP_INTC_EB 46 +#define CLK_AP_TMR0_EB 47 +#define CLK_AP_TMR1_EB 48 +#define CLK_AP_TMR2_EB 49 +#define CLK_PWM0_EB 50 +#define CLK_PWM1_EB 51 +#define CLK_PWM2_EB 52 +#define CLK_PWM3_EB 53 +#define CLK_AP_WDG_EB 54 +#define CLK_APCPU_WDG_EB 55 +#define CLK_SERDES_EB 56 +#define CLK_ARCH_RTC_EB 57 +#define CLK_KPD_RTC_EB 58 +#define CLK_AON_SYST_RTC_EB 59 +#define CLK_AP_SYST_RTC_EB 60 +#define CLK_AON_TMR_RTC_EB 61 +#define CLK_EIC_RTC_EB 62 +#define CLK_EIC_RTCDV5_EB 63 +#define CLK_AP_WDG_RTC_EB 64 +#define CLK_AC_WDG_RTC_EB 65 +#define CLK_AP_TMR0_RTC_EB 66 +#define CLK_AP_TMR1_RTC_EB 67 +#define CLK_AP_TMR2_RTC_EB 68 +#define CLK_DCXO_LC_RTC_EB 69 +#define CLK_BB_CAL_RTC_EB 70 +#define CLK_AP_EMMC_RTC_EB 71 +#define CLK_AP_SDIO0_RTC_EB 72 +#define CLK_AP_SDIO1_RTC_EB 73 +#define CLK_AP_SDIO2_RTC_EB 74 +#define CLK_DSI_CSI_TEST_EB 75 +#define CLK_DJTAG_TCK_EN 76 +#define CLK_DPHY_REF_EB 77 +#define CLK_DMC_REF_EB 78 +#define CLK_OTG_REF_EB 79 +#define CLK_TSEN_EB 80 +#define CLK_TMR_EB 81 +#define CLK_RC100M_REF_EB 82 +#define CLK_RC100M_FDK_EB 83 +#define CLK_DEBOUNCE_EB 84 +#define CLK_DET_32K_EB 85 +#define CLK_TOP_CSSYS_EB 86 +#define CLK_AP_AXI_EN 87 +#define CLK_SDIO0_2X_EN 88 +#define CLK_SDIO0_1X_EN 89 +#define CLK_SDIO1_2X_EN 90 +#define CLK_SDIO1_1X_EN 91 +#define CLK_SDIO2_2X_EN 92 +#define CLK_SDIO2_1X_EN 93 +#define CLK_EMMC_2X_EN 94 +#define CLK_EMMC_1X_EN 95 +#define CLK_PLL_TEST_EN 96 +#define CLK_CPHY_CFG_EN 97 +#define CLK_DEBUG_TS_EN 98 +#define CLK_ACCESS_AUD_EN 99 +#define CLK_AON_APB_GATE_NUM (CLK_ACCESS_AUD_EN + 1) + +#define CLK_MM_CPP_EB 0 +#define CLK_MM_JPG_EB 1 +#define CLK_MM_DCAM_EB 2 +#define CLK_MM_ISP_EB 3 +#define CLK_MM_CSI2_EB 4 +#define CLK_MM_CSI1_EB 5 +#define CLK_MM_CSI0_EB 6 +#define CLK_MM_CKG_EB 7 +#define CLK_ISP_AHB_EB 8 +#define CLK_MM_DVFS_EB 9 +#define CLK_MM_FD_EB 10 +#define CLK_MM_SENSOR2_EB 11 +#define CLK_MM_SENSOR1_EB 12 +#define CLK_MM_SENSOR0_EB 13 +#define CLK_MM_MIPI_CSI2_EB 14 +#define CLK_MM_MIPI_CSI1_EB 15 +#define CLK_MM_MIPI_CSI0_EB 16 +#define CLK_DCAM_AXI_EB 17 +#define CLK_ISP_AXI_EB 18 +#define CLK_MM_CPHY_EB 19 +#define CLK_MM_GATE_CLK_NUM (CLK_MM_CPHY_EB + 1) + +#define CLK_SIM0_EB 0 +#define CLK_IIS0_EB 1 +#define CLK_IIS1_EB 2 +#define CLK_IIS2_EB 3 +#define CLK_APB_REG_EB 4 +#define CLK_SPI0_EB 5 +#define CLK_SPI1_EB 6 +#define CLK_SPI2_EB 7 +#define CLK_SPI3_EB 8 +#define CLK_I2C0_EB 9 +#define CLK_I2C1_EB 10 +#define CLK_I2C2_EB 11 +#define CLK_I2C3_EB 12 +#define CLK_I2C4_EB 13 +#define CLK_UART0_EB 14 +#define CLK_UART1_EB 15 +#define CLK_UART2_EB 16 +#define CLK_SIM0_32K_EB 17 +#define CLK_SPI0_LFIN_EB 18 +#define CLK_SPI1_LFIN_EB 19 +#define CLK_SPI2_LFIN_EB 20 +#define CLK_SPI3_LFIN_EB 21 +#define CLK_SDIO0_EB 22 +#define CLK_SDIO1_EB 23 +#define CLK_SDIO2_EB 24 +#define CLK_EMMC_EB 25 +#define CLK_SDIO0_32K_EB 26 +#define CLK_SDIO1_32K_EB 27 +#define CLK_SDIO2_32K_EB 28 +#define CLK_EMMC_32K_EB 29 +#define CLK_AP_APB_GATE_NUM (CLK_EMMC_32K_EB + 1) + +#define CLK_GPU_CORE_EB 0 +#define CLK_GPU_CORE 1 +#define CLK_GPU_MEM_EB 2 +#define CLK_GPU_MEM 3 +#define CLK_GPU_SYS_EB 4 +#define CLK_GPU_SYS 5 +#define CLK_GPU_CLK_NUM (CLK_GPU_SYS + 1) + +#define CLK_AUDCP_IIS0_EB 0 +#define CLK_AUDCP_IIS1_EB 1 +#define CLK_AUDCP_IIS2_EB 2 +#define CLK_AUDCP_UART_EB 3 +#define CLK_AUDCP_DMA_CP_EB 4 +#define CLK_AUDCP_DMA_AP_EB 5 +#define CLK_AUDCP_SRC48K_EB 6 +#define CLK_AUDCP_MCDT_EB 7 +#define CLK_AUDCP_VBCIFD_EB 8 +#define CLK_AUDCP_VBC_EB 9 +#define CLK_AUDCP_SPLK_EB 10 +#define CLK_AUDCP_ICU_EB 11 +#define CLK_AUDCP_DMA_AP_ASHB_EB 12 +#define CLK_AUDCP_DMA_CP_ASHB_EB 13 +#define CLK_AUDCP_AUD_EB 14 +#define CLK_AUDCP_VBC_24M_EB 15 +#define CLK_AUDCP_TMR_26M_EB 16 +#define CLK_AUDCP_DVFS_ASHB_EB 17 +#define CLK_AUDCP_AHB_GATE_NUM (CLK_AUDCP_DVFS_ASHB_EB + 1) + +#define CLK_AUDCP_WDG_EB 0 +#define CLK_AUDCP_RTC_WDG_EB 1 +#define CLK_AUDCP_TMR0_EB 2 +#define CLK_AUDCP_TMR1_EB 3 +#define CLK_AUDCP_APB_GATE_NUM (CLK_AUDCP_TMR1_EB + 1) + +#define CLK_ACORE0 0 +#define CLK_ACORE1 1 +#define CLK_ACORE2 2 +#define CLK_ACORE3 3 +#define CLK_ACORE4 4 +#define CLK_ACORE5 5 +#define CLK_PCORE0 6 +#define CLK_PCORE1 7 +#define CLK_SCU 8 +#define CLK_ACE 9 +#define CLK_PERIPH 10 +#define CLK_GIC 11 +#define CLK_ATB 12 +#define CLK_DEBUG_APB 13 +#define CLK_APCPU_SEC_NUM (CLK_DEBUG_APB + 1) + +#endif /* _DT_BINDINGS_CLK_UMS512_H_ */ diff --git a/include/dt-bindings/clock/ti-dra7-atl.h b/include/dt-bindings/clock/ti-dra7-atl.h index 42dd4164f6f4..b0e71e3cce95 100644 --- a/include/dt-bindings/clock/ti-dra7-atl.h +++ b/include/dt-bindings/clock/ti-dra7-atl.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This header provides constants for DRA7 ATL (Audio Tracking Logic) * @@ -6,15 +7,6 @@ * Copyright (C) 2013 Texas Instruments, Inc. * * Peter Ujfalusi - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DT_BINDINGS_CLK_DRA7_ATL_H diff --git a/include/dt-bindings/gpio/gpio.h b/include/dt-bindings/gpio/gpio.h index c029467e828b..5566e58196a2 100644 --- a/include/dt-bindings/gpio/gpio.h +++ b/include/dt-bindings/gpio/gpio.h @@ -39,4 +39,7 @@ /* Bit 5 express pull down */ #define GPIO_PULL_DOWN 32 +/* Bit 6 express pull disable */ +#define GPIO_PULL_DISABLE 64 + #endif diff --git a/include/dt-bindings/pinctrl/hisi.h b/include/dt-bindings/pinctrl/hisi.h index 93064c750c8c..2175ec89c82f 100644 --- a/include/dt-bindings/pinctrl/hisi.h +++ b/include/dt-bindings/pinctrl/hisi.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This header provides constants for hisilicon pinctrl bindings. * * Copyright (c) 2015 HiSilicon Limited. * Copyright (c) 2015 Linaro Limited. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DT_BINDINGS_PINCTRL_HISI_H diff --git a/include/dt-bindings/pinctrl/keystone.h b/include/dt-bindings/pinctrl/keystone.h index 7f97d776a8ff..66f8aecada53 100644 --- a/include/dt-bindings/pinctrl/keystone.h +++ b/include/dt-bindings/pinctrl/keystone.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * This header provides constants for Keystone pinctrl bindings. * * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DT_BINDINGS_PINCTRL_KEYSTONE_H diff --git a/include/dt-bindings/power/mt6797-power.h b/include/dt-bindings/power/mt6797-power.h index a60c1d81cf75..bd451d860e6a 100644 --- a/include/dt-bindings/power/mt6797-power.h +++ b/include/dt-bindings/power/mt6797-power.h @@ -1,14 +1,7 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (c) 2017 MediaTek Inc. * Author: Mars.C - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _DT_BINDINGS_POWER_MT6797_POWER_H diff --git a/include/dt-bindings/reset/mt8186-resets.h b/include/dt-bindings/reset/mt8186-resets.h index 5f850370c42c..2e9029c22f38 100644 --- a/include/dt-bindings/reset/mt8186-resets.h +++ b/include/dt-bindings/reset/mt8186-resets.h @@ -7,6 +7,7 @@ #ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8186 #define _DT_BINDINGS_RESET_CONTROLLER_MT8186 +/* TOPRGU resets */ #define MT8186_TOPRGU_INFRA_SW_RST 0 #define MT8186_TOPRGU_MM_SW_RST 1 #define MT8186_TOPRGU_MFG_SW_RST 2 @@ -33,4 +34,8 @@ /* MMSYS resets */ #define MT8186_MMSYS_SW0_RST_B_DISP_DSI0 19 +/* INFRA resets */ +#define MT8186_INFRA_THERMAL_CTRL_RST 0 +#define MT8186_INFRA_PTP_CTRL_RST 1 + #endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8186 */ diff --git a/include/dt-bindings/reset/mt8192-resets.h b/include/dt-bindings/reset/mt8192-resets.h index 764ca9910fa9..12e2087c90a3 100644 --- a/include/dt-bindings/reset/mt8192-resets.h +++ b/include/dt-bindings/reset/mt8192-resets.h @@ -7,6 +7,7 @@ #ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8192 #define _DT_BINDINGS_RESET_CONTROLLER_MT8192 +/* TOPRGU resets */ #define MT8192_TOPRGU_MM_SW_RST 1 #define MT8192_TOPRGU_MFG_SW_RST 2 #define MT8192_TOPRGU_VENC_SW_RST 3 @@ -30,4 +31,11 @@ /* MMSYS resets */ #define MT8192_MMSYS_SW0_RST_B_DISP_DSI0 15 +/* INFRA resets */ +#define MT8192_INFRA_RST0_THERM_CTRL_SWRST 0 +#define MT8192_INFRA_RST2_PEXTP_PHY_SWRST 1 +#define MT8192_INFRA_RST3_THERM_CTRL_PTP_SWRST 2 +#define MT8192_INFRA_RST4_PCIE_TOP_SWRST 3 +#define MT8192_INFRA_RST4_THERM_CTRL_MCU_SWRST 4 + #endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8192 */ diff --git a/include/dt-bindings/reset/mt8195-resets.h b/include/dt-bindings/reset/mt8195-resets.h index a26bccc8b957..0b1937f14b36 100644 --- a/include/dt-bindings/reset/mt8195-resets.h +++ b/include/dt-bindings/reset/mt8195-resets.h @@ -7,6 +7,7 @@ #ifndef _DT_BINDINGS_RESET_CONTROLLER_MT8195 #define _DT_BINDINGS_RESET_CONTROLLER_MT8195 +/* TOPRGU resets */ #define MT8195_TOPRGU_CONN_MCU_SW_RST 0 #define MT8195_TOPRGU_INFRA_GRST_SW_RST 1 #define MT8195_TOPRGU_APU_SW_RST 2 @@ -26,4 +27,9 @@ #define MT8195_TOPRGU_SW_RST_NUM 16 +/* INFRA resets */ +#define MT8195_INFRA_RST0_THERM_CTRL_SWRST 0 +#define MT8195_INFRA_RST3_THERM_CTRL_PTP_SWRST 1 +#define MT8195_INFRA_RST4_THERM_CTRL_MCU_SWRST 2 + #endif /* _DT_BINDINGS_RESET_CONTROLLER_MT8195 */ diff --git a/include/kvm/arm_vgic.h b/include/kvm/arm_vgic.h index 2d8f2e90edc2..4df9e73a8bb5 100644 --- a/include/kvm/arm_vgic.h +++ b/include/kvm/arm_vgic.h @@ -364,7 +364,7 @@ struct vgic_cpu { extern struct static_key_false vgic_v2_cpuif_trap; extern struct static_key_false vgic_v3_cpuif_trap; -int kvm_vgic_addr(struct kvm *kvm, unsigned long type, u64 *addr, bool write); +int kvm_set_legacy_vgic_v2_addr(struct kvm *kvm, struct kvm_arm_device_addr *dev_addr); void kvm_vgic_early_init(struct kvm *kvm); int kvm_vgic_vcpu_init(struct kvm_vcpu *vcpu); int kvm_vgic_create(struct kvm *kvm, u32 type); diff --git a/include/linux/acpi.h b/include/linux/acpi.h index 7e7a33b6c8d7..f6d4539c3895 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -1431,7 +1431,6 @@ int find_acpi_cpu_topology(unsigned int cpu, int level); int find_acpi_cpu_topology_cluster(unsigned int cpu); int find_acpi_cpu_topology_package(unsigned int cpu); int find_acpi_cpu_topology_hetero_id(unsigned int cpu); -int find_acpi_cpu_cache_topology(unsigned int cpu, int level); #else static inline int acpi_pptt_cpu_is_thread(unsigned int cpu) { @@ -1453,10 +1452,6 @@ static inline int find_acpi_cpu_topology_hetero_id(unsigned int cpu) { return -EINVAL; } -static inline int find_acpi_cpu_cache_topology(unsigned int cpu, int level) -{ - return -EINVAL; -} #endif #ifdef CONFIG_ACPI_PCC diff --git a/include/linux/arch_topology.h b/include/linux/arch_topology.h index 58cbe18d825c..a07b510e7dc5 100644 --- a/include/linux/arch_topology.h +++ b/include/linux/arch_topology.h @@ -68,7 +68,6 @@ struct cpu_topology { int core_id; int cluster_id; int package_id; - int llc_id; cpumask_t thread_sibling; cpumask_t core_sibling; cpumask_t cluster_sibling; diff --git a/include/linux/base64.h b/include/linux/base64.h new file mode 100644 index 000000000000..660d4cb1ef31 --- /dev/null +++ b/include/linux/base64.h @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * base64 encoding, lifted from fs/crypto/fname.c. + */ + +#ifndef _LINUX_BASE64_H +#define _LINUX_BASE64_H + +#include + +#define BASE64_CHARS(nbytes) DIV_ROUND_UP((nbytes) * 4, 3) + +int base64_encode(const u8 *src, int len, char *dst); +int base64_decode(const char *src, int len, u8 *dst); + +#endif /* _LINUX_BASE64_H */ diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index dccdf1551c62..84b13fdd34a7 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -140,6 +140,8 @@ struct gendisk { struct request_queue *queue; void *private_data; + struct bio_set bio_split; + int flags; unsigned long state; #define GD_NEED_PART_SCAN 0 @@ -531,7 +533,6 @@ struct request_queue { struct blk_mq_tag_set *tag_set; struct list_head tag_set_list; - struct bio_set bio_split; struct dentry *debugfs_dir; struct dentry *sched_debugfs_dir; @@ -864,9 +865,9 @@ void blk_request_module(dev_t devt); extern int blk_register_queue(struct gendisk *disk); extern void blk_unregister_queue(struct gendisk *disk); void submit_bio_noacct(struct bio *bio); +struct bio *bio_split_to_limits(struct bio *bio); extern int blk_lld_busy(struct request_queue *q); -extern void blk_queue_split(struct bio **); extern int blk_queue_enter(struct request_queue *q, blk_mq_req_flags_t flags); extern void blk_queue_exit(struct request_queue *q); extern void blk_sync_queue(struct request_queue *q); diff --git a/include/linux/cacheinfo.h b/include/linux/cacheinfo.h index 4ff37cb763ae..00b7a6ae8617 100644 --- a/include/linux/cacheinfo.h +++ b/include/linux/cacheinfo.h @@ -82,6 +82,9 @@ struct cpu_cacheinfo *get_cpu_cacheinfo(unsigned int cpu); int init_cache_level(unsigned int cpu); int populate_cache_leaves(unsigned int cpu); int cache_setup_acpi(unsigned int cpu); +bool last_level_cache_is_valid(unsigned int cpu); +bool last_level_cache_is_shared(unsigned int cpu_x, unsigned int cpu_y); +int detect_cache_attributes(unsigned int cpu); #ifndef CONFIG_ACPI_PPTT /* * acpi_find_last_cache_level is only called on ACPI enabled diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 30037593da32..b1f88729fd82 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -845,6 +845,25 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, __devm_clk_hw_register_divider((dev), NULL, (name), (parent_name), NULL, \ NULL, (flags), (reg), (shift), (width), \ (clk_divider_flags), NULL, (lock)) +/** + * devm_clk_hw_register_divider_parent_hw - register a divider clock with the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_hw: pointer to parent clk + * @flags: framework-specific flags + * @reg: register address to adjust divider + * @shift: number of bits to shift the bitfield + * @width: width of the bitfield + * @clk_divider_flags: divider-specific flags for this clock + * @lock: shared register lock for this clock + */ +#define devm_clk_hw_register_divider_parent_hw(dev, name, parent_hw, flags, \ + reg, shift, width, \ + clk_divider_flags, lock) \ + __devm_clk_hw_register_divider((dev), NULL, (name), NULL, \ + (parent_hw), NULL, (flags), (reg), \ + (shift), (width), (clk_divider_flags), \ + NULL, (lock)) /** * devm_clk_hw_register_divider_table - register a table based divider clock * with the clock framework (devres variant) @@ -975,6 +994,13 @@ struct clk *clk_register_mux_table(struct device *dev, const char *name, (parent_names), NULL, NULL, (flags), (reg), \ (shift), BIT((width)) - 1, (clk_mux_flags), \ NULL, (lock)) +#define devm_clk_hw_register_mux_parent_hws(dev, name, parent_hws, \ + num_parents, flags, reg, shift, \ + width, clk_mux_flags, lock) \ + __devm_clk_hw_register_mux((dev), NULL, (name), (num_parents), NULL, \ + (parent_hws), NULL, (flags), (reg), \ + (shift), BIT((width)) - 1, \ + (clk_mux_flags), NULL, (lock)) int clk_mux_val_to_index(struct clk_hw *hw, const u32 *table, unsigned int flags, unsigned int val); @@ -1020,6 +1046,14 @@ struct clk_hw *devm_clk_hw_register_fixed_factor(struct device *dev, struct clk_hw *devm_clk_hw_register_fixed_factor_index(struct device *dev, const char *name, unsigned int index, unsigned long flags, unsigned int mult, unsigned int div); + +struct clk_hw *devm_clk_hw_register_fixed_factor_parent_hw(struct device *dev, + const char *name, const struct clk_hw *parent_hw, + unsigned long flags, unsigned int mult, unsigned int div); + +struct clk_hw *clk_hw_register_fixed_factor_parent_hw(struct device *dev, + const char *name, const struct clk_hw *parent_hw, + unsigned long flags, unsigned int mult, unsigned int div); /** * struct clk_fractional_divider - adjustable fractional divider clock * @@ -1190,10 +1224,8 @@ int __must_check devm_clk_hw_register(struct device *dev, struct clk_hw *hw); int __must_check of_clk_hw_register(struct device_node *node, struct clk_hw *hw); void clk_unregister(struct clk *clk); -void devm_clk_unregister(struct device *dev, struct clk *clk); void clk_hw_unregister(struct clk_hw *hw); -void devm_clk_hw_unregister(struct device *dev, struct clk_hw *hw); void clk_sync_state(struct device *dev); /* helper functions */ diff --git a/include/linux/clk.h b/include/linux/clk.h index 39faa54efe88..c13061cabdfc 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -443,32 +443,130 @@ int __must_check devm_clk_bulk_get_all(struct device *dev, * @dev: device for clock "consumer" * @id: clock consumer ID * - * Returns a struct clk corresponding to the clock producer, or + * Context: May sleep. + * + * Return: a struct clk corresponding to the clock producer, or * valid IS_ERR() condition containing errno. The implementation * uses @dev and @id to determine the clock consumer, and thereby * the clock producer. (IOW, @id may be identical strings, but * clk_get may return different clock producers depending on @dev.) * - * Drivers must assume that the clock source is not enabled. - * - * devm_clk_get should not be called from within interrupt context. + * Drivers must assume that the clock source is neither prepared nor + * enabled. * * The clock will automatically be freed when the device is unbound * from the bus. */ struct clk *devm_clk_get(struct device *dev, const char *id); +/** + * devm_clk_get_prepared - devm_clk_get() + clk_prepare() + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Context: May sleep. + * + * Return: a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. (IOW, @id may be identical strings, but + * clk_get may return different clock producers depending on @dev.) + * + * The returned clk (if valid) is prepared. Drivers must however assume + * that the clock is not enabled. + * + * The clock will automatically be unprepared and freed when the device + * is unbound from the bus. + */ +struct clk *devm_clk_get_prepared(struct device *dev, const char *id); + +/** + * devm_clk_get_enabled - devm_clk_get() + clk_prepare_enable() + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Context: May sleep. + * + * Return: a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. (IOW, @id may be identical strings, but + * clk_get may return different clock producers depending on @dev.) + * + * The returned clk (if valid) is prepared and enabled. + * + * The clock will automatically be disabled, unprepared and freed + * when the device is unbound from the bus. + */ +struct clk *devm_clk_get_enabled(struct device *dev, const char *id); + /** * devm_clk_get_optional - lookup and obtain a managed reference to an optional * clock producer. * @dev: device for clock "consumer" * @id: clock consumer ID * - * Behaves the same as devm_clk_get() except where there is no clock producer. - * In this case, instead of returning -ENOENT, the function returns NULL. + * Context: May sleep. + * + * Return: a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. If no such clk is found, it returns NULL + * which serves as a dummy clk. That's the only difference compared + * to devm_clk_get(). + * + * Drivers must assume that the clock source is neither prepared nor + * enabled. + * + * The clock will automatically be freed when the device is unbound + * from the bus. */ struct clk *devm_clk_get_optional(struct device *dev, const char *id); +/** + * devm_clk_get_optional_prepared - devm_clk_get_optional() + clk_prepare() + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Context: May sleep. + * + * Return: a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. If no such clk is found, it returns NULL + * which serves as a dummy clk. That's the only difference compared + * to devm_clk_get_prepared(). + * + * The returned clk (if valid) is prepared. Drivers must however + * assume that the clock is not enabled. + * + * The clock will automatically be unprepared and freed when the + * device is unbound from the bus. + */ +struct clk *devm_clk_get_optional_prepared(struct device *dev, const char *id); + +/** + * devm_clk_get_optional_enabled - devm_clk_get_optional() + + * clk_prepare_enable() + * @dev: device for clock "consumer" + * @id: clock consumer ID + * + * Context: May sleep. + * + * Return: a struct clk corresponding to the clock producer, or + * valid IS_ERR() condition containing errno. The implementation + * uses @dev and @id to determine the clock consumer, and thereby + * the clock producer. If no such clk is found, it returns NULL + * which serves as a dummy clk. That's the only difference compared + * to devm_clk_get_enabled(). + * + * The returned clk (if valid) is prepared and enabled. + * + * The clock will automatically be disabled, unprepared and freed + * when the device is unbound from the bus. + */ +struct clk *devm_clk_get_optional_enabled(struct device *dev, const char *id); + /** * devm_get_clk_from_child - lookup and obtain a managed reference to a * clock producer from child node. @@ -813,12 +911,36 @@ static inline struct clk *devm_clk_get(struct device *dev, const char *id) return NULL; } +static inline struct clk *devm_clk_get_prepared(struct device *dev, + const char *id) +{ + return NULL; +} + +static inline struct clk *devm_clk_get_enabled(struct device *dev, + const char *id) +{ + return NULL; +} + static inline struct clk *devm_clk_get_optional(struct device *dev, const char *id) { return NULL; } +static inline struct clk *devm_clk_get_optional_prepared(struct device *dev, + const char *id) +{ + return NULL; +} + +static inline struct clk *devm_clk_get_optional_enabled(struct device *dev, + const char *id) +{ + return NULL; +} + static inline int __must_check devm_clk_bulk_get(struct device *dev, int num_clks, struct clk_bulk_data *clks) { diff --git a/include/linux/clk/ti.h b/include/linux/clk/ti.h index 3486f20a3753..cbfcbf186ce3 100644 --- a/include/linux/clk/ti.h +++ b/include/linux/clk/ti.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI clock drivers support * * Copyright (C) 2013 Texas Instruments, Inc. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __LINUX_CLK_TI_H__ #define __LINUX_CLK_TI_H__ diff --git a/include/linux/cpumask.h b/include/linux/cpumask.h index fe29ac7cc469..4592d0845941 100644 --- a/include/linux/cpumask.h +++ b/include/linux/cpumask.h @@ -1071,4 +1071,22 @@ cpumap_print_list_to_buf(char *buf, const struct cpumask *mask, [0] = 1UL \ } } +/* + * Provide a valid theoretical max size for cpumap and cpulist sysfs files + * to avoid breaking userspace which may allocate a buffer based on the size + * reported by e.g. fstat. + * + * for cpumap NR_CPUS * 9/32 - 1 should be an exact length. + * + * For cpulist 7 is (ceil(log10(NR_CPUS)) + 1) allowing for NR_CPUS to be up + * to 2 orders of magnitude larger than 8192. And then we divide by 2 to + * cover a worst-case of every other cpu being on one of two nodes for a + * very large NR_CPUS. + * + * Use PAGE_SIZE as a minimum for smaller configurations. + */ +#define CPUMAP_FILE_MAX_BYTES ((((NR_CPUS * 9)/32 - 1) > PAGE_SIZE) \ + ? (NR_CPUS * 9)/32 - 1 : PAGE_SIZE) +#define CPULIST_FILE_MAX_BYTES (((NR_CPUS * 7)/2 > PAGE_SIZE) ? (NR_CPUS * 7)/2 : PAGE_SIZE) + #endif /* __LINUX_CPUMASK_H */ diff --git a/include/linux/device.h b/include/linux/device.h index dc941997795c..424b55df0272 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -905,6 +905,8 @@ struct device *device_find_child(struct device *dev, void *data, int (*match)(struct device *dev, void *data)); struct device *device_find_child_by_name(struct device *parent, const char *name); +struct device *device_find_any_child(struct device *parent); + int device_rename(struct device *dev, const char *new_name); int device_move(struct device *dev, struct device *new_parent, enum dpm_order dpm_order); diff --git a/include/linux/device/driver.h b/include/linux/device/driver.h index 700453017e1c..7acaabde5396 100644 --- a/include/linux/device/driver.h +++ b/include/linux/device/driver.h @@ -129,6 +129,7 @@ extern struct device_driver *driver_find(const char *name, struct bus_type *bus); extern int driver_probe_done(void); extern void wait_for_device_probe(void); +void __init wait_for_init_devices_probe(void); /* sysfs interface for exporting driver attributes */ @@ -241,7 +242,6 @@ driver_find_device_by_acpi_dev(struct device_driver *drv, const void *adev) extern int driver_deferred_probe_timeout; void driver_deferred_probe_add(struct device *dev); -int driver_deferred_probe_check_state(struct device *dev); void driver_init(void); /** diff --git a/include/linux/dma/imx-dma.h b/include/linux/dma/imx-dma.h index 8887762360d4..f487a4fa103a 100644 --- a/include/linux/dma/imx-dma.h +++ b/include/linux/dma/imx-dma.h @@ -70,6 +70,16 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan) * struct sdma_peripheral_config - SDMA config for audio * @n_fifos_src: Number of FIFOs for recording * @n_fifos_dst: Number of FIFOs for playback + * @stride_fifos_src: FIFO address stride for recording, 0 means all FIFOs are + * continuous, 1 means 1 word stride between FIFOs. All stride + * between FIFOs should be same. + * @stride_fifos_dst: FIFO address stride for playback + * @words_per_fifo: numbers of words per FIFO fetch/fill, 1 means + * one channel per FIFO, 2 means 2 channels per FIFO.. + * If 'n_fifos_src = 4' and 'words_per_fifo = 2', it + * means the first two words(channels) fetch from FIFO0 + * and then jump to FIFO1 for next two words, and so on + * after the last FIFO3 fetched, roll back to FIFO0. * @sw_done: Use software done. Needed for PDM (micfil) * * Some i.MX Audio devices (SAI, micfil) have multiple successive FIFO @@ -82,6 +92,9 @@ static inline int imx_dma_is_general_purpose(struct dma_chan *chan) struct sdma_peripheral_config { int n_fifos_src; int n_fifos_dst; + int stride_fifos_src; + int stride_fifos_dst; + int words_per_fifo; bool sw_done; }; diff --git a/include/linux/dma/qcom-gpi-dma.h b/include/linux/dma/qcom-gpi-dma.h index f46dc3372f11..6680dd1a43c6 100644 --- a/include/linux/dma/qcom-gpi-dma.h +++ b/include/linux/dma/qcom-gpi-dma.h @@ -26,7 +26,7 @@ enum spi_transfer_cmd { * @clk_div: source clock divider * @clk_src: serial clock * @cmd: spi cmd - * @fragmentation: keep CS assserted at end of sequence + * @fragmentation: keep CS asserted at end of sequence * @cs: chip select toggle * @set_config: set peripheral config * @rx_len: receive length for buffer diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h index b46b88e6aa0d..c923f4e60f24 100644 --- a/include/linux/dmaengine.h +++ b/include/linux/dmaengine.h @@ -50,7 +50,6 @@ enum dma_status { */ enum dma_transaction_type { DMA_MEMCPY, - DMA_MEMCPY_SG, DMA_XOR, DMA_PQ, DMA_XOR_VAL, @@ -887,11 +886,6 @@ struct dma_device { struct dma_async_tx_descriptor *(*device_prep_dma_memcpy)( struct dma_chan *chan, dma_addr_t dst, dma_addr_t src, size_t len, unsigned long flags); - struct dma_async_tx_descriptor *(*device_prep_dma_memcpy_sg)( - struct dma_chan *chan, - struct scatterlist *dst_sg, unsigned int dst_nents, - struct scatterlist *src_sg, unsigned int src_nents, - unsigned long flags); struct dma_async_tx_descriptor *(*device_prep_dma_xor)( struct dma_chan *chan, dma_addr_t dst, dma_addr_t *src, unsigned int src_cnt, size_t len, unsigned long flags); @@ -1060,20 +1054,6 @@ static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy( len, flags); } -static inline struct dma_async_tx_descriptor *dmaengine_prep_dma_memcpy_sg( - struct dma_chan *chan, - struct scatterlist *dst_sg, unsigned int dst_nents, - struct scatterlist *src_sg, unsigned int src_nents, - unsigned long flags) -{ - if (!chan || !chan->device || !chan->device->device_prep_dma_memcpy_sg) - return NULL; - - return chan->device->device_prep_dma_memcpy_sg(chan, dst_sg, dst_nents, - src_sg, src_nents, - flags); -} - static inline bool dmaengine_is_metadata_mode_supported(struct dma_chan *chan, enum dma_desc_metadata_mode mode) { diff --git a/include/linux/firmware/trusted_foundations.h b/include/linux/firmware/trusted_foundations.h index be5984bda592..931b6c5c72df 100644 --- a/include/linux/firmware/trusted_foundations.h +++ b/include/linux/firmware/trusted_foundations.h @@ -71,12 +71,16 @@ static inline void register_trusted_foundations( static inline void of_register_trusted_foundations(void) { + struct device_node *np = of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations"); + + if (!np) + return; + of_node_put(np); /* * If we find the target should enable TF but does not support it, * fail as the system won't be able to do much anyway */ - if (of_find_compatible_node(NULL, NULL, "tlm,trusted-foundations")) - register_trusted_foundations(NULL); + register_trusted_foundations(NULL); } static inline bool trusted_foundations_registered(void) diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index 9a81c4410b9f..89b9bdfca925 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -27,11 +27,15 @@ struct device; * driver needs its child devices to be bound with * their respective drivers as soon as they are * added. + * BEST_EFFORT: The fwnode/device needs to probe early and might be missing some + * suppliers. Only enforce ordering with suppliers that have + * drivers. */ #define FWNODE_FLAG_LINKS_ADDED BIT(0) #define FWNODE_FLAG_NOT_DEVICE BIT(1) #define FWNODE_FLAG_INITIALIZED BIT(2) #define FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD BIT(3) +#define FWNODE_FLAG_BEST_EFFORT BIT(4) struct fwnode_handle { struct fwnode_handle *secondary; diff --git a/include/linux/gpio.h b/include/linux/gpio.h index 008ad3ee56b7..a370387fa406 100644 --- a/include/linux/gpio.h +++ b/include/linux/gpio.h @@ -95,7 +95,6 @@ struct device; int devm_gpio_request(struct device *dev, unsigned gpio, const char *label); int devm_gpio_request_one(struct device *dev, unsigned gpio, unsigned long flags, const char *label); -void devm_gpio_free(struct device *dev, unsigned int gpio); #else /* ! CONFIG_GPIOLIB */ @@ -240,11 +239,6 @@ static inline int devm_gpio_request_one(struct device *dev, unsigned gpio, return -EINVAL; } -static inline void devm_gpio_free(struct device *dev, unsigned int gpio) -{ - WARN_ON(1); -} - #endif /* ! CONFIG_GPIOLIB */ #endif /* __LINUX_GPIO_H */ diff --git a/include/linux/gpio/machine.h b/include/linux/gpio/machine.h index 4d55da28e664..0b619eb7ae83 100644 --- a/include/linux/gpio/machine.h +++ b/include/linux/gpio/machine.h @@ -14,6 +14,7 @@ enum gpio_lookup_flags { GPIO_TRANSITORY = (1 << 3), GPIO_PULL_UP = (1 << 4), GPIO_PULL_DOWN = (1 << 5), + GPIO_PULL_DISABLE = (1 << 6), GPIO_LOOKUP_FLAGS_DEFAULT = GPIO_ACTIVE_HIGH | GPIO_PERSISTENT, }; diff --git a/include/linux/input/elan-i2c-ids.h b/include/linux/input/elan-i2c-ids.h index 520858d12680..51cca17ee94c 100644 --- a/include/linux/input/elan-i2c-ids.h +++ b/include/linux/input/elan-i2c-ids.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Elan I2C/SMBus Touchpad device whitelist * @@ -11,10 +12,6 @@ * copyright (c) 2011-2012 Cypress Semiconductor, Inc. * copyright (c) 2011-2012 Google, Inc. * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * * Trademarks are the property of their respective owners. */ diff --git a/include/linux/jbd2.h b/include/linux/jbd2.h index dc1724131300..0b7242370b56 100644 --- a/include/linux/jbd2.h +++ b/include/linux/jbd2.h @@ -54,14 +54,13 @@ * CONFIG_JBD2_DEBUG is on. */ #define JBD2_EXPENSIVE_CHECKING -extern ushort jbd2_journal_enable_debug; void __jbd2_debug(int level, const char *file, const char *func, unsigned int line, const char *fmt, ...); -#define jbd_debug(n, fmt, a...) \ +#define jbd2_debug(n, fmt, a...) \ __jbd2_debug((n), __FILE__, __func__, __LINE__, (fmt), ##a) #else -#define jbd_debug(n, fmt, a...) no_printk(fmt, ##a) +#define jbd2_debug(n, fmt, a...) no_printk(fmt, ##a) #endif extern void *jbd2_alloc(size_t size, gfp_t flags); @@ -1647,7 +1646,6 @@ extern void jbd2_clear_buffer_revoked_flags(journal_t *journal); */ int jbd2_log_start_commit(journal_t *journal, tid_t tid); -int __jbd2_log_start_commit(journal_t *journal, tid_t tid); int jbd2_journal_start_commit(journal_t *journal, tid_t *tid); int jbd2_log_wait_commit(journal_t *journal, tid_t tid); int jbd2_transaction_committed(journal_t *journal, tid_t tid); diff --git a/include/linux/kernfs.h b/include/linux/kernfs.h index e2ae15a6225e..367044d7708c 100644 --- a/include/linux/kernfs.h +++ b/include/linux/kernfs.h @@ -18,6 +18,7 @@ #include #include #include +#include struct file; struct dentry; @@ -34,6 +35,62 @@ struct kernfs_fs_context; struct kernfs_open_node; struct kernfs_iattrs; +/* + * NR_KERNFS_LOCK_BITS determines size (NR_KERNFS_LOCKS) of hash + * table of locks. + * Having a small hash table would impact scalability, since + * more and more kernfs_node objects will end up using same lock + * and having a very large hash table would waste memory. + * + * At the moment size of hash table of locks is being set based on + * the number of CPUs as follows: + * + * NR_CPU NR_KERNFS_LOCK_BITS NR_KERNFS_LOCKS + * 1 1 2 + * 2-3 2 4 + * 4-7 4 16 + * 8-15 6 64 + * 16-31 8 256 + * 32 and more 10 1024 + * + * The above relation between NR_CPU and number of locks is based + * on some internal experimentation which involved booting qemu + * with different values of smp, performing some sysfs operations + * on all CPUs and observing how increase in number of locks impacts + * completion time of these sysfs operations on each CPU. + */ +#ifdef CONFIG_SMP +#define NR_KERNFS_LOCK_BITS (2 * (ilog2(NR_CPUS < 32 ? NR_CPUS : 32))) +#else +#define NR_KERNFS_LOCK_BITS 1 +#endif + +#define NR_KERNFS_LOCKS (1 << NR_KERNFS_LOCK_BITS) + +/* + * There's one kernfs_open_file for each open file and one kernfs_open_node + * for each kernfs_node with one or more open files. + * + * filp->private_data points to seq_file whose ->private points to + * kernfs_open_file. + * + * kernfs_open_files are chained at kernfs_open_node->files, which is + * protected by kernfs_global_locks.open_file_mutex[i]. + * + * To reduce possible contention in sysfs access, arising due to single + * locks, use an array of locks (e.g. open_file_mutex) and use kernfs_node + * object address as hash keys to get the index of these locks. + * + * Hashed mutexes are safe to use here because operations using these don't + * rely on global exclusion. + * + * In future we intend to replace other global locks with hashed ones as well. + * kernfs_global_locks acts as a holder for all such hash tables. + */ +struct kernfs_global_locks { + struct mutex open_file_mutex[NR_KERNFS_LOCKS]; +}; + enum kernfs_node_type { KERNFS_DIR = 0x0001, KERNFS_FILE = 0x0002, @@ -114,7 +171,7 @@ struct kernfs_elem_symlink { struct kernfs_elem_attr { const struct kernfs_ops *ops; - struct kernfs_open_node *open; + struct kernfs_open_node __rcu *open; loff_t size; struct kernfs_node *notify_next; /* for kernfs_notify() */ }; diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 90a45ef7203b..1c480b1821e1 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -907,11 +907,6 @@ static inline struct kvm_vcpu *kvm_get_vcpu_by_id(struct kvm *kvm, int id) return NULL; } -static inline int kvm_vcpu_get_idx(struct kvm_vcpu *vcpu) -{ - return vcpu->vcpu_idx; -} - void kvm_destroy_vcpus(struct kvm *kvm); void vcpu_load(struct kvm_vcpu *vcpu); @@ -1139,7 +1134,6 @@ unsigned long gfn_to_hva_memslot_prot(struct kvm_memory_slot *slot, gfn_t gfn, bool *writable); void kvm_release_page_clean(struct page *page); void kvm_release_page_dirty(struct page *page); -void kvm_set_page_accessed(struct page *page); kvm_pfn_t gfn_to_pfn(struct kvm *kvm, gfn_t gfn); kvm_pfn_t gfn_to_pfn_prot(struct kvm *kvm, gfn_t gfn, bool write_fault, @@ -1231,7 +1225,6 @@ struct kvm_memory_slot *kvm_vcpu_gfn_to_memslot(struct kvm_vcpu *vcpu, gfn_t gfn kvm_pfn_t kvm_vcpu_gfn_to_pfn_atomic(struct kvm_vcpu *vcpu, gfn_t gfn); kvm_pfn_t kvm_vcpu_gfn_to_pfn(struct kvm_vcpu *vcpu, gfn_t gfn); int kvm_vcpu_map(struct kvm_vcpu *vcpu, gpa_t gpa, struct kvm_host_map *map); -struct page *kvm_vcpu_gfn_to_page(struct kvm_vcpu *vcpu, gfn_t gfn); void kvm_vcpu_unmap(struct kvm_vcpu *vcpu, struct kvm_host_map *map, bool dirty); unsigned long kvm_vcpu_gfn_to_hva(struct kvm_vcpu *vcpu, gfn_t gfn); unsigned long kvm_vcpu_gfn_to_hva_prot(struct kvm_vcpu *vcpu, gfn_t gfn, bool *writable); @@ -1358,6 +1351,7 @@ void kvm_flush_remote_tlbs(struct kvm *kvm); #ifdef KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min); +int __kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int capacity, int min); int kvm_mmu_memory_cache_nr_free_objects(struct kvm_mmu_memory_cache *mc); void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc); void *kvm_mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc); @@ -1436,6 +1430,8 @@ int kvm_arch_pm_notifier(struct kvm *kvm, unsigned long state); #ifdef __KVM_HAVE_ARCH_VCPU_DEBUGFS void kvm_arch_create_vcpu_debugfs(struct kvm_vcpu *vcpu, struct dentry *debugfs_dentry); +#else +static inline void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) {} #endif int kvm_arch_hardware_enable(void); @@ -1572,8 +1568,8 @@ void kvm_arch_sync_events(struct kvm *kvm); int kvm_cpu_has_pending_timer(struct kvm_vcpu *vcpu); -bool kvm_is_reserved_pfn(kvm_pfn_t pfn); -bool kvm_is_zone_device_pfn(kvm_pfn_t pfn); +struct page *kvm_pfn_to_refcounted_page(kvm_pfn_t pfn); +bool kvm_is_zone_device_page(struct page *page); struct kvm_irq_ack_notifier { struct hlist_node link; @@ -1719,12 +1715,6 @@ static inline hpa_t pfn_to_hpa(kvm_pfn_t pfn) return (hpa_t)pfn << PAGE_SHIFT; } -static inline struct page *kvm_vcpu_gpa_to_page(struct kvm_vcpu *vcpu, - gpa_t gpa) -{ - return kvm_vcpu_gfn_to_page(vcpu, gpa_to_gfn(gpa)); -} - static inline bool kvm_is_error_gpa(struct kvm *kvm, gpa_t gpa) { unsigned long hva = gfn_to_hva(kvm, gpa_to_gfn(gpa)); diff --git a/include/linux/kvm_types.h b/include/linux/kvm_types.h index ac1ebb37a0ff..3ca3db020e0e 100644 --- a/include/linux/kvm_types.h +++ b/include/linux/kvm_types.h @@ -19,6 +19,7 @@ struct kvm_memslots; enum kvm_mr_change; #include +#include #include #include @@ -69,6 +70,7 @@ struct gfn_to_pfn_cache { struct kvm_vcpu *vcpu; struct list_head list; rwlock_t lock; + struct mutex refresh_lock; void *khva; kvm_pfn_t pfn; enum pfn_cache_usage usage; @@ -83,12 +85,17 @@ struct gfn_to_pfn_cache { * MMU flows is problematic, as is triggering reclaim, I/O, etc... while * holding MMU locks. Note, these caches act more like prefetch buffers than * classical caches, i.e. objects are not returned to the cache on being freed. + * + * The @capacity field and @objects array are lazily initialized when the cache + * is topped up (__kvm_mmu_topup_memory_cache()). */ struct kvm_mmu_memory_cache { int nobjs; gfp_t gfp_zero; + gfp_t gfp_custom; struct kmem_cache *kmem_cache; - void *objects[KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE]; + int capacity; + void **objects; }; #endif diff --git a/include/linux/mbcache.h b/include/linux/mbcache.h index 20f1e3ff6013..2da63fd7b98f 100644 --- a/include/linux/mbcache.h +++ b/include/linux/mbcache.h @@ -13,8 +13,16 @@ struct mb_cache; struct mb_cache_entry { /* List of entries in cache - protected by cache->c_list_lock */ struct list_head e_list; - /* Hash table list - protected by hash chain bitlock */ + /* + * Hash table list - protected by hash chain bitlock. The entry is + * guaranteed to be hashed while e_refcnt > 0. + */ struct hlist_bl_node e_hash_list; + /* + * Entry refcount. Once it reaches zero, entry is unhashed and freed. + * While refcount > 0, the entry is guaranteed to stay in the hash and + * e.g. mb_cache_entry_try_delete() will fail. + */ atomic_t e_refcnt; /* Key in hash - stable during lifetime of the entry */ u32 e_key; @@ -29,17 +37,24 @@ void mb_cache_destroy(struct mb_cache *cache); int mb_cache_entry_create(struct mb_cache *cache, gfp_t mask, u32 key, u64 value, bool reusable); -void __mb_cache_entry_free(struct mb_cache_entry *entry); -static inline int mb_cache_entry_put(struct mb_cache *cache, - struct mb_cache_entry *entry) +void __mb_cache_entry_free(struct mb_cache *cache, + struct mb_cache_entry *entry); +void mb_cache_entry_wait_unused(struct mb_cache_entry *entry); +static inline void mb_cache_entry_put(struct mb_cache *cache, + struct mb_cache_entry *entry) { - if (!atomic_dec_and_test(&entry->e_refcnt)) - return 0; - __mb_cache_entry_free(entry); - return 1; + unsigned int cnt = atomic_dec_return(&entry->e_refcnt); + + if (cnt > 0) { + if (cnt <= 2) + wake_up_var(&entry->e_refcnt); + return; + } + __mb_cache_entry_free(cache, entry); } -void mb_cache_entry_delete(struct mb_cache *cache, u32 key, u64 value); +struct mb_cache_entry *mb_cache_entry_delete_or_get(struct mb_cache *cache, + u32 key, u64 value); struct mb_cache_entry *mb_cache_entry_get(struct mb_cache *cache, u32 key, u64 value); struct mb_cache_entry *mb_cache_entry_find_first(struct mb_cache *cache, diff --git a/include/linux/mfd/lp873x.h b/include/linux/mfd/lp873x.h index 5546688c7da7..fe8174cc8637 100644 --- a/include/linux/mfd/lp873x.h +++ b/include/linux/mfd/lp873x.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Functions to access LP873X power management chip. * * Copyright (C) 2016 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __LINUX_MFD_LP873X_H diff --git a/include/linux/mfd/tps65086.h b/include/linux/mfd/tps65086.h index e0a417e53766..16f87cccc003 100644 --- a/include/linux/mfd/tps65086.h +++ b/include/linux/mfd/tps65086.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65912 driver */ diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index db7091824ed0..877d9c41c53d 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * linux/mfd/tps65217.h * * Functions to access TPS65217 power management chip. * * Copyright (C) 2011 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __LINUX_MFD_TPS65217_H diff --git a/include/linux/mfd/tps65218.h b/include/linux/mfd/tps65218.h index 122e24ddbd4b..2946be2f15f3 100644 --- a/include/linux/mfd/tps65218.h +++ b/include/linux/mfd/tps65218.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * linux/mfd/tps65218.h * * Functions to access TPS65218 power management chip. * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. */ #ifndef __LINUX_MFD_TPS65218_H diff --git a/include/linux/mfd/tps65912.h b/include/linux/mfd/tps65912.h index 8a61386cb8c1..860ec0a16c96 100644 --- a/include/linux/mfd/tps65912.h +++ b/include/linux/mfd/tps65912.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Andrew F. Davis * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether expressed or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License version 2 for more details. - * * Based on the TPS65218 driver and the previous TPS65912 driver by * Margarita Olaya Cabrera */ diff --git a/include/linux/mfd/twl.h b/include/linux/mfd/twl.h index 8871cc5188a0..c8cd31756037 100644 --- a/include/linux/mfd/twl.h +++ b/include/linux/mfd/twl.h @@ -594,8 +594,6 @@ struct twl4030_gpio_platform_data { int (*setup)(struct device *dev, unsigned gpio, unsigned ngpio); - int (*teardown)(struct device *dev, - unsigned gpio, unsigned ngpio); }; struct twl4030_madc_platform_data { diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index ecda6e63d5f2..96b16fbe1aa4 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -733,10 +733,10 @@ enum { }; enum { - MR_CACHE_LAST_STD_ENTRY = 20, + MKEY_CACHE_LAST_STD_ENTRY = 20, MLX5_IMR_MTT_CACHE_ENTRY, MLX5_IMR_KSM_CACHE_ENTRY, - MAX_MR_CACHE_ENTRIES + MAX_MKEY_CACHE_ENTRIES }; struct mlx5_profile { @@ -745,7 +745,7 @@ struct mlx5_profile { struct { int size; int limit; - } mr_cache[MAX_MR_CACHE_ENTRIES]; + } mr_cache[MAX_MKEY_CACHE_ENTRIES]; }; struct mlx5_hca_cap { diff --git a/include/linux/mlx5/fs.h b/include/linux/mlx5/fs.h index ece3e35622d7..8e73c377da2c 100644 --- a/include/linux/mlx5/fs.h +++ b/include/linux/mlx5/fs.h @@ -178,6 +178,7 @@ struct mlx5_flow_table_attr { int max_fte; u32 level; u32 flags; + u16 uid; struct mlx5_flow_table *next_ft; struct { @@ -315,4 +316,5 @@ struct mlx5_pkt_reformat *mlx5_packet_reformat_alloc(struct mlx5_core_dev *dev, void mlx5_packet_reformat_dealloc(struct mlx5_core_dev *dev, struct mlx5_pkt_reformat *reformat); +u32 mlx5_flow_table_id(struct mlx5_flow_table *ft); #endif diff --git a/include/linux/mlx5/mlx5_ifc.h b/include/linux/mlx5/mlx5_ifc.h index 51b4e71017ee..4acd5610e96b 100644 --- a/include/linux/mlx5/mlx5_ifc.h +++ b/include/linux/mlx5/mlx5_ifc.h @@ -1371,7 +1371,9 @@ enum { }; struct mlx5_ifc_cmd_hca_cap_bits { - u8 reserved_at_0[0x1f]; + u8 reserved_at_0[0x10]; + u8 shared_object_to_user_object_allowed[0x1]; + u8 reserved_at_13[0xe]; u8 vhca_resource_manager[0x1]; u8 hca_cap_2[0x1]; @@ -8528,7 +8530,7 @@ struct mlx5_ifc_create_flow_table_out_bits { struct mlx5_ifc_create_flow_table_in_bits { u8 opcode[0x10]; - u8 reserved_at_10[0x10]; + u8 uid[0x10]; u8 reserved_at_20[0x10]; u8 op_mod[0x10]; diff --git a/include/linux/mmc/card.h b/include/linux/mmc/card.h index 37f975875102..8a30de08e913 100644 --- a/include/linux/mmc/card.h +++ b/include/linux/mmc/card.h @@ -219,7 +219,8 @@ struct sdio_cccr { wide_bus:1, high_power:1, high_speed:1, - disable_cd:1; + disable_cd:1, + enable_async_irq:1; }; struct sdio_cis { @@ -343,10 +344,16 @@ static inline bool mmc_large_sector(struct mmc_card *card) return card->ext_csd.data_sector_size == 4096; } +static inline int mmc_card_enable_async_irq(struct mmc_card *card) +{ + return card->cccr.enable_async_irq; +} + bool mmc_card_is_blockaddr(struct mmc_card *card); #define mmc_card_mmc(c) ((c)->type == MMC_TYPE_MMC) #define mmc_card_sd(c) ((c)->type == MMC_TYPE_SD) #define mmc_card_sdio(c) ((c)->type == MMC_TYPE_SDIO) +#define mmc_card_sd_combo(c) ((c)->type == MMC_TYPE_SD_COMBO) #endif /* LINUX_MMC_CARD_H */ diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h index c193c50ccd78..eb8bc5b9b0b7 100644 --- a/include/linux/mmc/host.h +++ b/include/linux/mmc/host.h @@ -93,6 +93,25 @@ struct mmc_clk_phase_map { struct mmc_host; +enum mmc_err_stat { + MMC_ERR_CMD_TIMEOUT, + MMC_ERR_CMD_CRC, + MMC_ERR_DAT_TIMEOUT, + MMC_ERR_DAT_CRC, + MMC_ERR_AUTO_CMD, + MMC_ERR_ADMA, + MMC_ERR_TUNING, + MMC_ERR_CMDQ_RED, + MMC_ERR_CMDQ_GCE, + MMC_ERR_CMDQ_ICCE, + MMC_ERR_REQ_TIMEOUT, + MMC_ERR_CMDQ_REQ_TIMEOUT, + MMC_ERR_ICE_CFG, + MMC_ERR_CTRL_TIMEOUT, + MMC_ERR_UNEXPECTED_IRQ, + MMC_ERR_MAX, +}; + struct mmc_host_ops { /* * It is optional for the host to implement pre_req and post_req in @@ -501,6 +520,7 @@ struct mmc_host { /* Host Software Queue support */ bool hsq_enabled; + u32 err_stats[MMC_ERR_MAX]; unsigned long private[] ____cacheline_aligned; }; @@ -635,6 +655,12 @@ static inline enum dma_data_direction mmc_get_dma_dir(struct mmc_data *data) return data->flags & MMC_DATA_WRITE ? DMA_TO_DEVICE : DMA_FROM_DEVICE; } +static inline void mmc_debugfs_err_stats_inc(struct mmc_host *host, + enum mmc_err_stat stat) +{ + host->err_stats[stat] += 1; +} + int mmc_send_tuning(struct mmc_host *host, u32 opcode, int *cmd_error); int mmc_send_abort_tuning(struct mmc_host *host, u32 opcode); int mmc_get_ext_csd(struct mmc_card *card, u8 **new_ext_csd); diff --git a/include/linux/mmc/mmc.h b/include/linux/mmc/mmc.h index d9a65c6a8816..9c50bc40f8ff 100644 --- a/include/linux/mmc/mmc.h +++ b/include/linux/mmc/mmc.h @@ -99,6 +99,12 @@ static inline bool mmc_op_multi(u32 opcode) opcode == MMC_READ_MULTIPLE_BLOCK; } +static inline bool mmc_op_tuning(u32 opcode) +{ + return opcode == MMC_SEND_TUNING_BLOCK || + opcode == MMC_SEND_TUNING_BLOCK_HS200; +} + /* * MMC_SWITCH argument format: * diff --git a/include/linux/mmc/sdio.h b/include/linux/mmc/sdio.h index 2a05d1ac4f0e..1ef400f28642 100644 --- a/include/linux/mmc/sdio.h +++ b/include/linux/mmc/sdio.h @@ -159,6 +159,11 @@ #define SDIO_DTSx_SET_TYPE_A (1 << SDIO_DRIVE_DTSx_SHIFT) #define SDIO_DTSx_SET_TYPE_C (2 << SDIO_DRIVE_DTSx_SHIFT) #define SDIO_DTSx_SET_TYPE_D (3 << SDIO_DRIVE_DTSx_SHIFT) + +#define SDIO_CCCR_INTERRUPT_EXT 0x16 +#define SDIO_INTERRUPT_EXT_SAI (1 << 0) +#define SDIO_INTERRUPT_EXT_EAI (1 << 1) + /* * Function Basic Registers (FBR) */ diff --git a/include/linux/nvme-auth.h b/include/linux/nvme-auth.h new file mode 100644 index 000000000000..dcb8030062dd --- /dev/null +++ b/include/linux/nvme-auth.h @@ -0,0 +1,41 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2021 Hannes Reinecke, SUSE Software Solutions + */ + +#ifndef _NVME_AUTH_H +#define _NVME_AUTH_H + +#include + +struct nvme_dhchap_key { + u8 *key; + size_t len; + u8 hash; +}; + +u32 nvme_auth_get_seqnum(void); +const char *nvme_auth_dhgroup_name(u8 dhgroup_id); +const char *nvme_auth_dhgroup_kpp(u8 dhgroup_id); +u8 nvme_auth_dhgroup_id(const char *dhgroup_name); + +const char *nvme_auth_hmac_name(u8 hmac_id); +const char *nvme_auth_digest_name(u8 hmac_id); +size_t nvme_auth_hmac_hash_len(u8 hmac_id); +u8 nvme_auth_hmac_id(const char *hmac_name); + +struct nvme_dhchap_key *nvme_auth_extract_key(unsigned char *secret, + u8 key_hash); +void nvme_auth_free_key(struct nvme_dhchap_key *key); +u8 *nvme_auth_transform_key(struct nvme_dhchap_key *key, char *nqn); +int nvme_auth_generate_key(u8 *secret, struct nvme_dhchap_key **ret_key); +int nvme_auth_augmented_challenge(u8 hmac_id, u8 *skey, size_t skey_len, + u8 *challenge, u8 *aug, size_t hlen); +int nvme_auth_gen_privkey(struct crypto_kpp *dh_tfm, u8 dh_gid); +int nvme_auth_gen_pubkey(struct crypto_kpp *dh_tfm, + u8 *host_key, size_t host_key_len); +int nvme_auth_gen_shared_secret(struct crypto_kpp *dh_tfm, + u8 *ctrl_key, size_t ctrl_key_len, + u8 *sess_key, size_t sess_key_len); + +#endif /* _NVME_AUTH_H */ diff --git a/include/linux/nvme.h b/include/linux/nvme.h index 07cfc922f8e4..ae53d74f3696 100644 --- a/include/linux/nvme.h +++ b/include/linux/nvme.h @@ -19,6 +19,7 @@ #define NVMF_TRSVCID_SIZE 32 #define NVMF_TRADDR_SIZE 256 #define NVMF_TSAS_SIZE 256 +#define NVMF_AUTH_HASH_LEN 64 #define NVME_DISC_SUBSYS_NAME "nqn.2014-08.org.nvmexpress.discovery" @@ -711,6 +712,10 @@ enum { NVME_AER_VS = 7, }; +enum { + NVME_AER_ERROR_PERSIST_INT_ERR = 0x03, +}; + enum { NVME_AER_NOTICE_NS_CHANGED = 0x00, NVME_AER_NOTICE_FW_ACT_STARTING = 0x01, @@ -1369,6 +1374,8 @@ enum nvmf_capsule_command { nvme_fabrics_type_property_set = 0x00, nvme_fabrics_type_connect = 0x01, nvme_fabrics_type_property_get = 0x04, + nvme_fabrics_type_auth_send = 0x05, + nvme_fabrics_type_auth_receive = 0x06, }; #define nvme_fabrics_type_name(type) { type, #type } @@ -1376,7 +1383,9 @@ enum nvmf_capsule_command { __print_symbolic(type, \ nvme_fabrics_type_name(nvme_fabrics_type_property_set), \ nvme_fabrics_type_name(nvme_fabrics_type_connect), \ - nvme_fabrics_type_name(nvme_fabrics_type_property_get)) + nvme_fabrics_type_name(nvme_fabrics_type_property_get), \ + nvme_fabrics_type_name(nvme_fabrics_type_auth_send), \ + nvme_fabrics_type_name(nvme_fabrics_type_auth_receive)) /* * If not fabrics command, fctype will be ignored. @@ -1472,6 +1481,11 @@ struct nvmf_connect_command { __u8 resv4[12]; }; +enum { + NVME_CONNECT_AUTHREQ_ASCR = (1 << 2), + NVME_CONNECT_AUTHREQ_ATR = (1 << 1), +}; + struct nvmf_connect_data { uuid_t hostid; __le16 cntlid; @@ -1506,6 +1520,200 @@ struct nvmf_property_get_command { __u8 resv4[16]; }; +struct nvmf_auth_common_command { + __u8 opcode; + __u8 resv1; + __u16 command_id; + __u8 fctype; + __u8 resv2[19]; + union nvme_data_ptr dptr; + __u8 resv3; + __u8 spsp0; + __u8 spsp1; + __u8 secp; + __le32 al_tl; + __u8 resv4[16]; +}; + +struct nvmf_auth_send_command { + __u8 opcode; + __u8 resv1; + __u16 command_id; + __u8 fctype; + __u8 resv2[19]; + union nvme_data_ptr dptr; + __u8 resv3; + __u8 spsp0; + __u8 spsp1; + __u8 secp; + __le32 tl; + __u8 resv4[16]; +}; + +struct nvmf_auth_receive_command { + __u8 opcode; + __u8 resv1; + __u16 command_id; + __u8 fctype; + __u8 resv2[19]; + union nvme_data_ptr dptr; + __u8 resv3; + __u8 spsp0; + __u8 spsp1; + __u8 secp; + __le32 al; + __u8 resv4[16]; +}; + +/* Value for secp */ +enum { + NVME_AUTH_DHCHAP_PROTOCOL_IDENTIFIER = 0xe9, +}; + +/* Defined value for auth_type */ +enum { + NVME_AUTH_COMMON_MESSAGES = 0x00, + NVME_AUTH_DHCHAP_MESSAGES = 0x01, +}; + +/* Defined messages for auth_id */ +enum { + NVME_AUTH_DHCHAP_MESSAGE_NEGOTIATE = 0x00, + NVME_AUTH_DHCHAP_MESSAGE_CHALLENGE = 0x01, + NVME_AUTH_DHCHAP_MESSAGE_REPLY = 0x02, + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS1 = 0x03, + NVME_AUTH_DHCHAP_MESSAGE_SUCCESS2 = 0x04, + NVME_AUTH_DHCHAP_MESSAGE_FAILURE2 = 0xf0, + NVME_AUTH_DHCHAP_MESSAGE_FAILURE1 = 0xf1, +}; + +struct nvmf_auth_dhchap_protocol_descriptor { + __u8 authid; + __u8 rsvd; + __u8 halen; + __u8 dhlen; + __u8 idlist[60]; +}; + +enum { + NVME_AUTH_DHCHAP_AUTH_ID = 0x01, +}; + +/* Defined hash functions for DH-HMAC-CHAP authentication */ +enum { + NVME_AUTH_HASH_SHA256 = 0x01, + NVME_AUTH_HASH_SHA384 = 0x02, + NVME_AUTH_HASH_SHA512 = 0x03, + NVME_AUTH_HASH_INVALID = 0xff, +}; + +/* Defined Diffie-Hellman group identifiers for DH-HMAC-CHAP authentication */ +enum { + NVME_AUTH_DHGROUP_NULL = 0x00, + NVME_AUTH_DHGROUP_2048 = 0x01, + NVME_AUTH_DHGROUP_3072 = 0x02, + NVME_AUTH_DHGROUP_4096 = 0x03, + NVME_AUTH_DHGROUP_6144 = 0x04, + NVME_AUTH_DHGROUP_8192 = 0x05, + NVME_AUTH_DHGROUP_INVALID = 0xff, +}; + +union nvmf_auth_protocol { + struct nvmf_auth_dhchap_protocol_descriptor dhchap; +}; + +struct nvmf_auth_dhchap_negotiate_data { + __u8 auth_type; + __u8 auth_id; + __le16 rsvd; + __le16 t_id; + __u8 sc_c; + __u8 napd; + union nvmf_auth_protocol auth_protocol[]; +}; + +struct nvmf_auth_dhchap_challenge_data { + __u8 auth_type; + __u8 auth_id; + __u16 rsvd1; + __le16 t_id; + __u8 hl; + __u8 rsvd2; + __u8 hashid; + __u8 dhgid; + __le16 dhvlen; + __le32 seqnum; + /* 'hl' bytes of challenge value */ + __u8 cval[]; + /* followed by 'dhvlen' bytes of DH value */ +}; + +struct nvmf_auth_dhchap_reply_data { + __u8 auth_type; + __u8 auth_id; + __le16 rsvd1; + __le16 t_id; + __u8 hl; + __u8 rsvd2; + __u8 cvalid; + __u8 rsvd3; + __le16 dhvlen; + __le32 seqnum; + /* 'hl' bytes of response data */ + __u8 rval[]; + /* followed by 'hl' bytes of Challenge value */ + /* followed by 'dhvlen' bytes of DH value */ +}; + +enum { + NVME_AUTH_DHCHAP_RESPONSE_VALID = (1 << 0), +}; + +struct nvmf_auth_dhchap_success1_data { + __u8 auth_type; + __u8 auth_id; + __le16 rsvd1; + __le16 t_id; + __u8 hl; + __u8 rsvd2; + __u8 rvalid; + __u8 rsvd3[7]; + /* 'hl' bytes of response value if 'rvalid' is set */ + __u8 rval[]; +}; + +struct nvmf_auth_dhchap_success2_data { + __u8 auth_type; + __u8 auth_id; + __le16 rsvd1; + __le16 t_id; + __u8 rsvd2[10]; +}; + +struct nvmf_auth_dhchap_failure_data { + __u8 auth_type; + __u8 auth_id; + __le16 rsvd1; + __le16 t_id; + __u8 rescode; + __u8 rescode_exp; +}; + +enum { + NVME_AUTH_DHCHAP_FAILURE_REASON_FAILED = 0x01, +}; + +enum { + NVME_AUTH_DHCHAP_FAILURE_FAILED = 0x01, + NVME_AUTH_DHCHAP_FAILURE_NOT_USABLE = 0x02, + NVME_AUTH_DHCHAP_FAILURE_CONCAT_MISMATCH = 0x03, + NVME_AUTH_DHCHAP_FAILURE_HASH_UNUSABLE = 0x04, + NVME_AUTH_DHCHAP_FAILURE_DHGROUP_UNUSABLE = 0x05, + NVME_AUTH_DHCHAP_FAILURE_INCORRECT_PAYLOAD = 0x06, + NVME_AUTH_DHCHAP_FAILURE_INCORRECT_MESSAGE = 0x07, +}; + + struct nvme_dbbuf { __u8 opcode; __u8 flags; @@ -1549,6 +1757,9 @@ struct nvme_command { struct nvmf_connect_command connect; struct nvmf_property_set_command prop_set; struct nvmf_property_get_command prop_get; + struct nvmf_auth_common_command auth_common; + struct nvmf_auth_send_command auth_send; + struct nvmf_auth_receive_command auth_receive; struct nvme_dbbuf dbbuf; struct nvme_directive_cmd directive; }; diff --git a/include/linux/of.h b/include/linux/of.h index 20a4e7cb7afe..766d002bddb9 100644 --- a/include/linux/of.h +++ b/include/linux/of.h @@ -207,7 +207,7 @@ static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) } #if defined(CONFIG_OF_DYNAMIC) || defined(CONFIG_SPARC) -static inline int of_property_check_flag(struct property *p, unsigned long flag) +static inline int of_property_check_flag(const struct property *p, unsigned long flag) { return test_bit(flag, &p->_flags); } @@ -812,7 +812,8 @@ static inline void of_node_clear_flag(struct device_node *n, unsigned long flag) { } -static inline int of_property_check_flag(struct property *p, unsigned long flag) +static inline int of_property_check_flag(const struct property *p, + unsigned long flag) { return 0; } diff --git a/include/linux/of_gpio.h b/include/linux/of_gpio.h index 8bf2ea859653..a5166eb93437 100644 --- a/include/linux/of_gpio.h +++ b/include/linux/of_gpio.h @@ -29,6 +29,7 @@ enum of_gpio_flags { OF_GPIO_TRANSITORY = 0x8, OF_GPIO_PULL_UP = 0x10, OF_GPIO_PULL_DOWN = 0x20, + OF_GPIO_PULL_DISABLE = 0x40, }; #ifdef CONFIG_OF_GPIO diff --git a/include/linux/of_platform.h b/include/linux/of_platform.h index 84a966623e78..d15b6cd5e1c3 100644 --- a/include/linux/of_platform.h +++ b/include/linux/of_platform.h @@ -61,16 +61,18 @@ static inline struct platform_device *of_find_device_by_node(struct device_node } #endif +extern int of_platform_bus_probe(struct device_node *root, + const struct of_device_id *matches, + struct device *parent); + +#ifdef CONFIG_OF_ADDRESS /* Platform devices and busses creation */ extern struct platform_device *of_platform_device_create(struct device_node *np, const char *bus_id, struct device *parent); extern int of_platform_device_destroy(struct device *dev, void *data); -extern int of_platform_bus_probe(struct device_node *root, - const struct of_device_id *matches, - struct device *parent); -#ifdef CONFIG_OF_ADDRESS + extern int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, @@ -84,6 +86,18 @@ extern int devm_of_platform_populate(struct device *dev); extern void devm_of_platform_depopulate(struct device *dev); #else +/* Platform devices and busses creation */ +static inline struct platform_device *of_platform_device_create(struct device_node *np, + const char *bus_id, + struct device *parent) +{ + return NULL; +} +static inline int of_platform_device_destroy(struct device *dev, void *data) +{ + return -ENODEV; +} + static inline int of_platform_populate(struct device_node *root, const struct of_device_id *matches, const struct of_dev_auxdata *lookup, diff --git a/include/linux/platform-feature.h b/include/linux/platform-feature.h deleted file mode 100644 index b2f48be999fa..000000000000 --- a/include/linux/platform-feature.h +++ /dev/null @@ -1,19 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _PLATFORM_FEATURE_H -#define _PLATFORM_FEATURE_H - -#include -#include - -/* The platform features are starting with the architecture specific ones. */ - -/* Used to enable platform specific DMA handling for virtio devices. */ -#define PLATFORM_VIRTIO_RESTRICTED_MEM_ACCESS (0 + PLATFORM_ARCH_FEAT_N) - -#define PLATFORM_FEAT_N (1 + PLATFORM_ARCH_FEAT_N) - -void platform_set(unsigned int feature); -void platform_clear(unsigned int feature); -bool platform_has(unsigned int feature); - -#endif /* _PLATFORM_FEATURE_H */ diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 8cfa8cfca77e..f13568b3e247 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -13,8 +13,8 @@ #ifndef __CROS_EC_COMMANDS_H #define __CROS_EC_COMMANDS_H - - +#include +#include #define BUILD_ASSERT(_cond) @@ -787,7 +787,7 @@ struct ec_host_response { * * Packets always start with a request or response header. They are followed * by data_len bytes of data. If the data_crc_present flag is set, the data - * bytes are followed by a CRC-8 of that data, using using x^8 + x^2 + x + 1 + * bytes are followed by a CRC-8 of that data, using x^8 + x^2 + x + 1 * polynomial. * * Host algorithm when sending a request q: diff --git a/include/linux/platform_data/cros_ec_proto.h b/include/linux/platform_data/cros_ec_proto.h index 138fd912c808..c82a8285d936 100644 --- a/include/linux/platform_data/cros_ec_proto.h +++ b/include/linux/platform_data/cros_ec_proto.h @@ -21,6 +21,9 @@ #define CROS_EC_DEV_SCP_NAME "cros_scp" #define CROS_EC_DEV_TP_NAME "cros_tp" +#define CROS_EC_DEV_EC_INDEX 0 +#define CROS_EC_DEV_PD_INDEX 1 + /* * The EC is unresponsive for a time after a reboot command. Add a * simple delay to make sure that the bus stays locked. @@ -231,8 +234,8 @@ bool cros_ec_check_features(struct cros_ec_dev *ec, int feature); int cros_ec_get_sensor_count(struct cros_ec_dev *ec); -int cros_ec_command(struct cros_ec_device *ec_dev, unsigned int version, int command, void *outdata, - int outsize, void *indata, int insize); +int cros_ec_cmd(struct cros_ec_device *ec_dev, unsigned int version, int command, void *outdata, + size_t outsize, void *indata, size_t insize); /** * cros_ec_get_time_ns() - Return time in ns. diff --git a/include/linux/platform_data/davinci_asp.h b/include/linux/platform_data/davinci_asp.h index 76b13ef67562..c8645b2ed3c0 100644 --- a/include/linux/platform_data/davinci_asp.h +++ b/include/linux/platform_data/davinci_asp.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI DaVinci Audio Serial Port support * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DAVINCI_ASP_H diff --git a/include/linux/platform_data/gpio-davinci.h b/include/linux/platform_data/gpio-davinci.h index e182a46e609f..b82e44662efe 100644 --- a/include/linux/platform_data/gpio-davinci.h +++ b/include/linux/platform_data/gpio-davinci.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * DaVinci GPIO Platform Related Defines * * Copyright (C) 2013 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __DAVINCI_GPIO_PLATFORM_H diff --git a/include/linux/platform_data/uio_dmem_genirq.h b/include/linux/platform_data/uio_dmem_genirq.h index 973c1bb32168..c8f6de685306 100644 --- a/include/linux/platform_data/uio_dmem_genirq.h +++ b/include/linux/platform_data/uio_dmem_genirq.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * include/linux/platform_data/uio_dmem_genirq.h * * Copyright (C) 2012 Damian Hobson-Garcia - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _UIO_DMEM_GENIRQ_H diff --git a/include/linux/platform_data/uio_pruss.h b/include/linux/platform_data/uio_pruss.h index 31f2e22661bc..f76fa393b802 100644 --- a/include/linux/platform_data/uio_pruss.h +++ b/include/linux/platform_data/uio_pruss.h @@ -1,18 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * include/linux/platform_data/uio_pruss.h * * Platform data for uio_pruss driver * * Copyright (C) 2010-11 Texas Instruments Incorporated - https://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _UIO_PRUSS_H_ diff --git a/include/linux/platform_data/usb-omap.h b/include/linux/platform_data/usb-omap.h index 5e70d667031c..580978e468f8 100644 --- a/include/linux/platform_data/usb-omap.h +++ b/include/linux/platform_data/usb-omap.h @@ -1,22 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * usb-omap.h - Platform data for the various OMAP USB IPs * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com - * - * This software is distributed under the terms of the GNU General Public - * License ("GPL") version 2, as published by the Free Software Foundation. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" - * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN - * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. */ #define OMAP3_HS_USB_PORTS 3 diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index a571b47ff362..98f2b2f20f3e 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -49,6 +49,7 @@ #define ASUS_WMI_DEVID_LED4 0x00020014 #define ASUS_WMI_DEVID_LED5 0x00020015 #define ASUS_WMI_DEVID_LED6 0x00020016 +#define ASUS_WMI_DEVID_MICMUTE_LED 0x00040017 /* Backlight and Brightness */ #define ASUS_WMI_DEVID_ALS_ENABLE 0x00050001 /* Ambient Light Sensor */ diff --git a/include/linux/platform_data/x86/p2sb.h b/include/linux/platform_data/x86/p2sb.h new file mode 100644 index 000000000000..a1d5fddc8f13 --- /dev/null +++ b/include/linux/platform_data/x86/p2sb.h @@ -0,0 +1,28 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Primary to Sideband (P2SB) bridge access support + */ + +#ifndef _PLATFORM_DATA_X86_P2SB_H +#define _PLATFORM_DATA_X86_P2SB_H + +#include +#include + +struct pci_bus; +struct resource; + +#if IS_BUILTIN(CONFIG_P2SB) + +int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem); + +#else /* CONFIG_P2SB */ + +static inline int p2sb_bar(struct pci_bus *bus, unsigned int devfn, struct resource *mem) +{ + return -ENODEV; +} + +#endif /* CONFIG_P2SB is not set */ + +#endif /* _PLATFORM_DATA_X86_P2SB_H */ diff --git a/include/linux/platform_data/x86/pmc_atom.h b/include/linux/platform_data/x86/pmc_atom.h index 6807839c718b..3edfb6d4e67a 100644 --- a/include/linux/platform_data/x86/pmc_atom.h +++ b/include/linux/platform_data/x86/pmc_atom.h @@ -47,7 +47,7 @@ #define PMC_S0I2_TMR 0x88 #define PMC_S0I3_TMR 0x8C #define PMC_S0_TMR 0x90 -/* Sleep state counter is in units of of 32us */ +/* Sleep state counter is in units of 32us */ #define PMC_TMR_SHIFT 5 /* Power status of power islands */ diff --git a/include/linux/platform_data/x86/simatic-ipc-base.h b/include/linux/platform_data/x86/simatic-ipc-base.h index 62d2bc774067..39fefd48cf4d 100644 --- a/include/linux/platform_data/x86/simatic-ipc-base.h +++ b/include/linux/platform_data/x86/simatic-ipc-base.h @@ -24,6 +24,4 @@ struct simatic_ipc_platform { u8 devmode; }; -u32 simatic_ipc_get_membase0(unsigned int p2sb); - #endif /* __PLATFORM_DATA_X86_SIMATIC_IPC_BASE_H */ diff --git a/include/linux/pm_wakeirq.h b/include/linux/pm_wakeirq.h index e63a63aa47a3..dd42d16945d0 100644 --- a/include/linux/pm_wakeirq.h +++ b/include/linux/pm_wakeirq.h @@ -1,15 +1,5 @@ -/* - * pm_wakeirq.h - Device wakeirq helper functions - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* pm_wakeirq.h - Device wakeirq helper functions */ #ifndef _LINUX_PM_WAKEIRQ_H #define _LINUX_PM_WAKEIRQ_H diff --git a/include/linux/reset/bcm63xx_pmb.h b/include/linux/reset/bcm63xx_pmb.h index bb4af7b5eb36..c77b6999518a 100644 --- a/include/linux/reset/bcm63xx_pmb.h +++ b/include/linux/reset/bcm63xx_pmb.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Broadcom BCM63xx Processor Monitor Bus shared routines (SMP and reset) * * Copyright (C) 2015, Broadcom Corporation * Author: Florian Fainelli - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __BCM63XX_PMB_H #define __BCM63XX_PMB_H diff --git a/include/linux/sched/user.h b/include/linux/sched/user.h index 00ed419dd464..f054d0360a75 100644 --- a/include/linux/sched/user.h +++ b/include/linux/sched/user.h @@ -24,7 +24,8 @@ struct user_struct { kuid_t uid; #if defined(CONFIG_PERF_EVENTS) || defined(CONFIG_BPF_SYSCALL) || \ - defined(CONFIG_NET) || defined(CONFIG_IO_URING) + defined(CONFIG_NET) || defined(CONFIG_IO_URING) || \ + defined(CONFIG_VFIO_PCI_ZDEV_KVM) atomic_long_t locked_vm; #endif #ifdef CONFIG_WATCH_QUEUE diff --git a/include/linux/soc/ti/knav_dma.h b/include/linux/soc/ti/knav_dma.h index 7127ec301537..18d806a8e52c 100644 --- a/include/linux/soc/ti/knav_dma.h +++ b/include/linux/soc/ti/knav_dma.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Copyright (C) 2014 Texas Instruments Incorporated * Authors: Sandeep Nair - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __SOC_TI_KEYSTONE_NAVIGATOR_DMA_H__ diff --git a/include/linux/soc/ti/knav_qmss.h b/include/linux/soc/ti/knav_qmss.h index c75ef99c99ca..175f466ebcc3 100644 --- a/include/linux/soc/ti/knav_qmss.h +++ b/include/linux/soc/ti/knav_qmss.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Keystone Navigator Queue Management Sub-System header * @@ -5,15 +6,6 @@ * Author: Sandeep Nair * Cyril Chemparathy * Santosh Shilimkar - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __SOC_TI_KNAV_QMSS_H__ diff --git a/include/linux/soc/ti/ti-msgmgr.h b/include/linux/soc/ti/ti-msgmgr.h index 69a8d7682c4b..543da257a5f2 100644 --- a/include/linux/soc/ti/ti-msgmgr.h +++ b/include/linux/soc/ti/ti-msgmgr.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Texas Instruments' Message Manager * * Copyright (C) 2015-2022 Texas Instruments Incorporated - https://www.ti.com/ * Nishanth Menon - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef TI_MSGMGR_H diff --git a/include/linux/sram.h b/include/linux/sram.h index 4fb405fb0480..d7dee19505c6 100644 --- a/include/linux/sram.h +++ b/include/linux/sram.h @@ -1,15 +1,5 @@ -/* - * Generic SRAM Driver Interface - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Generic SRAM Driver Interface */ #ifndef __LINUX_SRAM_H__ #define __LINUX_SRAM_H__ diff --git a/include/linux/sunrpc/bc_xprt.h b/include/linux/sunrpc/bc_xprt.h index f07c334c599f..db30a159f9d5 100644 --- a/include/linux/sunrpc/bc_xprt.h +++ b/include/linux/sunrpc/bc_xprt.h @@ -1,22 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /****************************************************************************** (c) 2008 NetApp. All Rights Reserved. -NetApp provides this source code under the GPL v2 License. -The GPL v2 license is available at -https://opensource.org/licenses/gpl-license.php. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ @@ -83,4 +69,3 @@ static inline void xprt_free_bc_request(struct rpc_rqst *req) } #endif /* CONFIG_SUNRPC_BACKCHANNEL */ #endif /* _LINUX_SUNRPC_BC_XPRT_H */ - diff --git a/include/linux/surface_aggregator/controller.h b/include/linux/surface_aggregator/controller.h index 74bfdffaf7b0..d11a1c6e3186 100644 --- a/include/linux/surface_aggregator/controller.h +++ b/include/linux/surface_aggregator/controller.h @@ -469,6 +469,67 @@ struct ssam_request_spec_md { return 0; \ } +/** + * SSAM_DEFINE_SYNC_REQUEST_WR() - Define synchronous SAM request function with + * both argument and return value. + * @name: Name of the generated function. + * @atype: Type of the request's argument. + * @rtype: Type of the request's return value. + * @spec: Specification (&struct ssam_request_spec) defining the request. + * + * Defines a function executing the synchronous SAM request specified by @spec, + * with the request taking an argument of type @atype and having a return value + * of type @rtype. The generated function takes care of setting up the request + * and response structs, buffer allocation, as well as execution of the request + * itself, returning once the request has been fully completed. The required + * transport buffer will be allocated on the stack. + * + * The generated function is defined as ``static int name(struct + * ssam_controller *ctrl, const atype *arg, rtype *ret)``, returning the status + * of the request, which is zero on success and negative on failure. The + * ``ctrl`` parameter is the controller via which the request is sent. The + * request argument is specified via the ``arg`` pointer. The request's return + * value is written to the memory pointed to by the ``ret`` parameter. + * + * Refer to ssam_request_sync_onstack() for more details on the behavior of + * the generated function. + */ +#define SSAM_DEFINE_SYNC_REQUEST_WR(name, atype, rtype, spec...) \ + static int name(struct ssam_controller *ctrl, const atype *arg, rtype *ret) \ + { \ + struct ssam_request_spec s = (struct ssam_request_spec)spec; \ + struct ssam_request rqst; \ + struct ssam_response rsp; \ + int status; \ + \ + rqst.target_category = s.target_category; \ + rqst.target_id = s.target_id; \ + rqst.command_id = s.command_id; \ + rqst.instance_id = s.instance_id; \ + rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE; \ + rqst.length = sizeof(atype); \ + rqst.payload = (u8 *)arg; \ + \ + rsp.capacity = sizeof(rtype); \ + rsp.length = 0; \ + rsp.pointer = (u8 *)ret; \ + \ + status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, sizeof(atype)); \ + if (status) \ + return status; \ + \ + if (rsp.length != sizeof(rtype)) { \ + struct device *dev = ssam_controller_device(ctrl); \ + dev_err(dev, \ + "rqst: invalid response length, expected %zu, got %zu (tc: %#04x, cid: %#04x)", \ + sizeof(rtype), rsp.length, rqst.target_category,\ + rqst.command_id); \ + return -EIO; \ + } \ + \ + return 0; \ + } + /** * SSAM_DEFINE_SYNC_REQUEST_MD_N() - Define synchronous multi-device SAM * request function with neither argument nor return value. @@ -613,6 +674,70 @@ struct ssam_request_spec_md { return 0; \ } +/** + * SSAM_DEFINE_SYNC_REQUEST_MD_WR() - Define synchronous multi-device SAM + * request function with both argument and return value. + * @name: Name of the generated function. + * @atype: Type of the request's argument. + * @rtype: Type of the request's return value. + * @spec: Specification (&struct ssam_request_spec_md) defining the request. + * + * Defines a function executing the synchronous SAM request specified by @spec, + * with the request taking an argument of type @atype and having a return value + * of type @rtype. Device specifying parameters are not hard-coded, but instead + * must be provided to the function. The generated function takes care of + * setting up the request and response structs, buffer allocation, as well as + * execution of the request itself, returning once the request has been fully + * completed. The required transport buffer will be allocated on the stack. + * + * The generated function is defined as ``static int name(struct + * ssam_controller *ctrl, u8 tid, u8 iid, const atype *arg, rtype *ret)``, + * returning the status of the request, which is zero on success and negative + * on failure. The ``ctrl`` parameter is the controller via which the request + * is sent, ``tid`` the target ID for the request, and ``iid`` the instance ID. + * The request argument is specified via the ``arg`` pointer. The request's + * return value is written to the memory pointed to by the ``ret`` parameter. + * + * Refer to ssam_request_sync_onstack() for more details on the behavior of + * the generated function. + */ +#define SSAM_DEFINE_SYNC_REQUEST_MD_WR(name, atype, rtype, spec...) \ + static int name(struct ssam_controller *ctrl, u8 tid, u8 iid, \ + const atype *arg, rtype *ret) \ + { \ + struct ssam_request_spec_md s = (struct ssam_request_spec_md)spec; \ + struct ssam_request rqst; \ + struct ssam_response rsp; \ + int status; \ + \ + rqst.target_category = s.target_category; \ + rqst.target_id = tid; \ + rqst.command_id = s.command_id; \ + rqst.instance_id = iid; \ + rqst.flags = s.flags | SSAM_REQUEST_HAS_RESPONSE; \ + rqst.length = sizeof(atype); \ + rqst.payload = (u8 *)arg; \ + \ + rsp.capacity = sizeof(rtype); \ + rsp.length = 0; \ + rsp.pointer = (u8 *)ret; \ + \ + status = ssam_request_sync_onstack(ctrl, &rqst, &rsp, sizeof(atype)); \ + if (status) \ + return status; \ + \ + if (rsp.length != sizeof(rtype)) { \ + struct device *dev = ssam_controller_device(ctrl); \ + dev_err(dev, \ + "rqst: invalid response length, expected %zu, got %zu (tc: %#04x, cid: %#04x)", \ + sizeof(rtype), rsp.length, rqst.target_category,\ + rqst.command_id); \ + return -EIO; \ + } \ + \ + return 0; \ + } + /* -- Event notifier/callbacks. --------------------------------------------- */ @@ -835,8 +960,28 @@ struct ssam_event_notifier { int ssam_notifier_register(struct ssam_controller *ctrl, struct ssam_event_notifier *n); -int ssam_notifier_unregister(struct ssam_controller *ctrl, - struct ssam_event_notifier *n); +int __ssam_notifier_unregister(struct ssam_controller *ctrl, + struct ssam_event_notifier *n, bool disable); + +/** + * ssam_notifier_unregister() - Unregister an event notifier. + * @ctrl: The controller the notifier has been registered on. + * @n: The event notifier to unregister. + * + * Unregister an event notifier. Decrement the usage counter of the associated + * SAM event if the notifier is not marked as an observer. If the usage counter + * reaches zero, the event will be disabled. + * + * Return: Returns zero on success, %-ENOENT if the given notifier block has + * not been registered on the controller. If the given notifier block was the + * last one associated with its specific event, returns the status of the + * event-disable EC-command. + */ +static inline int ssam_notifier_unregister(struct ssam_controller *ctrl, + struct ssam_event_notifier *n) +{ + return __ssam_notifier_unregister(ctrl, n, true); +} int ssam_controller_event_enable(struct ssam_controller *ctrl, struct ssam_event_registry reg, diff --git a/include/linux/surface_aggregator/device.h b/include/linux/surface_aggregator/device.h index cc257097eb05..46c45d1b6368 100644 --- a/include/linux/surface_aggregator/device.h +++ b/include/linux/surface_aggregator/device.h @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -148,17 +149,30 @@ struct ssam_device_uid { #define SSAM_SDEV(cat, tid, iid, fun) \ SSAM_DEVICE(SSAM_DOMAIN_SERIALHUB, SSAM_SSH_TC_##cat, tid, iid, fun) +/* + * enum ssam_device_flags - Flags for SSAM client devices. + * @SSAM_DEVICE_HOT_REMOVED_BIT: + * The device has been hot-removed. Further communication with it may time + * out and should be avoided. + */ +enum ssam_device_flags { + SSAM_DEVICE_HOT_REMOVED_BIT = 0, +}; + /** * struct ssam_device - SSAM client device. - * @dev: Driver model representation of the device. - * @ctrl: SSAM controller managing this device. - * @uid: UID identifying the device. + * @dev: Driver model representation of the device. + * @ctrl: SSAM controller managing this device. + * @uid: UID identifying the device. + * @flags: Device state flags, see &enum ssam_device_flags. */ struct ssam_device { struct device dev; struct ssam_controller *ctrl; struct ssam_device_uid uid; + + unsigned long flags; }; /** @@ -177,6 +191,8 @@ struct ssam_device_driver { void (*remove)(struct ssam_device *sdev); }; +#ifdef CONFIG_SURFACE_AGGREGATOR_BUS + extern struct bus_type ssam_bus_type; extern const struct device_type ssam_device_type; @@ -193,6 +209,15 @@ static inline bool is_ssam_device(struct device *d) return d->type == &ssam_device_type; } +#else /* CONFIG_SURFACE_AGGREGATOR_BUS */ + +static inline bool is_ssam_device(struct device *d) +{ + return false; +} + +#endif /* CONFIG_SURFACE_AGGREGATOR_BUS */ + /** * to_ssam_device() - Casts the given device to a SSAM client device. * @d: The device to cast. @@ -240,6 +265,35 @@ struct ssam_device *ssam_device_alloc(struct ssam_controller *ctrl, int ssam_device_add(struct ssam_device *sdev); void ssam_device_remove(struct ssam_device *sdev); +/** + * ssam_device_mark_hot_removed() - Mark the given device as hot-removed. + * @sdev: The device to mark as hot-removed. + * + * Mark the device as having been hot-removed. This signals drivers using the + * device that communication with the device should be avoided and may lead to + * timeouts. + */ +static inline void ssam_device_mark_hot_removed(struct ssam_device *sdev) +{ + dev_dbg(&sdev->dev, "marking device as hot-removed\n"); + set_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags); +} + +/** + * ssam_device_is_hot_removed() - Check if the given device has been + * hot-removed. + * @sdev: The device to check. + * + * Checks if the given device has been marked as hot-removed. See + * ssam_device_mark_hot_removed() for more details. + * + * Return: Returns ``true`` if the device has been marked as hot-removed. + */ +static inline bool ssam_device_is_hot_removed(struct ssam_device *sdev) +{ + return test_bit(SSAM_DEVICE_HOT_REMOVED_BIT, &sdev->flags); +} + /** * ssam_device_get() - Increment reference count of SSAM client device. * @sdev: The device to increment the reference count of. @@ -322,11 +376,62 @@ void ssam_device_driver_unregister(struct ssam_device_driver *d); /* -- Helpers for controller and hub devices. ------------------------------- */ #ifdef CONFIG_SURFACE_AGGREGATOR_BUS + +int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl, + struct fwnode_handle *node); void ssam_remove_clients(struct device *dev); + #else /* CONFIG_SURFACE_AGGREGATOR_BUS */ + +static inline int __ssam_register_clients(struct device *parent, struct ssam_controller *ctrl, + struct fwnode_handle *node) +{ + return 0; +} + static inline void ssam_remove_clients(struct device *dev) {} + #endif /* CONFIG_SURFACE_AGGREGATOR_BUS */ +/** + * ssam_register_clients() - Register all client devices defined under the + * given parent device. + * @dev: The parent device under which clients should be registered. + * @ctrl: The controller with which client should be registered. + * + * Register all clients that have via firmware nodes been defined as children + * of the given (parent) device. The respective child firmware nodes will be + * associated with the correspondingly created child devices. + * + * The given controller will be used to instantiate the new devices. See + * ssam_device_add() for details. + * + * Return: Returns zero on success, nonzero on failure. + */ +static inline int ssam_register_clients(struct device *dev, struct ssam_controller *ctrl) +{ + return __ssam_register_clients(dev, ctrl, dev_fwnode(dev)); +} + +/** + * ssam_device_register_clients() - Register all client devices defined under + * the given SSAM parent device. + * @sdev: The parent device under which clients should be registered. + * + * Register all clients that have via firmware nodes been defined as children + * of the given (parent) device. The respective child firmware nodes will be + * associated with the correspondingly created child devices. + * + * The controller used by the parent device will be used to instantiate the new + * devices. See ssam_device_add() for details. + * + * Return: Returns zero on success, nonzero on failure. + */ +static inline int ssam_device_register_clients(struct ssam_device *sdev) +{ + return ssam_register_clients(&sdev->dev, sdev->ctrl); +} + /* -- Helpers for client-device requests. ----------------------------------- */ @@ -430,4 +535,106 @@ static inline void ssam_remove_clients(struct device *dev) {} sdev->uid.instance, ret); \ } +/** + * SSAM_DEFINE_SYNC_REQUEST_CL_WR() - Define synchronous client-device SAM + * request function with argument and return value. + * @name: Name of the generated function. + * @atype: Type of the request's argument. + * @rtype: Type of the request's return value. + * @spec: Specification (&struct ssam_request_spec_md) defining the request. + * + * Defines a function executing the synchronous SAM request specified by @spec, + * with the request taking an argument of type @atype and having a return value + * of type @rtype. Device specifying parameters are not hard-coded, but instead + * are provided via the client device, specifically its UID, supplied when + * calling this function. The generated function takes care of setting up the + * request struct, buffer allocation, as well as execution of the request + * itself, returning once the request has been fully completed. The required + * transport buffer will be allocated on the stack. + * + * The generated function is defined as ``static int name(struct ssam_device + * *sdev, const atype *arg, rtype *ret)``, returning the status of the request, + * which is zero on success and negative on failure. The ``sdev`` parameter + * specifies both the target device of the request and by association the + * controller via which the request is sent. The request's argument is + * specified via the ``arg`` pointer. The request's return value is written to + * the memory pointed to by the ``ret`` parameter. + * + * Refer to ssam_request_sync_onstack() for more details on the behavior of + * the generated function. + */ +#define SSAM_DEFINE_SYNC_REQUEST_CL_WR(name, atype, rtype, spec...) \ + SSAM_DEFINE_SYNC_REQUEST_MD_WR(__raw_##name, atype, rtype, spec) \ + static int name(struct ssam_device *sdev, const atype *arg, rtype *ret) \ + { \ + return __raw_##name(sdev->ctrl, sdev->uid.target, \ + sdev->uid.instance, arg, ret); \ + } + + +/* -- Helpers for client-device notifiers. ---------------------------------- */ + +/** + * ssam_device_notifier_register() - Register an event notifier for the + * specified client device. + * @sdev: The device the notifier should be registered on. + * @n: The event notifier to register. + * + * Register an event notifier. Increment the usage counter of the associated + * SAM event if the notifier is not marked as an observer. If the event is not + * marked as an observer and is currently not enabled, it will be enabled + * during this call. If the notifier is marked as an observer, no attempt will + * be made at enabling any event and no reference count will be modified. + * + * Notifiers marked as observers do not need to be associated with one specific + * event, i.e. as long as no event matching is performed, only the event target + * category needs to be set. + * + * Return: Returns zero on success, %-ENOSPC if there have already been + * %INT_MAX notifiers for the event ID/type associated with the notifier block + * registered, %-ENOMEM if the corresponding event entry could not be + * allocated, %-ENODEV if the device is marked as hot-removed. If this is the + * first time that a notifier block is registered for the specific associated + * event, returns the status of the event-enable EC-command. + */ +static inline int ssam_device_notifier_register(struct ssam_device *sdev, + struct ssam_event_notifier *n) +{ + /* + * Note that this check does not provide any guarantees whatsoever as + * hot-removal could happen at any point and we can't protect against + * it. Nevertheless, if we can detect hot-removal, bail early to avoid + * communication timeouts. + */ + if (ssam_device_is_hot_removed(sdev)) + return -ENODEV; + + return ssam_notifier_register(sdev->ctrl, n); +} + +/** + * ssam_device_notifier_unregister() - Unregister an event notifier for the + * specified client device. + * @sdev: The device the notifier has been registered on. + * @n: The event notifier to unregister. + * + * Unregister an event notifier. Decrement the usage counter of the associated + * SAM event if the notifier is not marked as an observer. If the usage counter + * reaches zero, the event will be disabled. + * + * In case the device has been marked as hot-removed, the event will not be + * disabled on the EC, as in those cases any attempt at doing so may time out. + * + * Return: Returns zero on success, %-ENOENT if the given notifier block has + * not been registered on the controller. If the given notifier block was the + * last one associated with its specific event, returns the status of the + * event-disable EC-command. + */ +static inline int ssam_device_notifier_unregister(struct ssam_device *sdev, + struct ssam_event_notifier *n) +{ + return __ssam_notifier_unregister(sdev->ctrl, n, + !ssam_device_is_hot_removed(sdev)); +} + #endif /* _LINUX_SURFACE_AGGREGATOR_DEVICE_H */ diff --git a/include/linux/surface_aggregator/serial_hub.h b/include/linux/surface_aggregator/serial_hub.h index c3de43edcffa..45501b6e54e8 100644 --- a/include/linux/surface_aggregator/serial_hub.h +++ b/include/linux/surface_aggregator/serial_hub.h @@ -201,7 +201,7 @@ static inline u16 ssh_crc(const u8 *buf, size_t len) * exception of zero, which is not an event ID. Thus, this is also the * absolute maximum number of event handlers that can be registered. */ -#define SSH_NUM_EVENTS 34 +#define SSH_NUM_EVENTS 38 /* * SSH_NUM_TARGETS - The number of communication targets used in the protocol. @@ -292,40 +292,45 @@ struct ssam_span { * Windows driver. */ enum ssam_ssh_tc { - /* Category 0x00 is invalid for EC use. */ - SSAM_SSH_TC_SAM = 0x01, /* Generic system functionality, real-time clock. */ - SSAM_SSH_TC_BAT = 0x02, /* Battery/power subsystem. */ - SSAM_SSH_TC_TMP = 0x03, /* Thermal subsystem. */ - SSAM_SSH_TC_PMC = 0x04, - SSAM_SSH_TC_FAN = 0x05, - SSAM_SSH_TC_PoM = 0x06, - SSAM_SSH_TC_DBG = 0x07, - SSAM_SSH_TC_KBD = 0x08, /* Legacy keyboard (Laptop 1/2). */ - SSAM_SSH_TC_FWU = 0x09, - SSAM_SSH_TC_UNI = 0x0a, - SSAM_SSH_TC_LPC = 0x0b, - SSAM_SSH_TC_TCL = 0x0c, - SSAM_SSH_TC_SFL = 0x0d, - SSAM_SSH_TC_KIP = 0x0e, - SSAM_SSH_TC_EXT = 0x0f, - SSAM_SSH_TC_BLD = 0x10, - SSAM_SSH_TC_BAS = 0x11, /* Detachment system (Surface Book 2/3). */ - SSAM_SSH_TC_SEN = 0x12, - SSAM_SSH_TC_SRQ = 0x13, - SSAM_SSH_TC_MCU = 0x14, - SSAM_SSH_TC_HID = 0x15, /* Generic HID input subsystem. */ - SSAM_SSH_TC_TCH = 0x16, - SSAM_SSH_TC_BKL = 0x17, - SSAM_SSH_TC_TAM = 0x18, - SSAM_SSH_TC_ACC = 0x19, - SSAM_SSH_TC_UFI = 0x1a, - SSAM_SSH_TC_USC = 0x1b, - SSAM_SSH_TC_PEN = 0x1c, - SSAM_SSH_TC_VID = 0x1d, - SSAM_SSH_TC_AUD = 0x1e, - SSAM_SSH_TC_SMC = 0x1f, - SSAM_SSH_TC_KPD = 0x20, - SSAM_SSH_TC_REG = 0x21, /* Extended event registry. */ + /* Category 0x00 is invalid for EC use. */ + SSAM_SSH_TC_SAM = 0x01, /* Generic system functionality, real-time clock. */ + SSAM_SSH_TC_BAT = 0x02, /* Battery/power subsystem. */ + SSAM_SSH_TC_TMP = 0x03, /* Thermal subsystem. */ + SSAM_SSH_TC_PMC = 0x04, + SSAM_SSH_TC_FAN = 0x05, + SSAM_SSH_TC_PoM = 0x06, + SSAM_SSH_TC_DBG = 0x07, + SSAM_SSH_TC_KBD = 0x08, /* Legacy keyboard (Laptop 1/2). */ + SSAM_SSH_TC_FWU = 0x09, + SSAM_SSH_TC_UNI = 0x0a, + SSAM_SSH_TC_LPC = 0x0b, + SSAM_SSH_TC_TCL = 0x0c, + SSAM_SSH_TC_SFL = 0x0d, + SSAM_SSH_TC_KIP = 0x0e, /* Manages detachable peripherals (Pro X/8 keyboard cover) */ + SSAM_SSH_TC_EXT = 0x0f, + SSAM_SSH_TC_BLD = 0x10, + SSAM_SSH_TC_BAS = 0x11, /* Detachment system (Surface Book 2/3). */ + SSAM_SSH_TC_SEN = 0x12, + SSAM_SSH_TC_SRQ = 0x13, + SSAM_SSH_TC_MCU = 0x14, + SSAM_SSH_TC_HID = 0x15, /* Generic HID input subsystem. */ + SSAM_SSH_TC_TCH = 0x16, + SSAM_SSH_TC_BKL = 0x17, + SSAM_SSH_TC_TAM = 0x18, + SSAM_SSH_TC_ACC0 = 0x19, + SSAM_SSH_TC_UFI = 0x1a, + SSAM_SSH_TC_USC = 0x1b, + SSAM_SSH_TC_PEN = 0x1c, + SSAM_SSH_TC_VID = 0x1d, + SSAM_SSH_TC_AUD = 0x1e, + SSAM_SSH_TC_SMC = 0x1f, + SSAM_SSH_TC_KPD = 0x20, + SSAM_SSH_TC_REG = 0x21, /* Extended event registry. */ + SSAM_SSH_TC_SPT = 0x22, + SSAM_SSH_TC_SYS = 0x23, + SSAM_SSH_TC_ACC1 = 0x24, + SSAM_SSH_TC_SHB = 0x25, + SSAM_SSH_TC_POS = 0x26, /* For obtaining Laptop Studio screen position. */ }; diff --git a/include/linux/ti-emif-sram.h b/include/linux/ti-emif-sram.h index 2fc854155c27..441b2988e66a 100644 --- a/include/linux/ti-emif-sram.h +++ b/include/linux/ti-emif-sram.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI AM33XX EMIF Routines * * Copyright (C) 2016-2017 Texas Instruments Inc. * Dave Gerlach - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __LINUX_TI_EMIF_H #define __LINUX_TI_EMIF_H diff --git a/include/linux/ucb1400.h b/include/linux/ucb1400.h index 0968ef458447..22345391350b 100644 --- a/include/linux/ucb1400.h +++ b/include/linux/ucb1400.h @@ -84,8 +84,6 @@ struct ucb1400_gpio { struct gpio_chip gc; struct snd_ac97 *ac97; int gpio_offset; - int (*gpio_setup)(struct device *dev, int ngpio); - int (*gpio_teardown)(struct device *dev, int ngpio); }; struct ucb1400_ts { diff --git a/include/linux/usb.h b/include/linux/usb.h index 60bee864d897..f7a9914fc97f 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -584,6 +584,7 @@ struct usb3_lpm_parameters { * @authenticated: Crypto authentication passed * @wusb: device is Wireless USB * @lpm_capable: device supports LPM + * @lpm_devinit_allow: Allow USB3 device initiated LPM, exit latency is in range * @usb2_hw_lpm_capable: device can perform USB2 hardware LPM * @usb2_hw_lpm_besl_capable: device can perform USB2 hardware BESL LPM * @usb2_hw_lpm_enabled: USB2 hardware LPM is enabled @@ -666,6 +667,7 @@ struct usb_device { unsigned authenticated:1; unsigned wusb:1; unsigned lpm_capable:1; + unsigned lpm_devinit_allow:1; unsigned usb2_hw_lpm_capable:1; unsigned usb2_hw_lpm_besl_capable:1; unsigned usb2_hw_lpm_enabled:1; diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index 8fc2abd7aecb..ca796dc1a984 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -2,9 +2,6 @@ /* * Copyright (c) 2010 Daniel Mack * - * This software is distributed under the terms of the GNU General Public - * License ("GPL") version 2, as published by the Free Software Foundation. - * * This file holds USB constants and structures defined * by the USB Device Class Definition for Audio Devices in version 2.0. * Comments below reference relevant sections of the documents contained diff --git a/include/linux/usb/audio.h b/include/linux/usb/audio.h index 170acd500ea1..0747b24a1a7c 100644 --- a/include/linux/usb/audio.h +++ b/include/linux/usb/audio.h @@ -6,9 +6,6 @@ * Developed for Thumtronics by Grey Innovation * Ben Williamson * - * This software is distributed under the terms of the GNU General Public - * License ("GPL") version 2, as published by the Free Software Foundation. - * * This file holds USB constants and structures defined * by the USB Device Class Definition for Audio Devices. * Comments below reference relevant sections of that document: diff --git a/include/linux/usb/c67x00.h b/include/linux/usb/c67x00.h index 2fc39e3b7281..45e0757e58f3 100644 --- a/include/linux/usb/c67x00.h +++ b/include/linux/usb/c67x00.h @@ -3,21 +3,6 @@ * usb_c67x00.h: platform definitions for the Cypress C67X00 USB chip * * Copyright (C) 2006-2008 Barco N.V. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, - * MA 02110-1301 USA. */ #ifndef _LINUX_USB_C67X00_H diff --git a/include/linux/usb/cdc-wdm.h b/include/linux/usb/cdc-wdm.h index 9f5a51f79ba5..85417f00a89a 100644 --- a/include/linux/usb/cdc-wdm.h +++ b/include/linux/usb/cdc-wdm.h @@ -3,10 +3,6 @@ * USB CDC Device Management subdriver * * Copyright (c) 2012 Bjørn Mork - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. */ #ifndef __LINUX_USB_CDC_WDM_H diff --git a/include/linux/usb/cdc.h b/include/linux/usb/cdc.h index 35d784cf32a4..0af0db51bc89 100644 --- a/include/linux/usb/cdc.h +++ b/include/linux/usb/cdc.h @@ -3,10 +3,6 @@ * USB CDC common helpers * * Copyright (c) 2015 Oliver Neukum - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. */ #ifndef __LINUX_USB_CDC_H #define __LINUX_USB_CDC_H diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h index 043a7bfdfb41..814f65799200 100644 --- a/include/linux/usb/composite.h +++ b/include/linux/usb/composite.h @@ -3,20 +3,6 @@ * composite.h -- framework for usb gadgets which are composite devices * * Copyright (C) 2006-2008 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #ifndef __LINUX_USB_COMPOSITE_H diff --git a/include/linux/usb/ehci_def.h b/include/linux/usb/ehci_def.h index c892c5bc6638..fbabadd3b372 100644 --- a/include/linux/usb/ehci_def.h +++ b/include/linux/usb/ehci_def.h @@ -1,20 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2002 by David Brownell - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __LINUX_USB_EHCI_DEF_H diff --git a/include/linux/usb/ehci_pdriver.h b/include/linux/usb/ehci_pdriver.h index 89fc901e778f..0f1b166f5aa0 100644 --- a/include/linux/usb/ehci_pdriver.h +++ b/include/linux/usb/ehci_pdriver.h @@ -1,20 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Hauke Mehrtens - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __USB_CORE_EHCI_PDRIVER_H diff --git a/include/linux/usb/g_hid.h b/include/linux/usb/g_hid.h index 7581e488c237..d56bfedeb079 100644 --- a/include/linux/usb/g_hid.h +++ b/include/linux/usb/g_hid.h @@ -3,20 +3,6 @@ * g_hid.h -- Header file for USB HID gadget driver * * Copyright (C) 2010 Fabien Chouteau - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_G_HID_H diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 3ad58b7a0824..dc3092cea99e 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -10,8 +10,6 @@ * * (C) Copyright 2002-2004 by David Brownell * All Rights Reserved. - * - * This software is licensed under the GNU GPL version 2. */ #ifndef __LINUX_USB_GADGET_H diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 2c1fc9212cf2..67f8713d3fa3 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -1,20 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2001-2002 by David Brownell - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __USB_CORE_HCD_H @@ -66,6 +52,7 @@ struct giveback_urb_bh { bool running; + bool high_prio; spinlock_t lock; struct list_head head; struct tasklet_struct bh; diff --git a/include/linux/usb/input.h b/include/linux/usb/input.h index 974befa72ac0..5e759b2cf551 100644 --- a/include/linux/usb/input.h +++ b/include/linux/usb/input.h @@ -1,10 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2005 Dmitry Torokhov - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published by - * the Free Software Foundation. */ #ifndef __LINUX_USB_INPUT_H diff --git a/include/linux/usb/isp1301.h b/include/linux/usb/isp1301.h index dedb3b2473e8..fa986b926a12 100644 --- a/include/linux/usb/isp1301.h +++ b/include/linux/usb/isp1301.h @@ -3,16 +3,6 @@ * NXP ISP1301 USB transceiver driver * * Copyright (C) 2012 Roland Stigge - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef __LINUX_USB_ISP1301_H diff --git a/include/linux/usb/m66592.h b/include/linux/usb/m66592.h index 2dfe68183495..5f04de2b47fd 100644 --- a/include/linux/usb/m66592.h +++ b/include/linux/usb/m66592.h @@ -3,20 +3,6 @@ * M66592 driver platform data * * Copyright (C) 2009 Renesas Solutions Corp. - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __LINUX_USB_M66592_H diff --git a/include/linux/usb/musb-ux500.h b/include/linux/usb/musb-ux500.h index c4b7ad9850ca..d60dcfc56b5a 100644 --- a/include/linux/usb/musb-ux500.h +++ b/include/linux/usb/musb-ux500.h @@ -1,16 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2013 ST-Ericsson AB - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef __MUSB_UX500_H__ diff --git a/include/linux/usb/net2280.h b/include/linux/usb/net2280.h index 08b85caecfaf..f29fe6a1f415 100644 --- a/include/linux/usb/net2280.h +++ b/include/linux/usb/net2280.h @@ -5,20 +5,6 @@ * * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) * Copyright (C) 2003 David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_NET2280_H diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index dba55ccb9b53..98487fd7ab11 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -1,8 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * OF helpers for usb devices. - * - * This file is released under the GPLv2 */ #ifndef __LINUX_USB_OF_H diff --git a/include/linux/usb/ohci_pdriver.h b/include/linux/usb/ohci_pdriver.h index 7eb16cf587ee..2447c78b1766 100644 --- a/include/linux/usb/ohci_pdriver.h +++ b/include/linux/usb/ohci_pdriver.h @@ -1,20 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* * Copyright (C) 2012 Hauke Mehrtens - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY - * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software Foundation, - * Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef __USB_CORE_OHCI_PDRIVER_H diff --git a/include/linux/usb/onboard_hub.h b/include/linux/usb/onboard_hub.h new file mode 100644 index 000000000000..d9373230556e --- /dev/null +++ b/include/linux/usb/onboard_hub.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __LINUX_USB_ONBOARD_HUB_H +#define __LINUX_USB_ONBOARD_HUB_H + +struct usb_device; +struct list_head; + +#if IS_ENABLED(CONFIG_USB_ONBOARD_HUB) +void onboard_hub_create_pdevs(struct usb_device *parent_hub, struct list_head *pdev_list); +void onboard_hub_destroy_pdevs(struct list_head *pdev_list); +#else +static inline void onboard_hub_create_pdevs(struct usb_device *parent_hub, + struct list_head *pdev_list) {} +static inline void onboard_hub_destroy_pdevs(struct list_head *pdev_list) {} +#endif + +#endif /* __LINUX_USB_ONBOARD_HUB_H */ diff --git a/include/linux/usb/otg-fsm.h b/include/linux/usb/otg-fsm.h index 784659d4dc99..6135d076c53d 100644 --- a/include/linux/usb/otg-fsm.h +++ b/include/linux/usb/otg-fsm.h @@ -1,19 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ -/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. +/* + * Copyright (C) 2007,2008 Freescale Semiconductor, Inc. */ #ifndef __LINUX_USB_OTG_FSM_H diff --git a/include/linux/usb/pd.h b/include/linux/usb/pd.h index 96b7ff66f074..c59fb79a42e8 100644 --- a/include/linux/usb/pd.h +++ b/include/linux/usb/pd.h @@ -495,4 +495,42 @@ static inline unsigned int rdo_max_power(u32 rdo) #define PD_P_SNK_STDBY_MW 2500 /* 2500 mW */ +#if IS_ENABLED(CONFIG_TYPEC) + +struct usb_power_delivery; + +/** + * usb_power_delivery_desc - USB Power Delivery Descriptor + * @revision: USB Power Delivery Specification Revision + * @version: USB Power Delivery Specicication Version - optional + */ +struct usb_power_delivery_desc { + u16 revision; + u16 version; +}; + +/** + * usb_power_delivery_capabilities_desc - Description of USB Power Delivery Capabilities Message + * @pdo: The Power Data Objects in the Capability Message + * @role: Power role of the capabilities + */ +struct usb_power_delivery_capabilities_desc { + u32 pdo[PDO_MAX_OBJECTS]; + enum typec_role role; +}; + +struct usb_power_delivery_capabilities * +usb_power_delivery_register_capabilities(struct usb_power_delivery *pd, + struct usb_power_delivery_capabilities_desc *desc); +void usb_power_delivery_unregister_capabilities(struct usb_power_delivery_capabilities *cap); + +struct usb_power_delivery *usb_power_delivery_register(struct device *parent, + struct usb_power_delivery_desc *desc); +void usb_power_delivery_unregister(struct usb_power_delivery *pd); + +int usb_power_delivery_link_device(struct usb_power_delivery *pd, struct device *dev); +void usb_power_delivery_unlink_device(struct usb_power_delivery *pd, struct device *dev); + +#endif /* CONFIG_TYPEC */ + #endif /* __LINUX_USB_PD_H */ diff --git a/include/linux/usb/phy_companion.h b/include/linux/usb/phy_companion.h index 263196f05015..862aaeca2319 100644 --- a/include/linux/usb/phy_companion.h +++ b/include/linux/usb/phy_companion.h @@ -3,18 +3,8 @@ * phy-companion.h -- phy companion to indicate the comparator part of PHY * * Copyright (C) 2012 Texas Instruments Incorporated - https://www.ti.com - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. * * Author: Kishon Vijay Abraham I - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef __DRIVERS_PHY_COMPANION_H diff --git a/include/linux/usb/r8a66597.h b/include/linux/usb/r8a66597.h index c0753d026bbf..f0fa7ddadbaa 100644 --- a/include/linux/usb/r8a66597.h +++ b/include/linux/usb/r8a66597.h @@ -5,20 +5,6 @@ * Copyright (C) 2009 Renesas Solutions Corp. * * Author : Yoshihiro Shimoda - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * */ #ifndef __LINUX_USB_R8A66597_H diff --git a/include/linux/usb/rndis_host.h b/include/linux/usb/rndis_host.h index cc42db51bbba..489cfb1d00f6 100644 --- a/include/linux/usb/rndis_host.h +++ b/include/linux/usb/rndis_host.h @@ -2,20 +2,6 @@ /* * Host Side support for RNDIS Networking Links * Copyright (C) 2005 by David Brownell - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_RNDIS_HOST_H diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h index 16ea5a4cc586..8ea319f89e1f 100644 --- a/include/linux/usb/serial.h +++ b/include/linux/usb/serial.h @@ -4,11 +4,6 @@ * * Copyright (C) 1999 - 2012 * Greg Kroah-Hartman (greg@kroah.com) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * */ #ifndef __LINUX_USB_SERIAL_H diff --git a/include/linux/usb/storage.h b/include/linux/usb/storage.h index e0240f864548..2827ce72e502 100644 --- a/include/linux/usb/storage.h +++ b/include/linux/usb/storage.h @@ -9,8 +9,6 @@ * * This file contains definitions taken from the * USB Mass Storage Class Specification Overview - * - * Distributed under the terms of the GNU GPL, version two. */ /* Storage subclass codes */ diff --git a/drivers/usb/typec/tcpm/tcpci.h b/include/linux/usb/tcpci.h similarity index 99% rename from drivers/usb/typec/tcpm/tcpci.h rename to include/linux/usb/tcpci.h index b2edd45f13c6..20c0bedb8ec8 100644 --- a/drivers/usb/typec/tcpm/tcpci.h +++ b/include/linux/usb/tcpci.h @@ -9,6 +9,7 @@ #define __LINUX_USB_TCPCI_H #include +#include #define TCPC_VENDOR_ID 0x0 #define TCPC_PRODUCT_ID 0x2 diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index d3e65eb9e16f..46e73584b6e6 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -1,16 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2010 Google, Inc. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef __TEGRA_USB_PHY_H diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index fdf737d48b3b..7751bedcae5d 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -22,6 +22,8 @@ struct typec_altmode_ops; struct fwnode_handle; struct device; +struct usb_power_delivery; + enum typec_port_type { TYPEC_PORT_SRC, TYPEC_PORT_SNK, @@ -52,6 +54,16 @@ enum typec_role { TYPEC_SOURCE, }; +static inline int is_sink(enum typec_role role) +{ + return role == TYPEC_SINK; +} + +static inline int is_source(enum typec_role role) +{ + return role == TYPEC_SOURCE; +} + enum typec_pwr_opmode { TYPEC_PWR_MODE_USB, TYPEC_PWR_MODE_1_5A, @@ -213,6 +225,8 @@ struct typec_partner_desc { * @pr_set: Set Power Role * @vconn_set: Source VCONN * @port_type_set: Set port type + * @pd_get: Get available USB Power Delivery Capabilities. + * @pd_set: Set USB Power Delivery Capabilities. */ struct typec_operations { int (*try_role)(struct typec_port *port, int role); @@ -221,6 +235,8 @@ struct typec_operations { int (*vconn_set)(struct typec_port *port, enum typec_role role); int (*port_type_set)(struct typec_port *port, enum typec_port_type type); + struct usb_power_delivery **(*pd_get)(struct typec_port *port); + int (*pd_set)(struct typec_port *port, struct usb_power_delivery *pd); }; enum usb_pd_svdm_ver { @@ -240,6 +256,7 @@ enum usb_pd_svdm_ver { * @accessory: Supported Accessory Modes * @fwnode: Optional fwnode of the port * @driver_data: Private pointer for driver specific info + * @pd: Optional USB Power Delivery Support * @ops: Port operations vector * * Static capabilities of a single USB Type-C port. @@ -257,6 +274,8 @@ struct typec_capability { struct fwnode_handle *fwnode; void *driver_data; + struct usb_power_delivery *pd; + const struct typec_operations *ops; }; @@ -308,4 +327,8 @@ void typec_partner_set_svdm_version(struct typec_partner *partner, enum usb_pd_svdm_ver svdm_version); int typec_get_negotiated_svdm_version(struct typec_port *port); +int typec_port_set_usb_power_delivery(struct typec_port *port, struct usb_power_delivery *pd); +int typec_partner_set_usb_power_delivery(struct typec_partner *partner, + struct usb_power_delivery *pd); + #endif /* __LINUX_USB_TYPEC_H */ diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 65933cbe9129..350d49012659 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -124,7 +124,7 @@ struct typec_altmode *typec_match_altmode(struct typec_altmode **altmodes, /** * typec_altmode_get_orientation - Get cable plug orientation - * altmode: Handle to the alternate mode + * @altmode: Handle to the alternate mode */ static inline enum typec_orientation typec_altmode_get_orientation(struct typec_altmode *altmode) diff --git a/include/linux/usb/typec_mux.h b/include/linux/usb/typec_mux.h index ee57781dcf28..9292f0e07846 100644 --- a/include/linux/usb/typec_mux.h +++ b/include/linux/usb/typec_mux.h @@ -58,17 +58,13 @@ struct typec_mux_desc { void *drvdata; }; +#if IS_ENABLED(CONFIG_TYPEC) + struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, const struct typec_altmode_desc *desc); void typec_mux_put(struct typec_mux *mux); int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state); -static inline struct typec_mux * -typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) -{ - return fwnode_typec_mux_get(dev_fwnode(dev), desc); -} - struct typec_mux_dev * typec_mux_register(struct device *parent, const struct typec_mux_desc *desc); void typec_mux_unregister(struct typec_mux_dev *mux); @@ -76,4 +72,40 @@ void typec_mux_unregister(struct typec_mux_dev *mux); void typec_mux_set_drvdata(struct typec_mux_dev *mux, void *data); void *typec_mux_get_drvdata(struct typec_mux_dev *mux); +#else + +static inline struct typec_mux *fwnode_typec_mux_get(struct fwnode_handle *fwnode, + const struct typec_altmode_desc *desc) +{ + return NULL; +} + +static inline void typec_mux_put(struct typec_mux *mux) {} + +static inline int typec_mux_set(struct typec_mux *mux, struct typec_mux_state *state) +{ + return 0; +} + +static inline struct typec_mux_dev * +typec_mux_register(struct device *parent, const struct typec_mux_desc *desc) +{ + return ERR_PTR(-EOPNOTSUPP); +} +static inline void typec_mux_unregister(struct typec_mux_dev *mux) {} + +static inline void typec_mux_set_drvdata(struct typec_mux_dev *mux, void *data) {} +static inline void *typec_mux_get_drvdata(struct typec_mux_dev *mux) +{ + return ERR_PTR(-EOPNOTSUPP); +} + +#endif /* CONFIG_TYPEC */ + +static inline struct typec_mux * +typec_mux_get(struct device *dev, const struct typec_altmode_desc *desc) +{ + return fwnode_typec_mux_get(dev_fwnode(dev), desc); +} + #endif /* __USB_TYPEC_MUX */ diff --git a/include/linux/usb/typec_retimer.h b/include/linux/usb/typec_retimer.h new file mode 100644 index 000000000000..5e036b3360e2 --- /dev/null +++ b/include/linux/usb/typec_retimer.h @@ -0,0 +1,45 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +#ifndef __USB_TYPEC_RETIMER +#define __USB_TYPEC_RETIMER + +#include +#include + +struct device; +struct typec_retimer; +struct typec_altmode; +struct fwnode_handle; + +struct typec_retimer_state { + struct typec_altmode *alt; + unsigned long mode; + void *data; +}; + +typedef int (*typec_retimer_set_fn_t)(struct typec_retimer *retimer, + struct typec_retimer_state *state); + +struct typec_retimer_desc { + struct fwnode_handle *fwnode; + typec_retimer_set_fn_t set; + const char *name; + void *drvdata; +}; + +struct typec_retimer *fwnode_typec_retimer_get(struct fwnode_handle *fwnode); +void typec_retimer_put(struct typec_retimer *retimer); +int typec_retimer_set(struct typec_retimer *retimer, struct typec_retimer_state *state); + +static inline struct typec_retimer *typec_retimer_get(struct device *dev) +{ + return fwnode_typec_retimer_get(dev_fwnode(dev)); +} + +struct typec_retimer * +typec_retimer_register(struct device *parent, const struct typec_retimer_desc *desc); +void typec_retimer_unregister(struct typec_retimer *retimer); + +void *typec_retimer_get_drvdata(struct typec_retimer *retimer); + +#endif /* __USB_TYPEC_RETIMER */ diff --git a/include/linux/usb/ulpi.h b/include/linux/usb/ulpi.h index 36c2982780ad..5050f502c1ed 100644 --- a/include/linux/usb/ulpi.h +++ b/include/linux/usb/ulpi.h @@ -3,10 +3,6 @@ * ulpi.h -- ULPI defines and function prorotypes * * Copyright (C) 2010 Nokia Corporation - * - * This software is distributed under the terms of the GNU General - * Public License ("GPL") as published by the Free Software Foundation, - * version 2 of that License. */ #ifndef __LINUX_USB_ULPI_H diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h index 20020c1336d5..70a7e3cdb3c9 100644 --- a/include/linux/usb/usb338x.h +++ b/include/linux/usb/usb338x.h @@ -6,17 +6,6 @@ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com) * Copyright (C) 2003 David Brownell * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * */ #ifndef __LINUX_USB_USB338X_H diff --git a/include/linux/usb/usbnet.h b/include/linux/usb/usbnet.h index b42b72391a8d..9f08a584d707 100644 --- a/include/linux/usb/usbnet.h +++ b/include/linux/usb/usbnet.h @@ -4,20 +4,6 @@ * * Copyright (C) 2000-2005 by David Brownell * Copyright (C) 2003-2005 David Hollis - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #ifndef __LINUX_USB_USBNET_H diff --git a/include/linux/usb/xhci-dbgp.h b/include/linux/usb/xhci-dbgp.h index 01fe768873f9..171fd74b1cfc 100644 --- a/include/linux/usb/xhci-dbgp.h +++ b/include/linux/usb/xhci-dbgp.h @@ -5,10 +5,6 @@ * Copyright (C) 2016 Intel Corporation * * Author: Lu Baolu - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ #ifndef __LINUX_XHCI_DBGP_H diff --git a/include/linux/vfio_pci_core.h b/include/linux/vfio_pci_core.h index 23c176d4b073..d5d9e17f0156 100644 --- a/include/linux/vfio_pci_core.h +++ b/include/linux/vfio_pci_core.h @@ -206,15 +206,25 @@ static inline int vfio_pci_igd_init(struct vfio_pci_core_device *vdev) } #endif -#ifdef CONFIG_S390 +#ifdef CONFIG_VFIO_PCI_ZDEV_KVM extern int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, struct vfio_info_cap *caps); +int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev); +void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev); #else static inline int vfio_pci_info_zdev_add_caps(struct vfio_pci_core_device *vdev, struct vfio_info_cap *caps) { return -ENODEV; } + +static inline int vfio_pci_zdev_open_device(struct vfio_pci_core_device *vdev) +{ + return 0; +} + +static inline void vfio_pci_zdev_close_device(struct vfio_pci_core_device *vdev) +{} #endif /* Will be exported for vfio pci drivers usage */ diff --git a/include/linux/virtio_anchor.h b/include/linux/virtio_anchor.h new file mode 100644 index 000000000000..432e6c00b3ca --- /dev/null +++ b/include/linux/virtio_anchor.h @@ -0,0 +1,19 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_VIRTIO_ANCHOR_H +#define _LINUX_VIRTIO_ANCHOR_H + +#ifdef CONFIG_VIRTIO_ANCHOR +struct virtio_device; + +bool virtio_require_restricted_mem_acc(struct virtio_device *dev); +extern bool (*virtio_check_mem_acc_cb)(struct virtio_device *dev); + +static inline void virtio_set_mem_acc_cb(bool (*func)(struct virtio_device *)) +{ + virtio_check_mem_acc_cb = func; +} +#else +#define virtio_set_mem_acc_cb(func) do { } while (0) +#endif + +#endif /* _LINUX_VIRTIO_ANCHOR_H */ diff --git a/include/linux/wkup_m3_ipc.h b/include/linux/wkup_m3_ipc.h index 26d1eb058fa3..5e1b26f988e2 100644 --- a/include/linux/wkup_m3_ipc.h +++ b/include/linux/wkup_m3_ipc.h @@ -1,17 +1,9 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * TI Wakeup M3 for AMx3 SoCs Power Management Routines * * Copyright (C) 2015 Texas Instruments Incorporated - https://www.ti.com/ * Dave Gerlach - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef _LINUX_WKUP_M3_IPC_H diff --git a/include/media/i2c/adv7343.h b/include/media/i2c/adv7343.h index b8937035c5d3..d35d3e925795 100644 --- a/include/media/i2c/adv7343.h +++ b/include/media/i2c/adv7343.h @@ -1,16 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ADV7343 header file * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef ADV7343_H diff --git a/include/media/i2c/adv7393.h b/include/media/i2c/adv7393.h index b28edf351842..c73b36321d06 100644 --- a/include/media/i2c/adv7393.h +++ b/include/media/i2c/adv7393.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * ADV7393 header file * @@ -7,15 +8,6 @@ * Based on ADV7343 driver, * * Copyright (C) 2009 Texas Instruments Incorporated - http://www.ti.com/ - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed .as is. WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #ifndef ADV7393_H diff --git a/include/media/i2c/ov2659.h b/include/media/i2c/ov2659.h index 4216adc1ede2..c9ea318a8fc3 100644 --- a/include/media/i2c/ov2659.h +++ b/include/media/i2c/ov2659.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* * Omnivision OV2659 CMOS Image Sensor driver * @@ -5,19 +6,6 @@ * * Benoit Parrot * Lad, Prabhakar - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #ifndef OV2659_H diff --git a/include/rdma/ib_verbs.h b/include/rdma/ib_verbs.h index 9c6317cf80d5..7c2f76f34f6f 100644 --- a/include/rdma/ib_verbs.h +++ b/include/rdma/ib_verbs.h @@ -4603,7 +4603,7 @@ static inline enum rdma_ah_attr_type rdma_ah_find_type(struct ib_device *dev, /** * ib_lid_cpu16 - Return lid in 16bit CPU encoding. - * In the current implementation the only way to get + * In the current implementation the only way to * get the 32bit lid is from other sources for OPA. * For IB, lids will always be 16bits so cast the * value accordingly. diff --git a/include/rdma/rdma_cm.h b/include/rdma/rdma_cm.h index d989f030fae0..5b18e2e36ee6 100644 --- a/include/rdma/rdma_cm.h +++ b/include/rdma/rdma_cm.h @@ -108,6 +108,7 @@ struct rdma_cm_id { enum rdma_ucm_port_space ps; enum ib_qp_type qp_type; u32 port_num; + struct work_struct net_work; }; struct rdma_cm_id * diff --git a/include/scsi/libiscsi.h b/include/scsi/libiscsi.h index c0703cd20a99..654cc3918c94 100644 --- a/include/scsi/libiscsi.h +++ b/include/scsi/libiscsi.h @@ -135,9 +135,6 @@ struct iscsi_task { void *dd_data; /* driver/transport data */ }; -/* invalid scsi_task pointer */ -#define INVALID_SCSI_TASK (struct iscsi_task *)-1l - static inline int iscsi_task_has_unsol_data(struct iscsi_task *task) { return task->unsol_r2t.data_length > task->unsol_r2t.sent; @@ -213,6 +210,8 @@ struct iscsi_conn { struct list_head cmdqueue; /* data-path cmd queue */ struct list_head requeue; /* tasks needing another run */ struct work_struct xmitwork; /* per-conn. xmit workqueue */ + /* recv */ + struct work_struct recvwork; unsigned long flags; /* ISCSI_CONN_FLAGs */ /* negotiated params */ @@ -411,7 +410,7 @@ extern int iscsi_host_add(struct Scsi_Host *shost, struct device *pdev); extern struct Scsi_Host *iscsi_host_alloc(struct scsi_host_template *sht, int dd_data_size, bool xmit_can_sleep); -extern void iscsi_host_remove(struct Scsi_Host *shost); +extern void iscsi_host_remove(struct Scsi_Host *shost, bool is_shutdown); extern void iscsi_host_free(struct Scsi_Host *shost); extern int iscsi_target_alloc(struct scsi_target *starget); extern int iscsi_host_get_max_scsi_cmds(struct Scsi_Host *shost, @@ -452,8 +451,10 @@ extern int iscsi_conn_get_param(struct iscsi_cls_conn *cls_conn, extern int iscsi_conn_get_addr_param(struct sockaddr_storage *addr, enum iscsi_param param, char *buf); extern void iscsi_suspend_tx(struct iscsi_conn *conn); +extern void iscsi_suspend_rx(struct iscsi_conn *conn); extern void iscsi_suspend_queue(struct iscsi_conn *conn); -extern void iscsi_conn_queue_work(struct iscsi_conn *conn); +extern void iscsi_conn_queue_xmit(struct iscsi_conn *conn); +extern void iscsi_conn_queue_recv(struct iscsi_conn *conn); #define iscsi_conn_printk(prefix, _c, fmt, a...) \ iscsi_cls_conn_printk(prefix, ((struct iscsi_conn *)_c)->cls_conn, \ @@ -478,7 +479,7 @@ extern struct iscsi_task *iscsi_itt_to_task(struct iscsi_conn *, itt_t); extern void iscsi_requeue_task(struct iscsi_task *task); extern void iscsi_put_task(struct iscsi_task *task); extern void __iscsi_put_task(struct iscsi_task *task); -extern void __iscsi_get_task(struct iscsi_task *task); +extern bool iscsi_get_task(struct iscsi_task *task); extern void iscsi_complete_scsi_task(struct iscsi_task *task, uint32_t exp_cmdsn, uint32_t max_cmdsn); diff --git a/include/scsi/libsas.h b/include/scsi/libsas.h index ff04eb6d250b..2dbead74a2af 100644 --- a/include/scsi/libsas.h +++ b/include/scsi/libsas.h @@ -145,7 +145,7 @@ struct sata_device { struct ata_port *ap; struct ata_host *ata_host; - struct smp_resp rps_resp ____cacheline_aligned; /* report_phy_sata_resp */ + struct smp_rps_resp rps_resp ____cacheline_aligned; /* report_phy_sata_resp */ u8 fis[ATA_RESP_FIS_SIZE]; }; diff --git a/include/scsi/sas.h b/include/scsi/sas.h index acfc69fd72d0..71b749bed3b0 100644 --- a/include/scsi/sas.h +++ b/include/scsi/sas.h @@ -471,18 +471,6 @@ struct report_phy_sata_resp { __be32 crc; } __attribute__ ((packed)); -struct smp_resp { - u8 frame_type; - u8 function; - u8 result; - u8 reserved; - union { - struct report_general_resp rg; - struct discover_resp disc; - struct report_phy_sata_resp rps; - }; -} __attribute__ ((packed)); - #elif defined(__BIG_ENDIAN_BITFIELD) struct sas_identify_frame { /* Byte 0 */ @@ -704,20 +692,32 @@ struct report_phy_sata_resp { __be32 crc; } __attribute__ ((packed)); -struct smp_resp { - u8 frame_type; - u8 function; - u8 result; - u8 reserved; - union { - struct report_general_resp rg; - struct discover_resp disc; - struct report_phy_sata_resp rps; - }; -} __attribute__ ((packed)); - #else #error "Bitfield order not defined!" #endif +struct smp_rg_resp { + u8 frame_type; + u8 function; + u8 result; + u8 reserved; + struct report_general_resp rg; +} __attribute__ ((packed)); + +struct smp_disc_resp { + u8 frame_type; + u8 function; + u8 result; + u8 reserved; + struct discover_resp disc; +} __attribute__ ((packed)); + +struct smp_rps_resp { + u8 frame_type; + u8 function; + u8 result; + u8 reserved; + struct report_phy_sata_resp rps; +} __attribute__ ((packed)); + #endif /* _SAS_H_ */ diff --git a/include/scsi/scsi_transport_iscsi.h b/include/scsi/scsi_transport_iscsi.h index 9acb8422f680..cab52b0f11d0 100644 --- a/include/scsi/scsi_transport_iscsi.h +++ b/include/scsi/scsi_transport_iscsi.h @@ -162,7 +162,7 @@ struct iscsi_transport { * transport registration upcalls */ extern struct scsi_transport_template *iscsi_register_transport(struct iscsi_transport *tt); -extern int iscsi_unregister_transport(struct iscsi_transport *tt); +extern void iscsi_unregister_transport(struct iscsi_transport *tt); /* * control plane upcalls @@ -442,6 +442,7 @@ extern struct iscsi_cls_session *iscsi_create_session(struct Scsi_Host *shost, struct iscsi_transport *t, int dd_size, unsigned int target_id); +extern void iscsi_force_destroy_session(struct iscsi_cls_session *session); extern void iscsi_remove_session(struct iscsi_cls_session *session); extern void iscsi_free_session(struct iscsi_cls_session *session); extern struct iscsi_cls_conn *iscsi_alloc_conn(struct iscsi_cls_session *sess, diff --git a/include/target/iscsi/iscsi_target_core.h b/include/target/iscsi/iscsi_target_core.h index 8e68ace428d9..94d06ddfd80a 100644 --- a/include/target/iscsi/iscsi_target_core.h +++ b/include/target/iscsi/iscsi_target_core.h @@ -26,6 +26,7 @@ struct sock; #define ISCSI_RX_THREAD_NAME "iscsi_trx" #define ISCSI_TX_THREAD_NAME "iscsi_ttx" #define ISCSI_IQN_LEN 224 +#define NA_AUTHENTICATION_INHERITED -1 /* struct iscsi_node_attrib sanity values */ #define NA_DATAOUT_TIMEOUT 3 @@ -715,6 +716,7 @@ struct iscsi_login { } ____cacheline_aligned; struct iscsi_node_attrib { + s32 authentication; u32 dataout_timeout; u32 dataout_timeout_retries; u32 default_erl; @@ -758,6 +760,12 @@ struct iscsi_node_acl { struct iscsi_node_stat_grps node_stat_grps; }; +static inline struct iscsi_node_acl * +to_iscsi_nacl(struct se_node_acl *se_nacl) +{ + return container_of(se_nacl, struct iscsi_node_acl, se_node_acl); +} + struct iscsi_tpg_attrib { u32 authentication; u32 login_timeout; @@ -839,6 +847,12 @@ struct iscsi_portal_group { struct list_head tpg_list; } ____cacheline_aligned; +static inline struct iscsi_portal_group * +to_iscsi_tpg(struct se_portal_group *se_tpg) +{ + return container_of(se_tpg, struct iscsi_portal_group, tpg_se_tpg); +} + struct iscsi_wwn_stat_grps { struct config_group iscsi_stat_group; struct config_group iscsi_instance_group; diff --git a/include/target/target_core_backend.h b/include/target/target_core_backend.h index 773963a1e0b5..a3c193df25b3 100644 --- a/include/target/target_core_backend.h +++ b/include/target/target_core_backend.h @@ -37,6 +37,7 @@ struct target_backend_ops { struct se_dev_plug *(*plug_device)(struct se_device *se_dev); void (*unplug_device)(struct se_dev_plug *se_plug); + bool (*configure_unmap)(struct se_device *se_dev); ssize_t (*set_configfs_dev_params)(struct se_device *, const char *, ssize_t); ssize_t (*show_configfs_dev_params)(struct se_device *, char *); diff --git a/include/trace/events/scsi.h b/include/trace/events/scsi.h index 370ade0d4093..a2c7befd451a 100644 --- a/include/trace/events/scsi.h +++ b/include/trace/events/scsi.h @@ -166,6 +166,8 @@ TRACE_EVENT(scsi_dispatch_cmd_start, __field( unsigned int, lun ) __field( unsigned int, opcode ) __field( unsigned int, cmd_len ) + __field( int, driver_tag) + __field( int, scheduler_tag) __field( unsigned int, data_sglen ) __field( unsigned int, prot_sglen ) __field( unsigned char, prot_op ) @@ -179,6 +181,8 @@ TRACE_EVENT(scsi_dispatch_cmd_start, __entry->lun = cmd->device->lun; __entry->opcode = cmd->cmnd[0]; __entry->cmd_len = cmd->cmd_len; + __entry->driver_tag = scsi_cmd_to_rq(cmd)->tag; + __entry->scheduler_tag = scsi_cmd_to_rq(cmd)->internal_tag; __entry->data_sglen = scsi_sg_count(cmd); __entry->prot_sglen = scsi_prot_sg_count(cmd); __entry->prot_op = scsi_get_prot_op(cmd); @@ -186,11 +190,11 @@ TRACE_EVENT(scsi_dispatch_cmd_start, ), TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u prot_sgl=%u" \ - " prot_op=%s cmnd=(%s %s raw=%s)", + " prot_op=%s driver_tag=%d scheduler_tag=%d cmnd=(%s %s raw=%s)", __entry->host_no, __entry->channel, __entry->id, __entry->lun, __entry->data_sglen, __entry->prot_sglen, - show_prot_op_name(__entry->prot_op), - show_opcode_name(__entry->opcode), + show_prot_op_name(__entry->prot_op), __entry->driver_tag, + __entry->scheduler_tag, show_opcode_name(__entry->opcode), __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len)) ); @@ -209,6 +213,8 @@ TRACE_EVENT(scsi_dispatch_cmd_error, __field( int, rtn ) __field( unsigned int, opcode ) __field( unsigned int, cmd_len ) + __field( int, driver_tag) + __field( int, scheduler_tag) __field( unsigned int, data_sglen ) __field( unsigned int, prot_sglen ) __field( unsigned char, prot_op ) @@ -223,6 +229,8 @@ TRACE_EVENT(scsi_dispatch_cmd_error, __entry->rtn = rtn; __entry->opcode = cmd->cmnd[0]; __entry->cmd_len = cmd->cmd_len; + __entry->driver_tag = scsi_cmd_to_rq(cmd)->tag; + __entry->scheduler_tag = scsi_cmd_to_rq(cmd)->internal_tag; __entry->data_sglen = scsi_sg_count(cmd); __entry->prot_sglen = scsi_prot_sg_count(cmd); __entry->prot_op = scsi_get_prot_op(cmd); @@ -230,11 +238,12 @@ TRACE_EVENT(scsi_dispatch_cmd_error, ), TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u prot_sgl=%u" \ - " prot_op=%s cmnd=(%s %s raw=%s) rtn=%d", + " prot_op=%s driver_tag=%d scheduler_tag=%d cmnd=(%s %s raw=%s)" \ + " rtn=%d", __entry->host_no, __entry->channel, __entry->id, __entry->lun, __entry->data_sglen, __entry->prot_sglen, - show_prot_op_name(__entry->prot_op), - show_opcode_name(__entry->opcode), + show_prot_op_name(__entry->prot_op), __entry->driver_tag, + __entry->scheduler_tag, show_opcode_name(__entry->opcode), __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len), __entry->rtn) @@ -254,6 +263,8 @@ DECLARE_EVENT_CLASS(scsi_cmd_done_timeout_template, __field( int, result ) __field( unsigned int, opcode ) __field( unsigned int, cmd_len ) + __field( int, driver_tag) + __field( int, scheduler_tag) __field( unsigned int, data_sglen ) __field( unsigned int, prot_sglen ) __field( unsigned char, prot_op ) @@ -268,19 +279,21 @@ DECLARE_EVENT_CLASS(scsi_cmd_done_timeout_template, __entry->result = cmd->result; __entry->opcode = cmd->cmnd[0]; __entry->cmd_len = cmd->cmd_len; + __entry->driver_tag = scsi_cmd_to_rq(cmd)->tag; + __entry->scheduler_tag = scsi_cmd_to_rq(cmd)->internal_tag; __entry->data_sglen = scsi_sg_count(cmd); __entry->prot_sglen = scsi_prot_sg_count(cmd); __entry->prot_op = scsi_get_prot_op(cmd); memcpy(__get_dynamic_array(cmnd), cmd->cmnd, cmd->cmd_len); ), - TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u " \ - "prot_sgl=%u prot_op=%s cmnd=(%s %s raw=%s) result=(driver=" \ - "%s host=%s message=%s status=%s)", + TP_printk("host_no=%u channel=%u id=%u lun=%u data_sgl=%u prot_sgl=%u " \ + "prot_op=%s driver_tag=%d scheduler_tag=%d cmnd=(%s %s raw=%s) " \ + "result=(driver=%s host=%s message=%s status=%s)", __entry->host_no, __entry->channel, __entry->id, __entry->lun, __entry->data_sglen, __entry->prot_sglen, - show_prot_op_name(__entry->prot_op), - show_opcode_name(__entry->opcode), + show_prot_op_name(__entry->prot_op), __entry->driver_tag, + __entry->scheduler_tag, show_opcode_name(__entry->opcode), __parse_cdb(__get_dynamic_array(cmnd), __entry->cmd_len), __print_hex(__get_dynamic_array(cmnd), __entry->cmd_len), "DRIVER_OK", diff --git a/include/trace/hooks/ufshcd.h b/include/trace/hooks/ufshcd.h index bf4ba522e665..7f5c54574c0e 100644 --- a/include/trace/hooks/ufshcd.h +++ b/include/trace/hooks/ufshcd.h @@ -37,7 +37,8 @@ DECLARE_HOOK(android_vh_ufs_compl_command, struct uic_command; DECLARE_HOOK(android_vh_ufs_send_uic_command, - TP_PROTO(struct ufs_hba *hba, struct uic_command *ucmd, int str_t), + TP_PROTO(struct ufs_hba *hba, const struct uic_command *ucmd, + int str_t), TP_ARGS(hba, ucmd, str_t)); DECLARE_HOOK(android_vh_ufs_send_tm_command, diff --git a/include/uapi/linux/idxd.h b/include/uapi/linux/idxd.h index bce7c43657d5..095299c75828 100644 --- a/include/uapi/linux/idxd.h +++ b/include/uapi/linux/idxd.h @@ -89,14 +89,14 @@ enum iax_opcode { IAX_OPCODE_CRC64, IAX_OPCODE_ZERO_DECOMP_32 = 0x48, IAX_OPCODE_ZERO_DECOMP_16, - IAX_OPCODE_DECOMP_32 = 0x4c, - IAX_OPCODE_DECOMP_16, + IAX_OPCODE_ZERO_COMP_32 = 0x4c, + IAX_OPCODE_ZERO_COMP_16, IAX_OPCODE_SCAN = 0x50, IAX_OPCODE_SET_MEMBER, IAX_OPCODE_EXTRACT, IAX_OPCODE_SELECT, IAX_OPCODE_RLE_BURST, - IAX_OPCDE_FIND_UNIQUE, + IAX_OPCODE_FIND_UNIQUE, IAX_OPCODE_EXPAND, }; diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index cb6e3846d27b..eed0315a77a6 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -270,6 +270,8 @@ struct kvm_xen_exit { #define KVM_EXIT_X86_BUS_LOCK 33 #define KVM_EXIT_XEN 34 #define KVM_EXIT_RISCV_SBI 35 +#define KVM_EXIT_RISCV_CSR 36 +#define KVM_EXIT_NOTIFY 37 /* For KVM_EXIT_INTERNAL_ERROR */ /* Emulate instruction failed. */ @@ -496,6 +498,18 @@ struct kvm_run { unsigned long args[6]; unsigned long ret[2]; } riscv_sbi; + /* KVM_EXIT_RISCV_CSR */ + struct { + unsigned long csr_num; + unsigned long new_value; + unsigned long write_mask; + unsigned long ret_value; + } riscv_csr; + /* KVM_EXIT_NOTIFY */ + struct { +#define KVM_NOTIFY_CONTEXT_INVALID (1 << 0) + __u32 flags; + } notify; /* Fix the size of the union. */ char padding[256]; }; @@ -1157,6 +1171,12 @@ struct kvm_ppc_resize_hpt { #define KVM_CAP_VM_TSC_CONTROL 214 #define KVM_CAP_SYSTEM_EVENT_DATA 215 #define KVM_CAP_ARM_SYSTEM_SUSPEND 216 +#define KVM_CAP_S390_PROTECTED_DUMP 217 +#define KVM_CAP_X86_TRIPLE_FAULT_EVENT 218 +#define KVM_CAP_X86_NOTIFY_VMEXIT 219 +#define KVM_CAP_VM_DISABLE_NX_HUGE_PAGES 220 +#define KVM_CAP_S390_ZPCI_OP 221 +#define KVM_CAP_S390_CPU_TOPOLOGY 222 #ifdef KVM_CAP_IRQ_ROUTING @@ -1660,6 +1680,55 @@ struct kvm_s390_pv_unp { __u64 tweak; }; +enum pv_cmd_dmp_id { + KVM_PV_DUMP_INIT, + KVM_PV_DUMP_CONFIG_STOR_STATE, + KVM_PV_DUMP_COMPLETE, + KVM_PV_DUMP_CPU, +}; + +struct kvm_s390_pv_dmp { + __u64 subcmd; + __u64 buff_addr; + __u64 buff_len; + __u64 gaddr; /* For dump storage state */ + __u64 reserved[4]; +}; + +enum pv_cmd_info_id { + KVM_PV_INFO_VM, + KVM_PV_INFO_DUMP, +}; + +struct kvm_s390_pv_info_dump { + __u64 dump_cpu_buffer_len; + __u64 dump_config_mem_buffer_per_1m; + __u64 dump_config_finalize_len; +}; + +struct kvm_s390_pv_info_vm { + __u64 inst_calls_list[4]; + __u64 max_cpus; + __u64 max_guests; + __u64 max_guest_addr; + __u64 feature_indication; +}; + +struct kvm_s390_pv_info_header { + __u32 id; + __u32 len_max; + __u32 len_written; + __u32 reserved; +}; + +struct kvm_s390_pv_info { + struct kvm_s390_pv_info_header header; + union { + struct kvm_s390_pv_info_dump dump; + struct kvm_s390_pv_info_vm vm; + }; +}; + enum pv_cmd_id { KVM_PV_ENABLE, KVM_PV_DISABLE, @@ -1668,6 +1737,8 @@ enum pv_cmd_id { KVM_PV_VERIFY, KVM_PV_PREP_RESET, KVM_PV_UNSHARE_ALL, + KVM_PV_INFO, + KVM_PV_DUMP, }; struct kvm_pv_cmd { @@ -2119,4 +2190,41 @@ struct kvm_stats_desc { /* Available with KVM_CAP_XSAVE2 */ #define KVM_GET_XSAVE2 _IOR(KVMIO, 0xcf, struct kvm_xsave) +/* Available with KVM_CAP_S390_PROTECTED_DUMP */ +#define KVM_S390_PV_CPU_COMMAND _IOWR(KVMIO, 0xd0, struct kvm_pv_cmd) + +/* Available with KVM_CAP_X86_NOTIFY_VMEXIT */ +#define KVM_X86_NOTIFY_VMEXIT_ENABLED (1ULL << 0) +#define KVM_X86_NOTIFY_VMEXIT_USER (1ULL << 1) + +/* Available with KVM_CAP_S390_ZPCI_OP */ +#define KVM_S390_ZPCI_OP _IOW(KVMIO, 0xd1, struct kvm_s390_zpci_op) + +struct kvm_s390_zpci_op { + /* in */ + __u32 fh; /* target device */ + __u8 op; /* operation to perform */ + __u8 pad[3]; + union { + /* for KVM_S390_ZPCIOP_REG_AEN */ + struct { + __u64 ibv; /* Guest addr of interrupt bit vector */ + __u64 sb; /* Guest addr of summary bit */ + __u32 flags; + __u32 noi; /* Number of interrupts */ + __u8 isc; /* Guest interrupt subclass */ + __u8 sbo; /* Offset of guest summary bit vector */ + __u16 pad; + } reg_aen; + __u64 reserved[8]; + } u; +}; + +/* types for kvm_s390_zpci_op->op */ +#define KVM_S390_ZPCIOP_REG_AEN 0 +#define KVM_S390_ZPCIOP_DEREG_AEN 1 + +/* flags for kvm_s390_zpci_op->u.reg_aen.flags */ +#define KVM_S390_ZPCIOP_REGAEN_HOST (1 << 0) + #endif /* __LINUX_KVM_H */ diff --git a/include/uapi/linux/netfilter/xt_IDLETIMER.h b/include/uapi/linux/netfilter/xt_IDLETIMER.h index 07ae4e189044..104ac3250edd 100644 --- a/include/uapi/linux/netfilter/xt_IDLETIMER.h +++ b/include/uapi/linux/netfilter/xt_IDLETIMER.h @@ -1,6 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only WITH Linux-syscall-note */ /* - * linux/include/linux/netfilter/xt_IDLETIMER.h - * * Header file for Xtables timer target module. * * Copyright (C) 2004, 2010 Nokia Corporation @@ -10,20 +9,6 @@ * by Luciano Coelho * * Contact: Luciano Coelho - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * version 2 as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA */ #ifndef _XT_IDLETIMER_H diff --git a/include/uapi/linux/ublk_cmd.h b/include/uapi/linux/ublk_cmd.h index ca33092354ab..677edaab2b66 100644 --- a/include/uapi/linux/ublk_cmd.h +++ b/include/uapi/linux/ublk_cmd.h @@ -15,6 +15,8 @@ #define UBLK_CMD_DEL_DEV 0x05 #define UBLK_CMD_START_DEV 0x06 #define UBLK_CMD_STOP_DEV 0x07 +#define UBLK_CMD_SET_PARAMS 0x08 +#define UBLK_CMD_GET_PARAMS 0x09 /* * IO commands, issued by ublk server, and handled by ublk driver. @@ -28,12 +30,21 @@ * this IO request, request's handling result is committed to ublk * driver, meantime FETCH_REQ is piggyback, and FETCH_REQ has to be * handled before completing io request. + * + * NEED_GET_DATA: only used for write requests to set io addr and copy data + * When NEED_GET_DATA is set, ublksrv has to issue UBLK_IO_NEED_GET_DATA + * command after ublk driver returns UBLK_IO_RES_NEED_GET_DATA. + * + * It is only used if ublksrv set UBLK_F_NEED_GET_DATA flag + * while starting a ublk device. */ #define UBLK_IO_FETCH_REQ 0x20 #define UBLK_IO_COMMIT_AND_FETCH_REQ 0x21 +#define UBLK_IO_NEED_GET_DATA 0x22 /* only ABORT means that no re-fetch */ #define UBLK_IO_RES_OK 0 +#define UBLK_IO_RES_NEED_GET_DATA 1 #define UBLK_IO_RES_ABORT (-ENODEV) #define UBLKSRV_CMD_BUF_OFFSET 0 @@ -54,6 +65,15 @@ */ #define UBLK_F_URING_CMD_COMP_IN_TASK (1ULL << 1) +/* + * User should issue io cmd again for write requests to + * set io buffer address and copy data from bio vectors + * to the userspace io buffer. + * + * In this mode, task_work is not used. + */ +#define UBLK_F_NEED_GET_DATA (1UL << 2) + /* device state */ #define UBLK_S_DEV_DEAD 0 #define UBLK_S_DEV_LIVE 1 @@ -78,22 +98,23 @@ struct ublksrv_ctrl_cmd { struct ublksrv_ctrl_dev_info { __u16 nr_hw_queues; __u16 queue_depth; - __u16 block_size; __u16 state; + __u16 pad0; - __u32 rq_max_blocks; + __u32 max_io_buf_bytes; __u32 dev_id; - __u64 dev_blocks; - __s32 ublksrv_pid; - __s32 reserved0; + __u32 pad1; + __u64 flags; - __u64 flags_reserved; /* For ublksrv internal use, invisible to ublk driver */ __u64 ublksrv_flags; - __u64 reserved1[9]; + + __u64 reserved0; + __u64 reserved1; + __u64 reserved2; }; #define UBLK_IO_OP_READ 0 @@ -158,4 +179,49 @@ struct ublksrv_io_cmd { __u64 addr; }; +struct ublk_param_basic { +#define UBLK_ATTR_READ_ONLY (1 << 0) +#define UBLK_ATTR_ROTATIONAL (1 << 1) +#define UBLK_ATTR_VOLATILE_CACHE (1 << 2) +#define UBLK_ATTR_FUA (1 << 3) + __u32 attrs; + __u8 logical_bs_shift; + __u8 physical_bs_shift; + __u8 io_opt_shift; + __u8 io_min_shift; + + __u32 max_sectors; + __u32 chunk_sectors; + + __u64 dev_sectors; + __u64 virt_boundary_mask; +}; + +struct ublk_param_discard { + __u32 discard_alignment; + + __u32 discard_granularity; + __u32 max_discard_sectors; + + __u32 max_write_zeroes_sectors; + __u16 max_discard_segments; + __u16 reserved0; +}; + +struct ublk_params { + /* + * Total length of parameters, userspace has to set 'len' for both + * SET_PARAMS and GET_PARAMS command, and driver may update len + * if two sides use different version of 'ublk_params', same with + * 'types' fields. + */ + __u32 len; +#define UBLK_PARAM_TYPE_BASIC (1 << 0) +#define UBLK_PARAM_TYPE_DISCARD (1 << 1) + __u32 types; /* types of parameter included */ + + struct ublk_param_basic basic; + struct ublk_param_discard discard; +}; + #endif diff --git a/include/uapi/linux/usb/cdc.h b/include/uapi/linux/usb/cdc.h index acf3852bb676..1924cf665448 100644 --- a/include/uapi/linux/usb/cdc.h +++ b/include/uapi/linux/usb/cdc.h @@ -271,6 +271,10 @@ struct usb_cdc_line_coding { __u8 bDataBits; } __attribute__ ((packed)); +/* Control Signal Bitmap Values from 6.2.14 SetControlLineState */ +#define USB_CDC_CTRL_DTR (1 << 0) +#define USB_CDC_CTRL_RTS (1 << 1) + /* table 62; bits in multicast filter */ #define USB_CDC_PACKET_TYPE_PROMISCUOUS (1 << 0) #define USB_CDC_PACKET_TYPE_ALL_MULTICAST (1 << 1) /* no filter */ @@ -302,6 +306,15 @@ struct usb_cdc_notification { __le16 wLength; } __attribute__ ((packed)); +/* UART State Bitmap Values from 6.3.5 SerialState */ +#define USB_CDC_SERIAL_STATE_DCD (1 << 0) +#define USB_CDC_SERIAL_STATE_DSR (1 << 1) +#define USB_CDC_SERIAL_STATE_BREAK (1 << 2) +#define USB_CDC_SERIAL_STATE_RING_SIGNAL (1 << 3) +#define USB_CDC_SERIAL_STATE_FRAMING (1 << 4) +#define USB_CDC_SERIAL_STATE_PARITY (1 << 5) +#define USB_CDC_SERIAL_STATE_OVERRUN (1 << 6) + struct usb_cdc_speed_change { __le32 DLBitRRate; /* contains the downlink bit rate (IN pipe) */ __le32 ULBitRate; /* contains the uplink bit rate (OUT pipe) */ diff --git a/include/uapi/linux/vfio_zdev.h b/include/uapi/linux/vfio_zdev.h index b4309397b6b2..77f2aff1f27e 100644 --- a/include/uapi/linux/vfio_zdev.h +++ b/include/uapi/linux/vfio_zdev.h @@ -29,6 +29,9 @@ struct vfio_device_info_cap_zpci_base { __u16 fmb_length; /* Measurement Block Length (in bytes) */ __u8 pft; /* PCI Function Type */ __u8 gid; /* PCI function group ID */ + /* End of version 1 */ + __u32 fh; /* PCI function handle */ + /* End of version 2 */ }; /** @@ -47,6 +50,10 @@ struct vfio_device_info_cap_zpci_group { __u16 noi; /* Maximum number of MSIs */ __u16 maxstbl; /* Maximum Store Block Length */ __u8 version; /* Supported PCI Version */ + /* End of version 1 */ + __u8 reserved; + __u16 imaxstbl; /* Maximum Interpreted Store Block Length */ + /* End of version 2 */ }; /** diff --git a/include/uapi/rdma/erdma-abi.h b/include/uapi/rdma/erdma-abi.h new file mode 100644 index 000000000000..b7a0222f978f --- /dev/null +++ b/include/uapi/rdma/erdma-abi.h @@ -0,0 +1,49 @@ +/* SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) */ +/* + * Copyright (c) 2020-2022, Alibaba Group. + */ + +#ifndef __ERDMA_USER_H__ +#define __ERDMA_USER_H__ + +#include + +#define ERDMA_ABI_VERSION 1 + +struct erdma_ureq_create_cq { + __aligned_u64 db_record_va; + __aligned_u64 qbuf_va; + __u32 qbuf_len; + __u32 rsvd0; +}; + +struct erdma_uresp_create_cq { + __u32 cq_id; + __u32 num_cqe; +}; + +struct erdma_ureq_create_qp { + __aligned_u64 db_record_va; + __aligned_u64 qbuf_va; + __u32 qbuf_len; + __u32 rsvd0; +}; + +struct erdma_uresp_create_qp { + __u32 qp_id; + __u32 num_sqe; + __u32 num_rqe; + __u32 rq_offset; +}; + +struct erdma_uresp_alloc_ctx { + __u32 dev_id; + __u32 pad; + __u32 sdb_type; + __u32 sdb_offset; + __aligned_u64 sdb; + __aligned_u64 rdb; + __aligned_u64 cdb; +}; + +#endif diff --git a/include/uapi/rdma/ib_user_ioctl_verbs.h b/include/uapi/rdma/ib_user_ioctl_verbs.h index 3072e5d6b692..7dd56210226f 100644 --- a/include/uapi/rdma/ib_user_ioctl_verbs.h +++ b/include/uapi/rdma/ib_user_ioctl_verbs.h @@ -250,6 +250,7 @@ enum rdma_driver_id { RDMA_DRIVER_QIB, RDMA_DRIVER_EFA, RDMA_DRIVER_SIW, + RDMA_DRIVER_ERDMA, }; enum ib_uverbs_gid_type { diff --git a/include/uapi/rdma/mlx5_user_ioctl_cmds.h b/include/uapi/rdma/mlx5_user_ioctl_cmds.h index e539c84d63f1..3bee490eb585 100644 --- a/include/uapi/rdma/mlx5_user_ioctl_cmds.h +++ b/include/uapi/rdma/mlx5_user_ioctl_cmds.h @@ -228,6 +228,7 @@ enum mlx5_ib_objects { MLX5_IB_OBJECT_VAR, MLX5_IB_OBJECT_PP, MLX5_IB_OBJECT_UAR, + MLX5_IB_OBJECT_STEERING_ANCHOR, }; enum mlx5_ib_flow_matcher_create_attrs { @@ -248,6 +249,22 @@ enum mlx5_ib_flow_matcher_methods { MLX5_IB_METHOD_FLOW_MATCHER_DESTROY, }; +enum mlx5_ib_flow_steering_anchor_create_attrs { + MLX5_IB_ATTR_STEERING_ANCHOR_CREATE_HANDLE = (1U << UVERBS_ID_NS_SHIFT), + MLX5_IB_ATTR_STEERING_ANCHOR_FT_TYPE, + MLX5_IB_ATTR_STEERING_ANCHOR_PRIORITY, + MLX5_IB_ATTR_STEERING_ANCHOR_FT_ID, +}; + +enum mlx5_ib_flow_steering_anchor_destroy_attrs { + MLX5_IB_ATTR_STEERING_ANCHOR_DESTROY_HANDLE = (1U << UVERBS_ID_NS_SHIFT), +}; + +enum mlx5_ib_steering_anchor_methods { + MLX5_IB_METHOD_STEERING_ANCHOR_CREATE = (1U << UVERBS_ID_NS_SHIFT), + MLX5_IB_METHOD_STEERING_ANCHOR_DESTROY, +}; + enum mlx5_ib_device_query_context_attrs { MLX5_IB_ATTR_QUERY_CONTEXT_RESP_UCTX = (1U << UVERBS_ID_NS_SHIFT), }; diff --git a/include/ufs/ufshcd.h b/include/ufs/ufshcd.h index ad9d35a282c3..0ea4717f1154 100644 --- a/include/ufs/ufshcd.h +++ b/include/ufs/ufshcd.h @@ -577,6 +577,19 @@ enum ufshcd_quirks { * support physical host configuration. */ UFSHCD_QUIRK_SKIP_PH_CONFIGURATION = 1 << 16, + + /* + * This quirk needs to be enabled if the host controller has + * 64-bit addressing supported capability but it doesn't work. + */ + UFSHCD_QUIRK_BROKEN_64BIT_ADDRESS = 1 << 17, + + /* + * This quirk needs to be enabled if the host controller has + * auto-hibernate capability but it's FASTAUTO only. + */ + UFSHCD_QUIRK_HIBERN_FASTAUTO = 1 << 18, + /* * This quirk needs to be enabled if the host controller supports inline * encryption, but it needs to initialize the crypto capabilities in a @@ -1111,6 +1124,7 @@ extern int ufshcd_dme_get_attr(struct ufs_hba *hba, u32 attr_sel, u32 *mib_val, u8 peer); extern int ufshcd_config_pwr_mode(struct ufs_hba *hba, struct ufs_pa_layer_attr *desired_pwr_mode); +extern int ufshcd_uic_change_pwr_mode(struct ufs_hba *hba, u8 mode); /* UIC command interfaces for DME primitives */ #define DME_LOCAL 0 @@ -1216,6 +1230,8 @@ void ufshcd_map_desc_id_to_length(struct ufs_hba *hba, enum desc_idn desc_id, u32 ufshcd_get_local_unipro_ver(struct ufs_hba *hba); +int ufshcd_get_vreg(struct device *dev, struct ufs_vreg *vreg); + int ufshcd_send_uic_cmd(struct ufs_hba *hba, struct uic_command *uic_cmd); int ufshcd_exec_raw_upiu_cmd(struct ufs_hba *hba, @@ -1247,14 +1263,14 @@ static inline int ufshcd_vops_phy_initialization(struct ufs_hba *hba) return 0; } -extern struct ufs_pm_lvl_states ufs_pm_lvl_states[]; +extern const struct ufs_pm_lvl_states ufs_pm_lvl_states[]; int ufshcd_dump_regs(struct ufs_hba *hba, size_t offset, size_t len, const char *prefix); int __ufshcd_write_ee_control(struct ufs_hba *hba, u32 ee_ctrl_mask); int ufshcd_write_ee_control(struct ufs_hba *hba); -int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, u16 *other_mask, - u16 set, u16 clr); +int ufshcd_update_ee_control(struct ufs_hba *hba, u16 *mask, + const u16 *other_mask, u16 set, u16 clr); #endif /* End of Header */ diff --git a/include/ufs/unipro.h b/include/ufs/unipro.h index 0521f887e3ac..6c553f98fe57 100644 --- a/include/ufs/unipro.h +++ b/include/ufs/unipro.h @@ -38,6 +38,18 @@ /* * M-RX Configuration Attributes */ +#define RX_HS_G1_SYNC_LENGTH_CAP 0x008B +#define RX_HS_G1_PREP_LENGTH_CAP 0x008C +#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F +#define RX_HIBERN8TIME_CAPABILITY 0x0092 +#define RX_HS_G2_SYNC_LENGTH_CAP 0x0094 +#define RX_HS_G3_SYNC_LENGTH_CAP 0x0095 +#define RX_HS_G2_PREP_LENGTH_CAP 0x0096 +#define RX_HS_G3_PREP_LENGTH_CAP 0x0097 +#define RX_ADV_GRANULARITY_CAP 0x0098 +#define RX_HIBERN8TIME_CAP 0x0092 +#define RX_ADV_HIBERN8TIME_CAP 0x0099 +#define RX_ADV_MIN_ACTIVATETIME_CAP 0x009A #define RX_MODE 0x00A1 #define RX_HSRATE_SERIES 0x00A2 #define RX_HSGEAR 0x00A3 @@ -47,32 +59,19 @@ #define RX_ENTER_HIBERN8 0x00A7 #define RX_BYPASS_8B10B_ENABLE 0x00A8 #define RX_TERMINATION_FORCE_ENABLE 0x00A9 -#define RX_MIN_ACTIVATETIME_CAPABILITY 0x008F -#define RX_HIBERN8TIME_CAPABILITY 0x0092 +#define RXCALCTRL 0x00B4 +#define RXSQCTRL 0x00B5 +#define CFGRXCDR8 0x00BA +#define CFGRXOVR8 0x00BD +#define CFGRXOVR6 0x00BF +#define RXDIRECTCTRL2 0x00C7 +#define CFGRXOVR4 0x00E9 #define RX_REFCLKFREQ 0x00EB #define RX_CFGCLKFREQVAL 0x00EC #define CFGWIDEINLN 0x00F0 -#define CFGRXCDR8 0x00BA #define ENARXDIRECTCFG4 0x00F2 -#define CFGRXOVR8 0x00BD -#define RXDIRECTCTRL2 0x00C7 #define ENARXDIRECTCFG3 0x00F3 -#define RXCALCTRL 0x00B4 #define ENARXDIRECTCFG2 0x00F4 -#define CFGRXOVR4 0x00E9 -#define RXSQCTRL 0x00B5 -#define CFGRXOVR6 0x00BF -#define RX_HS_G1_SYNC_LENGTH_CAP 0x008B -#define RX_HS_G1_PREP_LENGTH_CAP 0x008C -#define RX_HS_G2_SYNC_LENGTH_CAP 0x0094 -#define RX_HS_G3_SYNC_LENGTH_CAP 0x0095 -#define RX_HS_G2_PREP_LENGTH_CAP 0x0096 -#define RX_HS_G3_PREP_LENGTH_CAP 0x0097 -#define RX_ADV_GRANULARITY_CAP 0x0098 -#define RX_MIN_ACTIVATETIME_CAP 0x008F -#define RX_HIBERN8TIME_CAP 0x0092 -#define RX_ADV_HIBERN8TIME_CAP 0x0099 -#define RX_ADV_MIN_ACTIVATETIME_CAP 0x009A #define is_mphy_tx_attr(attr) (attr < RX_MODE) @@ -103,47 +102,50 @@ /* * PHY Adapter attributes */ -#define PA_ACTIVETXDATALANES 0x1560 -#define PA_ACTIVERXDATALANES 0x1580 -#define PA_TXTRAILINGCLOCKS 0x1564 #define PA_PHY_TYPE 0x1500 #define PA_AVAILTXDATALANES 0x1520 -#define PA_AVAILRXDATALANES 0x1540 -#define PA_MINRXTRAILINGCLOCKS 0x1543 -#define PA_TXPWRSTATUS 0x1567 -#define PA_RXPWRSTATUS 0x1582 -#define PA_TXFORCECLOCK 0x1562 -#define PA_TXPWRMODE 0x1563 -#define PA_LEGACYDPHYESCDL 0x1570 #define PA_MAXTXSPEEDFAST 0x1521 #define PA_MAXTXSPEEDSLOW 0x1522 #define PA_MAXRXSPEEDFAST 0x1541 #define PA_MAXRXSPEEDSLOW 0x1542 #define PA_TXLINKSTARTUPHS 0x1544 +#define PA_AVAILRXDATALANES 0x1540 +#define PA_MINRXTRAILINGCLOCKS 0x1543 #define PA_LOCAL_TX_LCC_ENABLE 0x155E +#define PA_ACTIVETXDATALANES 0x1560 +#define PA_CONNECTEDTXDATALANES 0x1561 +#define PA_TXFORCECLOCK 0x1562 +#define PA_TXPWRMODE 0x1563 +#define PA_TXTRAILINGCLOCKS 0x1564 #define PA_TXSPEEDFAST 0x1565 #define PA_TXSPEEDSLOW 0x1566 -#define PA_REMOTEVERINFO 0x15A0 +#define PA_TXPWRSTATUS 0x1567 #define PA_TXGEAR 0x1568 #define PA_TXTERMINATION 0x1569 #define PA_HSSERIES 0x156A +#define PA_LEGACYDPHYESCDL 0x1570 #define PA_PWRMODE 0x1571 +#define PA_ACTIVERXDATALANES 0x1580 +#define PA_CONNECTEDRXDATALANES 0x1581 +#define PA_RXPWRSTATUS 0x1582 #define PA_RXGEAR 0x1583 #define PA_RXTERMINATION 0x1584 #define PA_MAXRXPWMGEAR 0x1586 #define PA_MAXRXHSGEAR 0x1587 +#define PA_PACPREQTIMEOUT 0x1590 +#define PA_PACPREQEOBTIMEOUT 0x1591 +#define PA_REMOTEVERINFO 0x15A0 +#define PA_LOGICALLANEMAP 0x15A1 +#define PA_SLEEPNOCONFIGTIME 0x15A2 +#define PA_STALLNOCONFIGTIME 0x15A3 +#define PA_SAVECONFIGTIME 0x15A4 #define PA_RXHSUNTERMCAP 0x15A5 #define PA_RXLSTERMCAP 0x15A6 #define PA_GRANULARITY 0x15AA -#define PA_PACPREQTIMEOUT 0x1590 -#define PA_PACPREQEOBTIMEOUT 0x1591 #define PA_HIBERN8TIME 0x15A7 #define PA_LOCALVERINFO 0x15A9 #define PA_GRANULARITY 0x15AA #define PA_TACTIVATE 0x15A8 -#define PA_PACPFRAMECOUNT 0x15C0 -#define PA_PACPERRORCOUNT 0x15C1 -#define PA_PHYTESTCONTROL 0x15C2 #define PA_PWRMODEUSERDATA0 0x15B0 #define PA_PWRMODEUSERDATA1 0x15B1 #define PA_PWRMODEUSERDATA2 0x15B2 @@ -156,12 +158,9 @@ #define PA_PWRMODEUSERDATA9 0x15B9 #define PA_PWRMODEUSERDATA10 0x15BA #define PA_PWRMODEUSERDATA11 0x15BB -#define PA_CONNECTEDTXDATALANES 0x1561 -#define PA_CONNECTEDRXDATALANES 0x1581 -#define PA_LOGICALLANEMAP 0x15A1 -#define PA_SLEEPNOCONFIGTIME 0x15A2 -#define PA_STALLNOCONFIGTIME 0x15A3 -#define PA_SAVECONFIGTIME 0x15A4 +#define PA_PACPFRAMECOUNT 0x15C0 +#define PA_PACPERRORCOUNT 0x15C1 +#define PA_PHYTESTCONTROL 0x15C2 #define PA_TXHSADAPTTYPE 0x15D4 /* Adpat type for PA_TXHSADAPTTYPE attribute */ @@ -173,9 +172,9 @@ #define PA_HIBERN8_TIME_UNIT_US 100 /*Other attributes*/ +#define VS_POWERSTATE 0xD083 #define VS_MPHYCFGUPDT 0xD085 #define VS_DEBUGOMC 0xD09E -#define VS_POWERSTATE 0xD083 #define PA_GRANULARITY_MIN_VAL 1 #define PA_GRANULARITY_MAX_VAL 6 @@ -229,6 +228,7 @@ enum ufs_hs_gear_tag { UFS_HS_G2, /* HS Gear 2 */ UFS_HS_G3, /* HS Gear 3 */ UFS_HS_G4, /* HS Gear 4 */ + UFS_HS_G5 /* HS Gear 5 */ }; enum ufs_unipro_ver { @@ -246,27 +246,27 @@ enum ufs_unipro_ver { /* * Data Link Layer Attributes */ +#define DL_TXPREEMPTIONCAP 0x2000 +#define DL_TC0TXMAXSDUSIZE 0x2001 +#define DL_TC0RXINITCREDITVAL 0x2002 +#define DL_TC1TXMAXSDUSIZE 0x2003 +#define DL_TC1RXINITCREDITVAL 0x2004 +#define DL_TC0TXBUFFERSIZE 0x2005 +#define DL_TC1TXBUFFERSIZE 0x2006 #define DL_TC0TXFCTHRESHOLD 0x2040 #define DL_FC0PROTTIMEOUTVAL 0x2041 #define DL_TC0REPLAYTIMEOUTVAL 0x2042 #define DL_AFC0REQTIMEOUTVAL 0x2043 #define DL_AFC0CREDITTHRESHOLD 0x2044 #define DL_TC0OUTACKTHRESHOLD 0x2045 +#define DL_PEERTC0PRESENT 0x2046 +#define DL_PEERTC0RXINITCREVAL 0x2047 #define DL_TC1TXFCTHRESHOLD 0x2060 #define DL_FC1PROTTIMEOUTVAL 0x2061 #define DL_TC1REPLAYTIMEOUTVAL 0x2062 #define DL_AFC1REQTIMEOUTVAL 0x2063 #define DL_AFC1CREDITTHRESHOLD 0x2064 #define DL_TC1OUTACKTHRESHOLD 0x2065 -#define DL_TXPREEMPTIONCAP 0x2000 -#define DL_TC0TXMAXSDUSIZE 0x2001 -#define DL_TC0RXINITCREDITVAL 0x2002 -#define DL_TC0TXBUFFERSIZE 0x2005 -#define DL_PEERTC0PRESENT 0x2046 -#define DL_PEERTC0RXINITCREVAL 0x2047 -#define DL_TC1TXMAXSDUSIZE 0x2003 -#define DL_TC1RXINITCREDITVAL 0x2004 -#define DL_TC1TXBUFFERSIZE 0x2006 #define DL_PEERTC1PRESENT 0x2066 #define DL_PEERTC1RXINITCREVAL 0x2067 diff --git a/include/xen/xen-ops.h b/include/xen/xen-ops.h index 80546960f8b7..dae0f350c678 100644 --- a/include/xen/xen-ops.h +++ b/include/xen/xen-ops.h @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -217,6 +218,7 @@ static inline void xen_preemptible_hcall_end(void) { } #ifdef CONFIG_XEN_GRANT_DMA_OPS void xen_grant_setup_dma_ops(struct device *dev); bool xen_is_grant_dma_device(struct device *dev); +bool xen_virtio_mem_acc(struct virtio_device *dev); #else static inline void xen_grant_setup_dma_ops(struct device *dev) { @@ -225,6 +227,13 @@ static inline bool xen_is_grant_dma_device(struct device *dev) { return false; } + +struct virtio_device; + +static inline bool xen_virtio_mem_acc(struct virtio_device *dev) +{ + return false; +} #endif /* CONFIG_XEN_GRANT_DMA_OPS */ #endif /* INCLUDE_XEN_OPS_H */ diff --git a/include/xen/xen.h b/include/xen/xen.h index 0780a81e140d..a99bab817523 100644 --- a/include/xen/xen.h +++ b/include/xen/xen.h @@ -52,14 +52,6 @@ bool xen_biovec_phys_mergeable(const struct bio_vec *vec1, extern u64 xen_saved_max_mem_size; #endif -#include - -static inline void xen_set_restricted_virtio_memory_access(void) -{ - if (IS_ENABLED(CONFIG_XEN_VIRTIO) && xen_domain()) - platform_set(PLATFORM_VIRTIO_RESTRICTED_MEM_ACCESS); -} - #ifdef CONFIG_XEN_UNPOPULATED_ALLOC int xen_alloc_unpopulated_pages(unsigned int nr_pages, struct page **pages); void xen_free_unpopulated_pages(unsigned int nr_pages, struct page **pages); diff --git a/kernel/Makefile b/kernel/Makefile index 03736930dd6d..fe1b6341e0a4 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -7,7 +7,7 @@ obj-y = fork.o exec_domain.o panic.o \ cpu.o exit.o softirq.o resource.o \ sysctl.o capability.o ptrace.o user.o \ signal.o sys.o umh.o workqueue.o pid.o task_work.o \ - extable.o params.o platform-feature.o \ + extable.o params.o \ kthread.o sys_ni.o nsproxy.o \ notifier.o ksysfs.o cred.o reboot.o \ async.o range.o smpboot.o ucount.o regset.o diff --git a/kernel/platform-feature.c b/kernel/platform-feature.c deleted file mode 100644 index cb6a6c3e4fed..000000000000 --- a/kernel/platform-feature.c +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include -#include -#include -#include - -#define PLATFORM_FEAT_ARRAY_SZ BITS_TO_LONGS(PLATFORM_FEAT_N) -static unsigned long __read_mostly platform_features[PLATFORM_FEAT_ARRAY_SZ]; - -void platform_set(unsigned int feature) -{ - set_bit(feature, platform_features); -} -EXPORT_SYMBOL_GPL(platform_set); - -void platform_clear(unsigned int feature) -{ - clear_bit(feature, platform_features); -} -EXPORT_SYMBOL_GPL(platform_clear); - -bool platform_has(unsigned int feature) -{ - return test_bit(feature, platform_features); -} -EXPORT_SYMBOL_GPL(platform_has); diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 79a71eb96111..35cd8287642a 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -1560,7 +1560,7 @@ config DEBUG_KOBJECT_RELEASE help kobjects are reference counted objects. This means that their last reference count put is not predictable, and the kobject can - live on past the point at which a driver decides to drop it's + live on past the point at which a driver decides to drop its initial reference to the kobject gained on allocation. An example of this would be a struct device which has just been unregistered. diff --git a/lib/Makefile b/lib/Makefile index 67482f5ec0e8..441795bf5d33 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -46,7 +46,7 @@ obj-y += bcd.o sort.o parser.o debug_locks.o random32.o \ bust_spinlocks.o kasprintf.o bitmap.o scatterlist.o \ list_sort.o uuid.o iov_iter.o clz_ctz.o \ bsearch.o find_bit.o llist.o memweight.o kfifo.o \ - percpu-refcount.o rhashtable.o \ + percpu-refcount.o rhashtable.o base64.o \ once.o refcount.o usercopy.o errseq.o bucket_locks.o \ generic-radix-tree.o obj-$(CONFIG_STRING_SELFTEST) += test_string.o diff --git a/lib/base64.c b/lib/base64.c new file mode 100644 index 000000000000..b736a7a431c5 --- /dev/null +++ b/lib/base64.c @@ -0,0 +1,103 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * base64.c - RFC4648-compliant base64 encoding + * + * Copyright (c) 2020 Hannes Reinecke, SUSE + * + * Based on the base64url routines from fs/crypto/fname.c + * (which are using the URL-safe base64 encoding), + * modified to use the standard coding table from RFC4648 section 4. + */ + +#include +#include +#include +#include +#include + +static const char base64_table[65] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +/** + * base64_encode() - base64-encode some binary data + * @src: the binary data to encode + * @srclen: the length of @src in bytes + * @dst: (output) the base64-encoded string. Not NUL-terminated. + * + * Encodes data using base64 encoding, i.e. the "Base 64 Encoding" specified + * by RFC 4648, including the '='-padding. + * + * Return: the length of the resulting base64-encoded string in bytes. + */ +int base64_encode(const u8 *src, int srclen, char *dst) +{ + u32 ac = 0; + int bits = 0; + int i; + char *cp = dst; + + for (i = 0; i < srclen; i++) { + ac = (ac << 8) | src[i]; + bits += 8; + do { + bits -= 6; + *cp++ = base64_table[(ac >> bits) & 0x3f]; + } while (bits >= 6); + } + if (bits) { + *cp++ = base64_table[(ac << (6 - bits)) & 0x3f]; + bits -= 6; + } + while (bits < 0) { + *cp++ = '='; + bits += 2; + } + return cp - dst; +} +EXPORT_SYMBOL_GPL(base64_encode); + +/** + * base64_decode() - base64-decode a string + * @src: the string to decode. Doesn't need to be NUL-terminated. + * @srclen: the length of @src in bytes + * @dst: (output) the decoded binary data + * + * Decodes a string using base64 encoding, i.e. the "Base 64 Encoding" + * specified by RFC 4648, including the '='-padding. + * + * This implementation hasn't been optimized for performance. + * + * Return: the length of the resulting decoded binary data in bytes, + * or -1 if the string isn't a valid base64 string. + */ +int base64_decode(const char *src, int srclen, u8 *dst) +{ + u32 ac = 0; + int bits = 0; + int i; + u8 *bp = dst; + + for (i = 0; i < srclen; i++) { + const char *p = strchr(base64_table, src[i]); + + if (src[i] == '=') { + ac = (ac << 6); + bits += 6; + if (bits >= 8) + bits -= 8; + continue; + } + if (p == NULL || src[i] == 0) + return -1; + ac = (ac << 6) | (p - base64_table); + bits += 6; + if (bits >= 8) { + bits -= 8; + *bp++ = (u8)(ac >> bits); + } + } + if (ac & ((1 << bits) - 1)) + return -1; + return bp - dst; +} +EXPORT_SYMBOL_GPL(base64_decode); diff --git a/net/hsr/hsr_debugfs.c b/net/hsr/hsr_debugfs.c index fe6094e9a2db..de476a417631 100644 --- a/net/hsr/hsr_debugfs.c +++ b/net/hsr/hsr_debugfs.c @@ -1,18 +1,10 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * debugfs code for HSR & PRP * Copyright (C) 2019 Texas Instruments Incorporated * * Author(s): * Murali Karicheri - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. */ #include #include diff --git a/net/ipv4/ipconfig.c b/net/ipv4/ipconfig.c index f53a0f2453af..e90bc0aa85c7 100644 --- a/net/ipv4/ipconfig.c +++ b/net/ipv4/ipconfig.c @@ -1434,6 +1434,7 @@ __be32 __init root_nfs_parse_addr(char *name) static int __init wait_for_devices(void) { int i; + bool try_init_devs = true; for (i = 0; i < DEVICE_WAIT_MAX; i++) { struct net_device *dev; @@ -1452,6 +1453,11 @@ static int __init wait_for_devices(void) rtnl_unlock(); if (found) return 0; + if (try_init_devs && + (ROOT_DEV == Root_NFS || ROOT_DEV == Root_CIFS)) { + try_init_devs = false; + wait_for_init_devices_probe(); + } ssleep(1); } return -ENODEV; diff --git a/net/sunrpc/backchannel_rqst.c b/net/sunrpc/backchannel_rqst.c index 5a6b61dcdf2d..253a54c2fcfe 100644 --- a/net/sunrpc/backchannel_rqst.c +++ b/net/sunrpc/backchannel_rqst.c @@ -1,23 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /****************************************************************************** (c) 2007 Network Appliance, Inc. All Rights Reserved. (c) 2009 NetApp. All Rights Reserved. -NetApp provides this source code under the GPL v2 License. -The GPL v2 license is available at -https://opensource.org/licenses/gpl-license.php. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ diff --git a/net/sunrpc/sunrpc.h b/net/sunrpc/sunrpc.h index 2f59464e6524..d4a362c9e4b3 100644 --- a/net/sunrpc/sunrpc.h +++ b/net/sunrpc/sunrpc.h @@ -1,22 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /****************************************************************************** (c) 2008 NetApp. All Rights Reserved. -NetApp provides this source code under the GPL v2 License. -The GPL v2 license is available at -https://opensource.org/licenses/gpl-license.php. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ diff --git a/samples/v4l/v4l2-pci-skeleton.c b/samples/v4l/v4l2-pci-skeleton.c index 6311b7465220..a61f94db18d9 100644 --- a/samples/v4l/v4l2-pci-skeleton.c +++ b/samples/v4l/v4l2-pci-skeleton.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * This is a V4L2 PCI Skeleton Driver. It gives an initial skeleton source * for use with other PCI drivers. @@ -6,19 +7,6 @@ * input 0 and an HDMI connector as input 1. * * Copyright 2014 Cisco Systems, Inc. and/or its affiliates. All rights reserved. - * - * This program is free software; you may redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. */ #include diff --git a/scripts/gcc-plugins/latent_entropy_plugin.c b/scripts/gcc-plugins/latent_entropy_plugin.c index 848918764174..39e86be60dd2 100644 --- a/scripts/gcc-plugins/latent_entropy_plugin.c +++ b/scripts/gcc-plugins/latent_entropy_plugin.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2012-2016 by the PaX Team * Copyright 2016 by Emese Revfy - * Licensed under the GPL v2 * * Note: the choice of the license means that the compilation process is * NOT 'eligible' as defined by gcc's library exception to the GPL v3, diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c index ff91885f9470..c5c2ce113c92 100644 --- a/scripts/gcc-plugins/stackleak_plugin.c +++ b/scripts/gcc-plugins/stackleak_plugin.c @@ -1,7 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2011-2017 by the PaX Team * Modified by Alexander Popov - * Licensed under the GPL v2 * * Note: the choice of the license means that the compilation process is * NOT 'eligible' as defined by gcc's library exception to the GPL v3, diff --git a/scripts/gcc-plugins/structleak_plugin.c b/scripts/gcc-plugins/structleak_plugin.c index 8bc04068ed39..d8c744233832 100644 --- a/scripts/gcc-plugins/structleak_plugin.c +++ b/scripts/gcc-plugins/structleak_plugin.c @@ -1,6 +1,6 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Copyright 2013-2017 by PaX Team - * Licensed under the GPL v2 * * Note: the choice of the license means that the compilation process is * NOT 'eligible' as defined by gcc's library exception to the GPL v3, diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c index 3abeaf0f1b1c..8f488f92936b 100644 --- a/sound/soc/bcm/cygnus-pcm.c +++ b/sound/soc/bcm/cygnus-pcm.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014-2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014-2015 Broadcom Corporation #include #include #include diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c index 9698f4531c90..b0254e724ec9 100644 --- a/sound/soc/bcm/cygnus-ssp.c +++ b/sound/soc/bcm/cygnus-ssp.c @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014-2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2014-2015 Broadcom Corporation #include #include #include diff --git a/sound/soc/bcm/cygnus-ssp.h b/sound/soc/bcm/cygnus-ssp.h index 33dd34305928..74152b2d770d 100644 --- a/sound/soc/bcm/cygnus-ssp.h +++ b/sound/soc/bcm/cygnus-ssp.h @@ -1,15 +1,5 @@ -/* - * Copyright (C) 2014-2015 Broadcom Corporation - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License as - * published by the Free Software Foundation version 2. - * - * This program is distributed "as is" WITHOUT ANY WARRANTY of any - * kind, whether express or implied; without even the implied warranty - * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - */ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* Copyright (C) 2014-2015 Broadcom Corporation */ #ifndef __CYGNUS_SSP_H__ #define __CYGNUS_SSP_H__ diff --git a/sound/spi/Kconfig b/sound/spi/Kconfig index 44d39fa635ae..f407c37c37fa 100644 --- a/sound/spi/Kconfig +++ b/sound/spi/Kconfig @@ -19,7 +19,7 @@ config SND_AT73C213 DAC can be found on Atmel development boards. This driver requires the Atmel SSC driver for sound sink, a - peripheral found on most AT91 and AVR32 microprocessors. + peripheral found on most AT91 microprocessors. To compile this driver as a module, choose M here: the module will be called snd-at73c213. diff --git a/tools/power/cpupower/debug/i386/dump_psb.c b/tools/power/cpupower/debug/i386/dump_psb.c index 2c768cf70128..6fb81b42ea61 100644 --- a/tools/power/cpupower/debug/i386/dump_psb.c +++ b/tools/power/cpupower/debug/i386/dump_psb.c @@ -1,7 +1,5 @@ -/* - * dump_psb. (c) 2004, Dave Jones, Red Hat Inc. - * Licensed under the GPL v2. - */ +// SPDX-License-Identifier: GPL-2.0-only +// dump_psb. (c) 2004, Dave Jones, Red Hat Inc. #include #include diff --git a/tools/power/x86/intel-speed-select/hfi-events.c b/tools/power/x86/intel-speed-select/hfi-events.c index 761375062505..f0ed69721308 100644 --- a/tools/power/x86/intel-speed-select/hfi-events.c +++ b/tools/power/x86/intel-speed-select/hfi-events.c @@ -144,7 +144,7 @@ static int family_handler(struct nl_msg *msg, void *arg) continue; res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); break; - }; + } return 0; } diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index dd372924bc82..d0400c6684ba 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -41,7 +41,7 @@ void process_level_change(int cpu) time_t tm; int ret; - if (pkg_id >= MAX_PACKAGE_COUNT || die_id > MAX_DIE_PER_PACKAGE) { + if (pkg_id >= MAX_PACKAGE_COUNT || die_id >= MAX_DIE_PER_PACKAGE) { debug_printf("Invalid package/die info for cpu:%d\n", cpu); return; } diff --git a/tools/testing/selftests/kvm/.gitignore b/tools/testing/selftests/kvm/.gitignore index 4509a3a7eeae..d625a3f83780 100644 --- a/tools/testing/selftests/kvm/.gitignore +++ b/tools/testing/selftests/kvm/.gitignore @@ -25,8 +25,10 @@ /x86_64/hyperv_cpuid /x86_64/hyperv_features /x86_64/hyperv_svm_test +/x86_64/max_vcpuid_cap_test /x86_64/mmio_warning_test -/x86_64/mmu_role_test +/x86_64/monitor_mwait_test +/x86_64/nx_huge_pages_test /x86_64/platform_info_test /x86_64/pmu_event_filter_test /x86_64/set_boot_cpu_id @@ -36,9 +38,11 @@ /x86_64/state_test /x86_64/svm_vmcall_test /x86_64/svm_int_ctl_test -/x86_64/tsc_scaling_sync +/x86_64/svm_nested_soft_inject_test /x86_64/sync_regs_test /x86_64/tsc_msrs_test +/x86_64/tsc_scaling_sync +/x86_64/ucna_injection_test /x86_64/userspace_io_test /x86_64/userspace_msr_exit_test /x86_64/vmx_apic_access_test @@ -46,6 +50,7 @@ /x86_64/vmx_dirty_log_test /x86_64/vmx_exception_with_invalid_guest_state /x86_64/vmx_invalid_nested_guest_state +/x86_64/vmx_msrs_test /x86_64/vmx_preemption_timer_test /x86_64/vmx_set_nested_state_test /x86_64/vmx_tsc_adjust_test @@ -56,6 +61,7 @@ /x86_64/xen_vmcall_test /x86_64/xss_msr_test /x86_64/vmx_pmu_caps_test +/x86_64/triple_fault_event_test /access_tracking_perf_test /demand_paging_test /dirty_log_test diff --git a/tools/testing/selftests/kvm/Makefile b/tools/testing/selftests/kvm/Makefile index 120951fc304a..c7f47429d6cd 100644 --- a/tools/testing/selftests/kvm/Makefile +++ b/tools/testing/selftests/kvm/Makefile @@ -69,6 +69,10 @@ LIBKVM_s390x += lib/s390x/ucall.c LIBKVM_riscv += lib/riscv/processor.c LIBKVM_riscv += lib/riscv/ucall.c +# Non-compiled test targets +TEST_PROGS_x86_64 += x86_64/nx_huge_pages_test.sh + +# Compiled test targets TEST_GEN_PROGS_x86_64 = x86_64/cpuid_test TEST_GEN_PROGS_x86_64 += x86_64/cr4_cpuid_sync_test TEST_GEN_PROGS_x86_64 += x86_64/get_msr_index_features @@ -82,7 +86,7 @@ TEST_GEN_PROGS_x86_64 += x86_64/hyperv_svm_test TEST_GEN_PROGS_x86_64 += x86_64/kvm_clock_test TEST_GEN_PROGS_x86_64 += x86_64/kvm_pv_test TEST_GEN_PROGS_x86_64 += x86_64/mmio_warning_test -TEST_GEN_PROGS_x86_64 += x86_64/mmu_role_test +TEST_GEN_PROGS_x86_64 += x86_64/monitor_mwait_test TEST_GEN_PROGS_x86_64 += x86_64/platform_info_test TEST_GEN_PROGS_x86_64 += x86_64/pmu_event_filter_test TEST_GEN_PROGS_x86_64 += x86_64/set_boot_cpu_id @@ -92,14 +96,17 @@ TEST_GEN_PROGS_x86_64 += x86_64/state_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_preemption_timer_test TEST_GEN_PROGS_x86_64 += x86_64/svm_vmcall_test TEST_GEN_PROGS_x86_64 += x86_64/svm_int_ctl_test +TEST_GEN_PROGS_x86_64 += x86_64/svm_nested_soft_inject_test TEST_GEN_PROGS_x86_64 += x86_64/tsc_scaling_sync TEST_GEN_PROGS_x86_64 += x86_64/sync_regs_test +TEST_GEN_PROGS_x86_64 += x86_64/ucna_injection_test TEST_GEN_PROGS_x86_64 += x86_64/userspace_io_test TEST_GEN_PROGS_x86_64 += x86_64/userspace_msr_exit_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_apic_access_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_close_while_nested_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_dirty_log_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_exception_with_invalid_guest_state +TEST_GEN_PROGS_x86_64 += x86_64/vmx_msrs_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_invalid_nested_guest_state TEST_GEN_PROGS_x86_64 += x86_64/vmx_set_nested_state_test TEST_GEN_PROGS_x86_64 += x86_64/vmx_tsc_adjust_test @@ -114,6 +121,8 @@ TEST_GEN_PROGS_x86_64 += x86_64/xen_shinfo_test TEST_GEN_PROGS_x86_64 += x86_64/xen_vmcall_test TEST_GEN_PROGS_x86_64 += x86_64/sev_migrate_tests TEST_GEN_PROGS_x86_64 += x86_64/amx_test +TEST_GEN_PROGS_x86_64 += x86_64/max_vcpuid_cap_test +TEST_GEN_PROGS_x86_64 += x86_64/triple_fault_event_test TEST_GEN_PROGS_x86_64 += access_tracking_perf_test TEST_GEN_PROGS_x86_64 += demand_paging_test TEST_GEN_PROGS_x86_64 += dirty_log_test @@ -130,6 +139,9 @@ TEST_GEN_PROGS_x86_64 += steal_time TEST_GEN_PROGS_x86_64 += kvm_binary_stats_test TEST_GEN_PROGS_x86_64 += system_counter_offset_test +# Compiled outputs used by test targets +TEST_GEN_PROGS_EXTENDED_x86_64 += x86_64/nx_huge_pages_test + TEST_GEN_PROGS_aarch64 += aarch64/arch_timer TEST_GEN_PROGS_aarch64 += aarch64/debug-exceptions TEST_GEN_PROGS_aarch64 += aarch64/get-reg-list @@ -169,7 +181,9 @@ TEST_GEN_PROGS_riscv += kvm_page_table_test TEST_GEN_PROGS_riscv += set_memory_region_test TEST_GEN_PROGS_riscv += kvm_binary_stats_test +TEST_PROGS += $(TEST_PROGS_$(UNAME_M)) TEST_GEN_PROGS += $(TEST_GEN_PROGS_$(UNAME_M)) +TEST_GEN_PROGS_EXTENDED += $(TEST_GEN_PROGS_EXTENDED_$(UNAME_M)) LIBKVM += $(LIBKVM_$(UNAME_M)) INSTALL_HDR_PATH = $(top_srcdir)/usr @@ -216,6 +230,7 @@ $(LIBKVM_S_OBJ): $(OUTPUT)/%.o: %.S x := $(shell mkdir -p $(sort $(dir $(TEST_GEN_PROGS)))) $(TEST_GEN_PROGS): $(LIBKVM_OBJS) +$(TEST_GEN_PROGS_EXTENDED): $(LIBKVM_OBJS) cscope: include_paths = $(LINUX_TOOL_INCLUDE) $(LINUX_HDR_PATH) include lib .. cscope: diff --git a/tools/testing/selftests/kvm/aarch64/arch_timer.c b/tools/testing/selftests/kvm/aarch64/arch_timer.c index 3b940a101bc0..574eb73f0e90 100644 --- a/tools/testing/selftests/kvm/aarch64/arch_timer.c +++ b/tools/testing/selftests/kvm/aarch64/arch_timer.c @@ -76,13 +76,8 @@ struct test_vcpu_shared_data { uint64_t xcnt; }; -struct test_vcpu { - uint32_t vcpuid; - pthread_t pt_vcpu_run; - struct kvm_vm *vm; -}; - -static struct test_vcpu test_vcpu[KVM_MAX_VCPUS]; +static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; +static pthread_t pt_vcpu_run[KVM_MAX_VCPUS]; static struct test_vcpu_shared_data vcpu_shared_data[KVM_MAX_VCPUS]; static int vtimer_irq, ptimer_irq; @@ -217,29 +212,32 @@ static void guest_code(void) static void *test_vcpu_run(void *arg) { + unsigned int vcpu_idx = (unsigned long)arg; struct ucall uc; - struct test_vcpu *vcpu = arg; + struct kvm_vcpu *vcpu = vcpus[vcpu_idx]; struct kvm_vm *vm = vcpu->vm; - uint32_t vcpuid = vcpu->vcpuid; - struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpuid]; + struct test_vcpu_shared_data *shared_data = &vcpu_shared_data[vcpu_idx]; - vcpu_run(vm, vcpuid); + vcpu_run(vcpu); /* Currently, any exit from guest is an indication of completion */ pthread_mutex_lock(&vcpu_done_map_lock); - set_bit(vcpuid, vcpu_done_map); + set_bit(vcpu_idx, vcpu_done_map); pthread_mutex_unlock(&vcpu_done_map_lock); - switch (get_ucall(vm, vcpuid, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: case UCALL_DONE: break; case UCALL_ABORT: sync_global_from_guest(vm, *shared_data); - TEST_FAIL("%s at %s:%ld\n\tvalues: %lu, %lu; %lu, vcpu: %u; stage: %u; iter: %u", - (const char *)uc.args[0], __FILE__, uc.args[1], - uc.args[2], uc.args[3], uc.args[4], vcpuid, - shared_data->guest_stage, shared_data->nr_iter); + REPORT_GUEST_ASSERT_N(uc, "values: %lu, %lu; %lu, vcpu %u; stage; %u; iter: %u", + GUEST_ASSERT_ARG(uc, 0), + GUEST_ASSERT_ARG(uc, 1), + GUEST_ASSERT_ARG(uc, 2), + vcpu_idx, + shared_data->guest_stage, + shared_data->nr_iter); break; default: TEST_FAIL("Unexpected guest exit\n"); @@ -265,7 +263,7 @@ static uint32_t test_get_pcpu(void) return pcpu; } -static int test_migrate_vcpu(struct test_vcpu *vcpu) +static int test_migrate_vcpu(unsigned int vcpu_idx) { int ret; cpu_set_t cpuset; @@ -274,15 +272,15 @@ static int test_migrate_vcpu(struct test_vcpu *vcpu) CPU_ZERO(&cpuset); CPU_SET(new_pcpu, &cpuset); - pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu->vcpuid, new_pcpu); + pr_debug("Migrating vCPU: %u to pCPU: %u\n", vcpu_idx, new_pcpu); - ret = pthread_setaffinity_np(vcpu->pt_vcpu_run, - sizeof(cpuset), &cpuset); + ret = pthread_setaffinity_np(pt_vcpu_run[vcpu_idx], + sizeof(cpuset), &cpuset); /* Allow the error where the vCPU thread is already finished */ TEST_ASSERT(ret == 0 || ret == ESRCH, - "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d\n", - vcpu->vcpuid, new_pcpu, ret); + "Failed to migrate the vCPU:%u to pCPU: %u; ret: %d\n", + vcpu_idx, new_pcpu, ret); return ret; } @@ -305,7 +303,7 @@ static void *test_vcpu_migration(void *arg) continue; } - test_migrate_vcpu(&test_vcpu[i]); + test_migrate_vcpu(i); } } while (test_args.nr_vcpus != n_done); @@ -314,16 +312,17 @@ static void *test_vcpu_migration(void *arg) static void test_run(struct kvm_vm *vm) { - int i, ret; pthread_t pt_vcpu_migration; + unsigned int i; + int ret; pthread_mutex_init(&vcpu_done_map_lock, NULL); vcpu_done_map = bitmap_zalloc(test_args.nr_vcpus); TEST_ASSERT(vcpu_done_map, "Failed to allocate vcpu done bitmap\n"); - for (i = 0; i < test_args.nr_vcpus; i++) { - ret = pthread_create(&test_vcpu[i].pt_vcpu_run, NULL, - test_vcpu_run, &test_vcpu[i]); + for (i = 0; i < (unsigned long)test_args.nr_vcpus; i++) { + ret = pthread_create(&pt_vcpu_run[i], NULL, test_vcpu_run, + (void *)(unsigned long)i); TEST_ASSERT(!ret, "Failed to create vCPU-%d pthread\n", i); } @@ -338,7 +337,7 @@ static void test_run(struct kvm_vm *vm) for (i = 0; i < test_args.nr_vcpus; i++) - pthread_join(test_vcpu[i].pt_vcpu_run, NULL); + pthread_join(pt_vcpu_run[i], NULL); if (test_args.migration_freq_ms) pthread_join(pt_vcpu_migration, NULL); @@ -349,12 +348,10 @@ static void test_run(struct kvm_vm *vm) static void test_init_timer_irq(struct kvm_vm *vm) { /* Timer initid should be same for all the vCPUs, so query only vCPU-0 */ - int vcpu0_fd = vcpu_get_fd(vm, 0); - - kvm_device_access(vcpu0_fd, KVM_ARM_VCPU_TIMER_CTRL, - KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq, false); - kvm_device_access(vcpu0_fd, KVM_ARM_VCPU_TIMER_CTRL, - KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq, false); + vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_IRQ_PTIMER, &ptimer_irq); + vcpu_device_attr_get(vcpus[0], KVM_ARM_VCPU_TIMER_CTRL, + KVM_ARM_VCPU_TIMER_IRQ_VTIMER, &vtimer_irq); sync_global_to_guest(vm, ptimer_irq); sync_global_to_guest(vm, vtimer_irq); @@ -370,25 +367,18 @@ static struct kvm_vm *test_vm_create(void) unsigned int i; int nr_vcpus = test_args.nr_vcpus; - vm = vm_create_default_with_vcpus(nr_vcpus, 0, 0, guest_code, NULL); + vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); vm_init_descriptor_tables(vm); vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handler); - for (i = 0; i < nr_vcpus; i++) { - vcpu_init_descriptor_tables(vm, i); - - test_vcpu[i].vcpuid = i; - test_vcpu[i].vm = vm; - } + for (i = 0; i < nr_vcpus; i++) + vcpu_init_descriptor_tables(vcpus[i]); ucall_init(vm, NULL); test_init_timer_irq(vm); gic_fd = vgic_v3_setup(vm, nr_vcpus, 64, GICD_BASE_GPA, GICR_BASE_GPA); - if (gic_fd < 0) { - print_skip("Failed to create vgic-v3"); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3"); /* Make all the test's cmdline args visible to the guest */ sync_global_to_guest(vm, test_args); @@ -478,10 +468,8 @@ int main(int argc, char *argv[]) if (!parse_args(argc, argv)) exit(KSFT_SKIP); - if (test_args.migration_freq_ms && get_nprocs() < 2) { - print_skip("At least two physical CPUs needed for vCPU migration"); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(!test_args.migration_freq_ms || get_nprocs() >= 2, + "At least two physical CPUs needed for vCPU migration"); vm = test_vm_create(); test_run(vm); diff --git a/tools/testing/selftests/kvm/aarch64/debug-exceptions.c b/tools/testing/selftests/kvm/aarch64/debug-exceptions.c index 63b2178210c4..2ee35cf9801e 100644 --- a/tools/testing/selftests/kvm/aarch64/debug-exceptions.c +++ b/tools/testing/selftests/kvm/aarch64/debug-exceptions.c @@ -3,8 +3,6 @@ #include #include -#define VCPU_ID 0 - #define MDSCR_KDE (1 << 13) #define MDSCR_MDE (1 << 15) #define MDSCR_SS (1 << 0) @@ -240,31 +238,29 @@ static void guest_svc_handler(struct ex_regs *regs) svc_addr = regs->pc; } -static int debug_version(struct kvm_vm *vm) +static int debug_version(struct kvm_vcpu *vcpu) { uint64_t id_aa64dfr0; - get_reg(vm, VCPU_ID, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1), &id_aa64dfr0); + vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_ID_AA64DFR0_EL1), &id_aa64dfr0); return id_aa64dfr0 & 0xf; } int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct ucall uc; int stage; - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); ucall_init(vm, NULL); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); - if (debug_version(vm) < 6) { - print_skip("Armv8 debug architecture not supported."); - kvm_vm_free(vm); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(debug_version(vcpu) >= 6, + "Armv8 debug architecture not supported."); vm_install_sync_handler(vm, VECTOR_SYNC_CURRENT, ESR_EC_BRK_INS, guest_sw_bp_handler); @@ -278,18 +274,16 @@ int main(int argc, char *argv[]) ESR_EC_SVC64, guest_svc_handler); for (stage = 0; stage < 11; stage++) { - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: TEST_ASSERT(uc.args[1] == stage, "Stage %d: Unexpected sync ucall, got %lx", stage, (ulong)uc.args[1]); break; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx", - (const char *)uc.args[0], - __FILE__, uc.args[1], uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/aarch64/get-reg-list.c b/tools/testing/selftests/kvm/aarch64/get-reg-list.c index d3a7dbfcbb3d..d287dd2cac0a 100644 --- a/tools/testing/selftests/kvm/aarch64/get-reg-list.c +++ b/tools/testing/selftests/kvm/aarch64/get-reg-list.c @@ -377,7 +377,7 @@ static void prepare_vcpu_init(struct vcpu_config *c, struct kvm_vcpu_init *init) init->features[s->feature / 32] |= 1 << (s->feature % 32); } -static void finalize_vcpu(struct kvm_vm *vm, uint32_t vcpuid, struct vcpu_config *c) +static void finalize_vcpu(struct kvm_vcpu *vcpu, struct vcpu_config *c) { struct reg_sublist *s; int feature; @@ -385,7 +385,7 @@ static void finalize_vcpu(struct kvm_vm *vm, uint32_t vcpuid, struct vcpu_config for_each_sublist(c, s) { if (s->finalize) { feature = s->feature; - vcpu_ioctl(vm, vcpuid, KVM_ARM_VCPU_FINALIZE, &feature); + vcpu_ioctl(vcpu, KVM_ARM_VCPU_FINALIZE, &feature); } } } @@ -395,10 +395,12 @@ static void check_supported(struct vcpu_config *c) struct reg_sublist *s; for_each_sublist(c, s) { - if (s->capability && !kvm_check_cap(s->capability)) { - fprintf(stderr, "%s: %s not available, skipping tests\n", config_name(c), s->name); - exit(KSFT_SKIP); - } + if (!s->capability) + continue; + + __TEST_REQUIRE(kvm_has_cap(s->capability), + "%s: %s not available, skipping tests\n", + config_name(c), s->name); } } @@ -411,17 +413,19 @@ static void run_test(struct vcpu_config *c) struct kvm_vcpu_init init = { .target = -1, }; int new_regs = 0, missing_regs = 0, i, n; int failed_get = 0, failed_set = 0, failed_reject = 0; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct reg_sublist *s; check_supported(c); - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); + vm = vm_create_barebones(); prepare_vcpu_init(c, &init); - aarch64_vcpu_add_default(vm, 0, &init, NULL); - finalize_vcpu(vm, 0, c); + vcpu = __vm_vcpu_add(vm, 0); + aarch64_vcpu_setup(vcpu, &init); + finalize_vcpu(vcpu, c); - reg_list = vcpu_get_reg_list(vm, 0); + reg_list = vcpu_get_reg_list(vcpu); if (fixup_core_regs) core_reg_fixup(); @@ -457,7 +461,7 @@ static void run_test(struct vcpu_config *c) bool reject_reg = false; int ret; - ret = _vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, ®); + ret = __vcpu_get_reg(vcpu, reg_list->reg[i], &addr); if (ret) { printf("%s: Failed to get ", config_name(c)); print_reg(c, reg.id); @@ -469,7 +473,7 @@ static void run_test(struct vcpu_config *c) for_each_sublist(c, s) { if (s->rejects_set && find_reg(s->rejects_set, s->rejects_set_n, reg.id)) { reject_reg = true; - ret = _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, ®); + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); if (ret != -1 || errno != EPERM) { printf("%s: Failed to reject (ret=%d, errno=%d) ", config_name(c), ret, errno); print_reg(c, reg.id); @@ -481,7 +485,7 @@ static void run_test(struct vcpu_config *c) } if (!reject_reg) { - ret = _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, ®); + ret = __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); if (ret) { printf("%s: Failed to set ", config_name(c)); print_reg(c, reg.id); diff --git a/tools/testing/selftests/kvm/aarch64/hypercalls.c b/tools/testing/selftests/kvm/aarch64/hypercalls.c index 41e0210b7a5e..a39da3fe4952 100644 --- a/tools/testing/selftests/kvm/aarch64/hypercalls.c +++ b/tools/testing/selftests/kvm/aarch64/hypercalls.c @@ -141,26 +141,6 @@ static void guest_code(void) GUEST_DONE(); } -static int set_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t val) -{ - struct kvm_one_reg reg = { - .id = id, - .addr = (uint64_t)&val, - }; - - return _vcpu_ioctl(vm, 0, KVM_SET_ONE_REG, ®); -} - -static void get_fw_reg(struct kvm_vm *vm, uint64_t id, uint64_t *addr) -{ - struct kvm_one_reg reg = { - .id = id, - .addr = (uint64_t)addr, - }; - - vcpu_ioctl(vm, 0, KVM_GET_ONE_REG, ®); -} - struct st_time { uint32_t rev; uint32_t attr; @@ -170,23 +150,19 @@ struct st_time { #define STEAL_TIME_SIZE ((sizeof(struct st_time) + 63) & ~63) #define ST_GPA_BASE (1 << 30) -static void steal_time_init(struct kvm_vm *vm) +static void steal_time_init(struct kvm_vcpu *vcpu) { uint64_t st_ipa = (ulong)ST_GPA_BASE; unsigned int gpages; - struct kvm_device_attr dev = { - .group = KVM_ARM_VCPU_PVTIME_CTRL, - .attr = KVM_ARM_VCPU_PVTIME_IPA, - .addr = (uint64_t)&st_ipa, - }; gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE); - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0); + vm_userspace_mem_region_add(vcpu->vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0); - vcpu_ioctl(vm, 0, KVM_SET_DEVICE_ATTR, &dev); + vcpu_device_attr_set(vcpu, KVM_ARM_VCPU_PVTIME_CTRL, + KVM_ARM_VCPU_PVTIME_IPA, &st_ipa); } -static void test_fw_regs_before_vm_start(struct kvm_vm *vm) +static void test_fw_regs_before_vm_start(struct kvm_vcpu *vcpu) { uint64_t val; unsigned int i; @@ -196,18 +172,18 @@ static void test_fw_regs_before_vm_start(struct kvm_vm *vm) const struct kvm_fw_reg_info *reg_info = &fw_reg_info[i]; /* First 'read' should be an upper limit of the features supported */ - get_fw_reg(vm, reg_info->reg, &val); + vcpu_get_reg(vcpu, reg_info->reg, &val); TEST_ASSERT(val == FW_REG_ULIMIT_VAL(reg_info->max_feat_bit), "Expected all the features to be set for reg: 0x%lx; expected: 0x%lx; read: 0x%lx\n", reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit), val); /* Test a 'write' by disabling all the features of the register map */ - ret = set_fw_reg(vm, reg_info->reg, 0); + ret = __vcpu_set_reg(vcpu, reg_info->reg, 0); TEST_ASSERT(ret == 0, "Failed to clear all the features of reg: 0x%lx; ret: %d\n", reg_info->reg, errno); - get_fw_reg(vm, reg_info->reg, &val); + vcpu_get_reg(vcpu, reg_info->reg, &val); TEST_ASSERT(val == 0, "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg); @@ -216,7 +192,7 @@ static void test_fw_regs_before_vm_start(struct kvm_vm *vm) * Avoid this check if all the bits are occupied. */ if (reg_info->max_feat_bit < 63) { - ret = set_fw_reg(vm, reg_info->reg, BIT(reg_info->max_feat_bit + 1)); + ret = __vcpu_set_reg(vcpu, reg_info->reg, BIT(reg_info->max_feat_bit + 1)); TEST_ASSERT(ret != 0 && errno == EINVAL, "Unexpected behavior or return value (%d) while setting an unsupported feature for reg: 0x%lx\n", errno, reg_info->reg); @@ -224,7 +200,7 @@ static void test_fw_regs_before_vm_start(struct kvm_vm *vm) } } -static void test_fw_regs_after_vm_start(struct kvm_vm *vm) +static void test_fw_regs_after_vm_start(struct kvm_vcpu *vcpu) { uint64_t val; unsigned int i; @@ -237,7 +213,7 @@ static void test_fw_regs_after_vm_start(struct kvm_vm *vm) * Before starting the VM, the test clears all the bits. * Check if that's still the case. */ - get_fw_reg(vm, reg_info->reg, &val); + vcpu_get_reg(vcpu, reg_info->reg, &val); TEST_ASSERT(val == 0, "Expected all the features to be cleared for reg: 0x%lx\n", reg_info->reg); @@ -247,77 +223,78 @@ static void test_fw_regs_after_vm_start(struct kvm_vm *vm) * the registers and should return EBUSY. Set the registers and check for * the expected errno. */ - ret = set_fw_reg(vm, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit)); + ret = __vcpu_set_reg(vcpu, reg_info->reg, FW_REG_ULIMIT_VAL(reg_info->max_feat_bit)); TEST_ASSERT(ret != 0 && errno == EBUSY, "Unexpected behavior or return value (%d) while setting a feature while VM is running for reg: 0x%lx\n", errno, reg_info->reg); } } -static struct kvm_vm *test_vm_create(void) +static struct kvm_vm *test_vm_create(struct kvm_vcpu **vcpu) { struct kvm_vm *vm; - vm = vm_create_default(0, 0, guest_code); + vm = vm_create_with_one_vcpu(vcpu, guest_code); ucall_init(vm, NULL); - steal_time_init(vm); + steal_time_init(*vcpu); return vm; } -static struct kvm_vm *test_guest_stage(struct kvm_vm *vm) +static void test_guest_stage(struct kvm_vm **vm, struct kvm_vcpu **vcpu) { - struct kvm_vm *ret_vm = vm; + int prev_stage = stage; - pr_debug("Stage: %d\n", stage); + pr_debug("Stage: %d\n", prev_stage); - switch (stage) { + /* Sync the stage early, the VM might be freed below. */ + stage++; + sync_global_to_guest(*vm, stage); + + switch (prev_stage) { case TEST_STAGE_REG_IFACE: - test_fw_regs_after_vm_start(vm); + test_fw_regs_after_vm_start(*vcpu); break; case TEST_STAGE_HVC_IFACE_FEAT_DISABLED: /* Start a new VM so that all the features are now enabled by default */ - kvm_vm_free(vm); - ret_vm = test_vm_create(); + kvm_vm_free(*vm); + *vm = test_vm_create(vcpu); break; case TEST_STAGE_HVC_IFACE_FEAT_ENABLED: case TEST_STAGE_HVC_IFACE_FALSE_INFO: break; default: - TEST_FAIL("Unknown test stage: %d\n", stage); + TEST_FAIL("Unknown test stage: %d\n", prev_stage); } - - stage++; - sync_global_to_guest(vm, stage); - - return ret_vm; } static void test_run(void) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct ucall uc; bool guest_done = false; - vm = test_vm_create(); + vm = test_vm_create(&vcpu); - test_fw_regs_before_vm_start(vm); + test_fw_regs_before_vm_start(vcpu); while (!guest_done) { - vcpu_run(vm, 0); + vcpu_run(vcpu); - switch (get_ucall(vm, 0, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: - vm = test_guest_stage(vm); + test_guest_stage(&vm, &vcpu); break; case UCALL_DONE: guest_done = true; break; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld\n\tvalues: 0x%lx, 0x%lx; 0x%lx, stage: %u", - (const char *)uc.args[0], __FILE__, uc.args[1], - uc.args[2], uc.args[3], uc.args[4], stage); + REPORT_GUEST_ASSERT_N(uc, "values: 0x%lx, 0x%lx; 0x%lx, stage: %u", + GUEST_ASSERT_ARG(uc, 0), + GUEST_ASSERT_ARG(uc, 1), + GUEST_ASSERT_ARG(uc, 2), stage); break; default: TEST_FAIL("Unexpected guest exit\n"); diff --git a/tools/testing/selftests/kvm/aarch64/psci_test.c b/tools/testing/selftests/kvm/aarch64/psci_test.c index 88541de21c41..f7621f6e938e 100644 --- a/tools/testing/selftests/kvm/aarch64/psci_test.c +++ b/tools/testing/selftests/kvm/aarch64/psci_test.c @@ -17,9 +17,6 @@ #include "processor.h" #include "test_util.h" -#define VCPU_ID_SOURCE 0 -#define VCPU_ID_TARGET 1 - #define CPU_ON_ENTRY_ADDR 0xfeedf00dul #define CPU_ON_CONTEXT_ID 0xdeadc0deul @@ -64,49 +61,48 @@ static uint64_t psci_features(uint32_t func_id) return res.a0; } -static void vcpu_power_off(struct kvm_vm *vm, uint32_t vcpuid) +static void vcpu_power_off(struct kvm_vcpu *vcpu) { struct kvm_mp_state mp_state = { .mp_state = KVM_MP_STATE_STOPPED, }; - vcpu_set_mp_state(vm, vcpuid, &mp_state); + vcpu_mp_state_set(vcpu, &mp_state); } -static struct kvm_vm *setup_vm(void *guest_code) +static struct kvm_vm *setup_vm(void *guest_code, struct kvm_vcpu **source, + struct kvm_vcpu **target) { struct kvm_vcpu_init init; struct kvm_vm *vm; - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); - kvm_vm_elf_load(vm, program_invocation_name); + vm = vm_create(2); ucall_init(vm, NULL); vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init); init.features[0] |= (1 << KVM_ARM_VCPU_PSCI_0_2); - aarch64_vcpu_add_default(vm, VCPU_ID_SOURCE, &init, guest_code); - aarch64_vcpu_add_default(vm, VCPU_ID_TARGET, &init, guest_code); + *source = aarch64_vcpu_add(vm, 0, &init, guest_code); + *target = aarch64_vcpu_add(vm, 1, &init, guest_code); return vm; } -static void enter_guest(struct kvm_vm *vm, uint32_t vcpuid) +static void enter_guest(struct kvm_vcpu *vcpu) { struct ucall uc; - vcpu_run(vm, vcpuid); - if (get_ucall(vm, vcpuid, &uc) == UCALL_ABORT) - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], __FILE__, - uc.args[1]); + vcpu_run(vcpu); + if (get_ucall(vcpu, &uc) == UCALL_ABORT) + REPORT_GUEST_ASSERT(uc); } -static void assert_vcpu_reset(struct kvm_vm *vm, uint32_t vcpuid) +static void assert_vcpu_reset(struct kvm_vcpu *vcpu) { uint64_t obs_pc, obs_x0; - get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), &obs_pc); - get_reg(vm, vcpuid, ARM64_CORE_REG(regs.regs[0]), &obs_x0); + vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &obs_pc); + vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.regs[0]), &obs_x0); TEST_ASSERT(obs_pc == CPU_ON_ENTRY_ADDR, "unexpected target cpu pc: %lx (expected: %lx)", @@ -134,37 +130,29 @@ static void guest_test_cpu_on(uint64_t target_cpu) static void host_test_cpu_on(void) { + struct kvm_vcpu *source, *target; uint64_t target_mpidr; struct kvm_vm *vm; struct ucall uc; - vm = setup_vm(guest_test_cpu_on); + vm = setup_vm(guest_test_cpu_on, &source, &target); /* * make sure the target is already off when executing the test. */ - vcpu_power_off(vm, VCPU_ID_TARGET); + vcpu_power_off(target); - get_reg(vm, VCPU_ID_TARGET, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr); - vcpu_args_set(vm, VCPU_ID_SOURCE, 1, target_mpidr & MPIDR_HWID_BITMASK); - enter_guest(vm, VCPU_ID_SOURCE); + vcpu_get_reg(target, KVM_ARM64_SYS_REG(SYS_MPIDR_EL1), &target_mpidr); + vcpu_args_set(source, 1, target_mpidr & MPIDR_HWID_BITMASK); + enter_guest(source); - if (get_ucall(vm, VCPU_ID_SOURCE, &uc) != UCALL_DONE) + if (get_ucall(source, &uc) != UCALL_DONE) TEST_FAIL("Unhandled ucall: %lu", uc.cmd); - assert_vcpu_reset(vm, VCPU_ID_TARGET); + assert_vcpu_reset(target); kvm_vm_free(vm); } -static void enable_system_suspend(struct kvm_vm *vm) -{ - struct kvm_enable_cap cap = { - .cap = KVM_CAP_ARM_SYSTEM_SUSPEND, - }; - - vm_enable_cap(vm, &cap); -} - static void guest_test_system_suspend(void) { uint64_t ret; @@ -179,16 +167,17 @@ static void guest_test_system_suspend(void) static void host_test_system_suspend(void) { + struct kvm_vcpu *source, *target; struct kvm_run *run; struct kvm_vm *vm; - vm = setup_vm(guest_test_system_suspend); - enable_system_suspend(vm); + vm = setup_vm(guest_test_system_suspend, &source, &target); + vm_enable_cap(vm, KVM_CAP_ARM_SYSTEM_SUSPEND, 0); - vcpu_power_off(vm, VCPU_ID_TARGET); - run = vcpu_state(vm, VCPU_ID_SOURCE); + vcpu_power_off(target); + run = source->run; - enter_guest(vm, VCPU_ID_SOURCE); + enter_guest(source); TEST_ASSERT(run->exit_reason == KVM_EXIT_SYSTEM_EVENT, "Unhandled exit reason: %u (%s)", @@ -202,10 +191,7 @@ static void host_test_system_suspend(void) int main(void) { - if (!kvm_check_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)) { - print_skip("KVM_CAP_ARM_SYSTEM_SUSPEND not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_SYSTEM_SUSPEND)); host_test_cpu_on(); host_test_system_suspend(); diff --git a/tools/testing/selftests/kvm/aarch64/vcpu_width_config.c b/tools/testing/selftests/kvm/aarch64/vcpu_width_config.c index 6e9402679229..80b74c6f152b 100644 --- a/tools/testing/selftests/kvm/aarch64/vcpu_width_config.c +++ b/tools/testing/selftests/kvm/aarch64/vcpu_width_config.c @@ -15,24 +15,25 @@ /* - * Add a vCPU, run KVM_ARM_VCPU_INIT with @init1, and then - * add another vCPU, and run KVM_ARM_VCPU_INIT with @init2. + * Add a vCPU, run KVM_ARM_VCPU_INIT with @init0, and then + * add another vCPU, and run KVM_ARM_VCPU_INIT with @init1. */ -static int add_init_2vcpus(struct kvm_vcpu_init *init1, - struct kvm_vcpu_init *init2) +static int add_init_2vcpus(struct kvm_vcpu_init *init0, + struct kvm_vcpu_init *init1) { + struct kvm_vcpu *vcpu0, *vcpu1; struct kvm_vm *vm; int ret; - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); + vm = vm_create_barebones(); - vm_vcpu_add(vm, 0); - ret = _vcpu_ioctl(vm, 0, KVM_ARM_VCPU_INIT, init1); + vcpu0 = __vm_vcpu_add(vm, 0); + ret = __vcpu_ioctl(vcpu0, KVM_ARM_VCPU_INIT, init0); if (ret) goto free_exit; - vm_vcpu_add(vm, 1); - ret = _vcpu_ioctl(vm, 1, KVM_ARM_VCPU_INIT, init2); + vcpu1 = __vm_vcpu_add(vm, 1); + ret = __vcpu_ioctl(vcpu1, KVM_ARM_VCPU_INIT, init1); free_exit: kvm_vm_free(vm); @@ -40,25 +41,26 @@ free_exit: } /* - * Add two vCPUs, then run KVM_ARM_VCPU_INIT for one vCPU with @init1, - * and run KVM_ARM_VCPU_INIT for another vCPU with @init2. + * Add two vCPUs, then run KVM_ARM_VCPU_INIT for one vCPU with @init0, + * and run KVM_ARM_VCPU_INIT for another vCPU with @init1. */ -static int add_2vcpus_init_2vcpus(struct kvm_vcpu_init *init1, - struct kvm_vcpu_init *init2) +static int add_2vcpus_init_2vcpus(struct kvm_vcpu_init *init0, + struct kvm_vcpu_init *init1) { + struct kvm_vcpu *vcpu0, *vcpu1; struct kvm_vm *vm; int ret; - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); + vm = vm_create_barebones(); - vm_vcpu_add(vm, 0); - vm_vcpu_add(vm, 1); + vcpu0 = __vm_vcpu_add(vm, 0); + vcpu1 = __vm_vcpu_add(vm, 1); - ret = _vcpu_ioctl(vm, 0, KVM_ARM_VCPU_INIT, init1); + ret = __vcpu_ioctl(vcpu0, KVM_ARM_VCPU_INIT, init0); if (ret) goto free_exit; - ret = _vcpu_ioctl(vm, 1, KVM_ARM_VCPU_INIT, init2); + ret = __vcpu_ioctl(vcpu1, KVM_ARM_VCPU_INIT, init1); free_exit: kvm_vm_free(vm); @@ -76,45 +78,42 @@ free_exit: */ int main(void) { - struct kvm_vcpu_init init1, init2; + struct kvm_vcpu_init init0, init1; struct kvm_vm *vm; int ret; - if (!kvm_check_cap(KVM_CAP_ARM_EL1_32BIT)) { - print_skip("KVM_CAP_ARM_EL1_32BIT is not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ARM_EL1_32BIT)); - /* Get the preferred target type and copy that to init2 for later use */ - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); - vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init1); + /* Get the preferred target type and copy that to init1 for later use */ + vm = vm_create_barebones(); + vm_ioctl(vm, KVM_ARM_PREFERRED_TARGET, &init0); kvm_vm_free(vm); - init2 = init1; + init1 = init0; /* Test with 64bit vCPUs */ - ret = add_init_2vcpus(&init1, &init1); + ret = add_init_2vcpus(&init0, &init0); TEST_ASSERT(ret == 0, "Configuring 64bit EL1 vCPUs failed unexpectedly"); - ret = add_2vcpus_init_2vcpus(&init1, &init1); + ret = add_2vcpus_init_2vcpus(&init0, &init0); TEST_ASSERT(ret == 0, "Configuring 64bit EL1 vCPUs failed unexpectedly"); /* Test with 32bit vCPUs */ - init1.features[0] = (1 << KVM_ARM_VCPU_EL1_32BIT); - ret = add_init_2vcpus(&init1, &init1); + init0.features[0] = (1 << KVM_ARM_VCPU_EL1_32BIT); + ret = add_init_2vcpus(&init0, &init0); TEST_ASSERT(ret == 0, "Configuring 32bit EL1 vCPUs failed unexpectedly"); - ret = add_2vcpus_init_2vcpus(&init1, &init1); + ret = add_2vcpus_init_2vcpus(&init0, &init0); TEST_ASSERT(ret == 0, "Configuring 32bit EL1 vCPUs failed unexpectedly"); /* Test with mixed-width vCPUs */ - init1.features[0] = 0; - init2.features[0] = (1 << KVM_ARM_VCPU_EL1_32BIT); - ret = add_init_2vcpus(&init1, &init2); + init0.features[0] = 0; + init1.features[0] = (1 << KVM_ARM_VCPU_EL1_32BIT); + ret = add_init_2vcpus(&init0, &init1); TEST_ASSERT(ret != 0, "Configuring mixed-width vCPUs worked unexpectedly"); - ret = add_2vcpus_init_2vcpus(&init1, &init2); + ret = add_2vcpus_init_2vcpus(&init0, &init1); TEST_ASSERT(ret != 0, "Configuring mixed-width vCPUs worked unexpectedly"); diff --git a/tools/testing/selftests/kvm/aarch64/vgic_init.c b/tools/testing/selftests/kvm/aarch64/vgic_init.c index 34379c98d2f4..e05ecb31823f 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_init.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_init.c @@ -32,14 +32,28 @@ struct vm_gic { static uint64_t max_phys_size; -/* helper to access a redistributor register */ -static int access_v3_redist_reg(int gicv3_fd, int vcpu, int offset, - uint32_t *val, bool write) +/* + * Helpers to access a redistributor register and verify the ioctl() failed or + * succeeded as expected, and provided the correct value on success. + */ +static void v3_redist_reg_get_errno(int gicv3_fd, int vcpu, int offset, + int want, const char *msg) { - uint64_t attr = REG_OFFSET(vcpu, offset); + uint32_t ignored_val; + int ret = __kvm_device_attr_get(gicv3_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, + REG_OFFSET(vcpu, offset), &ignored_val); - return _kvm_device_access(gicv3_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, - attr, val, write); + TEST_ASSERT(ret && errno == want, "%s; want errno = %d", msg, want); +} + +static void v3_redist_reg_get(int gicv3_fd, int vcpu, int offset, uint32_t want, + const char *msg) +{ + uint32_t val; + + kvm_device_attr_get(gicv3_fd, KVM_DEV_ARM_VGIC_GRP_REDIST_REGS, + REG_OFFSET(vcpu, offset), &val); + TEST_ASSERT(val == want, "%s; want '0x%x', got '0x%x'", msg, want, val); } /* dummy guest code */ @@ -52,22 +66,22 @@ static void guest_code(void) } /* we don't want to assert on run execution, hence that helper */ -static int run_vcpu(struct kvm_vm *vm, uint32_t vcpuid) +static int run_vcpu(struct kvm_vcpu *vcpu) { - ucall_init(vm, NULL); - int ret = _vcpu_ioctl(vm, vcpuid, KVM_RUN, NULL); - if (ret) - return -errno; - return 0; + ucall_init(vcpu->vm, NULL); + + return __vcpu_run(vcpu) ? -errno : 0; } -static struct vm_gic vm_gic_create_with_vcpus(uint32_t gic_dev_type, uint32_t nr_vcpus) +static struct vm_gic vm_gic_create_with_vcpus(uint32_t gic_dev_type, + uint32_t nr_vcpus, + struct kvm_vcpu *vcpus[]) { struct vm_gic v; v.gic_dev_type = gic_dev_type; - v.vm = vm_create_default_with_vcpus(nr_vcpus, 0, 0, guest_code, NULL); - v.gic_fd = kvm_create_device(v.vm, gic_dev_type, false); + v.vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); + v.gic_fd = kvm_create_device(v.vm, gic_dev_type); return v; } @@ -129,63 +143,60 @@ static void subtest_dist_rdist(struct vm_gic *v) : gic_v2_dist_region; /* Check existing group/attributes */ - kvm_device_check_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - dist.attr); + kvm_has_device_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, dist.attr); - kvm_device_check_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - rdist.attr); + kvm_has_device_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, rdist.attr); /* check non existing attribute */ - ret = _kvm_device_check_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, -1); + ret = __kvm_has_device_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, -1); TEST_ASSERT(ret && errno == ENXIO, "attribute not supported"); /* misaligned DIST and REDIST address settings */ addr = dist.alignment / 0x10; - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - dist.attr, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + dist.attr, &addr); TEST_ASSERT(ret && errno == EINVAL, "GIC dist base not aligned"); addr = rdist.alignment / 0x10; - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - rdist.attr, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + rdist.attr, &addr); TEST_ASSERT(ret && errno == EINVAL, "GIC redist/cpu base not aligned"); /* out of range address */ addr = max_phys_size; - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - dist.attr, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + dist.attr, &addr); TEST_ASSERT(ret && errno == E2BIG, "dist address beyond IPA limit"); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - rdist.attr, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + rdist.attr, &addr); TEST_ASSERT(ret && errno == E2BIG, "redist address beyond IPA limit"); /* Space for half a rdist (a rdist is: 2 * rdist.alignment). */ addr = max_phys_size - dist.alignment; - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - rdist.attr, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + rdist.attr, &addr); TEST_ASSERT(ret && errno == E2BIG, "half of the redist is beyond IPA limit"); /* set REDIST base address @0x0*/ addr = 0x00000; - kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - rdist.attr, &addr, true); + kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + rdist.attr, &addr); /* Attempt to create a second legacy redistributor region */ addr = 0xE0000; - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - rdist.attr, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + rdist.attr, &addr); TEST_ASSERT(ret && errno == EEXIST, "GIC redist base set again"); - ret = _kvm_device_check_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + ret = __kvm_has_device_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST); if (!ret) { /* Attempt to mix legacy and new redistributor regions */ addr = REDIST_REGION_ATTR_ADDR(NR_VCPUS, 0x100000, 0, 0); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, - &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "attempt to mix GICv3 REDIST and REDIST_REGION"); } @@ -195,8 +206,8 @@ static void subtest_dist_rdist(struct vm_gic *v) * on first vcpu run instead. */ addr = rdist.size - rdist.alignment; - kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - dist.attr, &addr, true); + kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + dist.attr, &addr); } /* Test the new REDIST region API */ @@ -205,71 +216,71 @@ static void subtest_v3_redist_regions(struct vm_gic *v) uint64_t addr, expected_addr; int ret; - ret = kvm_device_check_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST); + ret = __kvm_has_device_attr(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST); TEST_ASSERT(!ret, "Multiple redist regions advertised"); addr = REDIST_REGION_ATTR_ADDR(NR_VCPUS, 0x100000, 2, 0); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "redist region attr value with flags != 0"); addr = REDIST_REGION_ATTR_ADDR(0, 0x100000, 0, 0); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "redist region attr value with count== 0"); addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 1); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "attempt to register the first rdist region with index != 0"); addr = REDIST_REGION_ATTR_ADDR(2, 0x201000, 0, 1); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "rdist region with misaligned address"); addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 0); - kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 1); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "register an rdist region with already used index"); addr = REDIST_REGION_ATTR_ADDR(1, 0x210000, 0, 2); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "register an rdist region overlapping with another one"); addr = REDIST_REGION_ATTR_ADDR(1, 0x240000, 0, 2); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "register redist region with index not +1"); addr = REDIST_REGION_ATTR_ADDR(1, 0x240000, 0, 1); - kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); addr = REDIST_REGION_ATTR_ADDR(1, max_phys_size, 0, 2); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == E2BIG, "register redist region with base address beyond IPA range"); /* The last redist is above the pa range. */ addr = REDIST_REGION_ATTR_ADDR(2, max_phys_size - 0x30000, 0, 2); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == E2BIG, "register redist region with top address beyond IPA range"); addr = 0x260000; - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr); TEST_ASSERT(ret && errno == EINVAL, "Mix KVM_VGIC_V3_ADDR_TYPE_REDIST and REDIST_REGION"); @@ -282,28 +293,28 @@ static void subtest_v3_redist_regions(struct vm_gic *v) addr = REDIST_REGION_ATTR_ADDR(0, 0, 0, 0); expected_addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 0); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, false); + ret = __kvm_device_attr_get(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(!ret && addr == expected_addr, "read characteristics of region #0"); addr = REDIST_REGION_ATTR_ADDR(0, 0, 0, 1); expected_addr = REDIST_REGION_ATTR_ADDR(1, 0x240000, 0, 1); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, false); + ret = __kvm_device_attr_get(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(!ret && addr == expected_addr, "read characteristics of region #1"); addr = REDIST_REGION_ATTR_ADDR(0, 0, 0, 2); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, false); + ret = __kvm_device_attr_get(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == ENOENT, "read characteristics of non existing region"); addr = 0x260000; - kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_DIST, &addr, true); + kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_DIST, &addr); addr = REDIST_REGION_ATTR_ADDR(1, 0x260000, 0, 2); - ret = _kvm_device_access(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v->gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "register redist region colliding with dist"); } @@ -313,18 +324,19 @@ static void subtest_v3_redist_regions(struct vm_gic *v) */ static void test_vgic_then_vcpus(uint32_t gic_dev_type) { + struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; int ret, i; - v = vm_gic_create_with_vcpus(gic_dev_type, 1); + v = vm_gic_create_with_vcpus(gic_dev_type, 1, vcpus); subtest_dist_rdist(&v); /* Add the rest of the VCPUs */ for (i = 1; i < NR_VCPUS; ++i) - vm_vcpu_add_default(v.vm, i, guest_code); + vcpus[i] = vm_vcpu_add(v.vm, i, guest_code); - ret = run_vcpu(v.vm, 3); + ret = run_vcpu(vcpus[3]); TEST_ASSERT(ret == -EINVAL, "dist/rdist overlap detected on 1st vcpu run"); vm_gic_destroy(&v); @@ -333,14 +345,15 @@ static void test_vgic_then_vcpus(uint32_t gic_dev_type) /* All the VCPUs are created before the VGIC KVM device gets initialized */ static void test_vcpus_then_vgic(uint32_t gic_dev_type) { + struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; int ret; - v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS); + v = vm_gic_create_with_vcpus(gic_dev_type, NR_VCPUS, vcpus); subtest_dist_rdist(&v); - ret = run_vcpu(v.vm, 3); + ret = run_vcpu(vcpus[3]); TEST_ASSERT(ret == -EINVAL, "dist/rdist overlap detected on 1st vcpu run"); vm_gic_destroy(&v); @@ -348,52 +361,53 @@ static void test_vcpus_then_vgic(uint32_t gic_dev_type) static void test_v3_new_redist_regions(void) { + struct kvm_vcpu *vcpus[NR_VCPUS]; void *dummy = NULL; struct vm_gic v; uint64_t addr; int ret; - v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS); + v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); subtest_v3_redist_regions(&v); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); - ret = run_vcpu(v.vm, 3); + ret = run_vcpu(vcpus[3]); TEST_ASSERT(ret == -ENXIO, "running without sufficient number of rdists"); vm_gic_destroy(&v); /* step2 */ - v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS); + v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); subtest_v3_redist_regions(&v); addr = REDIST_REGION_ATTR_ADDR(1, 0x280000, 0, 2); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); - ret = run_vcpu(v.vm, 3); + ret = run_vcpu(vcpus[3]); TEST_ASSERT(ret == -EBUSY, "running without vgic explicit init"); vm_gic_destroy(&v); /* step 3 */ - v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS); + v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); subtest_v3_redist_regions(&v); - _kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, dummy, true); + ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, dummy); TEST_ASSERT(ret && errno == EFAULT, "register a third region allowing to cover the 4 vcpus"); addr = REDIST_REGION_ATTR_ADDR(1, 0x280000, 0, 2); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); - ret = run_vcpu(v.vm, 3); + ret = run_vcpu(vcpus[3]); TEST_ASSERT(!ret, "vcpu run"); vm_gic_destroy(&v); @@ -403,71 +417,77 @@ static void test_v3_typer_accesses(void) { struct vm_gic v; uint64_t addr; - uint32_t val; int ret, i; - v.vm = vm_create_default(0, 0, guest_code); + v.vm = vm_create(NR_VCPUS); + (void)vm_vcpu_add(v.vm, 0, guest_code); - v.gic_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); + v.gic_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_V3); - vm_vcpu_add_default(v.vm, 3, guest_code); + (void)vm_vcpu_add(v.vm, 3, guest_code); - ret = access_v3_redist_reg(v.gic_fd, 1, GICR_TYPER, &val, false); - TEST_ASSERT(ret && errno == EINVAL, "attempting to read GICR_TYPER of non created vcpu"); + v3_redist_reg_get_errno(v.gic_fd, 1, GICR_TYPER, EINVAL, + "attempting to read GICR_TYPER of non created vcpu"); - vm_vcpu_add_default(v.vm, 1, guest_code); + (void)vm_vcpu_add(v.vm, 1, guest_code); - ret = access_v3_redist_reg(v.gic_fd, 1, GICR_TYPER, &val, false); - TEST_ASSERT(ret && errno == EBUSY, "read GICR_TYPER before GIC initialized"); + v3_redist_reg_get_errno(v.gic_fd, 1, GICR_TYPER, EBUSY, + "read GICR_TYPER before GIC initialized"); - vm_vcpu_add_default(v.vm, 2, guest_code); + (void)vm_vcpu_add(v.vm, 2, guest_code); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); for (i = 0; i < NR_VCPUS ; i++) { - ret = access_v3_redist_reg(v.gic_fd, 0, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && !val, "read GICR_TYPER before rdist region setting"); + v3_redist_reg_get(v.gic_fd, i, GICR_TYPER, i * 0x100, + "read GICR_TYPER before rdist region setting"); } addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 0); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); /* The 2 first rdists should be put there (vcpu 0 and 3) */ - ret = access_v3_redist_reg(v.gic_fd, 0, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && !val, "read typer of rdist #0"); - - ret = access_v3_redist_reg(v.gic_fd, 3, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x310, "read typer of rdist #1"); + v3_redist_reg_get(v.gic_fd, 0, GICR_TYPER, 0x0, "read typer of rdist #0"); + v3_redist_reg_get(v.gic_fd, 3, GICR_TYPER, 0x310, "read typer of rdist #1"); addr = REDIST_REGION_ATTR_ADDR(10, 0x100000, 0, 1); - ret = _kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + ret = __kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); TEST_ASSERT(ret && errno == EINVAL, "collision with previous rdist region"); - ret = access_v3_redist_reg(v.gic_fd, 1, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x100, - "no redist region attached to vcpu #1 yet, last cannot be returned"); - - ret = access_v3_redist_reg(v.gic_fd, 2, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x200, - "no redist region attached to vcpu #2, last cannot be returned"); + v3_redist_reg_get(v.gic_fd, 1, GICR_TYPER, 0x100, + "no redist region attached to vcpu #1 yet, last cannot be returned"); + v3_redist_reg_get(v.gic_fd, 2, GICR_TYPER, 0x200, + "no redist region attached to vcpu #2, last cannot be returned"); addr = REDIST_REGION_ATTR_ADDR(10, 0x20000, 0, 1); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); - ret = access_v3_redist_reg(v.gic_fd, 1, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x100, "read typer of rdist #1"); - - ret = access_v3_redist_reg(v.gic_fd, 2, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x210, - "read typer of rdist #1, last properly returned"); + v3_redist_reg_get(v.gic_fd, 1, GICR_TYPER, 0x100, "read typer of rdist #1"); + v3_redist_reg_get(v.gic_fd, 2, GICR_TYPER, 0x210, + "read typer of rdist #1, last properly returned"); vm_gic_destroy(&v); } +static struct vm_gic vm_gic_v3_create_with_vcpuids(int nr_vcpus, + uint32_t vcpuids[]) +{ + struct vm_gic v; + int i; + + v.vm = vm_create(nr_vcpus); + for (i = 0; i < nr_vcpus; i++) + vm_vcpu_add(v.vm, vcpuids[i], guest_code); + + v.gic_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_V3); + + return v; +} + /** * Test GICR_TYPER last bit with new redist regions * rdist regions #1 and #2 are contiguous @@ -483,45 +503,30 @@ static void test_v3_last_bit_redist_regions(void) uint32_t vcpuids[] = { 0, 3, 5, 4, 1, 2 }; struct vm_gic v; uint64_t addr; - uint32_t val; - int ret; - v.vm = vm_create_default_with_vcpus(6, 0, 0, guest_code, vcpuids); + v = vm_gic_v3_create_with_vcpuids(ARRAY_SIZE(vcpuids), vcpuids); - v.gic_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); - - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); addr = REDIST_REGION_ATTR_ADDR(2, 0x100000, 0, 0); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); addr = REDIST_REGION_ATTR_ADDR(2, 0x240000, 0, 1); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); addr = REDIST_REGION_ATTR_ADDR(2, 0x200000, 0, 2); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &addr); - ret = access_v3_redist_reg(v.gic_fd, 0, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x000, "read typer of rdist #0"); - - ret = access_v3_redist_reg(v.gic_fd, 1, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x100, "read typer of rdist #1"); - - ret = access_v3_redist_reg(v.gic_fd, 2, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x200, "read typer of rdist #2"); - - ret = access_v3_redist_reg(v.gic_fd, 3, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x310, "read typer of rdist #3"); - - ret = access_v3_redist_reg(v.gic_fd, 5, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x500, "read typer of rdist #5"); - - ret = access_v3_redist_reg(v.gic_fd, 4, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x410, "read typer of rdist #4"); + v3_redist_reg_get(v.gic_fd, 0, GICR_TYPER, 0x000, "read typer of rdist #0"); + v3_redist_reg_get(v.gic_fd, 1, GICR_TYPER, 0x100, "read typer of rdist #1"); + v3_redist_reg_get(v.gic_fd, 2, GICR_TYPER, 0x200, "read typer of rdist #2"); + v3_redist_reg_get(v.gic_fd, 3, GICR_TYPER, 0x310, "read typer of rdist #3"); + v3_redist_reg_get(v.gic_fd, 5, GICR_TYPER, 0x500, "read typer of rdist #5"); + v3_redist_reg_get(v.gic_fd, 4, GICR_TYPER, 0x410, "read typer of rdist #4"); vm_gic_destroy(&v); } @@ -532,34 +537,21 @@ static void test_v3_last_bit_single_rdist(void) uint32_t vcpuids[] = { 0, 3, 5, 4, 1, 2 }; struct vm_gic v; uint64_t addr; - uint32_t val; - int ret; - v.vm = vm_create_default_with_vcpus(6, 0, 0, guest_code, vcpuids); + v = vm_gic_v3_create_with_vcpuids(ARRAY_SIZE(vcpuids), vcpuids); - v.gic_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_V3, false); - - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); addr = 0x10000; - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr); - ret = access_v3_redist_reg(v.gic_fd, 0, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x000, "read typer of rdist #0"); - - ret = access_v3_redist_reg(v.gic_fd, 3, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x300, "read typer of rdist #1"); - - ret = access_v3_redist_reg(v.gic_fd, 5, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x500, "read typer of rdist #2"); - - ret = access_v3_redist_reg(v.gic_fd, 1, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x100, "read typer of rdist #3"); - - ret = access_v3_redist_reg(v.gic_fd, 2, GICR_TYPER, &val, false); - TEST_ASSERT(!ret && val == 0x210, "read typer of rdist #3"); + v3_redist_reg_get(v.gic_fd, 0, GICR_TYPER, 0x000, "read typer of rdist #0"); + v3_redist_reg_get(v.gic_fd, 3, GICR_TYPER, 0x300, "read typer of rdist #1"); + v3_redist_reg_get(v.gic_fd, 5, GICR_TYPER, 0x500, "read typer of rdist #2"); + v3_redist_reg_get(v.gic_fd, 1, GICR_TYPER, 0x100, "read typer of rdist #3"); + v3_redist_reg_get(v.gic_fd, 2, GICR_TYPER, 0x210, "read typer of rdist #3"); vm_gic_destroy(&v); } @@ -567,30 +559,31 @@ static void test_v3_last_bit_single_rdist(void) /* Uses the legacy REDIST region API. */ static void test_v3_redist_ipa_range_check_at_vcpu_run(void) { + struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; int ret, i; uint64_t addr; - v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1); + v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, 1, vcpus); /* Set space for 3 redists, we have 1 vcpu, so this succeeds. */ addr = max_phys_size - (3 * 2 * 0x10000); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST, &addr); addr = 0x00000; - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_DIST, &addr, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_DIST, &addr); /* Add the rest of the VCPUs */ for (i = 1; i < NR_VCPUS; ++i) - vm_vcpu_add_default(v.vm, i, guest_code); + vcpus[i] = vm_vcpu_add(v.vm, i, guest_code); - kvm_device_access(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(v.gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); /* Attempt to run a vcpu without enough redist space. */ - ret = run_vcpu(v.vm, 2); + ret = run_vcpu(vcpus[2]); TEST_ASSERT(ret && errno == EINVAL, "redist base+size above PA range detected on 1st vcpu run"); @@ -599,39 +592,40 @@ static void test_v3_redist_ipa_range_check_at_vcpu_run(void) static void test_v3_its_region(void) { + struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; uint64_t addr; int its_fd, ret; - v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS); - its_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_ITS, false); + v = vm_gic_create_with_vcpus(KVM_DEV_TYPE_ARM_VGIC_V3, NR_VCPUS, vcpus); + its_fd = kvm_create_device(v.vm, KVM_DEV_TYPE_ARM_VGIC_ITS); addr = 0x401000; - ret = _kvm_device_access(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, &addr, true); + ret = __kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_ITS_ADDR_TYPE, &addr); TEST_ASSERT(ret && errno == EINVAL, "ITS region with misaligned address"); addr = max_phys_size; - ret = _kvm_device_access(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, &addr, true); + ret = __kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_ITS_ADDR_TYPE, &addr); TEST_ASSERT(ret && errno == E2BIG, "register ITS region with base address beyond IPA range"); addr = max_phys_size - 0x10000; - ret = _kvm_device_access(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, &addr, true); + ret = __kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_ITS_ADDR_TYPE, &addr); TEST_ASSERT(ret && errno == E2BIG, "Half of ITS region is beyond IPA range"); /* This one succeeds setting the ITS base */ addr = 0x400000; - kvm_device_access(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, &addr, true); + kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_ITS_ADDR_TYPE, &addr); addr = 0x300000; - ret = _kvm_device_access(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_ITS_ADDR_TYPE, &addr, true); + ret = __kvm_device_attr_set(its_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_ITS_ADDR_TYPE, &addr); TEST_ASSERT(ret && errno == EEXIST, "ITS base set again"); close(its_fd); @@ -643,34 +637,33 @@ static void test_v3_its_region(void) */ int test_kvm_device(uint32_t gic_dev_type) { + struct kvm_vcpu *vcpus[NR_VCPUS]; struct vm_gic v; - int ret, fd; uint32_t other; + int ret; - v.vm = vm_create_default_with_vcpus(NR_VCPUS, 0, 0, guest_code, NULL); + v.vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus); /* try to create a non existing KVM device */ - ret = _kvm_create_device(v.vm, 0, true, &fd); + ret = __kvm_test_create_device(v.vm, 0); TEST_ASSERT(ret && errno == ENODEV, "unsupported device"); /* trial mode */ - ret = _kvm_create_device(v.vm, gic_dev_type, true, &fd); + ret = __kvm_test_create_device(v.vm, gic_dev_type); if (ret) return ret; - v.gic_fd = kvm_create_device(v.vm, gic_dev_type, false); + v.gic_fd = kvm_create_device(v.vm, gic_dev_type); - ret = _kvm_create_device(v.vm, gic_dev_type, false, &fd); - TEST_ASSERT(ret && errno == EEXIST, "create GIC device twice"); - - kvm_create_device(v.vm, gic_dev_type, true); + ret = __kvm_create_device(v.vm, gic_dev_type); + TEST_ASSERT(ret < 0 && errno == EEXIST, "create GIC device twice"); /* try to create the other gic_dev_type */ other = VGIC_DEV_IS_V2(gic_dev_type) ? KVM_DEV_TYPE_ARM_VGIC_V3 : KVM_DEV_TYPE_ARM_VGIC_V2; - if (!_kvm_create_device(v.vm, other, true, &fd)) { - ret = _kvm_create_device(v.vm, other, false, &fd); - TEST_ASSERT(ret && errno == EINVAL, + if (!__kvm_test_create_device(v.vm, other)) { + ret = __kvm_test_create_device(v.vm, other); + TEST_ASSERT(ret && (errno == EINVAL || errno == EEXIST), "create GIC device while other version exists"); } @@ -698,6 +691,7 @@ int main(int ac, char **av) { int ret; int pa_bits; + int cnt_impl = 0; pa_bits = vm_guest_mode_params[VM_MODE_DEFAULT].pa_bits; max_phys_size = 1ULL << pa_bits; @@ -706,17 +700,19 @@ int main(int ac, char **av) if (!ret) { pr_info("Running GIC_v3 tests.\n"); run_tests(KVM_DEV_TYPE_ARM_VGIC_V3); - return 0; + cnt_impl++; } ret = test_kvm_device(KVM_DEV_TYPE_ARM_VGIC_V2); if (!ret) { pr_info("Running GIC_v2 tests.\n"); run_tests(KVM_DEV_TYPE_ARM_VGIC_V2); - return 0; + cnt_impl++; } - print_skip("No GICv2 nor GICv3 support"); - exit(KSFT_SKIP); + if (!cnt_impl) { + print_skip("No GICv2 nor GICv3 support"); + exit(KSFT_SKIP); + } return 0; } diff --git a/tools/testing/selftests/kvm/aarch64/vgic_irq.c b/tools/testing/selftests/kvm/aarch64/vgic_irq.c index 554ca649d470..17417220a083 100644 --- a/tools/testing/selftests/kvm/aarch64/vgic_irq.c +++ b/tools/testing/selftests/kvm/aarch64/vgic_irq.c @@ -22,7 +22,6 @@ #define GICD_BASE_GPA 0x08000000ULL #define GICR_BASE_GPA 0x080A0000ULL -#define VCPU_ID 0 /* * Stores the user specified args; it's passed to the guest and to every test @@ -589,7 +588,8 @@ static void kvm_set_gsi_routing_irqchip_check(struct kvm_vm *vm, } static void kvm_irq_write_ispendr_check(int gic_fd, uint32_t intid, - uint32_t vcpu, bool expect_failure) + struct kvm_vcpu *vcpu, + bool expect_failure) { /* * Ignore this when expecting failure as invalid intids will lead to @@ -630,8 +630,7 @@ static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { fd[f] = eventfd(0, 0); - TEST_ASSERT(fd[f] != -1, - "eventfd failed, errno: %i\n", errno); + TEST_ASSERT(fd[f] != -1, __KVM_SYSCALL_ERROR("eventfd()", fd[f])); } for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) { @@ -647,7 +646,7 @@ static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, val = 1; ret = write(fd[f], &val, sizeof(uint64_t)); TEST_ASSERT(ret == sizeof(uint64_t), - "Write to KVM_IRQFD failed with ret: %d\n", ret); + __KVM_SYSCALL_ERROR("write()", ret)); } for (f = 0, i = intid; i < (uint64_t)intid + num; i++, f++) @@ -660,15 +659,16 @@ static void kvm_routing_and_irqfd_check(struct kvm_vm *vm, (tmp) < (uint64_t)(first) + (uint64_t)(num); \ (tmp)++, (i)++) -static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, - struct kvm_inject_args *inject_args, - struct test_args *test_args) +static void run_guest_cmd(struct kvm_vcpu *vcpu, int gic_fd, + struct kvm_inject_args *inject_args, + struct test_args *test_args) { kvm_inject_cmd cmd = inject_args->cmd; uint32_t intid = inject_args->first_intid; uint32_t num = inject_args->num; int level = inject_args->level; bool expect_failure = inject_args->expect_failure; + struct kvm_vm *vm = vcpu->vm; uint64_t tmp; uint32_t i; @@ -706,12 +706,12 @@ static void run_guest_cmd(struct kvm_vm *vm, int gic_fd, break; case KVM_WRITE_ISPENDR: for (i = intid; i < intid + num; i++) - kvm_irq_write_ispendr_check(gic_fd, i, - VCPU_ID, expect_failure); + kvm_irq_write_ispendr_check(gic_fd, i, vcpu, + expect_failure); break; case KVM_WRITE_ISACTIVER: for (i = intid; i < intid + num; i++) - kvm_irq_write_isactiver(gic_fd, i, VCPU_ID); + kvm_irq_write_isactiver(gic_fd, i, vcpu); break; default: break; @@ -740,6 +740,7 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) { struct ucall uc; int gic_fd; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_inject_args inject_args; vm_vaddr_t args_gva; @@ -754,39 +755,34 @@ static void test_vgic(uint32_t nr_irqs, bool level_sensitive, bool eoi_split) print_args(&args); - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); ucall_init(vm, NULL); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); /* Setup the guest args page (so it gets the args). */ args_gva = vm_vaddr_alloc_page(vm); memcpy(addr_gva2hva(vm, args_gva), &args, sizeof(args)); - vcpu_args_set(vm, 0, 1, args_gva); + vcpu_args_set(vcpu, 1, args_gva); gic_fd = vgic_v3_setup(vm, 1, nr_irqs, GICD_BASE_GPA, GICR_BASE_GPA); - if (gic_fd < 0) { - print_skip("Failed to create vgic-v3, skipping"); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(gic_fd >= 0, "Failed to create vgic-v3, skipping"); vm_install_exception_handler(vm, VECTOR_IRQ_CURRENT, guest_irq_handlers[args.eoi_split][args.level_sensitive]); while (1) { - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: kvm_inject_get_call(vm, &uc, &inject_args); - run_guest_cmd(vm, gic_fd, &inject_args, &args); + run_guest_cmd(vcpu, gic_fd, &inject_args, &args); break; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld\n\tvalues: %#lx, %#lx", - (const char *)uc.args[0], - __FILE__, uc.args[1], uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/access_tracking_perf_test.c b/tools/testing/selftests/kvm/access_tracking_perf_test.c index d8909032317a..1c2749b1481a 100644 --- a/tools/testing/selftests/kvm/access_tracking_perf_test.c +++ b/tools/testing/selftests/kvm/access_tracking_perf_test.c @@ -74,7 +74,7 @@ struct test_params { uint64_t vcpu_memory_bytes; /* The number of vCPUs to create in the VM. */ - int vcpus; + int nr_vcpus; }; static uint64_t pread_uint64(int fd, const char *filename, uint64_t index) @@ -104,10 +104,7 @@ static uint64_t lookup_pfn(int pagemap_fd, struct kvm_vm *vm, uint64_t gva) return 0; pfn = entry & PAGEMAP_PFN_MASK; - if (!pfn) { - print_skip("Looking up PFNs requires CAP_SYS_ADMIN"); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(pfn, "Looking up PFNs requires CAP_SYS_ADMIN"); return pfn; } @@ -127,10 +124,12 @@ static void mark_page_idle(int page_idle_fd, uint64_t pfn) "Set page_idle bits for PFN 0x%" PRIx64, pfn); } -static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) +static void mark_vcpu_memory_idle(struct kvm_vm *vm, + struct perf_test_vcpu_args *vcpu_args) { - uint64_t base_gva = perf_test_args.vcpu_args[vcpu_id].gva; - uint64_t pages = perf_test_args.vcpu_args[vcpu_id].pages; + int vcpu_idx = vcpu_args->vcpu_idx; + uint64_t base_gva = vcpu_args->gva; + uint64_t pages = vcpu_args->pages; uint64_t page; uint64_t still_idle = 0; uint64_t no_pfn = 0; @@ -138,7 +137,7 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) int pagemap_fd; /* If vCPUs are using an overlapping region, let vCPU 0 mark it idle. */ - if (overlap_memory_access && vcpu_id) + if (overlap_memory_access && vcpu_idx) return; page_idle_fd = open("/sys/kernel/mm/page_idle/bitmap", O_RDWR); @@ -170,7 +169,7 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) */ TEST_ASSERT(no_pfn < pages / 100, "vCPU %d: No PFN for %" PRIu64 " out of %" PRIu64 " pages.", - vcpu_id, no_pfn, pages); + vcpu_idx, no_pfn, pages); /* * Test that at least 90% of memory has been marked idle (the rest might @@ -183,17 +182,16 @@ static void mark_vcpu_memory_idle(struct kvm_vm *vm, int vcpu_id) TEST_ASSERT(still_idle < pages / 10, "vCPU%d: Too many pages still idle (%"PRIu64 " out of %" PRIu64 ").\n", - vcpu_id, still_idle, pages); + vcpu_idx, still_idle, pages); close(page_idle_fd); close(pagemap_fd); } -static void assert_ucall(struct kvm_vm *vm, uint32_t vcpu_id, - uint64_t expected_ucall) +static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_ucall) { struct ucall uc; - uint64_t actual_ucall = get_ucall(vm, vcpu_id, &uc); + uint64_t actual_ucall = get_ucall(vcpu, &uc); TEST_ASSERT(expected_ucall == actual_ucall, "Guest exited unexpectedly (expected ucall %" PRIu64 @@ -217,28 +215,29 @@ static bool spin_wait_for_next_iteration(int *current_iteration) static void vcpu_thread_main(struct perf_test_vcpu_args *vcpu_args) { + struct kvm_vcpu *vcpu = vcpu_args->vcpu; struct kvm_vm *vm = perf_test_args.vm; - int vcpu_id = vcpu_args->vcpu_id; + int vcpu_idx = vcpu_args->vcpu_idx; int current_iteration = 0; while (spin_wait_for_next_iteration(¤t_iteration)) { switch (READ_ONCE(iteration_work)) { case ITERATION_ACCESS_MEMORY: - vcpu_run(vm, vcpu_id); - assert_ucall(vm, vcpu_id, UCALL_SYNC); + vcpu_run(vcpu); + assert_ucall(vcpu, UCALL_SYNC); break; case ITERATION_MARK_IDLE: - mark_vcpu_memory_idle(vm, vcpu_id); + mark_vcpu_memory_idle(vm, vcpu_args); break; }; - vcpu_last_completed_iteration[vcpu_id] = current_iteration; + vcpu_last_completed_iteration[vcpu_idx] = current_iteration; } } -static void spin_wait_for_vcpu(int vcpu_id, int target_iteration) +static void spin_wait_for_vcpu(int vcpu_idx, int target_iteration) { - while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != + while (READ_ONCE(vcpu_last_completed_iteration[vcpu_idx]) != target_iteration) { continue; } @@ -250,12 +249,11 @@ enum access_type { ACCESS_WRITE, }; -static void run_iteration(struct kvm_vm *vm, int vcpus, const char *description) +static void run_iteration(struct kvm_vm *vm, int nr_vcpus, const char *description) { struct timespec ts_start; struct timespec ts_elapsed; - int next_iteration; - int vcpu_id; + int next_iteration, i; /* Kick off the vCPUs by incrementing iteration. */ next_iteration = ++iteration; @@ -263,23 +261,23 @@ static void run_iteration(struct kvm_vm *vm, int vcpus, const char *description) clock_gettime(CLOCK_MONOTONIC, &ts_start); /* Wait for all vCPUs to finish the iteration. */ - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) - spin_wait_for_vcpu(vcpu_id, next_iteration); + for (i = 0; i < nr_vcpus; i++) + spin_wait_for_vcpu(i, next_iteration); ts_elapsed = timespec_elapsed(ts_start); pr_info("%-30s: %ld.%09lds\n", description, ts_elapsed.tv_sec, ts_elapsed.tv_nsec); } -static void access_memory(struct kvm_vm *vm, int vcpus, enum access_type access, - const char *description) +static void access_memory(struct kvm_vm *vm, int nr_vcpus, + enum access_type access, const char *description) { perf_test_set_wr_fract(vm, (access == ACCESS_READ) ? INT_MAX : 1); iteration_work = ITERATION_ACCESS_MEMORY; - run_iteration(vm, vcpus, description); + run_iteration(vm, nr_vcpus, description); } -static void mark_memory_idle(struct kvm_vm *vm, int vcpus) +static void mark_memory_idle(struct kvm_vm *vm, int nr_vcpus) { /* * Even though this parallelizes the work across vCPUs, this is still a @@ -289,37 +287,37 @@ static void mark_memory_idle(struct kvm_vm *vm, int vcpus) */ pr_debug("Marking VM memory idle (slow)...\n"); iteration_work = ITERATION_MARK_IDLE; - run_iteration(vm, vcpus, "Mark memory idle"); + run_iteration(vm, nr_vcpus, "Mark memory idle"); } static void run_test(enum vm_guest_mode mode, void *arg) { struct test_params *params = arg; struct kvm_vm *vm; - int vcpus = params->vcpus; + int nr_vcpus = params->nr_vcpus; - vm = perf_test_create_vm(mode, vcpus, params->vcpu_memory_bytes, 1, + vm = perf_test_create_vm(mode, nr_vcpus, params->vcpu_memory_bytes, 1, params->backing_src, !overlap_memory_access); - perf_test_start_vcpu_threads(vcpus, vcpu_thread_main); + perf_test_start_vcpu_threads(nr_vcpus, vcpu_thread_main); pr_info("\n"); - access_memory(vm, vcpus, ACCESS_WRITE, "Populating memory"); + access_memory(vm, nr_vcpus, ACCESS_WRITE, "Populating memory"); /* As a control, read and write to the populated memory first. */ - access_memory(vm, vcpus, ACCESS_WRITE, "Writing to populated memory"); - access_memory(vm, vcpus, ACCESS_READ, "Reading from populated memory"); + access_memory(vm, nr_vcpus, ACCESS_WRITE, "Writing to populated memory"); + access_memory(vm, nr_vcpus, ACCESS_READ, "Reading from populated memory"); /* Repeat on memory that has been marked as idle. */ - mark_memory_idle(vm, vcpus); - access_memory(vm, vcpus, ACCESS_WRITE, "Writing to idle memory"); - mark_memory_idle(vm, vcpus); - access_memory(vm, vcpus, ACCESS_READ, "Reading from idle memory"); + mark_memory_idle(vm, nr_vcpus); + access_memory(vm, nr_vcpus, ACCESS_WRITE, "Writing to idle memory"); + mark_memory_idle(vm, nr_vcpus); + access_memory(vm, nr_vcpus, ACCESS_READ, "Reading from idle memory"); /* Set done to signal the vCPU threads to exit */ done = true; - perf_test_join_vcpu_threads(vcpus); + perf_test_join_vcpu_threads(nr_vcpus); perf_test_destroy_vm(vm); } @@ -347,7 +345,7 @@ int main(int argc, char *argv[]) struct test_params params = { .backing_src = DEFAULT_VM_MEM_SRC, .vcpu_memory_bytes = DEFAULT_PER_VCPU_MEM_SIZE, - .vcpus = 1, + .nr_vcpus = 1, }; int page_idle_fd; int opt; @@ -363,7 +361,7 @@ int main(int argc, char *argv[]) params.vcpu_memory_bytes = parse_size(optarg); break; case 'v': - params.vcpus = atoi(optarg); + params.nr_vcpus = atoi(optarg); break; case 'o': overlap_memory_access = true; @@ -379,10 +377,8 @@ int main(int argc, char *argv[]) } page_idle_fd = open("/sys/kernel/mm/page_idle/bitmap", O_RDWR); - if (page_idle_fd < 0) { - print_skip("CONFIG_IDLE_PAGE_TRACKING is not enabled"); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(page_idle_fd >= 0, + "CONFIG_IDLE_PAGE_TRACKING is not enabled"); close(page_idle_fd); for_each_guest_mode(run_test, ¶ms); diff --git a/tools/testing/selftests/kvm/demand_paging_test.c b/tools/testing/selftests/kvm/demand_paging_test.c index 6a719d065599..779ae54f89c4 100644 --- a/tools/testing/selftests/kvm/demand_paging_test.c +++ b/tools/testing/selftests/kvm/demand_paging_test.c @@ -44,28 +44,26 @@ static char *guest_data_prototype; static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) { - int ret; - int vcpu_id = vcpu_args->vcpu_id; - struct kvm_vm *vm = perf_test_args.vm; - struct kvm_run *run; + struct kvm_vcpu *vcpu = vcpu_args->vcpu; + int vcpu_idx = vcpu_args->vcpu_idx; + struct kvm_run *run = vcpu->run; struct timespec start; struct timespec ts_diff; - - run = vcpu_state(vm, vcpu_id); + int ret; clock_gettime(CLOCK_MONOTONIC, &start); /* Let the guest access its memory */ - ret = _vcpu_run(vm, vcpu_id); + ret = _vcpu_run(vcpu); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); - if (get_ucall(vm, vcpu_id, NULL) != UCALL_SYNC) { + if (get_ucall(vcpu, NULL) != UCALL_SYNC) { TEST_ASSERT(false, "Invalid guest sync status: exit_reason=%s\n", exit_reason_str(run->exit_reason)); } ts_diff = timespec_elapsed(start); - PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_id, + PER_VCPU_DEBUG("vCPU %d execution time: %ld.%.9lds\n", vcpu_idx, ts_diff.tv_sec, ts_diff.tv_nsec); } @@ -223,6 +221,7 @@ static void setup_demand_paging(struct kvm_vm *vm, struct uffdio_api uffdio_api; struct uffdio_register uffdio_register; uint64_t expected_ioctls = ((uint64_t) 1) << _UFFDIO_COPY; + int ret; PER_PAGE_DEBUG("Userfaultfd %s mode, faults resolved with %s\n", is_minor ? "MINOR" : "MISSING", @@ -242,19 +241,18 @@ static void setup_demand_paging(struct kvm_vm *vm, } uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); - TEST_ASSERT(uffd >= 0, "uffd creation failed, errno: %d", errno); + TEST_ASSERT(uffd >= 0, __KVM_SYSCALL_ERROR("userfaultfd()", uffd)); uffdio_api.api = UFFD_API; uffdio_api.features = 0; - TEST_ASSERT(ioctl(uffd, UFFDIO_API, &uffdio_api) != -1, - "ioctl UFFDIO_API failed: %" PRIu64, - (uint64_t)uffdio_api.api); + ret = ioctl(uffd, UFFDIO_API, &uffdio_api); + TEST_ASSERT(ret != -1, __KVM_SYSCALL_ERROR("UFFDIO_API", ret)); uffdio_register.range.start = (uint64_t)hva; uffdio_register.range.len = len; uffdio_register.mode = uffd_mode; - TEST_ASSERT(ioctl(uffd, UFFDIO_REGISTER, &uffdio_register) != -1, - "ioctl UFFDIO_REGISTER failed"); + ret = ioctl(uffd, UFFDIO_REGISTER, &uffdio_register); + TEST_ASSERT(ret != -1, __KVM_SYSCALL_ERROR("UFFDIO_REGISTER", ret)); TEST_ASSERT((uffdio_register.ioctls & expected_ioctls) == expected_ioctls, "missing userfaultfd ioctls"); @@ -285,8 +283,7 @@ static void run_test(enum vm_guest_mode mode, void *arg) struct timespec ts_diff; int *pipefds = NULL; struct kvm_vm *vm; - int vcpu_id; - int r; + int r, i; vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, 1, p->src_type, p->partition_vcpu_memory_access); @@ -309,12 +306,12 @@ static void run_test(enum vm_guest_mode mode, void *arg) pipefds = malloc(sizeof(int) * nr_vcpus * 2); TEST_ASSERT(pipefds, "Unable to allocate memory for pipefd"); - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { + for (i = 0; i < nr_vcpus; i++) { struct perf_test_vcpu_args *vcpu_args; void *vcpu_hva; void *vcpu_alias; - vcpu_args = &perf_test_args.vcpu_args[vcpu_id]; + vcpu_args = &perf_test_args.vcpu_args[i]; /* Cache the host addresses of the region */ vcpu_hva = addr_gpa2hva(vm, vcpu_args->gpa); @@ -324,13 +321,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) * Set up user fault fd to handle demand paging * requests. */ - r = pipe2(&pipefds[vcpu_id * 2], + r = pipe2(&pipefds[i * 2], O_CLOEXEC | O_NONBLOCK); TEST_ASSERT(!r, "Failed to set up pipefd"); - setup_demand_paging(vm, &uffd_handler_threads[vcpu_id], - pipefds[vcpu_id * 2], p->uffd_mode, - p->uffd_delay, &uffd_args[vcpu_id], + setup_demand_paging(vm, &uffd_handler_threads[i], + pipefds[i * 2], p->uffd_mode, + p->uffd_delay, &uffd_args[i], vcpu_hva, vcpu_alias, vcpu_args->pages * perf_test_args.guest_page_size); } @@ -350,11 +347,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) char c; /* Tell the user fault fd handler threads to quit */ - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { - r = write(pipefds[vcpu_id * 2 + 1], &c, 1); + for (i = 0; i < nr_vcpus; i++) { + r = write(pipefds[i * 2 + 1], &c, 1); TEST_ASSERT(r == 1, "Unable to write to pipefd"); - pthread_join(uffd_handler_threads[vcpu_id], NULL); + pthread_join(uffd_handler_threads[i], NULL); } } diff --git a/tools/testing/selftests/kvm/dirty_log_perf_test.c b/tools/testing/selftests/kvm/dirty_log_perf_test.c index d60a34cdfaee..f99e39a672d3 100644 --- a/tools/testing/selftests/kvm/dirty_log_perf_test.c +++ b/tools/testing/selftests/kvm/dirty_log_perf_test.c @@ -59,6 +59,7 @@ static void arch_cleanup_vm(struct kvm_vm *vm) static int nr_vcpus = 1; static uint64_t guest_percpu_mem_size = DEFAULT_PER_VCPU_MEM_SIZE; +static bool run_vcpus_while_disabling_dirty_logging; /* Host variables */ static u64 dirty_log_manual_caps; @@ -68,54 +69,59 @@ static int vcpu_last_completed_iteration[KVM_MAX_VCPUS]; static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) { - int ret; - struct kvm_vm *vm = perf_test_args.vm; + struct kvm_vcpu *vcpu = vcpu_args->vcpu; + int vcpu_idx = vcpu_args->vcpu_idx; uint64_t pages_count = 0; struct kvm_run *run; struct timespec start; struct timespec ts_diff; struct timespec total = (struct timespec){0}; struct timespec avg; - int vcpu_id = vcpu_args->vcpu_id; + int ret; - run = vcpu_state(vm, vcpu_id); + run = vcpu->run; while (!READ_ONCE(host_quit)) { int current_iteration = READ_ONCE(iteration); clock_gettime(CLOCK_MONOTONIC, &start); - ret = _vcpu_run(vm, vcpu_id); + ret = _vcpu_run(vcpu); ts_diff = timespec_elapsed(start); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); - TEST_ASSERT(get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC, + TEST_ASSERT(get_ucall(vcpu, NULL) == UCALL_SYNC, "Invalid guest sync status: exit_reason=%s\n", exit_reason_str(run->exit_reason)); - pr_debug("Got sync event from vCPU %d\n", vcpu_id); - vcpu_last_completed_iteration[vcpu_id] = current_iteration; + pr_debug("Got sync event from vCPU %d\n", vcpu_idx); + vcpu_last_completed_iteration[vcpu_idx] = current_iteration; pr_debug("vCPU %d updated last completed iteration to %d\n", - vcpu_id, vcpu_last_completed_iteration[vcpu_id]); + vcpu_idx, vcpu_last_completed_iteration[vcpu_idx]); if (current_iteration) { pages_count += vcpu_args->pages; total = timespec_add(total, ts_diff); pr_debug("vCPU %d iteration %d dirty memory time: %ld.%.9lds\n", - vcpu_id, current_iteration, ts_diff.tv_sec, + vcpu_idx, current_iteration, ts_diff.tv_sec, ts_diff.tv_nsec); } else { pr_debug("vCPU %d iteration %d populate memory time: %ld.%.9lds\n", - vcpu_id, current_iteration, ts_diff.tv_sec, + vcpu_idx, current_iteration, ts_diff.tv_sec, ts_diff.tv_nsec); } + /* + * Keep running the guest while dirty logging is being disabled + * (iteration is negative) so that vCPUs are accessing memory + * for the entire duration of zapping collapsible SPTEs. + */ while (current_iteration == READ_ONCE(iteration) && - !READ_ONCE(host_quit)) {} + READ_ONCE(iteration) >= 0 && !READ_ONCE(host_quit)) {} } - avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_id]); + avg = timespec_div(total, vcpu_last_completed_iteration[vcpu_idx]); pr_debug("\nvCPU %d dirtied 0x%lx pages over %d iterations in %ld.%.9lds. (Avg %ld.%.9lds/iteration)\n", - vcpu_id, pages_count, vcpu_last_completed_iteration[vcpu_id], + vcpu_idx, pages_count, vcpu_last_completed_iteration[vcpu_idx], total.tv_sec, total.tv_nsec, avg.tv_sec, avg.tv_nsec); } @@ -207,14 +213,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) uint64_t guest_num_pages; uint64_t host_num_pages; uint64_t pages_per_slot; - int vcpu_id; struct timespec start; struct timespec ts_diff; struct timespec get_dirty_log_total = (struct timespec){0}; struct timespec vcpu_dirty_total = (struct timespec){0}; struct timespec avg; - struct kvm_enable_cap cap = {}; struct timespec clear_dirty_log_total = (struct timespec){0}; + int i; vm = perf_test_create_vm(mode, nr_vcpus, guest_percpu_mem_size, p->slots, p->backing_src, @@ -222,18 +227,16 @@ static void run_test(enum vm_guest_mode mode, void *arg) perf_test_set_wr_fract(vm, p->wr_fract); - guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm_get_page_shift(vm); + guest_num_pages = (nr_vcpus * guest_percpu_mem_size) >> vm->page_shift; guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages); host_num_pages = vm_num_host_pages(mode, guest_num_pages); pages_per_slot = host_num_pages / p->slots; bitmaps = alloc_bitmaps(p->slots, pages_per_slot); - if (dirty_log_manual_caps) { - cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2; - cap.args[0] = dirty_log_manual_caps; - vm_enable_cap(vm, &cap); - } + if (dirty_log_manual_caps) + vm_enable_cap(vm, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, + dirty_log_manual_caps); arch_setup_vm(vm, nr_vcpus); @@ -242,15 +245,15 @@ static void run_test(enum vm_guest_mode mode, void *arg) host_quit = false; clock_gettime(CLOCK_MONOTONIC, &start); - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) - vcpu_last_completed_iteration[vcpu_id] = -1; + for (i = 0; i < nr_vcpus; i++) + vcpu_last_completed_iteration[i] = -1; perf_test_start_vcpu_threads(nr_vcpus, vcpu_worker); /* Allow the vCPUs to populate memory */ pr_debug("Starting iteration %d - Populating\n", iteration); - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { - while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) != + for (i = 0; i < nr_vcpus; i++) { + while (READ_ONCE(vcpu_last_completed_iteration[i]) != iteration) ; } @@ -275,8 +278,8 @@ static void run_test(enum vm_guest_mode mode, void *arg) iteration++; pr_debug("Starting iteration %d\n", iteration); - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { - while (READ_ONCE(vcpu_last_completed_iteration[vcpu_id]) + for (i = 0; i < nr_vcpus; i++) { + while (READ_ONCE(vcpu_last_completed_iteration[i]) != iteration) ; } @@ -305,6 +308,14 @@ static void run_test(enum vm_guest_mode mode, void *arg) } } + /* + * Run vCPUs while dirty logging is being disabled to stress disabling + * in terms of both performance and correctness. Opt-in via command + * line as this significantly increases time to disable dirty logging. + */ + if (run_vcpus_while_disabling_dirty_logging) + WRITE_ONCE(iteration, -1); + /* Disable dirty logging */ clock_gettime(CLOCK_MONOTONIC, &start); disable_dirty_logging(vm, p->slots); @@ -312,7 +323,11 @@ static void run_test(enum vm_guest_mode mode, void *arg) pr_info("Disabling dirty logging time: %ld.%.9lds\n", ts_diff.tv_sec, ts_diff.tv_nsec); - /* Tell the vcpu thread to quit */ + /* + * Tell the vCPU threads to quit. No need to manually check that vCPUs + * have stopped running after disabling dirty logging, the join will + * wait for them to exit. + */ host_quit = true; perf_test_join_vcpu_threads(nr_vcpus); @@ -352,6 +367,9 @@ static void help(char *name) " Warning: a low offset can conflict with the loaded test code.\n"); guest_modes_help(); printf(" -n: Run the vCPUs in nested mode (L2)\n"); + printf(" -e: Run vCPUs while dirty logging is being disabled. This\n" + " can significantly increase runtime, especially if there\n" + " isn't a dedicated pCPU for the main thread.\n"); printf(" -b: specify the size of the memory region which should be\n" " dirtied by each vCPU. e.g. 10M or 3G.\n" " (default: 1G)\n"); @@ -388,8 +406,11 @@ int main(int argc, char *argv[]) guest_modes_append_default(); - while ((opt = getopt(argc, argv, "ghi:p:m:nb:f:v:os:x:")) != -1) { + while ((opt = getopt(argc, argv, "eghi:p:m:nb:f:v:os:x:")) != -1) { switch (opt) { + case 'e': + /* 'e' is for evil. */ + run_vcpus_while_disabling_dirty_logging = true; case 'g': dirty_log_manual_caps = 0; break; diff --git a/tools/testing/selftests/kvm/dirty_log_test.c b/tools/testing/selftests/kvm/dirty_log_test.c index 3fcd89e195c7..9c883c94d478 100644 --- a/tools/testing/selftests/kvm/dirty_log_test.c +++ b/tools/testing/selftests/kvm/dirty_log_test.c @@ -23,8 +23,6 @@ #include "guest_modes.h" #include "processor.h" -#define VCPU_ID 1 - /* The memory slot index to track dirty pages */ #define TEST_MEM_SLOT_INDEX 1 @@ -212,34 +210,31 @@ static void sem_wait_until(sem_t *sem) static bool clear_log_supported(void) { - return kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); + return kvm_has_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); } static void clear_log_create_vm_done(struct kvm_vm *vm) { - struct kvm_enable_cap cap = {}; u64 manual_caps; manual_caps = kvm_check_cap(KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2); TEST_ASSERT(manual_caps, "MANUAL_CAPS is zero!"); manual_caps &= (KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | KVM_DIRTY_LOG_INITIALLY_SET); - cap.cap = KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2; - cap.args[0] = manual_caps; - vm_enable_cap(vm, &cap); + vm_enable_cap(vm, KVM_CAP_MANUAL_DIRTY_LOG_PROTECT2, manual_caps); } -static void dirty_log_collect_dirty_pages(struct kvm_vm *vm, int slot, +static void dirty_log_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, void *bitmap, uint32_t num_pages) { - kvm_vm_get_dirty_log(vm, slot, bitmap); + kvm_vm_get_dirty_log(vcpu->vm, slot, bitmap); } -static void clear_log_collect_dirty_pages(struct kvm_vm *vm, int slot, +static void clear_log_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, void *bitmap, uint32_t num_pages) { - kvm_vm_get_dirty_log(vm, slot, bitmap); - kvm_vm_clear_dirty_log(vm, slot, bitmap, 0, num_pages); + kvm_vm_get_dirty_log(vcpu->vm, slot, bitmap); + kvm_vm_clear_dirty_log(vcpu->vm, slot, bitmap, 0, num_pages); } /* Should only be called after a GUEST_SYNC */ @@ -253,14 +248,14 @@ static void vcpu_handle_sync_stop(void) } } -static void default_after_vcpu_run(struct kvm_vm *vm, int ret, int err) +static void default_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; TEST_ASSERT(ret == 0 || (ret == -1 && err == EINTR), "vcpu run failed: errno=%d", err); - TEST_ASSERT(get_ucall(vm, VCPU_ID, NULL) == UCALL_SYNC, + TEST_ASSERT(get_ucall(vcpu, NULL) == UCALL_SYNC, "Invalid guest sync status: exit_reason=%s\n", exit_reason_str(run->exit_reason)); @@ -269,7 +264,7 @@ static void default_after_vcpu_run(struct kvm_vm *vm, int ret, int err) static bool dirty_ring_supported(void) { - return kvm_check_cap(KVM_CAP_DIRTY_LOG_RING); + return kvm_has_cap(KVM_CAP_DIRTY_LOG_RING); } static void dirty_ring_create_vm_done(struct kvm_vm *vm) @@ -331,7 +326,7 @@ static void dirty_ring_continue_vcpu(void) sem_post(&sem_vcpu_cont); } -static void dirty_ring_collect_dirty_pages(struct kvm_vm *vm, int slot, +static void dirty_ring_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, void *bitmap, uint32_t num_pages) { /* We only have one vcpu */ @@ -351,10 +346,10 @@ static void dirty_ring_collect_dirty_pages(struct kvm_vm *vm, int slot, } /* Only have one vcpu */ - count = dirty_ring_collect_one(vcpu_map_dirty_ring(vm, VCPU_ID), + count = dirty_ring_collect_one(vcpu_map_dirty_ring(vcpu), slot, bitmap, num_pages, &fetch_index); - cleared = kvm_vm_reset_dirty_ring(vm); + cleared = kvm_vm_reset_dirty_ring(vcpu->vm); /* Cleared pages should be the same as collected */ TEST_ASSERT(cleared == count, "Reset dirty pages (%u) mismatch " @@ -369,12 +364,12 @@ static void dirty_ring_collect_dirty_pages(struct kvm_vm *vm, int slot, pr_info("Iteration %ld collected %u pages\n", iteration, count); } -static void dirty_ring_after_vcpu_run(struct kvm_vm *vm, int ret, int err) +static void dirty_ring_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; /* A ucall-sync or ring-full event is allowed */ - if (get_ucall(vm, VCPU_ID, NULL) == UCALL_SYNC) { + if (get_ucall(vcpu, NULL) == UCALL_SYNC) { /* We should allow this to continue */ ; } else if (run->exit_reason == KVM_EXIT_DIRTY_RING_FULL || @@ -408,10 +403,10 @@ struct log_mode { /* Hook when the vm creation is done (before vcpu creation) */ void (*create_vm_done)(struct kvm_vm *vm); /* Hook to collect the dirty pages into the bitmap provided */ - void (*collect_dirty_pages) (struct kvm_vm *vm, int slot, + void (*collect_dirty_pages) (struct kvm_vcpu *vcpu, int slot, void *bitmap, uint32_t num_pages); /* Hook to call when after each vcpu run */ - void (*after_vcpu_run)(struct kvm_vm *vm, int ret, int err); + void (*after_vcpu_run)(struct kvm_vcpu *vcpu, int ret, int err); void (*before_vcpu_join) (void); } log_modes[LOG_MODE_NUM] = { { @@ -473,22 +468,22 @@ static void log_mode_create_vm_done(struct kvm_vm *vm) mode->create_vm_done(vm); } -static void log_mode_collect_dirty_pages(struct kvm_vm *vm, int slot, +static void log_mode_collect_dirty_pages(struct kvm_vcpu *vcpu, int slot, void *bitmap, uint32_t num_pages) { struct log_mode *mode = &log_modes[host_log_mode]; TEST_ASSERT(mode->collect_dirty_pages != NULL, "collect_dirty_pages() is required for any log mode!"); - mode->collect_dirty_pages(vm, slot, bitmap, num_pages); + mode->collect_dirty_pages(vcpu, slot, bitmap, num_pages); } -static void log_mode_after_vcpu_run(struct kvm_vm *vm, int ret, int err) +static void log_mode_after_vcpu_run(struct kvm_vcpu *vcpu, int ret, int err) { struct log_mode *mode = &log_modes[host_log_mode]; if (mode->after_vcpu_run) - mode->after_vcpu_run(vm, ret, err); + mode->after_vcpu_run(vcpu, ret, err); } static void log_mode_before_vcpu_join(void) @@ -509,16 +504,15 @@ static void generate_random_array(uint64_t *guest_array, uint64_t size) static void *vcpu_worker(void *data) { - int ret, vcpu_fd; - struct kvm_vm *vm = data; + int ret; + struct kvm_vcpu *vcpu = data; + struct kvm_vm *vm = vcpu->vm; uint64_t *guest_array; uint64_t pages_count = 0; struct kvm_signal_mask *sigmask = alloca(offsetof(struct kvm_signal_mask, sigset) + sizeof(sigset_t)); sigset_t *sigset = (sigset_t *) &sigmask->sigset; - vcpu_fd = vcpu_get_fd(vm, VCPU_ID); - /* * SIG_IPI is unblocked atomically while in KVM_RUN. It causes the * ioctl to return with -EINTR, but it is still pending and we need @@ -527,7 +521,7 @@ static void *vcpu_worker(void *data) sigmask->len = 8; pthread_sigmask(0, NULL, sigset); sigdelset(sigset, SIG_IPI); - vcpu_ioctl(vm, VCPU_ID, KVM_SET_SIGNAL_MASK, sigmask); + vcpu_ioctl(vcpu, KVM_SET_SIGNAL_MASK, sigmask); sigemptyset(sigset); sigaddset(sigset, SIG_IPI); @@ -539,13 +533,13 @@ static void *vcpu_worker(void *data) generate_random_array(guest_array, TEST_PAGES_PER_LOOP); pages_count += TEST_PAGES_PER_LOOP; /* Let the guest dirty the random pages */ - ret = ioctl(vcpu_fd, KVM_RUN, NULL); + ret = __vcpu_run(vcpu); if (ret == -1 && errno == EINTR) { int sig = -1; sigwait(sigset, &sig); assert(sig == SIG_IPI); } - log_mode_after_vcpu_run(vm, ret, errno); + log_mode_after_vcpu_run(vcpu, ret, errno); } pr_info("Dirtied %"PRIu64" pages\n", pages_count); @@ -671,21 +665,17 @@ static void vm_dirty_log_verify(enum vm_guest_mode mode, unsigned long *bmap) } } -static struct kvm_vm *create_vm(enum vm_guest_mode mode, uint32_t vcpuid, +static struct kvm_vm *create_vm(enum vm_guest_mode mode, struct kvm_vcpu **vcpu, uint64_t extra_mem_pages, void *guest_code) { struct kvm_vm *vm; - uint64_t extra_pg_pages = extra_mem_pages / 512 * 2; pr_info("Testing guest mode: %s\n", vm_guest_mode_string(mode)); - vm = vm_create(mode, DEFAULT_GUEST_PHY_PAGES + extra_pg_pages, O_RDWR); - kvm_vm_elf_load(vm, program_invocation_name); -#ifdef __x86_64__ - vm_create_irqchip(vm); -#endif + vm = __vm_create(mode, 1, extra_mem_pages); + log_mode_create_vm_done(vm); - vm_vcpu_add_default(vm, vcpuid, guest_code); + *vcpu = vm_vcpu_add(vm, 0, guest_code); return vm; } @@ -701,6 +691,7 @@ struct test_params { static void run_test(enum vm_guest_mode mode, void *arg) { struct test_params *p = arg; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; unsigned long *bmap; @@ -718,25 +709,23 @@ static void run_test(enum vm_guest_mode mode, void *arg) * (e.g., 64K page size guest will need even less memory for * page tables). */ - vm = create_vm(mode, VCPU_ID, - 2ul << (DIRTY_MEM_BITS - PAGE_SHIFT_4K), - guest_code); + vm = create_vm(mode, &vcpu, + 2ul << (DIRTY_MEM_BITS - PAGE_SHIFT_4K), guest_code); - guest_page_size = vm_get_page_size(vm); + guest_page_size = vm->page_size; /* * A little more than 1G of guest page sized pages. Cover the * case where the size is not aligned to 64 pages. */ - guest_num_pages = (1ul << (DIRTY_MEM_BITS - - vm_get_page_shift(vm))) + 3; + guest_num_pages = (1ul << (DIRTY_MEM_BITS - vm->page_shift)) + 3; guest_num_pages = vm_adjust_num_guest_pages(mode, guest_num_pages); host_page_size = getpagesize(); host_num_pages = vm_num_host_pages(mode, guest_num_pages); if (!p->phys_offset) { - guest_test_phys_mem = (vm_get_max_gfn(vm) - - guest_num_pages) * guest_page_size; + guest_test_phys_mem = (vm->max_gfn - guest_num_pages) * + guest_page_size; guest_test_phys_mem = align_down(guest_test_phys_mem, host_page_size); } else { guest_test_phys_mem = p->phys_offset; @@ -781,12 +770,12 @@ static void run_test(enum vm_guest_mode mode, void *arg) host_clear_count = 0; host_track_next_count = 0; - pthread_create(&vcpu_thread, NULL, vcpu_worker, vm); + pthread_create(&vcpu_thread, NULL, vcpu_worker, vcpu); while (iteration < p->iterations) { /* Give the vcpu thread some time to dirty some pages */ usleep(p->interval * 1000); - log_mode_collect_dirty_pages(vm, TEST_MEM_SLOT_INDEX, + log_mode_collect_dirty_pages(vcpu, TEST_MEM_SLOT_INDEX, bmap, host_num_pages); /* diff --git a/tools/testing/selftests/kvm/hardware_disable_test.c b/tools/testing/selftests/kvm/hardware_disable_test.c index b21c69a56daa..f5d59b9934f1 100644 --- a/tools/testing/selftests/kvm/hardware_disable_test.c +++ b/tools/testing/selftests/kvm/hardware_disable_test.c @@ -27,12 +27,6 @@ sem_t *sem; -/* Arguments for the pthreads */ -struct payload { - struct kvm_vm *vm; - uint32_t index; -}; - static void guest_code(void) { for (;;) @@ -42,14 +36,14 @@ static void guest_code(void) static void *run_vcpu(void *arg) { - struct payload *payload = (struct payload *)arg; - struct kvm_run *state = vcpu_state(payload->vm, payload->index); + struct kvm_vcpu *vcpu = arg; + struct kvm_run *run = vcpu->run; - vcpu_run(payload->vm, payload->index); + vcpu_run(vcpu); TEST_ASSERT(false, "%s: exited with reason %d: %s\n", - __func__, state->exit_reason, - exit_reason_str(state->exit_reason)); + __func__, run->exit_reason, + exit_reason_str(run->exit_reason)); pthread_exit(NULL); } @@ -92,11 +86,11 @@ static inline void check_join(pthread_t thread, void **retval) static void run_test(uint32_t run) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; cpu_set_t cpu_set; pthread_t threads[VCPU_NUM]; pthread_t throw_away; - struct payload payloads[VCPU_NUM]; void *b; uint32_t i, j; @@ -104,18 +98,13 @@ static void run_test(uint32_t run) for (i = 0; i < VCPU_NUM; i++) CPU_SET(i, &cpu_set); - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); - kvm_vm_elf_load(vm, program_invocation_name); - vm_create_irqchip(vm); + vm = vm_create(VCPU_NUM); pr_debug("%s: [%d] start vcpus\n", __func__, run); for (i = 0; i < VCPU_NUM; ++i) { - vm_vcpu_add_default(vm, i, guest_code); - payloads[i].vm = vm; - payloads[i].index = i; + vcpu = vm_vcpu_add(vm, i, guest_code); - check_create_thread(&threads[i], NULL, run_vcpu, - (void *)&payloads[i]); + check_create_thread(&threads[i], NULL, run_vcpu, vcpu); check_set_affinity(threads[i], &cpu_set); for (j = 0; j < SLEEPING_THREAD_NUM; ++j) { diff --git a/tools/testing/selftests/kvm/include/aarch64/processor.h b/tools/testing/selftests/kvm/include/aarch64/processor.h index 59ece9d4e0d1..a8124f9dd68a 100644 --- a/tools/testing/selftests/kvm/include/aarch64/processor.h +++ b/tools/testing/selftests/kvm/include/aarch64/processor.h @@ -19,7 +19,7 @@ /* * KVM_ARM64_SYS_REG(sys_reg_id): Helper macro to convert * SYS_* register definitions in asm/sysreg.h to use in KVM - * calls such as get_reg() and set_reg(). + * calls such as vcpu_get_reg() and vcpu_set_reg(). */ #define KVM_ARM64_SYS_REG(sys_reg_id) \ ARM64_SYS_REG(sys_reg_Op0(sys_reg_id), \ @@ -47,25 +47,9 @@ #define MPIDR_HWID_BITMASK (0xff00fffffful) -static inline void get_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id, uint64_t *addr) -{ - struct kvm_one_reg reg; - reg.id = id; - reg.addr = (uint64_t)addr; - vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, ®); -} - -static inline void set_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id, uint64_t val) -{ - struct kvm_one_reg reg; - reg.id = id; - reg.addr = (uint64_t)&val; - vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, ®); -} - -void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init *init); -void aarch64_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_vcpu_init *init, void *guest_code); +void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init); +struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + struct kvm_vcpu_init *init, void *guest_code); struct ex_regs { u64 regs[31]; @@ -117,7 +101,7 @@ void aarch64_get_supported_page_sizes(uint32_t ipa, bool *ps4k, bool *ps16k, bool *ps64k); void vm_init_descriptor_tables(struct kvm_vm *vm); -void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid); +void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu); typedef void(*handler_fn)(struct ex_regs *); void vm_install_exception_handler(struct kvm_vm *vm, @@ -207,4 +191,6 @@ void smccc_hvc(uint32_t function_id, uint64_t arg0, uint64_t arg1, uint64_t arg2, uint64_t arg3, uint64_t arg4, uint64_t arg5, uint64_t arg6, struct arm_smccc_res *res); +uint32_t guest_get_vcpuid(void); + #endif /* SELFTEST_KVM_PROCESSOR_H */ diff --git a/tools/testing/selftests/kvm/include/aarch64/vgic.h b/tools/testing/selftests/kvm/include/aarch64/vgic.h index 4442081221a0..0ac6f05c63f9 100644 --- a/tools/testing/selftests/kvm/include/aarch64/vgic.h +++ b/tools/testing/selftests/kvm/include/aarch64/vgic.h @@ -8,6 +8,8 @@ #include +#include "kvm_util.h" + #define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \ (((uint64_t)(count) << 52) | \ ((uint64_t)((base) >> 16) << 16) | \ @@ -26,8 +28,8 @@ void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level); /* The vcpu arg only applies to private interrupts. */ -void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu); -void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu); +void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu); +void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu); #define KVM_IRQCHIP_NUM_PINS (1020 - 32) diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h index 92cef0ffb19e..24fde97f6121 100644 --- a/tools/testing/selftests/kvm/include/kvm_util_base.h +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h @@ -9,9 +9,14 @@ #include "test_util.h" -#include "asm/kvm.h" +#include +#include "linux/hashtable.h" #include "linux/list.h" -#include "linux/kvm.h" +#include +#include +#include "linux/rbtree.h" + + #include #include "sparsebit.h" @@ -21,20 +26,88 @@ #define NSEC_PER_SEC 1000000000L -/* - * Callers of kvm_util only have an incomplete/opaque description of the - * structure kvm_util is using to maintain the state of a VM. - */ -struct kvm_vm; - typedef uint64_t vm_paddr_t; /* Virtual Machine (Guest) physical address */ typedef uint64_t vm_vaddr_t; /* Virtual Machine (Guest) virtual address */ +struct userspace_mem_region { + struct kvm_userspace_memory_region region; + struct sparsebit *unused_phy_pages; + int fd; + off_t offset; + void *host_mem; + void *host_alias; + void *mmap_start; + void *mmap_alias; + size_t mmap_size; + struct rb_node gpa_node; + struct rb_node hva_node; + struct hlist_node slot_node; +}; + +struct kvm_vcpu { + struct list_head list; + uint32_t id; + int fd; + struct kvm_vm *vm; + struct kvm_run *run; +#ifdef __x86_64__ + struct kvm_cpuid2 *cpuid; +#endif + struct kvm_dirty_gfn *dirty_gfns; + uint32_t fetch_index; + uint32_t dirty_gfns_count; +}; + +struct userspace_mem_regions { + struct rb_root gpa_tree; + struct rb_root hva_tree; + DECLARE_HASHTABLE(slot_hash, 9); +}; + +struct kvm_vm { + int mode; + unsigned long type; + int kvm_fd; + int fd; + unsigned int pgtable_levels; + unsigned int page_size; + unsigned int page_shift; + unsigned int pa_bits; + unsigned int va_bits; + uint64_t max_gfn; + struct list_head vcpus; + struct userspace_mem_regions regions; + struct sparsebit *vpages_valid; + struct sparsebit *vpages_mapped; + bool has_irqchip; + bool pgd_created; + vm_paddr_t pgd; + vm_vaddr_t gdt; + vm_vaddr_t tss; + vm_vaddr_t idt; + vm_vaddr_t handlers; + uint32_t dirty_ring_size; + + /* Cache of information for binary stats interface */ + int stats_fd; + struct kvm_stats_header stats_header; + struct kvm_stats_desc *stats_desc; +}; + + +#define kvm_for_each_vcpu(vm, i, vcpu) \ + for ((i) = 0; (i) <= (vm)->last_vcpu_id; (i)++) \ + if (!((vcpu) = vm->vcpus[i])) \ + continue; \ + else + +struct userspace_mem_region * +memslot2region(struct kvm_vm *vm, uint32_t memslot); + /* Minimum allocated guest virtual and physical addresses */ #define KVM_UTIL_MIN_VADDR 0x2000 #define KVM_GUEST_PAGE_TABLE_MIN_PADDR 0x180000 -#define DEFAULT_GUEST_PHY_PAGES 512 #define DEFAULT_GUEST_STACK_VADDR_MIN 0xab6000 #define DEFAULT_STACK_PGS 5 @@ -102,49 +175,194 @@ extern const struct vm_guest_mode_params vm_guest_mode_params[]; int open_path_or_exit(const char *path, int flags); int open_kvm_dev_path_or_exit(void); -int kvm_check_cap(long cap); -int vm_check_cap(struct kvm_vm *vm, long cap); -int vm_enable_cap(struct kvm_vm *vm, struct kvm_enable_cap *cap); -int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id, - struct kvm_enable_cap *cap); +unsigned int kvm_check_cap(long cap); + +static inline bool kvm_has_cap(long cap) +{ + return kvm_check_cap(cap); +} + +#define __KVM_SYSCALL_ERROR(_name, _ret) \ + "%s failed, rc: %i errno: %i (%s)", (_name), (_ret), errno, strerror(errno) + +#define __KVM_IOCTL_ERROR(_name, _ret) __KVM_SYSCALL_ERROR(_name, _ret) +#define KVM_IOCTL_ERROR(_ioctl, _ret) __KVM_IOCTL_ERROR(#_ioctl, _ret) + +#define kvm_do_ioctl(fd, cmd, arg) \ +({ \ + static_assert(!_IOC_SIZE(cmd) || sizeof(*arg) == _IOC_SIZE(cmd), ""); \ + ioctl(fd, cmd, arg); \ +}) + +#define __kvm_ioctl(kvm_fd, cmd, arg) \ + kvm_do_ioctl(kvm_fd, cmd, arg) + + +#define _kvm_ioctl(kvm_fd, cmd, name, arg) \ +({ \ + int ret = __kvm_ioctl(kvm_fd, cmd, arg); \ + \ + TEST_ASSERT(!ret, __KVM_IOCTL_ERROR(name, ret)); \ +}) + +#define kvm_ioctl(kvm_fd, cmd, arg) \ + _kvm_ioctl(kvm_fd, cmd, #cmd, arg) + +static __always_inline void static_assert_is_vm(struct kvm_vm *vm) { } + +#define __vm_ioctl(vm, cmd, arg) \ +({ \ + static_assert_is_vm(vm); \ + kvm_do_ioctl((vm)->fd, cmd, arg); \ +}) + +#define _vm_ioctl(vm, cmd, name, arg) \ +({ \ + int ret = __vm_ioctl(vm, cmd, arg); \ + \ + TEST_ASSERT(!ret, __KVM_IOCTL_ERROR(name, ret)); \ +}) + +#define vm_ioctl(vm, cmd, arg) \ + _vm_ioctl(vm, cmd, #cmd, arg) + + +static __always_inline void static_assert_is_vcpu(struct kvm_vcpu *vcpu) { } + +#define __vcpu_ioctl(vcpu, cmd, arg) \ +({ \ + static_assert_is_vcpu(vcpu); \ + kvm_do_ioctl((vcpu)->fd, cmd, arg); \ +}) + +#define _vcpu_ioctl(vcpu, cmd, name, arg) \ +({ \ + int ret = __vcpu_ioctl(vcpu, cmd, arg); \ + \ + TEST_ASSERT(!ret, __KVM_IOCTL_ERROR(name, ret)); \ +}) + +#define vcpu_ioctl(vcpu, cmd, arg) \ + _vcpu_ioctl(vcpu, cmd, #cmd, arg) + +/* + * Looks up and returns the value corresponding to the capability + * (KVM_CAP_*) given by cap. + */ +static inline int vm_check_cap(struct kvm_vm *vm, long cap) +{ + int ret = __vm_ioctl(vm, KVM_CHECK_EXTENSION, (void *)cap); + + TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_CHECK_EXTENSION, ret)); + return ret; +} + +static inline int __vm_enable_cap(struct kvm_vm *vm, uint32_t cap, uint64_t arg0) +{ + struct kvm_enable_cap enable_cap = { .cap = cap, .args = { arg0 } }; + + return __vm_ioctl(vm, KVM_ENABLE_CAP, &enable_cap); +} +static inline void vm_enable_cap(struct kvm_vm *vm, uint32_t cap, uint64_t arg0) +{ + struct kvm_enable_cap enable_cap = { .cap = cap, .args = { arg0 } }; + + vm_ioctl(vm, KVM_ENABLE_CAP, &enable_cap); +} + void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size); const char *vm_guest_mode_string(uint32_t i); -struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm); void kvm_vm_free(struct kvm_vm *vmp); -void kvm_vm_restart(struct kvm_vm *vmp, int perm); +void kvm_vm_restart(struct kvm_vm *vmp); void kvm_vm_release(struct kvm_vm *vmp); -void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log); -void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log, - uint64_t first_page, uint32_t num_pages); -uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm); - int kvm_memcmp_hva_gva(void *hva, struct kvm_vm *vm, const vm_vaddr_t gva, size_t len); - void kvm_vm_elf_load(struct kvm_vm *vm, const char *filename); int kvm_memfd_alloc(size_t size, bool hugepages); void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); -/* - * VM VCPU Dump - * - * Input Args: - * stream - Output FILE stream - * vm - Virtual Machine - * vcpuid - VCPU ID - * indent - Left margin indent amount - * - * Output Args: None - * - * Return: None - * - * Dumps the current state of the VCPU specified by @vcpuid, within the VM - * given by @vm, to the FILE stream given by @stream. - */ -void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, - uint8_t indent); +static inline void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log) +{ + struct kvm_dirty_log args = { .dirty_bitmap = log, .slot = slot }; + + vm_ioctl(vm, KVM_GET_DIRTY_LOG, &args); +} + +static inline void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log, + uint64_t first_page, uint32_t num_pages) +{ + struct kvm_clear_dirty_log args = { + .dirty_bitmap = log, + .slot = slot, + .first_page = first_page, + .num_pages = num_pages + }; + + vm_ioctl(vm, KVM_CLEAR_DIRTY_LOG, &args); +} + +static inline uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm) +{ + return __vm_ioctl(vm, KVM_RESET_DIRTY_RINGS, NULL); +} + +static inline int vm_get_stats_fd(struct kvm_vm *vm) +{ + int fd = __vm_ioctl(vm, KVM_GET_STATS_FD, NULL); + + TEST_ASSERT(fd >= 0, KVM_IOCTL_ERROR(KVM_GET_STATS_FD, fd)); + return fd; +} + +static inline void read_stats_header(int stats_fd, struct kvm_stats_header *header) +{ + ssize_t ret; + + ret = read(stats_fd, header, sizeof(*header)); + TEST_ASSERT(ret == sizeof(*header), "Read stats header"); +} + +struct kvm_stats_desc *read_stats_descriptors(int stats_fd, + struct kvm_stats_header *header); + +static inline ssize_t get_stats_descriptor_size(struct kvm_stats_header *header) +{ + /* + * The base size of the descriptor is defined by KVM's ABI, but the + * size of the name field is variable, as far as KVM's ABI is + * concerned. For a given instance of KVM, the name field is the same + * size for all stats and is provided in the overall stats header. + */ + return sizeof(struct kvm_stats_desc) + header->name_size; +} + +static inline struct kvm_stats_desc *get_stats_descriptor(struct kvm_stats_desc *stats, + int index, + struct kvm_stats_header *header) +{ + /* + * Note, size_desc includes the size of the name field, which is + * variable. i.e. this is NOT equivalent to &stats_desc[i]. + */ + return (void *)stats + index * get_stats_descriptor_size(header); +} + +void read_stat_data(int stats_fd, struct kvm_stats_header *header, + struct kvm_stats_desc *desc, uint64_t *data, + size_t max_elements); + +void __vm_get_stat(struct kvm_vm *vm, const char *stat_name, uint64_t *data, + size_t max_elements); + +static inline uint64_t vm_get_stat(struct kvm_vm *vm, const char *stat_name) +{ + uint64_t data; + + __vm_get_stat(vm, stat_name, &data, 1); + return data; +} void vm_create_irqchip(struct kvm_vm *vm); @@ -157,18 +375,10 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, uint64_t guest_paddr, uint32_t slot, uint64_t npages, uint32_t flags); -void vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid, unsigned long ioctl, - void *arg); -int _vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid, unsigned long ioctl, - void *arg); -void vm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg); -int _vm_ioctl(struct kvm_vm *vm, unsigned long cmd, void *arg); -void kvm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg); -int _kvm_ioctl(struct kvm_vm *vm, unsigned long ioctl, void *arg); void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags); void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa); void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot); -void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid); +struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id); vm_vaddr_t vm_vaddr_alloc(struct kvm_vm *vm, size_t sz, vm_vaddr_t vaddr_min); vm_vaddr_t vm_vaddr_alloc_pages(struct kvm_vm *vm, int nr_pages); vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm); @@ -180,42 +390,219 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva); vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva); void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa); -/* - * Address Guest Virtual to Guest Physical - * - * Input Args: - * vm - Virtual Machine - * gva - VM virtual address - * - * Output Args: None - * - * Return: - * Equivalent VM physical address - * - * Returns the VM physical address of the translated VM virtual - * address given by @gva. - */ -vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva); +void vcpu_run(struct kvm_vcpu *vcpu); +int _vcpu_run(struct kvm_vcpu *vcpu); -struct kvm_run *vcpu_state(struct kvm_vm *vm, uint32_t vcpuid); -void vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); -int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid); -int vcpu_get_fd(struct kvm_vm *vm, uint32_t vcpuid); -void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid); -void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_guest_debug *debug); -void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_mp_state *mp_state); -struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vm *vm, uint32_t vcpuid); -void vcpu_regs_get(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs); -void vcpu_regs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs); +static inline int __vcpu_run(struct kvm_vcpu *vcpu) +{ + return __vcpu_ioctl(vcpu, KVM_RUN, NULL); +} + +void vcpu_run_complete_io(struct kvm_vcpu *vcpu); +struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu); + +static inline void vcpu_enable_cap(struct kvm_vcpu *vcpu, uint32_t cap, + uint64_t arg0) +{ + struct kvm_enable_cap enable_cap = { .cap = cap, .args = { arg0 } }; + + vcpu_ioctl(vcpu, KVM_ENABLE_CAP, &enable_cap); +} + +static inline void vcpu_guest_debug_set(struct kvm_vcpu *vcpu, + struct kvm_guest_debug *debug) +{ + vcpu_ioctl(vcpu, KVM_SET_GUEST_DEBUG, debug); +} + +static inline void vcpu_mp_state_get(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + vcpu_ioctl(vcpu, KVM_GET_MP_STATE, mp_state); +} +static inline void vcpu_mp_state_set(struct kvm_vcpu *vcpu, + struct kvm_mp_state *mp_state) +{ + vcpu_ioctl(vcpu, KVM_SET_MP_STATE, mp_state); +} + +static inline void vcpu_regs_get(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + vcpu_ioctl(vcpu, KVM_GET_REGS, regs); +} + +static inline void vcpu_regs_set(struct kvm_vcpu *vcpu, struct kvm_regs *regs) +{ + vcpu_ioctl(vcpu, KVM_SET_REGS, regs); +} +static inline void vcpu_sregs_get(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + vcpu_ioctl(vcpu, KVM_GET_SREGS, sregs); + +} +static inline void vcpu_sregs_set(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + vcpu_ioctl(vcpu, KVM_SET_SREGS, sregs); +} +static inline int _vcpu_sregs_set(struct kvm_vcpu *vcpu, struct kvm_sregs *sregs) +{ + return __vcpu_ioctl(vcpu, KVM_SET_SREGS, sregs); +} +static inline void vcpu_fpu_get(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + vcpu_ioctl(vcpu, KVM_GET_FPU, fpu); +} +static inline void vcpu_fpu_set(struct kvm_vcpu *vcpu, struct kvm_fpu *fpu) +{ + vcpu_ioctl(vcpu, KVM_SET_FPU, fpu); +} + +static inline int __vcpu_get_reg(struct kvm_vcpu *vcpu, uint64_t id, void *addr) +{ + struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)addr }; + + return __vcpu_ioctl(vcpu, KVM_GET_ONE_REG, ®); +} +static inline int __vcpu_set_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) +{ + struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)&val }; + + return __vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); +} +static inline void vcpu_get_reg(struct kvm_vcpu *vcpu, uint64_t id, void *addr) +{ + struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)addr }; + + vcpu_ioctl(vcpu, KVM_GET_ONE_REG, ®); +} +static inline void vcpu_set_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t val) +{ + struct kvm_one_reg reg = { .id = id, .addr = (uint64_t)&val }; + + vcpu_ioctl(vcpu, KVM_SET_ONE_REG, ®); +} + +#ifdef __KVM_HAVE_VCPU_EVENTS +static inline void vcpu_events_get(struct kvm_vcpu *vcpu, + struct kvm_vcpu_events *events) +{ + vcpu_ioctl(vcpu, KVM_GET_VCPU_EVENTS, events); +} +static inline void vcpu_events_set(struct kvm_vcpu *vcpu, + struct kvm_vcpu_events *events) +{ + vcpu_ioctl(vcpu, KVM_SET_VCPU_EVENTS, events); +} +#endif +#ifdef __x86_64__ +static inline void vcpu_nested_state_get(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state) +{ + vcpu_ioctl(vcpu, KVM_GET_NESTED_STATE, state); +} +static inline int __vcpu_nested_state_set(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state) +{ + return __vcpu_ioctl(vcpu, KVM_SET_NESTED_STATE, state); +} + +static inline void vcpu_nested_state_set(struct kvm_vcpu *vcpu, + struct kvm_nested_state *state) +{ + vcpu_ioctl(vcpu, KVM_SET_NESTED_STATE, state); +} +#endif +static inline int vcpu_get_stats_fd(struct kvm_vcpu *vcpu) +{ + int fd = __vcpu_ioctl(vcpu, KVM_GET_STATS_FD, NULL); + + TEST_ASSERT(fd >= 0, KVM_IOCTL_ERROR(KVM_GET_STATS_FD, fd)); + return fd; +} + +int __kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr); + +static inline void kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr) +{ + int ret = __kvm_has_device_attr(dev_fd, group, attr); + + TEST_ASSERT(!ret, "KVM_HAS_DEVICE_ATTR failed, rc: %i errno: %i", ret, errno); +} + +int __kvm_device_attr_get(int dev_fd, uint32_t group, uint64_t attr, void *val); + +static inline void kvm_device_attr_get(int dev_fd, uint32_t group, + uint64_t attr, void *val) +{ + int ret = __kvm_device_attr_get(dev_fd, group, attr, val); + + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_GET_DEVICE_ATTR, ret)); +} + +int __kvm_device_attr_set(int dev_fd, uint32_t group, uint64_t attr, void *val); + +static inline void kvm_device_attr_set(int dev_fd, uint32_t group, + uint64_t attr, void *val) +{ + int ret = __kvm_device_attr_set(dev_fd, group, attr, val); + + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_DEVICE_ATTR, ret)); +} + +static inline int __vcpu_has_device_attr(struct kvm_vcpu *vcpu, uint32_t group, + uint64_t attr) +{ + return __kvm_has_device_attr(vcpu->fd, group, attr); +} + +static inline void vcpu_has_device_attr(struct kvm_vcpu *vcpu, uint32_t group, + uint64_t attr) +{ + kvm_has_device_attr(vcpu->fd, group, attr); +} + +static inline int __vcpu_device_attr_get(struct kvm_vcpu *vcpu, uint32_t group, + uint64_t attr, void *val) +{ + return __kvm_device_attr_get(vcpu->fd, group, attr, val); +} + +static inline void vcpu_device_attr_get(struct kvm_vcpu *vcpu, uint32_t group, + uint64_t attr, void *val) +{ + kvm_device_attr_get(vcpu->fd, group, attr, val); +} + +static inline int __vcpu_device_attr_set(struct kvm_vcpu *vcpu, uint32_t group, + uint64_t attr, void *val) +{ + return __kvm_device_attr_set(vcpu->fd, group, attr, val); +} + +static inline void vcpu_device_attr_set(struct kvm_vcpu *vcpu, uint32_t group, + uint64_t attr, void *val) +{ + kvm_device_attr_set(vcpu->fd, group, attr, val); +} + +int __kvm_test_create_device(struct kvm_vm *vm, uint64_t type); +int __kvm_create_device(struct kvm_vm *vm, uint64_t type); + +static inline int kvm_create_device(struct kvm_vm *vm, uint64_t type) +{ + int fd = __kvm_create_device(vm, type); + + TEST_ASSERT(fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_DEVICE, fd)); + return fd; +} + +void *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu); /* * VM VCPU Args Set * * Input Args: * vm - Virtual Machine - * vcpuid - VCPU ID * num - number of arguments * ... - arguments, each of type uint64_t * @@ -223,59 +610,16 @@ void vcpu_regs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs); * * Return: None * - * Sets the first @num function input registers of the VCPU with @vcpuid, - * per the C calling convention of the architecture, to the values given - * as variable args. Each of the variable args is expected to be of type - * uint64_t. The maximum @num can be is specific to the architecture. + * Sets the first @num input parameters for the function at @vcpu's entry point, + * per the C calling convention of the architecture, to the values given as + * variable args. Each of the variable args is expected to be of type uint64_t. + * The maximum @num can be is specific to the architecture. */ -void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...); +void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...); -void vcpu_sregs_get(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_sregs *sregs); -void vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_sregs *sregs); -int _vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_sregs *sregs); -void vcpu_fpu_get(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_fpu *fpu); -void vcpu_fpu_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_fpu *fpu); -void vcpu_get_reg(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_one_reg *reg); -void vcpu_set_reg(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_one_reg *reg); -#ifdef __KVM_HAVE_VCPU_EVENTS -void vcpu_events_get(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_vcpu_events *events); -void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_vcpu_events *events); -#endif -#ifdef __x86_64__ -void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_nested_state *state); -int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_nested_state *state, bool ignore_error); -#endif -void *vcpu_map_dirty_ring(struct kvm_vm *vm, uint32_t vcpuid); - -int _kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr); -int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr); -int _kvm_create_device(struct kvm_vm *vm, uint64_t type, bool test, int *fd); -int kvm_create_device(struct kvm_vm *vm, uint64_t type, bool test); -int _kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, - void *val, bool write); -int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, - void *val, bool write); void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level); -int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr); -int vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr); -int _vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr, void *val, bool write); -int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr, void *val, bool write); - #define KVM_MAX_IRQ_ROUTES 4096 struct kvm_irq_routing *kvm_gsi_routing_create(void); @@ -286,26 +630,6 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing); const char *exit_reason_str(unsigned int exit_reason); -void virt_pgd_alloc(struct kvm_vm *vm); - -/* - * VM Virtual Page Map - * - * Input Args: - * vm - Virtual Machine - * vaddr - VM Virtual Address - * paddr - VM Physical Address - * memslot - Memory region slot for new virtual translation tables - * - * Output Args: None - * - * Return: None - * - * Within @vm, creates a virtual translation for the page starting - * at @vaddr to the page starting at @paddr. - */ -void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr); - vm_paddr_t vm_phy_page_alloc(struct kvm_vm *vm, vm_paddr_t paddr_min, uint32_t memslot); vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, @@ -313,55 +637,54 @@ vm_paddr_t vm_phy_pages_alloc(struct kvm_vm *vm, size_t num, vm_paddr_t vm_alloc_page_table(struct kvm_vm *vm); /* - * Create a VM with reasonable defaults - * - * Input Args: - * vcpuid - The id of the single VCPU to add to the VM. - * extra_mem_pages - The number of extra pages to add (this will - * decide how much extra space we will need to - * setup the page tables using memslot 0) - * guest_code - The vCPU's entry point - * - * Output Args: None - * - * Return: - * Pointer to opaque structure that describes the created VM. + * ____vm_create() does KVM_CREATE_VM and little else. __vm_create() also + * loads the test binary into guest memory and creates an IRQ chip (x86 only). + * __vm_create() does NOT create vCPUs, @nr_runnable_vcpus is used purely to + * calculate the amount of memory needed for per-vCPU data, e.g. stacks. */ -struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages, - void *guest_code); +struct kvm_vm *____vm_create(enum vm_guest_mode mode, uint64_t nr_pages); +struct kvm_vm *__vm_create(enum vm_guest_mode mode, uint32_t nr_runnable_vcpus, + uint64_t nr_extra_pages); -/* Same as vm_create_default, but can be used for more than one vcpu */ -struct kvm_vm *vm_create_default_with_vcpus(uint32_t nr_vcpus, uint64_t extra_mem_pages, - uint32_t num_percpu_pages, void *guest_code, - uint32_t vcpuids[]); +static inline struct kvm_vm *vm_create_barebones(void) +{ + return ____vm_create(VM_MODE_DEFAULT, 0); +} -/* Like vm_create_default_with_vcpus, but accepts mode and slot0 memory as a parameter */ -struct kvm_vm *vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus, - uint64_t slot0_mem_pages, uint64_t extra_mem_pages, - uint32_t num_percpu_pages, void *guest_code, - uint32_t vcpuids[]); +static inline struct kvm_vm *vm_create(uint32_t nr_runnable_vcpus) +{ + return __vm_create(VM_MODE_DEFAULT, nr_runnable_vcpus, 0); +} -/* Create a default VM without any vcpus. */ -struct kvm_vm *vm_create_without_vcpus(enum vm_guest_mode mode, uint64_t pages); +struct kvm_vm *__vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus, + uint64_t extra_mem_pages, + void *guest_code, struct kvm_vcpu *vcpus[]); + +static inline struct kvm_vm *vm_create_with_vcpus(uint32_t nr_vcpus, + void *guest_code, + struct kvm_vcpu *vcpus[]) +{ + return __vm_create_with_vcpus(VM_MODE_DEFAULT, nr_vcpus, 0, + guest_code, vcpus); +} /* - * Adds a vCPU with reasonable defaults (e.g. a stack) - * - * Input Args: - * vm - Virtual Machine - * vcpuid - The id of the VCPU to add to the VM. - * guest_code - The vCPU's entry point + * Create a VM with a single vCPU with reasonable defaults and @extra_mem_pages + * additional pages of guest memory. Returns the VM and vCPU (via out param). */ -void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code); +struct kvm_vm *__vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, + uint64_t extra_mem_pages, + void *guest_code); -bool vm_is_unrestricted_guest(struct kvm_vm *vm); +static inline struct kvm_vm *vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, + void *guest_code) +{ + return __vm_create_with_one_vcpu(vcpu, 0, guest_code); +} + +struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm); -unsigned int vm_get_page_size(struct kvm_vm *vm); -unsigned int vm_get_page_shift(struct kvm_vm *vm); unsigned long vm_compute_max_gfn(struct kvm_vm *vm); -uint64_t vm_get_max_gfn(struct kvm_vm *vm); -int vm_get_fd(struct kvm_vm *vm); - unsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size); unsigned int vm_num_host_pages(enum vm_guest_mode mode, unsigned int num_guest_pages); unsigned int vm_num_guest_pages(enum vm_guest_mode mode, unsigned int num_host_pages); @@ -381,11 +704,6 @@ struct kvm_userspace_memory_region * kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start, uint64_t end); -struct kvm_dirty_log * -allocate_kvm_dirty_log(struct kvm_userspace_memory_region *region); - -int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd); - #define sync_global_to_guest(vm, g) ({ \ typeof(g) *_p = addr_gva2hva(vm, (vm_vaddr_t)&(g)); \ memcpy(_p, &(g), sizeof(g)); \ @@ -396,11 +714,124 @@ int vm_create_device(struct kvm_vm *vm, struct kvm_create_device *cd); memcpy(&(g), _p, sizeof(g)); \ }) -void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid); +void assert_on_unhandled_exception(struct kvm_vcpu *vcpu); -int vm_get_stats_fd(struct kvm_vm *vm); -int vcpu_get_stats_fd(struct kvm_vm *vm, uint32_t vcpuid); +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, + uint8_t indent); -uint32_t guest_get_vcpuid(void); +static inline void vcpu_dump(FILE *stream, struct kvm_vcpu *vcpu, + uint8_t indent) +{ + vcpu_arch_dump(stream, vcpu, indent); +} + +/* + * Adds a vCPU with reasonable defaults (e.g. a stack) + * + * Input Args: + * vm - Virtual Machine + * vcpu_id - The id of the VCPU to add to the VM. + * guest_code - The vCPU's entry point + */ +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + void *guest_code); + +static inline struct kvm_vcpu *vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + void *guest_code) +{ + return vm_arch_vcpu_add(vm, vcpu_id, guest_code); +} + +/* Re-create a vCPU after restarting a VM, e.g. for state save/restore tests. */ +struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id); + +static inline struct kvm_vcpu *vm_vcpu_recreate(struct kvm_vm *vm, + uint32_t vcpu_id) +{ + return vm_arch_vcpu_recreate(vm, vcpu_id); +} + +void vcpu_arch_free(struct kvm_vcpu *vcpu); + +void virt_arch_pgd_alloc(struct kvm_vm *vm); + +static inline void virt_pgd_alloc(struct kvm_vm *vm) +{ + virt_arch_pgd_alloc(vm); +} + +/* + * VM Virtual Page Map + * + * Input Args: + * vm - Virtual Machine + * vaddr - VM Virtual Address + * paddr - VM Physical Address + * memslot - Memory region slot for new virtual translation tables + * + * Output Args: None + * + * Return: None + * + * Within @vm, creates a virtual translation for the page starting + * at @vaddr to the page starting at @paddr. + */ +void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr); + +static inline void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +{ + virt_arch_pg_map(vm, vaddr, paddr); +} + + +/* + * Address Guest Virtual to Guest Physical + * + * Input Args: + * vm - Virtual Machine + * gva - VM virtual address + * + * Output Args: None + * + * Return: + * Equivalent VM physical address + * + * Returns the VM physical address of the translated VM virtual + * address given by @gva. + */ +vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva); + +static inline vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +{ + return addr_arch_gva2gpa(vm, gva); +} + +/* + * Virtual Translation Tables Dump + * + * Input Args: + * stream - Output FILE stream + * vm - Virtual Machine + * indent - Left margin indent amount + * + * Output Args: None + * + * Return: None + * + * Dumps to the FILE stream given by @stream, the contents of all the + * virtual translation tables for the VM given by @vm. + */ +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); + +static inline void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +{ + virt_arch_dump(stream, vm, indent); +} + + +static inline int __vm_disable_nx_huge_pages(struct kvm_vm *vm) +{ + return __vm_enable_cap(vm, KVM_CAP_VM_DISABLE_NX_HUGE_PAGES, 0); +} #endif /* SELFTEST_KVM_UTIL_BASE_H */ diff --git a/tools/testing/selftests/kvm/include/perf_test_util.h b/tools/testing/selftests/kvm/include/perf_test_util.h index d822cb670f1c..eaa88df0555a 100644 --- a/tools/testing/selftests/kvm/include/perf_test_util.h +++ b/tools/testing/selftests/kvm/include/perf_test_util.h @@ -25,7 +25,8 @@ struct perf_test_vcpu_args { uint64_t pages; /* Only used by the host userspace part of the vCPU thread */ - int vcpu_id; + struct kvm_vcpu *vcpu; + int vcpu_idx; }; struct perf_test_args { @@ -44,7 +45,7 @@ struct perf_test_args { extern struct perf_test_args perf_test_args; -struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, +struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus, uint64_t vcpu_memory_bytes, int slots, enum vm_mem_backing_src_type backing_src, bool partition_vcpu_memory_access); @@ -57,6 +58,6 @@ void perf_test_join_vcpu_threads(int vcpus); void perf_test_guest_code(uint32_t vcpu_id); uint64_t perf_test_nested_pages(int nr_vcpus); -void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus); +void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]); #endif /* SELFTEST_KVM_PERF_TEST_UTIL_H */ diff --git a/tools/testing/selftests/kvm/include/riscv/processor.h b/tools/testing/selftests/kvm/include/riscv/processor.h index 4fcfd1c0389d..d00d213c3805 100644 --- a/tools/testing/selftests/kvm/include/riscv/processor.h +++ b/tools/testing/selftests/kvm/include/riscv/processor.h @@ -38,26 +38,6 @@ static inline uint64_t __kvm_reg_id(uint64_t type, uint64_t idx, KVM_REG_RISCV_TIMER_REG(name), \ KVM_REG_SIZE_U64) -static inline void get_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id, - unsigned long *addr) -{ - struct kvm_one_reg reg; - - reg.id = id; - reg.addr = (unsigned long)addr; - vcpu_get_reg(vm, vcpuid, ®); -} - -static inline void set_reg(struct kvm_vm *vm, uint32_t vcpuid, uint64_t id, - unsigned long val) -{ - struct kvm_one_reg reg; - - reg.id = id; - reg.addr = (unsigned long)&val; - vcpu_set_reg(vm, vcpuid, ®); -} - /* L3 index Bit[47:39] */ #define PGTBL_L3_INDEX_MASK 0x0000FF8000000000ULL #define PGTBL_L3_INDEX_SHIFT 39 diff --git a/tools/testing/selftests/kvm/include/test_util.h b/tools/testing/selftests/kvm/include/test_util.h index 99e0dcdc923f..5c5a88180b6c 100644 --- a/tools/testing/selftests/kvm/include/test_util.h +++ b/tools/testing/selftests/kvm/include/test_util.h @@ -34,6 +34,13 @@ static inline int _no_printf(const char *format, ...) { return 0; } #endif void print_skip(const char *fmt, ...) __attribute__((format(printf, 1, 2))); +#define __TEST_REQUIRE(f, fmt, ...) \ +do { \ + if (!(f)) \ + ksft_exit_skip("- " fmt "\n", ##__VA_ARGS__); \ +} while (0) + +#define TEST_REQUIRE(f) __TEST_REQUIRE(f, "Requirement not met: %s", #f) ssize_t test_write(int fd, const void *buf, size_t count); ssize_t test_read(int fd, void *buf, size_t count); diff --git a/tools/testing/selftests/kvm/include/ucall_common.h b/tools/testing/selftests/kvm/include/ucall_common.h index 9eecc9d40b79..ee79d180e07e 100644 --- a/tools/testing/selftests/kvm/include/ucall_common.h +++ b/tools/testing/selftests/kvm/include/ucall_common.h @@ -6,6 +6,7 @@ */ #ifndef SELFTEST_KVM_UCALL_COMMON_H #define SELFTEST_KVM_UCALL_COMMON_H +#include "test_util.h" /* Common ucalls */ enum { @@ -16,7 +17,7 @@ enum { UCALL_UNHANDLED, }; -#define UCALL_MAX_ARGS 6 +#define UCALL_MAX_ARGS 7 struct ucall { uint64_t cmd; @@ -26,17 +27,26 @@ struct ucall { void ucall_init(struct kvm_vm *vm, void *arg); void ucall_uninit(struct kvm_vm *vm); void ucall(uint64_t cmd, int nargs, ...); -uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc); +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc); #define GUEST_SYNC_ARGS(stage, arg1, arg2, arg3, arg4) \ ucall(UCALL_SYNC, 6, "hello", stage, arg1, arg2, arg3, arg4) #define GUEST_SYNC(stage) ucall(UCALL_SYNC, 2, "hello", stage) #define GUEST_DONE() ucall(UCALL_DONE, 0) -#define __GUEST_ASSERT(_condition, _condstr, _nargs, _args...) do { \ - if (!(_condition)) \ - ucall(UCALL_ABORT, 2 + _nargs, \ - "Failed guest assert: " \ - _condstr, __LINE__, _args); \ + +enum guest_assert_builtin_args { + GUEST_ERROR_STRING, + GUEST_FILE, + GUEST_LINE, + GUEST_ASSERT_BUILTIN_NARGS +}; + +#define __GUEST_ASSERT(_condition, _condstr, _nargs, _args...) \ +do { \ + if (!(_condition)) \ + ucall(UCALL_ABORT, GUEST_ASSERT_BUILTIN_NARGS + _nargs, \ + "Failed guest assert: " _condstr, \ + __FILE__, __LINE__, ##_args); \ } while (0) #define GUEST_ASSERT(_condition) \ @@ -56,4 +66,45 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc); #define GUEST_ASSERT_EQ(a, b) __GUEST_ASSERT((a) == (b), #a " == " #b, 2, a, b) +#define __REPORT_GUEST_ASSERT(_ucall, fmt, _args...) \ + TEST_FAIL("%s at %s:%ld\n" fmt, \ + (const char *)(_ucall).args[GUEST_ERROR_STRING], \ + (const char *)(_ucall).args[GUEST_FILE], \ + (_ucall).args[GUEST_LINE], \ + ##_args) + +#define GUEST_ASSERT_ARG(ucall, i) ((ucall).args[GUEST_ASSERT_BUILTIN_NARGS + i]) + +#define REPORT_GUEST_ASSERT(ucall) \ + __REPORT_GUEST_ASSERT((ucall), "") + +#define REPORT_GUEST_ASSERT_1(ucall, fmt) \ + __REPORT_GUEST_ASSERT((ucall), \ + fmt, \ + GUEST_ASSERT_ARG((ucall), 0)) + +#define REPORT_GUEST_ASSERT_2(ucall, fmt) \ + __REPORT_GUEST_ASSERT((ucall), \ + fmt, \ + GUEST_ASSERT_ARG((ucall), 0), \ + GUEST_ASSERT_ARG((ucall), 1)) + +#define REPORT_GUEST_ASSERT_3(ucall, fmt) \ + __REPORT_GUEST_ASSERT((ucall), \ + fmt, \ + GUEST_ASSERT_ARG((ucall), 0), \ + GUEST_ASSERT_ARG((ucall), 1), \ + GUEST_ASSERT_ARG((ucall), 2)) + +#define REPORT_GUEST_ASSERT_4(ucall, fmt) \ + __REPORT_GUEST_ASSERT((ucall), \ + fmt, \ + GUEST_ASSERT_ARG((ucall), 0), \ + GUEST_ASSERT_ARG((ucall), 1), \ + GUEST_ASSERT_ARG((ucall), 2), \ + GUEST_ASSERT_ARG((ucall), 3)) + +#define REPORT_GUEST_ASSERT_N(ucall, fmt, args...) \ + __REPORT_GUEST_ASSERT((ucall), fmt, ##args) + #endif /* SELFTEST_KVM_UCALL_COMMON_H */ diff --git a/tools/testing/selftests/kvm/include/x86_64/apic.h b/tools/testing/selftests/kvm/include/x86_64/apic.h index ac88557dcc9a..bed316fdecd5 100644 --- a/tools/testing/selftests/kvm/include/x86_64/apic.h +++ b/tools/testing/selftests/kvm/include/x86_64/apic.h @@ -35,6 +35,7 @@ #define APIC_SPIV_APIC_ENABLED (1 << 8) #define APIC_IRR 0x200 #define APIC_ICR 0x300 +#define APIC_LVTCMCI 0x2f0 #define APIC_DEST_SELF 0x40000 #define APIC_DEST_ALLINC 0x80000 #define APIC_DEST_ALLBUT 0xC0000 diff --git a/tools/testing/selftests/kvm/include/x86_64/evmcs.h b/tools/testing/selftests/kvm/include/x86_64/evmcs.h index cc5d14a45702..3c9260f8e116 100644 --- a/tools/testing/selftests/kvm/include/x86_64/evmcs.h +++ b/tools/testing/selftests/kvm/include/x86_64/evmcs.h @@ -241,7 +241,7 @@ struct hv_enlightened_vmcs { extern struct hv_enlightened_vmcs *current_evmcs; extern struct hv_vp_assist_page *current_vp_assist; -int vcpu_enable_evmcs(struct kvm_vm *vm, int vcpu_id); +int vcpu_enable_evmcs(struct kvm_vcpu *vcpu); static inline int enable_vp_assist(uint64_t vp_assist_pa, void *vp_assist) { diff --git a/tools/testing/selftests/kvm/include/x86_64/mce.h b/tools/testing/selftests/kvm/include/x86_64/mce.h new file mode 100644 index 000000000000..6119321f3f5d --- /dev/null +++ b/tools/testing/selftests/kvm/include/x86_64/mce.h @@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * tools/testing/selftests/kvm/include/x86_64/mce.h + * + * Copyright (C) 2022, Google LLC. + */ + +#ifndef SELFTEST_KVM_MCE_H +#define SELFTEST_KVM_MCE_H + +#define MCG_CTL_P BIT_ULL(8) /* MCG_CTL register available */ +#define MCG_SER_P BIT_ULL(24) /* MCA recovery/new status bits */ +#define MCG_LMCE_P BIT_ULL(27) /* Local machine check supported */ +#define MCG_CMCI_P BIT_ULL(10) /* CMCI supported */ +#define KVM_MAX_MCE_BANKS 32 +#define MCG_CAP_BANKS_MASK 0xff /* Bit 0-7 of the MCG_CAP register are #banks */ +#define MCI_STATUS_VAL (1ULL << 63) /* valid error */ +#define MCI_STATUS_UC (1ULL << 61) /* uncorrected error */ +#define MCI_STATUS_EN (1ULL << 60) /* error enabled */ +#define MCI_STATUS_MISCV (1ULL << 59) /* misc error reg. valid */ +#define MCI_STATUS_ADDRV (1ULL << 58) /* addr reg. valid */ +#define MCM_ADDR_PHYS 2 /* physical address */ +#define MCI_CTL2_CMCI_EN BIT_ULL(30) + +#endif /* SELFTEST_KVM_MCE_H */ diff --git a/tools/testing/selftests/kvm/include/x86_64/processor.h b/tools/testing/selftests/kvm/include/x86_64/processor.h index 6ce185449259..45edf45821d0 100644 --- a/tools/testing/selftests/kvm/include/x86_64/processor.h +++ b/tools/testing/selftests/kvm/include/x86_64/processor.h @@ -15,8 +15,12 @@ #include #include +#include + #include "../kvm_util.h" +#define NMI_VECTOR 0x02 + #define X86_EFLAGS_FIXED (1u << 1) #define X86_CR4_VME (1ul << 0) @@ -41,24 +45,122 @@ #define X86_CR4_SMAP (1ul << 21) #define X86_CR4_PKE (1ul << 22) -/* CPUID.1.ECX */ -#define CPUID_VMX (1ul << 5) -#define CPUID_SMX (1ul << 6) -#define CPUID_PCID (1ul << 17) -#define CPUID_XSAVE (1ul << 26) +/* Note, these are ordered alphabetically to match kvm_cpuid_entry2. Eww. */ +enum cpuid_output_regs { + KVM_CPUID_EAX, + KVM_CPUID_EBX, + KVM_CPUID_ECX, + KVM_CPUID_EDX +}; -/* CPUID.7.EBX */ -#define CPUID_FSGSBASE (1ul << 0) -#define CPUID_SMEP (1ul << 7) -#define CPUID_SMAP (1ul << 20) +/* + * Pack the information into a 64-bit value so that each X86_FEATURE_XXX can be + * passed by value with no overhead. + */ +struct kvm_x86_cpu_feature { + u32 function; + u16 index; + u8 reg; + u8 bit; +}; +#define KVM_X86_CPU_FEATURE(fn, idx, gpr, __bit) \ +({ \ + struct kvm_x86_cpu_feature feature = { \ + .function = fn, \ + .index = idx, \ + .reg = KVM_CPUID_##gpr, \ + .bit = __bit, \ + }; \ + \ + feature; \ +}) -/* CPUID.7.ECX */ -#define CPUID_UMIP (1ul << 2) -#define CPUID_PKU (1ul << 3) -#define CPUID_LA57 (1ul << 16) +/* + * Basic Leafs, a.k.a. Intel defined + */ +#define X86_FEATURE_MWAIT KVM_X86_CPU_FEATURE(0x1, 0, ECX, 3) +#define X86_FEATURE_VMX KVM_X86_CPU_FEATURE(0x1, 0, ECX, 5) +#define X86_FEATURE_SMX KVM_X86_CPU_FEATURE(0x1, 0, ECX, 6) +#define X86_FEATURE_PDCM KVM_X86_CPU_FEATURE(0x1, 0, ECX, 15) +#define X86_FEATURE_PCID KVM_X86_CPU_FEATURE(0x1, 0, ECX, 17) +#define X86_FEATURE_X2APIC KVM_X86_CPU_FEATURE(0x1, 0, ECX, 21) +#define X86_FEATURE_MOVBE KVM_X86_CPU_FEATURE(0x1, 0, ECX, 22) +#define X86_FEATURE_TSC_DEADLINE_TIMER KVM_X86_CPU_FEATURE(0x1, 0, ECX, 24) +#define X86_FEATURE_XSAVE KVM_X86_CPU_FEATURE(0x1, 0, ECX, 26) +#define X86_FEATURE_OSXSAVE KVM_X86_CPU_FEATURE(0x1, 0, ECX, 27) +#define X86_FEATURE_RDRAND KVM_X86_CPU_FEATURE(0x1, 0, ECX, 30) +#define X86_FEATURE_MCE KVM_X86_CPU_FEATURE(0x1, 0, EDX, 7) +#define X86_FEATURE_APIC KVM_X86_CPU_FEATURE(0x1, 0, EDX, 9) +#define X86_FEATURE_CLFLUSH KVM_X86_CPU_FEATURE(0x1, 0, EDX, 19) +#define X86_FEATURE_XMM KVM_X86_CPU_FEATURE(0x1, 0, EDX, 25) +#define X86_FEATURE_XMM2 KVM_X86_CPU_FEATURE(0x1, 0, EDX, 26) +#define X86_FEATURE_FSGSBASE KVM_X86_CPU_FEATURE(0x7, 0, EBX, 0) +#define X86_FEATURE_TSC_ADJUST KVM_X86_CPU_FEATURE(0x7, 0, EBX, 1) +#define X86_FEATURE_HLE KVM_X86_CPU_FEATURE(0x7, 0, EBX, 4) +#define X86_FEATURE_SMEP KVM_X86_CPU_FEATURE(0x7, 0, EBX, 7) +#define X86_FEATURE_INVPCID KVM_X86_CPU_FEATURE(0x7, 0, EBX, 10) +#define X86_FEATURE_RTM KVM_X86_CPU_FEATURE(0x7, 0, EBX, 11) +#define X86_FEATURE_MPX KVM_X86_CPU_FEATURE(0x7, 0, EBX, 14) +#define X86_FEATURE_SMAP KVM_X86_CPU_FEATURE(0x7, 0, EBX, 20) +#define X86_FEATURE_PCOMMIT KVM_X86_CPU_FEATURE(0x7, 0, EBX, 22) +#define X86_FEATURE_CLFLUSHOPT KVM_X86_CPU_FEATURE(0x7, 0, EBX, 23) +#define X86_FEATURE_CLWB KVM_X86_CPU_FEATURE(0x7, 0, EBX, 24) +#define X86_FEATURE_UMIP KVM_X86_CPU_FEATURE(0x7, 0, ECX, 2) +#define X86_FEATURE_PKU KVM_X86_CPU_FEATURE(0x7, 0, ECX, 3) +#define X86_FEATURE_LA57 KVM_X86_CPU_FEATURE(0x7, 0, ECX, 16) +#define X86_FEATURE_RDPID KVM_X86_CPU_FEATURE(0x7, 0, ECX, 22) +#define X86_FEATURE_SHSTK KVM_X86_CPU_FEATURE(0x7, 0, ECX, 7) +#define X86_FEATURE_IBT KVM_X86_CPU_FEATURE(0x7, 0, EDX, 20) +#define X86_FEATURE_AMX_TILE KVM_X86_CPU_FEATURE(0x7, 0, EDX, 24) +#define X86_FEATURE_SPEC_CTRL KVM_X86_CPU_FEATURE(0x7, 0, EDX, 26) +#define X86_FEATURE_ARCH_CAPABILITIES KVM_X86_CPU_FEATURE(0x7, 0, EDX, 29) +#define X86_FEATURE_PKS KVM_X86_CPU_FEATURE(0x7, 0, ECX, 31) +#define X86_FEATURE_XTILECFG KVM_X86_CPU_FEATURE(0xD, 0, EAX, 17) +#define X86_FEATURE_XTILEDATA KVM_X86_CPU_FEATURE(0xD, 0, EAX, 18) +#define X86_FEATURE_XSAVES KVM_X86_CPU_FEATURE(0xD, 1, EAX, 3) +#define X86_FEATURE_XFD KVM_X86_CPU_FEATURE(0xD, 1, EAX, 4) -/* CPUID.0x8000_0001.EDX */ -#define CPUID_GBPAGES (1ul << 26) +/* + * Extended Leafs, a.k.a. AMD defined + */ +#define X86_FEATURE_SVM KVM_X86_CPU_FEATURE(0x80000001, 0, ECX, 2) +#define X86_FEATURE_NX KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 20) +#define X86_FEATURE_GBPAGES KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 26) +#define X86_FEATURE_RDTSCP KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 27) +#define X86_FEATURE_LM KVM_X86_CPU_FEATURE(0x80000001, 0, EDX, 29) +#define X86_FEATURE_RDPRU KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 4) +#define X86_FEATURE_AMD_IBPB KVM_X86_CPU_FEATURE(0x80000008, 0, EBX, 12) +#define X86_FEATURE_NPT KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 0) +#define X86_FEATURE_LBRV KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 1) +#define X86_FEATURE_NRIPS KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 3) +#define X86_FEATURE_TSCRATEMSR KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 4) +#define X86_FEATURE_PAUSEFILTER KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 10) +#define X86_FEATURE_PFTHRESHOLD KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 12) +#define X86_FEATURE_VGIF KVM_X86_CPU_FEATURE(0x8000000A, 0, EDX, 16) +#define X86_FEATURE_SEV KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 1) +#define X86_FEATURE_SEV_ES KVM_X86_CPU_FEATURE(0x8000001F, 0, EAX, 3) + +/* + * KVM defined paravirt features. + */ +#define X86_FEATURE_KVM_CLOCKSOURCE KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 0) +#define X86_FEATURE_KVM_NOP_IO_DELAY KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 1) +#define X86_FEATURE_KVM_MMU_OP KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 2) +#define X86_FEATURE_KVM_CLOCKSOURCE2 KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 3) +#define X86_FEATURE_KVM_ASYNC_PF KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 4) +#define X86_FEATURE_KVM_STEAL_TIME KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 5) +#define X86_FEATURE_KVM_PV_EOI KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 6) +#define X86_FEATURE_KVM_PV_UNHALT KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 7) +/* Bit 8 apparently isn't used?!?! */ +#define X86_FEATURE_KVM_PV_TLB_FLUSH KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 9) +#define X86_FEATURE_KVM_ASYNC_PF_VMEXIT KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 10) +#define X86_FEATURE_KVM_PV_SEND_IPI KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 11) +#define X86_FEATURE_KVM_POLL_CONTROL KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 12) +#define X86_FEATURE_KVM_PV_SCHED_YIELD KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 13) +#define X86_FEATURE_KVM_ASYNC_PF_INT KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 14) +#define X86_FEATURE_KVM_MSI_EXT_DEST_ID KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 15) +#define X86_FEATURE_KVM_HC_MAP_GPA_RANGE KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 16) +#define X86_FEATURE_KVM_MIGRATION_CONTROL KVM_X86_CPU_FEATURE(0x40000001, 0, EAX, 17) /* Page table bitfield declarations */ #define PTE_PRESENT_MASK BIT_ULL(0) @@ -300,10 +402,13 @@ static inline void outl(uint16_t port, uint32_t value) __asm__ __volatile__("outl %%eax, %%dx" : : "d"(port), "a"(value)); } -static inline void cpuid(uint32_t *eax, uint32_t *ebx, - uint32_t *ecx, uint32_t *edx) +static inline void __cpuid(uint32_t function, uint32_t index, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) { - /* ecx is often an input as well as an output. */ + *eax = function; + *ecx = index; + asm volatile("cpuid" : "=a" (*eax), "=b" (*ebx), @@ -313,6 +418,24 @@ static inline void cpuid(uint32_t *eax, uint32_t *ebx, : "memory"); } +static inline void cpuid(uint32_t function, + uint32_t *eax, uint32_t *ebx, + uint32_t *ecx, uint32_t *edx) +{ + return __cpuid(function, 0, eax, ebx, ecx, edx); +} + +static inline bool this_cpu_has(struct kvm_x86_cpu_feature feature) +{ + uint32_t gprs[4]; + + __cpuid(feature.function, feature.index, + &gprs[KVM_CPUID_EAX], &gprs[KVM_CPUID_EBX], + &gprs[KVM_CPUID_ECX], &gprs[KVM_CPUID_EDX]); + + return gprs[feature.reg] & BIT(feature.bit); +} + #define SET_XMM(__var, __xmm) \ asm volatile("movq %0, %%"#__xmm : : "r"(__var) : #__xmm) @@ -385,6 +508,21 @@ static inline void cpu_relax(void) asm volatile("rep; nop" ::: "memory"); } +#define vmmcall() \ + __asm__ __volatile__( \ + "vmmcall\n" \ + ) + +#define ud2() \ + __asm__ __volatile__( \ + "ud2\n" \ + ) + +#define hlt() \ + __asm__ __volatile__( \ + "hlt\n" \ + ) + bool is_intel_cpu(void); bool is_amd_cpu(void); @@ -405,39 +543,198 @@ static inline unsigned int x86_model(unsigned int eax) return ((eax >> 12) & 0xf0) | ((eax >> 4) & 0x0f); } -struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid); -void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_x86_state *state); +struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu); +void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state); void kvm_x86_state_cleanup(struct kvm_x86_state *state); -struct kvm_msr_list *kvm_get_msr_index_list(void); +const struct kvm_msr_list *kvm_get_msr_index_list(void); +const struct kvm_msr_list *kvm_get_feature_msr_index_list(void); +bool kvm_msr_is_in_save_restore_list(uint32_t msr_index); uint64_t kvm_get_feature_msr(uint64_t msr_index); -struct kvm_cpuid2 *kvm_get_supported_cpuid(void); -struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vm *vm, uint32_t vcpuid); -int __vcpu_set_cpuid(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_cpuid2 *cpuid); -void vcpu_set_cpuid(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_cpuid2 *cpuid); - -struct kvm_cpuid_entry2 * -kvm_get_supported_cpuid_index(uint32_t function, uint32_t index); - -static inline struct kvm_cpuid_entry2 * -kvm_get_supported_cpuid_entry(uint32_t function) +static inline void vcpu_msrs_get(struct kvm_vcpu *vcpu, + struct kvm_msrs *msrs) { - return kvm_get_supported_cpuid_index(function, 0); + int r = __vcpu_ioctl(vcpu, KVM_GET_MSRS, msrs); + + TEST_ASSERT(r == msrs->nmsrs, + "KVM_GET_MSRS failed, r: %i (failed on MSR %x)", + r, r < 0 || r >= msrs->nmsrs ? -1 : msrs->entries[r].index); +} +static inline void vcpu_msrs_set(struct kvm_vcpu *vcpu, struct kvm_msrs *msrs) +{ + int r = __vcpu_ioctl(vcpu, KVM_SET_MSRS, msrs); + + TEST_ASSERT(r == msrs->nmsrs, + "KVM_GET_MSRS failed, r: %i (failed on MSR %x)", + r, r < 0 || r >= msrs->nmsrs ? -1 : msrs->entries[r].index); +} +static inline void vcpu_debugregs_get(struct kvm_vcpu *vcpu, + struct kvm_debugregs *debugregs) +{ + vcpu_ioctl(vcpu, KVM_GET_DEBUGREGS, debugregs); +} +static inline void vcpu_debugregs_set(struct kvm_vcpu *vcpu, + struct kvm_debugregs *debugregs) +{ + vcpu_ioctl(vcpu, KVM_SET_DEBUGREGS, debugregs); +} +static inline void vcpu_xsave_get(struct kvm_vcpu *vcpu, + struct kvm_xsave *xsave) +{ + vcpu_ioctl(vcpu, KVM_GET_XSAVE, xsave); +} +static inline void vcpu_xsave2_get(struct kvm_vcpu *vcpu, + struct kvm_xsave *xsave) +{ + vcpu_ioctl(vcpu, KVM_GET_XSAVE2, xsave); +} +static inline void vcpu_xsave_set(struct kvm_vcpu *vcpu, + struct kvm_xsave *xsave) +{ + vcpu_ioctl(vcpu, KVM_SET_XSAVE, xsave); +} +static inline void vcpu_xcrs_get(struct kvm_vcpu *vcpu, + struct kvm_xcrs *xcrs) +{ + vcpu_ioctl(vcpu, KVM_GET_XCRS, xcrs); +} +static inline void vcpu_xcrs_set(struct kvm_vcpu *vcpu, struct kvm_xcrs *xcrs) +{ + vcpu_ioctl(vcpu, KVM_SET_XCRS, xcrs); } -uint64_t vcpu_get_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index); -int _vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, - uint64_t msr_value); -void vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, - uint64_t msr_value); +const struct kvm_cpuid2 *kvm_get_supported_cpuid(void); +const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void); +const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu); + +bool kvm_cpuid_has(const struct kvm_cpuid2 *cpuid, + struct kvm_x86_cpu_feature feature); + +static inline bool kvm_cpu_has(struct kvm_x86_cpu_feature feature) +{ + return kvm_cpuid_has(kvm_get_supported_cpuid(), feature); +} + +static inline size_t kvm_cpuid2_size(int nr_entries) +{ + return sizeof(struct kvm_cpuid2) + + sizeof(struct kvm_cpuid_entry2) * nr_entries; +} + +/* + * Allocate a "struct kvm_cpuid2* instance, with the 0-length arrary of + * entries sized to hold @nr_entries. The caller is responsible for freeing + * the struct. + */ +static inline struct kvm_cpuid2 *allocate_kvm_cpuid2(int nr_entries) +{ + struct kvm_cpuid2 *cpuid; + + cpuid = malloc(kvm_cpuid2_size(nr_entries)); + TEST_ASSERT(cpuid, "-ENOMEM when allocating kvm_cpuid2"); + + cpuid->nent = nr_entries; + + return cpuid; +} + +const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, + uint32_t function, uint32_t index); +void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid); +void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu); + +static inline struct kvm_cpuid_entry2 *__vcpu_get_cpuid_entry(struct kvm_vcpu *vcpu, + uint32_t function, + uint32_t index) +{ + return (struct kvm_cpuid_entry2 *)get_cpuid_entry(vcpu->cpuid, + function, index); +} + +static inline struct kvm_cpuid_entry2 *vcpu_get_cpuid_entry(struct kvm_vcpu *vcpu, + uint32_t function) +{ + return __vcpu_get_cpuid_entry(vcpu, function, 0); +} + +static inline int __vcpu_set_cpuid(struct kvm_vcpu *vcpu) +{ + int r; + + TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first"); + r = __vcpu_ioctl(vcpu, KVM_SET_CPUID2, vcpu->cpuid); + if (r) + return r; + + /* On success, refresh the cache to pick up adjustments made by KVM. */ + vcpu_ioctl(vcpu, KVM_GET_CPUID2, vcpu->cpuid); + return 0; +} + +static inline void vcpu_set_cpuid(struct kvm_vcpu *vcpu) +{ + TEST_ASSERT(vcpu->cpuid, "Must do vcpu_init_cpuid() first"); + vcpu_ioctl(vcpu, KVM_SET_CPUID2, vcpu->cpuid); + + /* Refresh the cache to pick up adjustments made by KVM. */ + vcpu_ioctl(vcpu, KVM_GET_CPUID2, vcpu->cpuid); +} + +void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr); + +void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function); +void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu, + struct kvm_x86_cpu_feature feature, + bool set); + +static inline void vcpu_set_cpuid_feature(struct kvm_vcpu *vcpu, + struct kvm_x86_cpu_feature feature) +{ + vcpu_set_or_clear_cpuid_feature(vcpu, feature, true); + +} + +static inline void vcpu_clear_cpuid_feature(struct kvm_vcpu *vcpu, + struct kvm_x86_cpu_feature feature) +{ + vcpu_set_or_clear_cpuid_feature(vcpu, feature, false); +} + +static inline const struct kvm_cpuid_entry2 *__kvm_get_supported_cpuid_entry(uint32_t function, + uint32_t index) +{ + return get_cpuid_entry(kvm_get_supported_cpuid(), function, index); +} + +static inline const struct kvm_cpuid_entry2 *kvm_get_supported_cpuid_entry(uint32_t function) +{ + return __kvm_get_supported_cpuid_entry(function, 0); +} + +uint64_t vcpu_get_msr(struct kvm_vcpu *vcpu, uint64_t msr_index); +int _vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index, uint64_t msr_value); + +static inline void vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index, + uint64_t msr_value) +{ + int r = _vcpu_set_msr(vcpu, msr_index, msr_value); + + TEST_ASSERT(r == 1, KVM_IOCTL_ERROR(KVM_SET_MSRS, r)); +} + +static inline uint32_t kvm_get_cpuid_max_basic(void) +{ + return kvm_get_supported_cpuid_entry(0)->eax; +} + +static inline uint32_t kvm_get_cpuid_max_extended(void) +{ + return kvm_get_supported_cpuid_entry(0x80000000)->eax; +} -uint32_t kvm_get_cpuid_max_basic(void); -uint32_t kvm_get_cpuid_max_extended(void); void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits); +bool vm_is_unrestricted_guest(struct kvm_vm *vm); struct ex_regs { uint64_t rax, rcx, rdx, rbx; @@ -452,35 +749,94 @@ struct ex_regs { }; void vm_init_descriptor_tables(struct kvm_vm *vm); -void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid); +void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu); void vm_install_exception_handler(struct kvm_vm *vm, int vector, void (*handler)(struct ex_regs *)); -uint64_t vm_get_page_table_entry(struct kvm_vm *vm, int vcpuid, uint64_t vaddr); -void vm_set_page_table_entry(struct kvm_vm *vm, int vcpuid, uint64_t vaddr, - uint64_t pte); +/* If a toddler were to say "abracadabra". */ +#define KVM_EXCEPTION_MAGIC 0xabacadabaull /* - * get_cpuid() - find matching CPUID entry and return pointer to it. + * KVM selftest exception fixup uses registers to coordinate with the exception + * handler, versus the kernel's in-memory tables and KVM-Unit-Tests's in-memory + * per-CPU data. Using only registers avoids having to map memory into the + * guest, doesn't require a valid, stable GS.base, and reduces the risk of + * for recursive faults when accessing memory in the handler. The downside to + * using registers is that it restricts what registers can be used by the actual + * instruction. But, selftests are 64-bit only, making register* pressure a + * minor concern. Use r9-r11 as they are volatile, i.e. don't need* to be saved + * by the callee, and except for r11 are not implicit parameters to any + * instructions. Ideally, fixup would use r8-r10 and thus avoid implicit + * parameters entirely, but Hyper-V's hypercall ABI uses r8 and testing Hyper-V + * is higher priority than testing non-faulting SYSCALL/SYSRET. + * + * Note, the fixup handler deliberately does not handle #DE, i.e. the vector + * is guaranteed to be non-zero on fault. + * + * REGISTER INPUTS: + * r9 = MAGIC + * r10 = RIP + * r11 = new RIP on fault + * + * REGISTER OUTPUTS: + * r9 = exception vector (non-zero) */ -struct kvm_cpuid_entry2 *get_cpuid(struct kvm_cpuid2 *cpuid, uint32_t function, - uint32_t index); -/* - * set_cpuid() - overwrites a matching cpuid entry with the provided value. - * matches based on ent->function && ent->index. returns true - * if a match was found and successfully overwritten. - * @cpuid: the kvm cpuid list to modify. - * @ent: cpuid entry to insert - */ -bool set_cpuid(struct kvm_cpuid2 *cpuid, struct kvm_cpuid_entry2 *ent); +#define KVM_ASM_SAFE(insn) \ + "mov $" __stringify(KVM_EXCEPTION_MAGIC) ", %%r9\n\t" \ + "lea 1f(%%rip), %%r10\n\t" \ + "lea 2f(%%rip), %%r11\n\t" \ + "1: " insn "\n\t" \ + "mov $0, %[vector]\n\t" \ + "jmp 3f\n\t" \ + "2:\n\t" \ + "mov %%r9b, %[vector]\n\t" \ + "3:\n\t" + +#define KVM_ASM_SAFE_OUTPUTS(v) [vector] "=qm"(v) +#define KVM_ASM_SAFE_CLOBBERS "r9", "r10", "r11" + +#define kvm_asm_safe(insn, inputs...) \ +({ \ + uint8_t vector; \ + \ + asm volatile(KVM_ASM_SAFE(insn) \ + : KVM_ASM_SAFE_OUTPUTS(vector) \ + : inputs \ + : KVM_ASM_SAFE_CLOBBERS); \ + vector; \ +}) + +static inline uint8_t rdmsr_safe(uint32_t msr, uint64_t *val) +{ + uint8_t vector; + uint32_t a, d; + + asm volatile(KVM_ASM_SAFE("rdmsr") + : "=a"(a), "=d"(d), KVM_ASM_SAFE_OUTPUTS(vector) + : "c"(msr) + : KVM_ASM_SAFE_CLOBBERS); + + *val = (uint64_t)a | ((uint64_t)d << 32); + return vector; +} + +static inline uint8_t wrmsr_safe(uint32_t msr, uint64_t val) +{ + return kvm_asm_safe("wrmsr", "a"(val & -1u), "d"(val >> 32), "c"(msr)); +} + +uint64_t vm_get_page_table_entry(struct kvm_vm *vm, struct kvm_vcpu *vcpu, + uint64_t vaddr); +void vm_set_page_table_entry(struct kvm_vm *vm, struct kvm_vcpu *vcpu, + uint64_t vaddr, uint64_t pte); uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3); -struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void); -void vcpu_set_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid); -struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid); -void vm_xsave_req_perm(int bit); +void __vm_xsave_require_permission(int bit, const char *name); + +#define vm_xsave_require_permission(perm) \ + __vm_xsave_require_permission(perm, #perm) enum pg_level { PG_LEVEL_NONE, diff --git a/tools/testing/selftests/kvm/include/x86_64/svm.h b/tools/testing/selftests/kvm/include/x86_64/svm.h index 2225e5077350..c8343ff84f7f 100644 --- a/tools/testing/selftests/kvm/include/x86_64/svm.h +++ b/tools/testing/selftests/kvm/include/x86_64/svm.h @@ -218,8 +218,6 @@ struct __attribute__ ((__packed__)) vmcb { struct vmcb_save_area save; }; -#define SVM_CPUID_FUNC 0x8000000a - #define SVM_VM_CR_SVM_DISABLE 4 #define SVM_SELECTOR_S_SHIFT 4 diff --git a/tools/testing/selftests/kvm/include/x86_64/svm_util.h b/tools/testing/selftests/kvm/include/x86_64/svm_util.h index a25aabd8f5e7..a339b537a575 100644 --- a/tools/testing/selftests/kvm/include/x86_64/svm_util.h +++ b/tools/testing/selftests/kvm/include/x86_64/svm_util.h @@ -13,9 +13,8 @@ #include "svm.h" #include "processor.h" -#define CPUID_SVM_BIT 2 -#define CPUID_SVM BIT_ULL(CPUID_SVM_BIT) - +#define SVM_EXIT_EXCP_BASE 0x040 +#define SVM_EXIT_HLT 0x078 #define SVM_EXIT_MSR 0x07c #define SVM_EXIT_VMMCALL 0x081 @@ -36,21 +35,19 @@ struct svm_test_data { uint64_t msr_gpa; }; +#define stgi() \ + __asm__ __volatile__( \ + "stgi\n" \ + ) + +#define clgi() \ + __asm__ __volatile__( \ + "clgi\n" \ + ) + struct svm_test_data *vcpu_alloc_svm(struct kvm_vm *vm, vm_vaddr_t *p_svm_gva); void generic_svm_setup(struct svm_test_data *svm, void *guest_rip, void *guest_rsp); void run_guest(struct vmcb *vmcb, uint64_t vmcb_gpa); -bool nested_svm_supported(void); -void nested_svm_check_supported(void); - -static inline bool cpu_has_svm(void) -{ - u32 eax = 0x80000001, ecx; - - asm("cpuid" : - "=a" (eax), "=c" (ecx) : "0" (eax) : "ebx", "edx"); - - return ecx & CPUID_SVM; -} int open_sev_dev_path_or_exit(void); diff --git a/tools/testing/selftests/kvm/include/x86_64/vmx.h b/tools/testing/selftests/kvm/include/x86_64/vmx.h index cc3604f8f1d3..99fa1410964c 100644 --- a/tools/testing/selftests/kvm/include/x86_64/vmx.h +++ b/tools/testing/selftests/kvm/include/x86_64/vmx.h @@ -607,8 +607,6 @@ bool prepare_for_vmx_operation(struct vmx_pages *vmx); void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp); bool load_vmcs(struct vmx_pages *vmx); -bool nested_vmx_supported(void); -void nested_vmx_check_supported(void); bool ept_1g_pages_supported(void); void nested_pg_map(struct vmx_pages *vmx, struct kvm_vm *vm, diff --git a/tools/testing/selftests/kvm/kvm_binary_stats_test.c b/tools/testing/selftests/kvm/kvm_binary_stats_test.c index 17f65d514915..0b45ac593387 100644 --- a/tools/testing/selftests/kvm/kvm_binary_stats_test.c +++ b/tools/testing/selftests/kvm/kvm_binary_stats_test.c @@ -26,163 +26,167 @@ static void stats_test(int stats_fd) int i; size_t size_desc; size_t size_data = 0; - struct kvm_stats_header *header; + struct kvm_stats_header header; char *id; struct kvm_stats_desc *stats_desc; u64 *stats_data; struct kvm_stats_desc *pdesc; + u32 type, unit, base; /* Read kvm stats header */ - header = malloc(sizeof(*header)); - TEST_ASSERT(header, "Allocate memory for stats header"); + read_stats_header(stats_fd, &header); - ret = read(stats_fd, header, sizeof(*header)); - TEST_ASSERT(ret == sizeof(*header), "Read stats header"); - size_desc = sizeof(*stats_desc) + header->name_size; + size_desc = get_stats_descriptor_size(&header); /* Read kvm stats id string */ - id = malloc(header->name_size); + id = malloc(header.name_size); TEST_ASSERT(id, "Allocate memory for id string"); - ret = read(stats_fd, id, header->name_size); - TEST_ASSERT(ret == header->name_size, "Read id string"); + + ret = read(stats_fd, id, header.name_size); + TEST_ASSERT(ret == header.name_size, "Read id string"); /* Check id string, that should start with "kvm" */ - TEST_ASSERT(!strncmp(id, "kvm", 3) && strlen(id) < header->name_size, - "Invalid KVM stats type, id: %s", id); + TEST_ASSERT(!strncmp(id, "kvm", 3) && strlen(id) < header.name_size, + "Invalid KVM stats type, id: %s", id); /* Sanity check for other fields in header */ - if (header->num_desc == 0) { + if (header.num_desc == 0) { printf("No KVM stats defined!"); return; } - /* Check overlap */ - TEST_ASSERT(header->desc_offset > 0 && header->data_offset > 0 - && header->desc_offset >= sizeof(*header) - && header->data_offset >= sizeof(*header), - "Invalid offset fields in header"); - TEST_ASSERT(header->desc_offset > header->data_offset || - (header->desc_offset + size_desc * header->num_desc <= - header->data_offset), - "Descriptor block is overlapped with data block"); + /* + * The descriptor and data offsets must be valid, they must not overlap + * the header, and the descriptor and data blocks must not overlap each + * other. Note, the data block is rechecked after its size is known. + */ + TEST_ASSERT(header.desc_offset && header.desc_offset >= sizeof(header) && + header.data_offset && header.data_offset >= sizeof(header), + "Invalid offset fields in header"); + + TEST_ASSERT(header.desc_offset > header.data_offset || + (header.desc_offset + size_desc * header.num_desc <= header.data_offset), + "Descriptor block is overlapped with data block"); - /* Allocate memory for stats descriptors */ - stats_desc = calloc(header->num_desc, size_desc); - TEST_ASSERT(stats_desc, "Allocate memory for stats descriptors"); /* Read kvm stats descriptors */ - ret = pread(stats_fd, stats_desc, - size_desc * header->num_desc, header->desc_offset); - TEST_ASSERT(ret == size_desc * header->num_desc, - "Read KVM stats descriptors"); + stats_desc = read_stats_descriptors(stats_fd, &header); /* Sanity check for fields in descriptors */ - for (i = 0; i < header->num_desc; ++i) { - pdesc = (void *)stats_desc + i * size_desc; + for (i = 0; i < header.num_desc; ++i) { + pdesc = get_stats_descriptor(stats_desc, i, &header); + type = pdesc->flags & KVM_STATS_TYPE_MASK; + unit = pdesc->flags & KVM_STATS_UNIT_MASK; + base = pdesc->flags & KVM_STATS_BASE_MASK; + + /* Check name string */ + TEST_ASSERT(strlen(pdesc->name) < header.name_size, + "KVM stats name (index: %d) too long", i); + /* Check type,unit,base boundaries */ - TEST_ASSERT((pdesc->flags & KVM_STATS_TYPE_MASK) - <= KVM_STATS_TYPE_MAX, "Unknown KVM stats type"); - TEST_ASSERT((pdesc->flags & KVM_STATS_UNIT_MASK) - <= KVM_STATS_UNIT_MAX, "Unknown KVM stats unit"); - TEST_ASSERT((pdesc->flags & KVM_STATS_BASE_MASK) - <= KVM_STATS_BASE_MAX, "Unknown KVM stats base"); - /* Check exponent for stats unit + TEST_ASSERT(type <= KVM_STATS_TYPE_MAX, + "Unknown KVM stats (%s) type: %u", pdesc->name, type); + TEST_ASSERT(unit <= KVM_STATS_UNIT_MAX, + "Unknown KVM stats (%s) unit: %u", pdesc->name, unit); + TEST_ASSERT(base <= KVM_STATS_BASE_MAX, + "Unknown KVM stats (%s) base: %u", pdesc->name, base); + + /* + * Check exponent for stats unit * Exponent for counter should be greater than or equal to 0 * Exponent for unit bytes should be greater than or equal to 0 * Exponent for unit seconds should be less than or equal to 0 * Exponent for unit clock cycles should be greater than or * equal to 0 + * Exponent for unit boolean should be 0 */ switch (pdesc->flags & KVM_STATS_UNIT_MASK) { case KVM_STATS_UNIT_NONE: case KVM_STATS_UNIT_BYTES: case KVM_STATS_UNIT_CYCLES: TEST_ASSERT(pdesc->exponent >= 0, - "Unsupported KVM stats unit"); + "Unsupported KVM stats (%s) exponent: %i", + pdesc->name, pdesc->exponent); break; case KVM_STATS_UNIT_SECONDS: TEST_ASSERT(pdesc->exponent <= 0, - "Unsupported KVM stats unit"); + "Unsupported KVM stats (%s) exponent: %i", + pdesc->name, pdesc->exponent); + break; + case KVM_STATS_UNIT_BOOLEAN: + TEST_ASSERT(pdesc->exponent == 0, + "Unsupported KVM stats (%s) exponent: %d", + pdesc->name, pdesc->exponent); break; } - /* Check name string */ - TEST_ASSERT(strlen(pdesc->name) < header->name_size, - "KVM stats name(%s) too long", pdesc->name); + /* Check size field, which should not be zero */ - TEST_ASSERT(pdesc->size, "KVM descriptor(%s) with size of 0", - pdesc->name); + TEST_ASSERT(pdesc->size, + "KVM descriptor(%s) with size of 0", pdesc->name); /* Check bucket_size field */ switch (pdesc->flags & KVM_STATS_TYPE_MASK) { case KVM_STATS_TYPE_LINEAR_HIST: TEST_ASSERT(pdesc->bucket_size, - "Bucket size of Linear Histogram stats (%s) is zero", - pdesc->name); + "Bucket size of Linear Histogram stats (%s) is zero", + pdesc->name); break; default: TEST_ASSERT(!pdesc->bucket_size, - "Bucket size of stats (%s) is not zero", - pdesc->name); + "Bucket size of stats (%s) is not zero", + pdesc->name); } size_data += pdesc->size * sizeof(*stats_data); } - /* Check overlap */ - TEST_ASSERT(header->data_offset >= header->desc_offset - || header->data_offset + size_data <= header->desc_offset, - "Data block is overlapped with Descriptor block"); + + /* + * Now that the size of the data block is known, verify the data block + * doesn't overlap the descriptor block. + */ + TEST_ASSERT(header.data_offset >= header.desc_offset || + header.data_offset + size_data <= header.desc_offset, + "Data block is overlapped with Descriptor block"); + /* Check validity of all stats data size */ - TEST_ASSERT(size_data >= header->num_desc * sizeof(*stats_data), - "Data size is not correct"); + TEST_ASSERT(size_data >= header.num_desc * sizeof(*stats_data), + "Data size is not correct"); + /* Check stats offset */ - for (i = 0; i < header->num_desc; ++i) { - pdesc = (void *)stats_desc + i * size_desc; + for (i = 0; i < header.num_desc; ++i) { + pdesc = get_stats_descriptor(stats_desc, i, &header); TEST_ASSERT(pdesc->offset < size_data, - "Invalid offset (%u) for stats: %s", - pdesc->offset, pdesc->name); + "Invalid offset (%u) for stats: %s", + pdesc->offset, pdesc->name); } /* Allocate memory for stats data */ stats_data = malloc(size_data); TEST_ASSERT(stats_data, "Allocate memory for stats data"); /* Read kvm stats data as a bulk */ - ret = pread(stats_fd, stats_data, size_data, header->data_offset); + ret = pread(stats_fd, stats_data, size_data, header.data_offset); TEST_ASSERT(ret == size_data, "Read KVM stats data"); /* Read kvm stats data one by one */ - size_data = 0; - for (i = 0; i < header->num_desc; ++i) { - pdesc = (void *)stats_desc + i * size_desc; - ret = pread(stats_fd, stats_data, - pdesc->size * sizeof(*stats_data), - header->data_offset + size_data); - TEST_ASSERT(ret == pdesc->size * sizeof(*stats_data), - "Read data of KVM stats: %s", pdesc->name); - size_data += pdesc->size * sizeof(*stats_data); + for (i = 0; i < header.num_desc; ++i) { + pdesc = get_stats_descriptor(stats_desc, i, &header); + read_stat_data(stats_fd, &header, pdesc, stats_data, + pdesc->size); } free(stats_data); free(stats_desc); free(id); - free(header); } static void vm_stats_test(struct kvm_vm *vm) { - int stats_fd; - - /* Get fd for VM stats */ - stats_fd = vm_get_stats_fd(vm); - TEST_ASSERT(stats_fd >= 0, "Get VM stats fd"); + int stats_fd = vm_get_stats_fd(vm); stats_test(stats_fd); close(stats_fd); TEST_ASSERT(fcntl(stats_fd, F_GETFD) == -1, "Stats fd not freed"); } -static void vcpu_stats_test(struct kvm_vm *vm, int vcpu_id) +static void vcpu_stats_test(struct kvm_vcpu *vcpu) { - int stats_fd; - - /* Get fd for VCPU stats */ - stats_fd = vcpu_get_stats_fd(vm, vcpu_id); - TEST_ASSERT(stats_fd >= 0, "Get VCPU stats fd"); + int stats_fd = vcpu_get_stats_fd(vcpu); stats_test(stats_fd); close(stats_fd); @@ -203,6 +207,7 @@ static void vcpu_stats_test(struct kvm_vm *vm, int vcpu_id) int main(int argc, char *argv[]) { int i, j; + struct kvm_vcpu **vcpus; struct kvm_vm **vms; int max_vm = DEFAULT_NUM_VM; int max_vcpu = DEFAULT_NUM_VCPU; @@ -220,26 +225,26 @@ int main(int argc, char *argv[]) } /* Check the extension for binary stats */ - if (kvm_check_cap(KVM_CAP_BINARY_STATS_FD) <= 0) { - print_skip("Binary form statistics interface is not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_BINARY_STATS_FD)); /* Create VMs and VCPUs */ vms = malloc(sizeof(vms[0]) * max_vm); TEST_ASSERT(vms, "Allocate memory for storing VM pointers"); + + vcpus = malloc(sizeof(struct kvm_vcpu *) * max_vm * max_vcpu); + TEST_ASSERT(vcpus, "Allocate memory for storing vCPU pointers"); + for (i = 0; i < max_vm; ++i) { - vms[i] = vm_create(VM_MODE_DEFAULT, - DEFAULT_GUEST_PHY_PAGES, O_RDWR); + vms[i] = vm_create_barebones(); for (j = 0; j < max_vcpu; ++j) - vm_vcpu_add(vms[i], j); + vcpus[i * max_vcpu + j] = __vm_vcpu_add(vms[i], j); } /* Check stats read for every VM and VCPU */ for (i = 0; i < max_vm; ++i) { vm_stats_test(vms[i]); for (j = 0; j < max_vcpu; ++j) - vcpu_stats_test(vms[i], j); + vcpu_stats_test(vcpus[i * max_vcpu + j]); } for (i = 0; i < max_vm; ++i) diff --git a/tools/testing/selftests/kvm/kvm_create_max_vcpus.c b/tools/testing/selftests/kvm/kvm_create_max_vcpus.c index aed9dc3ca1e9..31b3cb24b9a7 100644 --- a/tools/testing/selftests/kvm/kvm_create_max_vcpus.c +++ b/tools/testing/selftests/kvm/kvm_create_max_vcpus.c @@ -28,11 +28,11 @@ void test_vcpu_creation(int first_vcpu_id, int num_vcpus) pr_info("Testing creating %d vCPUs, with IDs %d...%d.\n", num_vcpus, first_vcpu_id, first_vcpu_id + num_vcpus - 1); - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); + vm = vm_create_barebones(); for (i = first_vcpu_id; i < first_vcpu_id + num_vcpus; i++) /* This asserts that the vCPU was created. */ - vm_vcpu_add(vm, i); + __vm_vcpu_add(vm, i); kvm_vm_free(vm); } @@ -64,11 +64,9 @@ int main(int argc, char *argv[]) rl.rlim_max = nr_fds_wanted; int r = setrlimit(RLIMIT_NOFILE, &rl); - if (r < 0) { - printf("RLIMIT_NOFILE hard limit is too low (%d, wanted %d)\n", + __TEST_REQUIRE(r >= 0, + "RLIMIT_NOFILE hard limit is too low (%d, wanted %d)\n", old_rlim_max, nr_fds_wanted); - exit(KSFT_SKIP); - } } else { TEST_ASSERT(!setrlimit(RLIMIT_NOFILE, &rl), "setrlimit() failed!"); } diff --git a/tools/testing/selftests/kvm/kvm_page_table_test.c b/tools/testing/selftests/kvm/kvm_page_table_test.c index 2c4a7563a4f8..f42c6ac6d71d 100644 --- a/tools/testing/selftests/kvm/kvm_page_table_test.c +++ b/tools/testing/selftests/kvm/kvm_page_table_test.c @@ -46,11 +46,6 @@ static const char * const test_stage_string[] = { "KVM_ADJUST_MAPPINGS", }; -struct vcpu_args { - int vcpu_id; - bool vcpu_write; -}; - struct test_args { struct kvm_vm *vm; uint64_t guest_test_virt_mem; @@ -60,7 +55,7 @@ struct test_args { uint64_t large_num_pages; uint64_t host_pages_per_lpage; enum vm_mem_backing_src_type src_type; - struct vcpu_args vcpu_args[KVM_MAX_VCPUS]; + struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; }; /* @@ -92,17 +87,13 @@ static uint64_t guest_test_phys_mem; */ static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; -static void guest_code(int vcpu_id) +static void guest_code(bool do_write) { struct test_args *p = &test_args; - struct vcpu_args *vcpu_args = &p->vcpu_args[vcpu_id]; enum test_stage *current_stage = &guest_test_stage; uint64_t addr; int i, j; - /* Make sure vCPU args data structure is not corrupt */ - GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id); - while (true) { addr = p->guest_test_virt_mem; @@ -123,7 +114,7 @@ static void guest_code(int vcpu_id) */ case KVM_CREATE_MAPPINGS: for (i = 0; i < p->large_num_pages; i++) { - if (vcpu_args->vcpu_write) + if (do_write) *(uint64_t *)addr = 0x0123456789ABCDEF; else READ_ONCE(*(uint64_t *)addr); @@ -193,17 +184,14 @@ static void guest_code(int vcpu_id) static void *vcpu_worker(void *data) { - int ret; - struct vcpu_args *vcpu_args = data; - struct kvm_vm *vm = test_args.vm; - int vcpu_id = vcpu_args->vcpu_id; - struct kvm_run *run; + struct kvm_vcpu *vcpu = data; + bool do_write = !(vcpu->id % 2); struct timespec start; struct timespec ts_diff; enum test_stage stage; + int ret; - vcpu_args_set(vm, vcpu_id, 1, vcpu_id); - run = vcpu_state(vm, vcpu_id); + vcpu_args_set(vcpu, 1, do_write); while (!READ_ONCE(host_quit)) { ret = sem_wait(&test_stage_updated); @@ -213,15 +201,15 @@ static void *vcpu_worker(void *data) return NULL; clock_gettime(CLOCK_MONOTONIC_RAW, &start); - ret = _vcpu_run(vm, vcpu_id); + ret = _vcpu_run(vcpu); ts_diff = timespec_elapsed(start); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); - TEST_ASSERT(get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC, + TEST_ASSERT(get_ucall(vcpu, NULL) == UCALL_SYNC, "Invalid guest sync status: exit_reason=%s\n", - exit_reason_str(run->exit_reason)); + exit_reason_str(vcpu->run->exit_reason)); - pr_debug("Got sync event from vCPU %d\n", vcpu_id); + pr_debug("Got sync event from vCPU %d\n", vcpu->id); stage = READ_ONCE(*current_stage); /* @@ -230,7 +218,7 @@ static void *vcpu_worker(void *data) */ pr_debug("vCPU %d has completed stage %s\n" "execution time is: %ld.%.9lds\n\n", - vcpu_id, test_stage_string[stage], + vcpu->id, test_stage_string[stage], ts_diff.tv_sec, ts_diff.tv_nsec); ret = sem_post(&test_stage_completed); @@ -250,7 +238,6 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) { int ret; struct test_params *p = arg; - struct vcpu_args *vcpu_args; enum vm_mem_backing_src_type src_type = p->src_type; uint64_t large_page_size = get_backing_src_pagesz(src_type); uint64_t guest_page_size = vm_guest_mode_params[mode].page_size; @@ -260,7 +247,6 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) uint64_t alignment; void *host_test_mem; struct kvm_vm *vm; - int vcpu_id; /* Align up the test memory size */ alignment = max(large_page_size, guest_page_size); @@ -268,12 +254,12 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) /* Create a VM with enough guest pages */ guest_num_pages = test_mem_size / guest_page_size; - vm = vm_create_with_vcpus(mode, nr_vcpus, DEFAULT_GUEST_PHY_PAGES, - guest_num_pages, 0, guest_code, NULL); + vm = __vm_create_with_vcpus(mode, nr_vcpus, guest_num_pages, + guest_code, test_args.vcpus); /* Align down GPA of the testing memslot */ if (!p->phys_offset) - guest_test_phys_mem = (vm_get_max_gfn(vm) - guest_num_pages) * + guest_test_phys_mem = (vm->max_gfn - guest_num_pages) * guest_page_size; else guest_test_phys_mem = p->phys_offset; @@ -292,12 +278,6 @@ static struct kvm_vm *pre_init_before_test(enum vm_guest_mode mode, void *arg) test_args.host_pages_per_lpage = large_page_size / host_page_size; test_args.src_type = src_type; - for (vcpu_id = 0; vcpu_id < KVM_MAX_VCPUS; vcpu_id++) { - vcpu_args = &test_args.vcpu_args[vcpu_id]; - vcpu_args->vcpu_id = vcpu_id; - vcpu_args->vcpu_write = !(vcpu_id % 2); - } - /* Add an extra memory slot with specified backing src type */ vm_userspace_mem_region_add(vm, src_type, guest_test_phys_mem, TEST_MEM_SLOT_INDEX, guest_num_pages, 0); @@ -363,12 +343,11 @@ static void vcpus_complete_new_stage(enum test_stage stage) static void run_test(enum vm_guest_mode mode, void *arg) { - int ret; pthread_t *vcpu_threads; struct kvm_vm *vm; - int vcpu_id; struct timespec start; struct timespec ts_diff; + int ret, i; /* Create VM with vCPUs and make some pre-initialization */ vm = pre_init_before_test(mode, arg); @@ -379,10 +358,9 @@ static void run_test(enum vm_guest_mode mode, void *arg) host_quit = false; *current_stage = KVM_BEFORE_MAPPINGS; - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { - pthread_create(&vcpu_threads[vcpu_id], NULL, vcpu_worker, - &test_args.vcpu_args[vcpu_id]); - } + for (i = 0; i < nr_vcpus; i++) + pthread_create(&vcpu_threads[i], NULL, vcpu_worker, + test_args.vcpus[i]); vcpus_complete_new_stage(*current_stage); pr_info("Started all vCPUs successfully\n"); @@ -424,13 +402,13 @@ static void run_test(enum vm_guest_mode mode, void *arg) /* Tell the vcpu thread to quit */ host_quit = true; - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { + for (i = 0; i < nr_vcpus; i++) { ret = sem_post(&test_stage_updated); TEST_ASSERT(ret == 0, "Error in sem_post"); } - for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) - pthread_join(vcpu_threads[vcpu_id], NULL); + for (i = 0; i < nr_vcpus; i++) + pthread_join(vcpu_threads[i], NULL); ret = sem_destroy(&test_stage_updated); TEST_ASSERT(ret == 0, "Error in sem_destroy"); diff --git a/tools/testing/selftests/kvm/lib/aarch64/processor.c b/tools/testing/selftests/kvm/lib/aarch64/processor.c index 6a041289fa80..6f5551368944 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/processor.c +++ b/tools/testing/selftests/kvm/lib/aarch64/processor.c @@ -10,7 +10,6 @@ #include "guest_modes.h" #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "processor.h" #define DEFAULT_ARM64_GUEST_STACK_VADDR_MIN 0xac0000 @@ -75,7 +74,7 @@ static uint64_t __maybe_unused ptrs_per_pte(struct kvm_vm *vm) return 1 << (vm->page_shift - 3); } -void virt_pgd_alloc(struct kvm_vm *vm) +void virt_arch_pgd_alloc(struct kvm_vm *vm) { if (!vm->pgd_created) { vm_paddr_t paddr = vm_phy_pages_alloc(vm, @@ -132,14 +131,14 @@ static void _virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, *ptep |= (attr_idx << 2) | (1 << 10) /* Access Flag */; } -void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) { uint64_t attr_idx = 4; /* NORMAL (See DEFAULT_MAIR_EL1) */ _virt_pg_map(vm, vaddr, paddr, attr_idx); } -vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) { uint64_t *ptep; @@ -196,7 +195,7 @@ static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, uint64_t p #endif } -void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) { int level = 4 - (vm->pgtable_levels - 1); uint64_t pgd, *ptep; @@ -213,9 +212,10 @@ void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) } } -void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init *init) +void aarch64_vcpu_setup(struct kvm_vcpu *vcpu, struct kvm_vcpu_init *init) { struct kvm_vcpu_init default_init = { .target = -1, }; + struct kvm_vm *vm = vcpu->vm; uint64_t sctlr_el1, tcr_el1; if (!init) @@ -227,16 +227,16 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init init->target = preferred.target; } - vcpu_ioctl(vm, vcpuid, KVM_ARM_VCPU_INIT, init); + vcpu_ioctl(vcpu, KVM_ARM_VCPU_INIT, init); /* * Enable FP/ASIMD to avoid trapping when accessing Q0-Q15 * registers, which the variable argument list macros do. */ - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_CPACR_EL1), 3 << 20); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_CPACR_EL1), 3 << 20); - get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &sctlr_el1); - get_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TCR_EL1), &tcr_el1); + vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), &sctlr_el1); + vcpu_get_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TCR_EL1), &tcr_el1); /* Configure base granule size */ switch (vm->mode) { @@ -297,46 +297,49 @@ void aarch64_vcpu_setup(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_vcpu_init tcr_el1 |= (1 << 8) | (1 << 10) | (3 << 12); tcr_el1 |= (64 - vm->va_bits) /* T0SZ */; - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), sctlr_el1); - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TCR_EL1), tcr_el1); - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_MAIR_EL1), DEFAULT_MAIR_EL1); - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TTBR0_EL1), vm->pgd); - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_TPIDR_EL1), vcpuid); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_SCTLR_EL1), sctlr_el1); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TCR_EL1), tcr_el1); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_MAIR_EL1), DEFAULT_MAIR_EL1); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TTBR0_EL1), vm->pgd); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_TPIDR_EL1), vcpu->id); } -void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) { uint64_t pstate, pc; - get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pstate), &pstate); - get_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), &pc); + vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pstate), &pstate); + vcpu_get_reg(vcpu, ARM64_CORE_REG(regs.pc), &pc); fprintf(stream, "%*spstate: 0x%.16lx pc: 0x%.16lx\n", indent, "", pstate, pc); } -void aarch64_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_vcpu_init *init, void *guest_code) +struct kvm_vcpu *aarch64_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + struct kvm_vcpu_init *init, void *guest_code) { size_t stack_size = vm->page_size == 4096 ? DEFAULT_STACK_PGS * vm->page_size : vm->page_size; uint64_t stack_vaddr = vm_vaddr_alloc(vm, stack_size, DEFAULT_ARM64_GUEST_STACK_VADDR_MIN); + struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id); - vm_vcpu_add(vm, vcpuid); - aarch64_vcpu_setup(vm, vcpuid, init); + aarch64_vcpu_setup(vcpu, init); - set_reg(vm, vcpuid, ARM64_CORE_REG(sp_el1), stack_vaddr + stack_size); - set_reg(vm, vcpuid, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code); + vcpu_set_reg(vcpu, ARM64_CORE_REG(sp_el1), stack_vaddr + stack_size); + vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.pc), (uint64_t)guest_code); + + return vcpu; } -void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + void *guest_code) { - aarch64_vcpu_add_default(vm, vcpuid, NULL, guest_code); + return aarch64_vcpu_add(vm, vcpu_id, NULL, guest_code); } -void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) +void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) { va_list ap; int i; @@ -347,8 +350,8 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) va_start(ap, num); for (i = 0; i < num; i++) { - set_reg(vm, vcpuid, ARM64_CORE_REG(regs.regs[i]), - va_arg(ap, uint64_t)); + vcpu_set_reg(vcpu, ARM64_CORE_REG(regs.regs[i]), + va_arg(ap, uint64_t)); } va_end(ap); @@ -361,11 +364,11 @@ void kvm_exit_unexpected_exception(int vector, uint64_t ec, bool valid_ec) ; } -void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) { struct ucall uc; - if (get_ucall(vm, vcpuid, &uc) != UCALL_UNHANDLED) + if (get_ucall(vcpu, &uc) != UCALL_UNHANDLED) return; if (uc.args[2]) /* valid_ec */ { @@ -383,11 +386,11 @@ struct handlers { handler_fn exception_handlers[VECTOR_NUM][ESR_EC_NUM]; }; -void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid) +void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu) { extern char vectors; - set_reg(vm, vcpuid, KVM_ARM64_SYS_REG(SYS_VBAR_EL1), (uint64_t)&vectors); + vcpu_set_reg(vcpu, KVM_ARM64_SYS_REG(SYS_VBAR_EL1), (uint64_t)&vectors); } void route_exception(struct ex_regs *regs, int vector) @@ -469,19 +472,19 @@ void aarch64_get_supported_page_sizes(uint32_t ipa, }; kvm_fd = open_kvm_dev_path_or_exit(); - vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, ipa); - TEST_ASSERT(vm_fd >= 0, "Can't create VM"); + vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, (void *)(unsigned long)ipa); + TEST_ASSERT(vm_fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, vm_fd)); vcpu_fd = ioctl(vm_fd, KVM_CREATE_VCPU, 0); - TEST_ASSERT(vcpu_fd >= 0, "Can't create vcpu"); + TEST_ASSERT(vcpu_fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, vcpu_fd)); err = ioctl(vm_fd, KVM_ARM_PREFERRED_TARGET, &preferred_init); - TEST_ASSERT(err == 0, "Can't get target"); + TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_ARM_PREFERRED_TARGET, err)); err = ioctl(vcpu_fd, KVM_ARM_VCPU_INIT, &preferred_init); - TEST_ASSERT(err == 0, "Can't get init vcpu"); + TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_ARM_VCPU_INIT, err)); err = ioctl(vcpu_fd, KVM_GET_ONE_REG, ®); - TEST_ASSERT(err == 0, "Can't get MMFR0"); + TEST_ASSERT(err == 0, KVM_IOCTL_ERROR(KVM_GET_ONE_REG, vcpu_fd)); *ps4k = ((val >> 28) & 0xf) != 0xf; *ps64k = ((val >> 24) & 0xf) == 0; diff --git a/tools/testing/selftests/kvm/lib/aarch64/ucall.c b/tools/testing/selftests/kvm/lib/aarch64/ucall.c index be1d9728c4ce..ed237b744690 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/ucall.c +++ b/tools/testing/selftests/kvm/lib/aarch64/ucall.c @@ -5,7 +5,6 @@ * Copyright (C) 2018, Red Hat, Inc. */ #include "kvm_util.h" -#include "../kvm_util_internal.h" static vm_vaddr_t *ucall_exit_mmio_addr; @@ -52,7 +51,7 @@ void ucall_init(struct kvm_vm *vm, void *arg) * lower and won't match physical addresses. */ bits = vm->va_bits - 1; - bits = vm->pa_bits < bits ? vm->pa_bits : bits; + bits = min(vm->pa_bits, bits); end = 1ul << bits; start = end * 5 / 8; step = end / 16; @@ -78,7 +77,7 @@ void ucall(uint64_t cmd, int nargs, ...) int i; WRITE_ONCE(uc.cmd, cmd); - nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS; + nargs = min(nargs, UCALL_MAX_ARGS); va_start(va, nargs); for (i = 0; i < nargs; ++i) @@ -88,9 +87,9 @@ void ucall(uint64_t cmd, int nargs, ...) WRITE_ONCE(*ucall_exit_mmio_addr, (vm_vaddr_t)&uc); } -uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) { - struct kvm_run *run = vcpu_state(vm, vcpu_id); + struct kvm_run *run = vcpu->run; struct ucall ucall = {}; if (uc) @@ -103,9 +102,9 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) TEST_ASSERT(run->mmio.is_write && run->mmio.len == 8, "Unexpected ucall exit mmio address access"); memcpy(&gva, run->mmio.data, sizeof(gva)); - memcpy(&ucall, addr_gva2hva(vm, gva), sizeof(ucall)); + memcpy(&ucall, addr_gva2hva(vcpu->vm, gva), sizeof(ucall)); - vcpu_run_complete_io(vm, vcpu_id); + vcpu_run_complete_io(vcpu); if (uc) memcpy(uc, &ucall, sizeof(ucall)); } diff --git a/tools/testing/selftests/kvm/lib/aarch64/vgic.c b/tools/testing/selftests/kvm/lib/aarch64/vgic.c index 5d45046c1b80..b5f28d21a947 100644 --- a/tools/testing/selftests/kvm/lib/aarch64/vgic.c +++ b/tools/testing/selftests/kvm/lib/aarch64/vgic.c @@ -9,7 +9,6 @@ #include #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "vgic.h" #include "gic.h" #include "gic_v3.h" @@ -52,31 +51,30 @@ int vgic_v3_setup(struct kvm_vm *vm, unsigned int nr_vcpus, uint32_t nr_irqs, nr_vcpus, nr_vcpus_created); /* Distributor setup */ - if (_kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3, - false, &gic_fd) != 0) - return -1; + gic_fd = __kvm_create_device(vm, KVM_DEV_TYPE_ARM_VGIC_V3); + if (gic_fd < 0) + return gic_fd; - kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, - 0, &nr_irqs, true); + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irqs); - kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); - kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa, true); + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa); nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_DIST_SIZE); virt_map(vm, gicd_base_gpa, gicd_base_gpa, nr_gic_pages); /* Redistributor setup */ redist_attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, gicr_base_gpa, 0, 0); - kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, - KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &redist_attr, true); + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, + KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &redist_attr); nr_gic_pages = vm_calc_num_guest_pages(vm->mode, KVM_VGIC_V3_REDIST_SIZE * nr_vcpus); virt_map(vm, gicr_base_gpa, gicr_base_gpa, nr_gic_pages); - kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, - KVM_DEV_ARM_VGIC_CTRL_INIT, NULL, true); + kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, + KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); return gic_fd; } @@ -89,14 +87,14 @@ int _kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) uint64_t val; int ret; - ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, - attr, &val, false); + ret = __kvm_device_attr_get(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, + attr, &val); if (ret != 0) return ret; val |= 1U << index; - ret = _kvm_device_access(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, - attr, &val, true); + ret = __kvm_device_attr_set(gic_fd, KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, + attr, &val); return ret; } @@ -104,8 +102,7 @@ void kvm_irq_set_level_info(int gic_fd, uint32_t intid, int level) { int ret = _kvm_irq_set_level_info(gic_fd, intid, level); - TEST_ASSERT(ret == 0, "KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO failed, " - "rc: %i errno: %i", ret, errno); + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_DEV_ARM_VGIC_GRP_LEVEL_INFO, ret)); } int _kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) @@ -127,12 +124,11 @@ void kvm_arm_irq_line(struct kvm_vm *vm, uint32_t intid, int level) { int ret = _kvm_arm_irq_line(vm, intid, level); - TEST_ASSERT(ret == 0, "KVM_IRQ_LINE failed, rc: %i errno: %i", - ret, errno); + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret)); } -static void vgic_poke_irq(int gic_fd, uint32_t intid, - uint32_t vcpu, uint64_t reg_off) +static void vgic_poke_irq(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu, + uint64_t reg_off) { uint64_t reg = intid / 32; uint64_t index = intid % 32; @@ -145,7 +141,7 @@ static void vgic_poke_irq(int gic_fd, uint32_t intid, if (intid_is_private) { /* TODO: only vcpu 0 implemented for now. */ - assert(vcpu == 0); + assert(vcpu->id == 0); attr += SZ_64K; } @@ -158,17 +154,17 @@ static void vgic_poke_irq(int gic_fd, uint32_t intid, * intid will just make the read/writes point to above the intended * register space (i.e., ICPENDR after ISPENDR). */ - kvm_device_access(gic_fd, group, attr, &val, false); + kvm_device_attr_get(gic_fd, group, attr, &val); val |= 1ULL << index; - kvm_device_access(gic_fd, group, attr, &val, true); + kvm_device_attr_set(gic_fd, group, attr, &val); } -void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, uint32_t vcpu) +void kvm_irq_write_ispendr(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu) { vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISPENDR); } -void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, uint32_t vcpu) +void kvm_irq_write_isactiver(int gic_fd, uint32_t intid, struct kvm_vcpu *vcpu) { vgic_poke_irq(gic_fd, intid, vcpu, GICD_ISACTIVER); } diff --git a/tools/testing/selftests/kvm/lib/elf.c b/tools/testing/selftests/kvm/lib/elf.c index 13e8e3dcf984..9f54c098d9d0 100644 --- a/tools/testing/selftests/kvm/lib/elf.c +++ b/tools/testing/selftests/kvm/lib/elf.c @@ -11,7 +11,6 @@ #include #include "kvm_util.h" -#include "kvm_util_internal.h" static void elfhdr_get(const char *filename, Elf64_Ehdr *hdrp) { diff --git a/tools/testing/selftests/kvm/lib/guest_modes.c b/tools/testing/selftests/kvm/lib/guest_modes.c index 8784013b747c..99a575bbbc52 100644 --- a/tools/testing/selftests/kvm/lib/guest_modes.c +++ b/tools/testing/selftests/kvm/lib/guest_modes.c @@ -65,9 +65,9 @@ void guest_modes_append_default(void) struct kvm_s390_vm_cpu_processor info; kvm_fd = open_kvm_dev_path_or_exit(); - vm_fd = ioctl(kvm_fd, KVM_CREATE_VM, 0); - kvm_device_access(vm_fd, KVM_S390_VM_CPU_MODEL, - KVM_S390_VM_CPU_PROCESSOR, &info, false); + vm_fd = __kvm_ioctl(kvm_fd, KVM_CREATE_VM, NULL); + kvm_device_attr_get(vm_fd, KVM_S390_VM_CPU_MODEL, + KVM_S390_VM_CPU_PROCESSOR, &info); close(vm_fd); close(kvm_fd); /* Starting with z13 we have 47bits of physical address */ diff --git a/tools/testing/selftests/kvm/lib/kvm_util.c b/tools/testing/selftests/kvm/lib/kvm_util.c index 1665a220abcb..9889fe0d8919 100644 --- a/tools/testing/selftests/kvm/lib/kvm_util.c +++ b/tools/testing/selftests/kvm/lib/kvm_util.c @@ -8,7 +8,6 @@ #define _GNU_SOURCE /* for program_invocation_name */ #include "test_util.h" #include "kvm_util.h" -#include "kvm_util_internal.h" #include "processor.h" #include @@ -27,10 +26,7 @@ int open_path_or_exit(const char *path, int flags) int fd; fd = open(path, flags); - if (fd < 0) { - print_skip("%s not available (errno: %d)", path, errno); - exit(KSFT_SKIP); - } + __TEST_REQUIRE(fd >= 0, "%s not available (errno: %d)", path, errno); return fd; } @@ -70,121 +66,34 @@ int open_kvm_dev_path_or_exit(void) * Looks up and returns the value corresponding to the capability * (KVM_CAP_*) given by cap. */ -int kvm_check_cap(long cap) +unsigned int kvm_check_cap(long cap) { int ret; int kvm_fd; kvm_fd = open_kvm_dev_path_or_exit(); - ret = ioctl(kvm_fd, KVM_CHECK_EXTENSION, cap); - TEST_ASSERT(ret >= 0, "KVM_CHECK_EXTENSION IOCTL failed,\n" - " rc: %i errno: %i", ret, errno); + ret = __kvm_ioctl(kvm_fd, KVM_CHECK_EXTENSION, (void *)cap); + TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_CHECK_EXTENSION, ret)); close(kvm_fd); - return ret; -} - -/* VM Check Capability - * - * Input Args: - * vm - Virtual Machine - * cap - Capability - * - * Output Args: None - * - * Return: - * On success, the Value corresponding to the capability (KVM_CAP_*) - * specified by the value of cap. On failure a TEST_ASSERT failure - * is produced. - * - * Looks up and returns the value corresponding to the capability - * (KVM_CAP_*) given by cap. - */ -int vm_check_cap(struct kvm_vm *vm, long cap) -{ - int ret; - - ret = ioctl(vm->fd, KVM_CHECK_EXTENSION, cap); - TEST_ASSERT(ret >= 0, "KVM_CHECK_EXTENSION VM IOCTL failed,\n" - " rc: %i errno: %i", ret, errno); - - return ret; -} - -/* VM Enable Capability - * - * Input Args: - * vm - Virtual Machine - * cap - Capability - * - * Output Args: None - * - * Return: On success, 0. On failure a TEST_ASSERT failure is produced. - * - * Enables a capability (KVM_CAP_*) on the VM. - */ -int vm_enable_cap(struct kvm_vm *vm, struct kvm_enable_cap *cap) -{ - int ret; - - ret = ioctl(vm->fd, KVM_ENABLE_CAP, cap); - TEST_ASSERT(ret == 0, "KVM_ENABLE_CAP IOCTL failed,\n" - " rc: %i errno: %i", ret, errno); - - return ret; -} - -/* VCPU Enable Capability - * - * Input Args: - * vm - Virtual Machine - * vcpu_id - VCPU - * cap - Capability - * - * Output Args: None - * - * Return: On success, 0. On failure a TEST_ASSERT failure is produced. - * - * Enables a capability (KVM_CAP_*) on the VCPU. - */ -int vcpu_enable_cap(struct kvm_vm *vm, uint32_t vcpu_id, - struct kvm_enable_cap *cap) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpu_id); - int r; - - TEST_ASSERT(vcpu, "cannot find vcpu %d", vcpu_id); - - r = ioctl(vcpu->fd, KVM_ENABLE_CAP, cap); - TEST_ASSERT(!r, "KVM_ENABLE_CAP vCPU ioctl failed,\n" - " rc: %i, errno: %i", r, errno); - - return r; + return (unsigned int)ret; } void vm_enable_dirty_ring(struct kvm_vm *vm, uint32_t ring_size) { - struct kvm_enable_cap cap = { 0 }; - - cap.cap = KVM_CAP_DIRTY_LOG_RING; - cap.args[0] = ring_size; - vm_enable_cap(vm, &cap); + vm_enable_cap(vm, KVM_CAP_DIRTY_LOG_RING, ring_size); vm->dirty_ring_size = ring_size; } -static void vm_open(struct kvm_vm *vm, int perm) +static void vm_open(struct kvm_vm *vm) { - vm->kvm_fd = _open_kvm_dev_path_or_exit(perm); + vm->kvm_fd = _open_kvm_dev_path_or_exit(O_RDWR); - if (!kvm_check_cap(KVM_CAP_IMMEDIATE_EXIT)) { - print_skip("immediate_exit not available"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_IMMEDIATE_EXIT)); - vm->fd = ioctl(vm->kvm_fd, KVM_CREATE_VM, vm->type); - TEST_ASSERT(vm->fd >= 0, "KVM_CREATE_VM ioctl failed, " - "rc: %i errno: %i", vm->fd, errno); + vm->fd = __kvm_ioctl(vm->kvm_fd, KVM_CREATE_VM, (void *)vm->type); + TEST_ASSERT(vm->fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, vm->fd)); } const char *vm_guest_mode_string(uint32_t i) @@ -234,31 +143,12 @@ const struct vm_guest_mode_params vm_guest_mode_params[] = { _Static_assert(sizeof(vm_guest_mode_params)/sizeof(struct vm_guest_mode_params) == NUM_VM_MODES, "Missing new mode params?"); -/* - * VM Create - * - * Input Args: - * mode - VM Mode (e.g. VM_MODE_P52V48_4K) - * phy_pages - Physical memory pages - * perm - permission - * - * Output Args: None - * - * Return: - * Pointer to opaque structure that describes the created VM. - * - * Creates a VM with the mode specified by mode (e.g. VM_MODE_P52V48_4K). - * When phy_pages is non-zero, a memory region of phy_pages physical pages - * is created and mapped starting at guest physical address 0. The file - * descriptor to control the created VM is created with the permissions - * given by perm (e.g. O_RDWR). - */ -struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm) +struct kvm_vm *____vm_create(enum vm_guest_mode mode, uint64_t nr_pages) { struct kvm_vm *vm; - pr_debug("%s: mode='%s' pages='%ld' perm='%d'\n", __func__, - vm_guest_mode_string(mode), phy_pages, perm); + pr_debug("%s: mode='%s' pages='%ld'\n", __func__, + vm_guest_mode_string(mode), nr_pages); vm = calloc(1, sizeof(*vm)); TEST_ASSERT(vm != NULL, "Insufficient Memory"); @@ -340,7 +230,7 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm) vm->type = KVM_VM_TYPE_ARM_IPA_SIZE(vm->pa_bits); #endif - vm_open(vm, perm); + vm_open(vm); /* Limit to VA-bit canonical virtual addresses. */ vm->vpages_valid = sparsebit_alloc(); @@ -355,18 +245,56 @@ struct kvm_vm *vm_create(enum vm_guest_mode mode, uint64_t phy_pages, int perm) /* Allocate and setup memory for guest. */ vm->vpages_mapped = sparsebit_alloc(); - if (phy_pages != 0) + if (nr_pages != 0) vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, - 0, 0, phy_pages, 0); + 0, 0, nr_pages, 0); return vm; } -struct kvm_vm *vm_create_without_vcpus(enum vm_guest_mode mode, uint64_t pages) +static uint64_t vm_nr_pages_required(enum vm_guest_mode mode, + uint32_t nr_runnable_vcpus, + uint64_t extra_mem_pages) { + uint64_t nr_pages; + + TEST_ASSERT(nr_runnable_vcpus, + "Use vm_create_barebones() for VMs that _never_ have vCPUs\n"); + + TEST_ASSERT(nr_runnable_vcpus <= kvm_check_cap(KVM_CAP_MAX_VCPUS), + "nr_vcpus = %d too large for host, max-vcpus = %d", + nr_runnable_vcpus, kvm_check_cap(KVM_CAP_MAX_VCPUS)); + + /* + * Arbitrarily allocate 512 pages (2mb when page size is 4kb) for the + * test code and other per-VM assets that will be loaded into memslot0. + */ + nr_pages = 512; + + /* Account for the per-vCPU stacks on behalf of the test. */ + nr_pages += nr_runnable_vcpus * DEFAULT_STACK_PGS; + + /* + * Account for the number of pages needed for the page tables. The + * maximum page table size for a memory region will be when the + * smallest page size is used. Considering each page contains x page + * table descriptors, the total extra size for page tables (for extra + * N pages) will be: N/x+N/x^2+N/x^3+... which is definitely smaller + * than N/x*2. + */ + nr_pages += (nr_pages + extra_mem_pages) / PTES_PER_MIN_PAGE * 2; + + return vm_adjust_num_guest_pages(mode, nr_pages); +} + +struct kvm_vm *__vm_create(enum vm_guest_mode mode, uint32_t nr_runnable_vcpus, + uint64_t nr_extra_pages) +{ + uint64_t nr_pages = vm_nr_pages_required(mode, nr_runnable_vcpus, + nr_extra_pages); struct kvm_vm *vm; - vm = vm_create(mode, pages, O_RDWR); + vm = ____vm_create(mode, nr_pages); kvm_vm_elf_load(vm, program_invocation_name); @@ -382,9 +310,7 @@ struct kvm_vm *vm_create_without_vcpus(enum vm_guest_mode mode, uint64_t pages) * Input Args: * mode - VM Mode (e.g. VM_MODE_P52V48_4K) * nr_vcpus - VCPU count - * slot0_mem_pages - Slot0 physical memory size * extra_mem_pages - Non-slot0 physical memory total size - * num_percpu_pages - Per-cpu physical memory pages * guest_code - Guest entry point * vcpuids - VCPU IDs * @@ -393,64 +319,39 @@ struct kvm_vm *vm_create_without_vcpus(enum vm_guest_mode mode, uint64_t pages) * Return: * Pointer to opaque structure that describes the created VM. * - * Creates a VM with the mode specified by mode (e.g. VM_MODE_P52V48_4K), - * with customized slot0 memory size, at least 512 pages currently. + * Creates a VM with the mode specified by mode (e.g. VM_MODE_P52V48_4K). * extra_mem_pages is only used to calculate the maximum page table size, * no real memory allocation for non-slot0 memory in this function. */ -struct kvm_vm *vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus, - uint64_t slot0_mem_pages, uint64_t extra_mem_pages, - uint32_t num_percpu_pages, void *guest_code, - uint32_t vcpuids[]) +struct kvm_vm *__vm_create_with_vcpus(enum vm_guest_mode mode, uint32_t nr_vcpus, + uint64_t extra_mem_pages, + void *guest_code, struct kvm_vcpu *vcpus[]) { - uint64_t vcpu_pages, extra_pg_pages, pages; struct kvm_vm *vm; int i; - /* Force slot0 memory size not small than DEFAULT_GUEST_PHY_PAGES */ - if (slot0_mem_pages < DEFAULT_GUEST_PHY_PAGES) - slot0_mem_pages = DEFAULT_GUEST_PHY_PAGES; + TEST_ASSERT(!nr_vcpus || vcpus, "Must provide vCPU array"); - /* The maximum page table size for a memory region will be when the - * smallest pages are used. Considering each page contains x page - * table descriptors, the total extra size for page tables (for extra - * N pages) will be: N/x+N/x^2+N/x^3+... which is definitely smaller - * than N/x*2. - */ - vcpu_pages = (DEFAULT_STACK_PGS + num_percpu_pages) * nr_vcpus; - extra_pg_pages = (slot0_mem_pages + extra_mem_pages + vcpu_pages) / PTES_PER_MIN_PAGE * 2; - pages = slot0_mem_pages + vcpu_pages + extra_pg_pages; + vm = __vm_create(mode, nr_vcpus, extra_mem_pages); - TEST_ASSERT(nr_vcpus <= kvm_check_cap(KVM_CAP_MAX_VCPUS), - "nr_vcpus = %d too large for host, max-vcpus = %d", - nr_vcpus, kvm_check_cap(KVM_CAP_MAX_VCPUS)); - - pages = vm_adjust_num_guest_pages(mode, pages); - - vm = vm_create_without_vcpus(mode, pages); - - for (i = 0; i < nr_vcpus; ++i) { - uint32_t vcpuid = vcpuids ? vcpuids[i] : i; - - vm_vcpu_add_default(vm, vcpuid, guest_code); - } + for (i = 0; i < nr_vcpus; ++i) + vcpus[i] = vm_vcpu_add(vm, i, guest_code); return vm; } -struct kvm_vm *vm_create_default_with_vcpus(uint32_t nr_vcpus, uint64_t extra_mem_pages, - uint32_t num_percpu_pages, void *guest_code, - uint32_t vcpuids[]) +struct kvm_vm *__vm_create_with_one_vcpu(struct kvm_vcpu **vcpu, + uint64_t extra_mem_pages, + void *guest_code) { - return vm_create_with_vcpus(VM_MODE_DEFAULT, nr_vcpus, DEFAULT_GUEST_PHY_PAGES, - extra_mem_pages, num_percpu_pages, guest_code, vcpuids); -} + struct kvm_vcpu *vcpus[1]; + struct kvm_vm *vm; -struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages, - void *guest_code) -{ - return vm_create_default_with_vcpus(1, extra_mem_pages, 0, guest_code, - (uint32_t []){ vcpuid }); + vm = __vm_create_with_vcpus(VM_MODE_DEFAULT, 1, extra_mem_pages, + guest_code, vcpus); + + *vcpu = vcpus[0]; + return vm; } /* @@ -458,7 +359,6 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages, * * Input Args: * vm - VM that has been released before - * perm - permission * * Output Args: None * @@ -466,12 +366,12 @@ struct kvm_vm *vm_create_default(uint32_t vcpuid, uint64_t extra_mem_pages, * global state, such as the irqchip and the memory regions that are mapped * into the guest. */ -void kvm_vm_restart(struct kvm_vm *vmp, int perm) +void kvm_vm_restart(struct kvm_vm *vmp) { int ctr; struct userspace_mem_region *region; - vm_open(vmp, perm); + vm_open(vmp); if (vmp->has_irqchip) vm_create_irqchip(vmp); @@ -488,34 +388,17 @@ void kvm_vm_restart(struct kvm_vm *vmp, int perm) } } -void kvm_vm_get_dirty_log(struct kvm_vm *vm, int slot, void *log) +__weak struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, + uint32_t vcpu_id) { - struct kvm_dirty_log args = { .dirty_bitmap = log, .slot = slot }; - int ret; - - ret = ioctl(vm->fd, KVM_GET_DIRTY_LOG, &args); - TEST_ASSERT(ret == 0, "%s: KVM_GET_DIRTY_LOG failed: %s", - __func__, strerror(-ret)); + return __vm_vcpu_add(vm, vcpu_id); } -void kvm_vm_clear_dirty_log(struct kvm_vm *vm, int slot, void *log, - uint64_t first_page, uint32_t num_pages) +struct kvm_vcpu *vm_recreate_with_one_vcpu(struct kvm_vm *vm) { - struct kvm_clear_dirty_log args = { - .dirty_bitmap = log, .slot = slot, - .first_page = first_page, - .num_pages = num_pages - }; - int ret; + kvm_vm_restart(vm); - ret = ioctl(vm->fd, KVM_CLEAR_DIRTY_LOG, &args); - TEST_ASSERT(ret == 0, "%s: KVM_CLEAR_DIRTY_LOG failed: %s", - __func__, strerror(-ret)); -} - -uint32_t kvm_vm_reset_dirty_ring(struct kvm_vm *vm) -{ - return ioctl(vm->fd, KVM_RESET_DIRTY_RINGS); + return vm_vcpu_recreate(vm, 0); } /* @@ -589,32 +472,9 @@ kvm_userspace_memory_region_find(struct kvm_vm *vm, uint64_t start, return ®ion->region; } -/* - * VCPU Find - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: None - * - * Return: - * Pointer to VCPU structure - * - * Locates a vcpu structure that describes the VCPU specified by vcpuid and - * returns a pointer to it. Returns NULL if the VM doesn't contain a VCPU - * for the specified vcpuid. - */ -struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid) +__weak void vcpu_arch_free(struct kvm_vcpu *vcpu) { - struct vcpu *vcpu; - list_for_each_entry(vcpu, &vm->vcpus, list) { - if (vcpu->id == vcpuid) - return vcpu; - } - - return NULL; } /* @@ -629,43 +489,41 @@ struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid) * * Removes a vCPU from a VM and frees its resources. */ -static void vm_vcpu_rm(struct kvm_vm *vm, struct vcpu *vcpu) +static void vm_vcpu_rm(struct kvm_vm *vm, struct kvm_vcpu *vcpu) { int ret; if (vcpu->dirty_gfns) { ret = munmap(vcpu->dirty_gfns, vm->dirty_ring_size); - TEST_ASSERT(ret == 0, "munmap of VCPU dirty ring failed, " - "rc: %i errno: %i", ret, errno); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); vcpu->dirty_gfns = NULL; } - ret = munmap(vcpu->state, vcpu_mmap_sz()); - TEST_ASSERT(ret == 0, "munmap of VCPU fd failed, rc: %i " - "errno: %i", ret, errno); + ret = munmap(vcpu->run, vcpu_mmap_sz()); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); + ret = close(vcpu->fd); - TEST_ASSERT(ret == 0, "Close of VCPU fd failed, rc: %i " - "errno: %i", ret, errno); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret)); list_del(&vcpu->list); + + vcpu_arch_free(vcpu); free(vcpu); } void kvm_vm_release(struct kvm_vm *vmp) { - struct vcpu *vcpu, *tmp; + struct kvm_vcpu *vcpu, *tmp; int ret; list_for_each_entry_safe(vcpu, tmp, &vmp->vcpus, list) vm_vcpu_rm(vmp, vcpu); ret = close(vmp->fd); - TEST_ASSERT(ret == 0, "Close of vm fd failed,\n" - " vmp->fd: %i rc: %i errno: %i", vmp->fd, ret, errno); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret)); ret = close(vmp->kvm_fd); - TEST_ASSERT(ret == 0, "Close of /dev/kvm fd failed,\n" - " vmp->kvm_fd: %i rc: %i errno: %i", vmp->kvm_fd, ret, errno); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("close()", ret)); } static void __vm_mem_region_delete(struct kvm_vm *vm, @@ -681,13 +539,11 @@ static void __vm_mem_region_delete(struct kvm_vm *vm, } region->region.memory_size = 0; - ret = ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region); - TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed, " - "rc: %i errno: %i", ret, errno); + vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); sparsebit_free(®ion->unused_phy_pages); ret = munmap(region->mmap_start, region->mmap_size); - TEST_ASSERT(ret == 0, "munmap failed, rc: %i errno: %i", ret, errno); + TEST_ASSERT(!ret, __KVM_SYSCALL_ERROR("munmap()", ret)); free(region); } @@ -704,6 +560,12 @@ void kvm_vm_free(struct kvm_vm *vmp) if (vmp == NULL) return; + /* Free cached stats metadata and close FD */ + if (vmp->stats_fd) { + free(vmp->stats_desc); + close(vmp->stats_fd); + } + /* Free userspace_mem_regions. */ hash_for_each_safe(vmp->regions.slot_hash, ctr, node, region, slot_node) __vm_mem_region_delete(vmp, region, false); @@ -727,14 +589,13 @@ int kvm_memfd_alloc(size_t size, bool hugepages) memfd_flags |= MFD_HUGETLB; fd = memfd_create("kvm_selftest", memfd_flags); - TEST_ASSERT(fd != -1, "memfd_create() failed, errno: %i (%s)", - errno, strerror(errno)); + TEST_ASSERT(fd != -1, __KVM_SYSCALL_ERROR("memfd_create()", fd)); r = ftruncate(fd, size); - TEST_ASSERT(!r, "ftruncate() failed, errno: %i (%s)", errno, strerror(errno)); + TEST_ASSERT(!r, __KVM_SYSCALL_ERROR("ftruncate()", r)); r = fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, size); - TEST_ASSERT(!r, "fallocate() failed, errno: %i (%s)", errno, strerror(errno)); + TEST_ASSERT(!r, __KVM_SYSCALL_ERROR("fallocate()", r)); return fd; } @@ -1000,8 +861,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, vm_mem_backing_src_alias(src_type)->flag, region->fd, 0); TEST_ASSERT(region->mmap_start != MAP_FAILED, - "test_malloc failed, mmap_start: %p errno: %i", - region->mmap_start, errno); + __KVM_SYSCALL_ERROR("mmap()", (int)(unsigned long)MAP_FAILED)); TEST_ASSERT(!is_backing_src_hugetlb(src_type) || region->mmap_start == align_ptr_up(region->mmap_start, backing_src_pagesz), @@ -1029,7 +889,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, region->region.guest_phys_addr = guest_paddr; region->region.memory_size = npages * vm->page_size; region->region.userspace_addr = (uintptr_t) region->host_mem; - ret = ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region); + ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n" " rc: %i errno: %i\n" " slot: %u flags: 0x%x\n" @@ -1049,7 +909,7 @@ void vm_userspace_mem_region_add(struct kvm_vm *vm, vm_mem_backing_src_alias(src_type)->flag, region->fd, 0); TEST_ASSERT(region->mmap_alias != MAP_FAILED, - "mmap of alias failed, errno: %i", errno); + __KVM_SYSCALL_ERROR("mmap()", (int)(unsigned long)MAP_FAILED)); /* Align host alias address */ region->host_alias = align_ptr_up(region->mmap_alias, alignment); @@ -1112,7 +972,7 @@ void vm_mem_region_set_flags(struct kvm_vm *vm, uint32_t slot, uint32_t flags) region->region.flags = flags; - ret = ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region); + ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); TEST_ASSERT(ret == 0, "KVM_SET_USER_MEMORY_REGION IOCTL failed,\n" " rc: %i errno: %i slot: %u flags: 0x%x", @@ -1142,7 +1002,7 @@ void vm_mem_region_move(struct kvm_vm *vm, uint32_t slot, uint64_t new_gpa) region->region.guest_phys_addr = new_gpa; - ret = ioctl(vm->fd, KVM_SET_USER_MEMORY_REGION, ®ion->region); + ret = __vm_ioctl(vm, KVM_SET_USER_MEMORY_REGION, ®ion->region); TEST_ASSERT(!ret, "KVM_SET_USER_MEMORY_REGION failed\n" "ret: %i errno: %i slot: %u new_gpa: 0x%lx", @@ -1167,19 +1027,7 @@ void vm_mem_region_delete(struct kvm_vm *vm, uint32_t slot) __vm_mem_region_delete(vm, memslot2region(vm, slot), true); } -/* - * VCPU mmap Size - * - * Input Args: None - * - * Output Args: None - * - * Return: - * Size of VCPU state - * - * Returns the size of the structure pointed to by the return value - * of vcpu_state(). - */ +/* Returns the size of a vCPU's kvm_run structure. */ static int vcpu_mmap_sz(void) { int dev_fd, ret; @@ -1188,59 +1036,57 @@ static int vcpu_mmap_sz(void) ret = ioctl(dev_fd, KVM_GET_VCPU_MMAP_SIZE, NULL); TEST_ASSERT(ret >= sizeof(struct kvm_run), - "%s KVM_GET_VCPU_MMAP_SIZE ioctl failed, rc: %i errno: %i", - __func__, ret, errno); + KVM_IOCTL_ERROR(KVM_GET_VCPU_MMAP_SIZE, ret)); close(dev_fd); return ret; } -/* - * VM VCPU Add - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: None - * - * Return: None - * - * Adds a virtual CPU to the VM specified by vm with the ID given by vcpuid. - * No additional VCPU setup is done. - */ -void vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpuid) +static bool vcpu_exists(struct kvm_vm *vm, uint32_t vcpu_id) { - struct vcpu *vcpu; + struct kvm_vcpu *vcpu; + + list_for_each_entry(vcpu, &vm->vcpus, list) { + if (vcpu->id == vcpu_id) + return true; + } + + return false; +} + +/* + * Adds a virtual CPU to the VM specified by vm with the ID given by vcpu_id. + * No additional vCPU setup is done. Returns the vCPU. + */ +struct kvm_vcpu *__vm_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id) +{ + struct kvm_vcpu *vcpu; /* Confirm a vcpu with the specified id doesn't already exist. */ - vcpu = vcpu_find(vm, vcpuid); - if (vcpu != NULL) - TEST_FAIL("vcpu with the specified id " - "already exists,\n" - " requested vcpuid: %u\n" - " existing vcpuid: %u state: %p", - vcpuid, vcpu->id, vcpu->state); + TEST_ASSERT(!vcpu_exists(vm, vcpu_id), "vCPU%d already exists\n", vcpu_id); /* Allocate and initialize new vcpu structure. */ vcpu = calloc(1, sizeof(*vcpu)); TEST_ASSERT(vcpu != NULL, "Insufficient Memory"); - vcpu->id = vcpuid; - vcpu->fd = ioctl(vm->fd, KVM_CREATE_VCPU, vcpuid); - TEST_ASSERT(vcpu->fd >= 0, "KVM_CREATE_VCPU failed, rc: %i errno: %i", - vcpu->fd, errno); - TEST_ASSERT(vcpu_mmap_sz() >= sizeof(*vcpu->state), "vcpu mmap size " + vcpu->vm = vm; + vcpu->id = vcpu_id; + vcpu->fd = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)(unsigned long)vcpu_id); + TEST_ASSERT(vcpu->fd >= 0, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, vcpu->fd)); + + TEST_ASSERT(vcpu_mmap_sz() >= sizeof(*vcpu->run), "vcpu mmap size " "smaller than expected, vcpu_mmap_sz: %i expected_min: %zi", - vcpu_mmap_sz(), sizeof(*vcpu->state)); - vcpu->state = (struct kvm_run *) mmap(NULL, vcpu_mmap_sz(), + vcpu_mmap_sz(), sizeof(*vcpu->run)); + vcpu->run = (struct kvm_run *) mmap(NULL, vcpu_mmap_sz(), PROT_READ | PROT_WRITE, MAP_SHARED, vcpu->fd, 0); - TEST_ASSERT(vcpu->state != MAP_FAILED, "mmap vcpu_state failed, " - "vcpu id: %u errno: %i", vcpuid, errno); + TEST_ASSERT(vcpu->run != MAP_FAILED, + __KVM_SYSCALL_ERROR("mmap()", (int)(unsigned long)MAP_FAILED)); /* Add to linked-list of VCPUs. */ list_add(&vcpu->list, &vm->vcpus); + + return vcpu; } /* @@ -1336,8 +1182,6 @@ va_found: * vm - Virtual Machine * sz - Size in bytes * vaddr_min - Minimum starting virtual address - * data_memslot - Memory region slot for data pages - * pgd_memslot - Memory region slot for new virtual translation tables * * Output Args: None * @@ -1423,7 +1267,6 @@ vm_vaddr_t vm_vaddr_alloc_page(struct kvm_vm *vm) * vaddr - Virtuall address to map * paddr - VM Physical Address * npages - The number of pages to map - * pgd_memslot - Memory region slot for new virtual translation tables * * Output Args: None * @@ -1534,11 +1377,10 @@ vm_paddr_t addr_hva2gpa(struct kvm_vm *vm, void *hva) * (without failing the test) if the guest memory is not shared (so * no alias exists). * - * When vm_create() and related functions are called with a shared memory - * src_type, we also create a writable, shared alias mapping of the - * underlying guest memory. This allows the host to manipulate guest memory - * without mapping that memory in the guest's address space. And, for - * userfaultfd-based demand paging, we can do so without triggering userfaults. + * Create a writable, shared virtual=>physical alias for the specific GPA. + * The primary use case is to allow the host selftest to manipulate guest + * memory without mapping said memory in the guest's address space. And, for + * userfaultfd-based demand paging, to do so without triggering userfaults. */ void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa) { @@ -1556,452 +1398,90 @@ void *addr_gpa2alias(struct kvm_vm *vm, vm_paddr_t gpa) return (void *) ((uintptr_t) region->host_alias + offset); } -/* - * VM Create IRQ Chip - * - * Input Args: - * vm - Virtual Machine - * - * Output Args: None - * - * Return: None - * - * Creates an interrupt controller chip for the VM specified by vm. - */ +/* Create an interrupt controller chip for the specified VM. */ void vm_create_irqchip(struct kvm_vm *vm) { - int ret; - - ret = ioctl(vm->fd, KVM_CREATE_IRQCHIP, 0); - TEST_ASSERT(ret == 0, "KVM_CREATE_IRQCHIP IOCTL failed, " - "rc: %i errno: %i", ret, errno); + vm_ioctl(vm, KVM_CREATE_IRQCHIP, NULL); vm->has_irqchip = true; } -/* - * VM VCPU State - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: None - * - * Return: - * Pointer to structure that describes the state of the VCPU. - * - * Locates and returns a pointer to a structure that describes the - * state of the VCPU with the given vcpuid. - */ -struct kvm_run *vcpu_state(struct kvm_vm *vm, uint32_t vcpuid) +int _vcpu_run(struct kvm_vcpu *vcpu) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - return vcpu->state; -} - -/* - * VM VCPU Run - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: None - * - * Return: None - * - * Switch to executing the code for the VCPU given by vcpuid, within the VM - * given by vm. - */ -void vcpu_run(struct kvm_vm *vm, uint32_t vcpuid) -{ - int ret = _vcpu_run(vm, vcpuid); - TEST_ASSERT(ret == 0, "KVM_RUN IOCTL failed, " - "rc: %i errno: %i", ret, errno); -} - -int _vcpu_run(struct kvm_vm *vm, uint32_t vcpuid) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); int rc; - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); do { - rc = ioctl(vcpu->fd, KVM_RUN, NULL); + rc = __vcpu_run(vcpu); } while (rc == -1 && errno == EINTR); - assert_on_unhandled_exception(vm, vcpuid); + assert_on_unhandled_exception(vcpu); return rc; } -int vcpu_get_fd(struct kvm_vm *vm, uint32_t vcpuid) +/* + * Invoke KVM_RUN on a vCPU until KVM returns something other than -EINTR. + * Assert if the KVM returns an error (other than -EINTR). + */ +void vcpu_run(struct kvm_vcpu *vcpu) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); + int ret = _vcpu_run(vcpu); - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - return vcpu->fd; + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_RUN, ret)); } -void vcpu_run_complete_io(struct kvm_vm *vm, uint32_t vcpuid) +void vcpu_run_complete_io(struct kvm_vcpu *vcpu) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); int ret; - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - vcpu->state->immediate_exit = 1; - ret = ioctl(vcpu->fd, KVM_RUN, NULL); - vcpu->state->immediate_exit = 0; + vcpu->run->immediate_exit = 1; + ret = __vcpu_run(vcpu); + vcpu->run->immediate_exit = 0; TEST_ASSERT(ret == -1 && errno == EINTR, "KVM_RUN IOCTL didn't exit immediately, rc: %i, errno: %i", ret, errno); } -void vcpu_set_guest_debug(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_guest_debug *debug) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret = ioctl(vcpu->fd, KVM_SET_GUEST_DEBUG, debug); - - TEST_ASSERT(ret == 0, "KVM_SET_GUEST_DEBUG failed: %d", ret); -} - /* - * VM VCPU Set MP State - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * mp_state - mp_state to be set - * - * Output Args: None - * - * Return: None - * - * Sets the MP state of the VCPU given by vcpuid, to the state given - * by mp_state. - */ -void vcpu_set_mp_state(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_mp_state *mp_state) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_SET_MP_STATE, mp_state); - TEST_ASSERT(ret == 0, "KVM_SET_MP_STATE IOCTL failed, " - "rc: %i errno: %i", ret, errno); -} - -/* - * VM VCPU Get Reg List - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: - * None - * - * Return: - * A pointer to an allocated struct kvm_reg_list - * * Get the list of guest registers which are supported for - * KVM_GET_ONE_REG/KVM_SET_ONE_REG calls + * KVM_GET_ONE_REG/KVM_SET_ONE_REG ioctls. Returns a kvm_reg_list pointer, + * it is the caller's responsibility to free the list. */ -struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vm *vm, uint32_t vcpuid) +struct kvm_reg_list *vcpu_get_reg_list(struct kvm_vcpu *vcpu) { struct kvm_reg_list reg_list_n = { .n = 0 }, *reg_list; int ret; - ret = _vcpu_ioctl(vm, vcpuid, KVM_GET_REG_LIST, ®_list_n); + ret = __vcpu_ioctl(vcpu, KVM_GET_REG_LIST, ®_list_n); TEST_ASSERT(ret == -1 && errno == E2BIG, "KVM_GET_REG_LIST n=0"); + reg_list = calloc(1, sizeof(*reg_list) + reg_list_n.n * sizeof(__u64)); reg_list->n = reg_list_n.n; - vcpu_ioctl(vm, vcpuid, KVM_GET_REG_LIST, reg_list); + vcpu_ioctl(vcpu, KVM_GET_REG_LIST, reg_list); return reg_list; } -/* - * VM VCPU Regs Get - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: - * regs - current state of VCPU regs - * - * Return: None - * - * Obtains the current register state for the VCPU specified by vcpuid - * and stores it at the location given by regs. - */ -void vcpu_regs_get(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs) +void *vcpu_map_dirty_ring(struct kvm_vcpu *vcpu) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_GET_REGS, regs); - TEST_ASSERT(ret == 0, "KVM_GET_REGS failed, rc: %i errno: %i", - ret, errno); -} - -/* - * VM VCPU Regs Set - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * regs - Values to set VCPU regs to - * - * Output Args: None - * - * Return: None - * - * Sets the regs of the VCPU specified by vcpuid to the values - * given by regs. - */ -void vcpu_regs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_regs *regs) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_SET_REGS, regs); - TEST_ASSERT(ret == 0, "KVM_SET_REGS failed, rc: %i errno: %i", - ret, errno); -} - -#ifdef __KVM_HAVE_VCPU_EVENTS -void vcpu_events_get(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_vcpu_events *events) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_GET_VCPU_EVENTS, events); - TEST_ASSERT(ret == 0, "KVM_GET_VCPU_EVENTS, failed, rc: %i errno: %i", - ret, errno); -} - -void vcpu_events_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_vcpu_events *events) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_SET_VCPU_EVENTS, events); - TEST_ASSERT(ret == 0, "KVM_SET_VCPU_EVENTS, failed, rc: %i errno: %i", - ret, errno); -} -#endif - -#ifdef __x86_64__ -void vcpu_nested_state_get(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_nested_state *state) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_GET_NESTED_STATE, state); - TEST_ASSERT(ret == 0, - "KVM_SET_NESTED_STATE failed, ret: %i errno: %i", - ret, errno); -} - -int vcpu_nested_state_set(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_nested_state *state, bool ignore_error) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_SET_NESTED_STATE, state); - if (!ignore_error) { - TEST_ASSERT(ret == 0, - "KVM_SET_NESTED_STATE failed, ret: %i errno: %i", - ret, errno); - } - - return ret; -} -#endif - -/* - * VM VCPU System Regs Get - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * - * Output Args: - * sregs - current state of VCPU system regs - * - * Return: None - * - * Obtains the current system register state for the VCPU specified by - * vcpuid and stores it at the location given by sregs. - */ -void vcpu_sregs_get(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_sregs *sregs) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, KVM_GET_SREGS, sregs); - TEST_ASSERT(ret == 0, "KVM_GET_SREGS failed, rc: %i errno: %i", - ret, errno); -} - -/* - * VM VCPU System Regs Set - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * sregs - Values to set VCPU system regs to - * - * Output Args: None - * - * Return: None - * - * Sets the system regs of the VCPU specified by vcpuid to the values - * given by sregs. - */ -void vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_sregs *sregs) -{ - int ret = _vcpu_sregs_set(vm, vcpuid, sregs); - TEST_ASSERT(ret == 0, "KVM_SET_SREGS IOCTL failed, " - "rc: %i errno: %i", ret, errno); -} - -int _vcpu_sregs_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_sregs *sregs) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - return ioctl(vcpu->fd, KVM_SET_SREGS, sregs); -} - -void vcpu_fpu_get(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_fpu *fpu) -{ - int ret; - - ret = _vcpu_ioctl(vm, vcpuid, KVM_GET_FPU, fpu); - TEST_ASSERT(ret == 0, "KVM_GET_FPU failed, rc: %i errno: %i (%s)", - ret, errno, strerror(errno)); -} - -void vcpu_fpu_set(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_fpu *fpu) -{ - int ret; - - ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_FPU, fpu); - TEST_ASSERT(ret == 0, "KVM_SET_FPU failed, rc: %i errno: %i (%s)", - ret, errno, strerror(errno)); -} - -void vcpu_get_reg(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_one_reg *reg) -{ - int ret; - - ret = _vcpu_ioctl(vm, vcpuid, KVM_GET_ONE_REG, reg); - TEST_ASSERT(ret == 0, "KVM_GET_ONE_REG failed, rc: %i errno: %i (%s)", - ret, errno, strerror(errno)); -} - -void vcpu_set_reg(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_one_reg *reg) -{ - int ret; - - ret = _vcpu_ioctl(vm, vcpuid, KVM_SET_ONE_REG, reg); - TEST_ASSERT(ret == 0, "KVM_SET_ONE_REG failed, rc: %i errno: %i (%s)", - ret, errno, strerror(errno)); -} - -/* - * VCPU Ioctl - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * cmd - Ioctl number - * arg - Argument to pass to the ioctl - * - * Return: None - * - * Issues an arbitrary ioctl on a VCPU fd. - */ -void vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid, - unsigned long cmd, void *arg) -{ - int ret; - - ret = _vcpu_ioctl(vm, vcpuid, cmd, arg); - TEST_ASSERT(ret == 0, "vcpu ioctl %lu failed, rc: %i errno: %i (%s)", - cmd, ret, errno, strerror(errno)); -} - -int _vcpu_ioctl(struct kvm_vm *vm, uint32_t vcpuid, - unsigned long cmd, void *arg) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int ret; - - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - ret = ioctl(vcpu->fd, cmd, arg); - - return ret; -} - -void *vcpu_map_dirty_ring(struct kvm_vm *vm, uint32_t vcpuid) -{ - struct vcpu *vcpu; - uint32_t size = vm->dirty_ring_size; + uint32_t page_size = vcpu->vm->page_size; + uint32_t size = vcpu->vm->dirty_ring_size; TEST_ASSERT(size > 0, "Should enable dirty ring first"); - vcpu = vcpu_find(vm, vcpuid); - - TEST_ASSERT(vcpu, "Cannot find vcpu %u", vcpuid); - if (!vcpu->dirty_gfns) { void *addr; - addr = mmap(NULL, size, PROT_READ, - MAP_PRIVATE, vcpu->fd, - vm->page_size * KVM_DIRTY_LOG_PAGE_OFFSET); + addr = mmap(NULL, size, PROT_READ, MAP_PRIVATE, vcpu->fd, + page_size * KVM_DIRTY_LOG_PAGE_OFFSET); TEST_ASSERT(addr == MAP_FAILED, "Dirty ring mapped private"); - addr = mmap(NULL, size, PROT_READ | PROT_EXEC, - MAP_PRIVATE, vcpu->fd, - vm->page_size * KVM_DIRTY_LOG_PAGE_OFFSET); + addr = mmap(NULL, size, PROT_READ | PROT_EXEC, MAP_PRIVATE, vcpu->fd, + page_size * KVM_DIRTY_LOG_PAGE_OFFSET); TEST_ASSERT(addr == MAP_FAILED, "Dirty ring mapped exec"); - addr = mmap(NULL, size, PROT_READ | PROT_WRITE, - MAP_SHARED, vcpu->fd, - vm->page_size * KVM_DIRTY_LOG_PAGE_OFFSET); + addr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpu->fd, + page_size * KVM_DIRTY_LOG_PAGE_OFFSET); TEST_ASSERT(addr != MAP_FAILED, "Dirty ring map failed"); vcpu->dirty_gfns = addr; @@ -2011,63 +1491,11 @@ void *vcpu_map_dirty_ring(struct kvm_vm *vm, uint32_t vcpuid) return vcpu->dirty_gfns; } -/* - * VM Ioctl - * - * Input Args: - * vm - Virtual Machine - * cmd - Ioctl number - * arg - Argument to pass to the ioctl - * - * Return: None - * - * Issues an arbitrary ioctl on a VM fd. - */ -void vm_ioctl(struct kvm_vm *vm, unsigned long cmd, void *arg) -{ - int ret; - - ret = _vm_ioctl(vm, cmd, arg); - TEST_ASSERT(ret == 0, "vm ioctl %lu failed, rc: %i errno: %i (%s)", - cmd, ret, errno, strerror(errno)); -} - -int _vm_ioctl(struct kvm_vm *vm, unsigned long cmd, void *arg) -{ - return ioctl(vm->fd, cmd, arg); -} - -/* - * KVM system ioctl - * - * Input Args: - * vm - Virtual Machine - * cmd - Ioctl number - * arg - Argument to pass to the ioctl - * - * Return: None - * - * Issues an arbitrary ioctl on a KVM fd. - */ -void kvm_ioctl(struct kvm_vm *vm, unsigned long cmd, void *arg) -{ - int ret; - - ret = ioctl(vm->kvm_fd, cmd, arg); - TEST_ASSERT(ret == 0, "KVM ioctl %lu failed, rc: %i errno: %i (%s)", - cmd, ret, errno, strerror(errno)); -} - -int _kvm_ioctl(struct kvm_vm *vm, unsigned long cmd, void *arg) -{ - return ioctl(vm->kvm_fd, cmd, arg); -} - /* * Device Ioctl */ -int _kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr) +int __kvm_has_device_attr(int dev_fd, uint32_t group, uint64_t attr) { struct kvm_device_attr attribute = { .group = group, @@ -2078,43 +1506,31 @@ int _kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr) return ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attribute); } -int kvm_device_check_attr(int dev_fd, uint32_t group, uint64_t attr) +int __kvm_test_create_device(struct kvm_vm *vm, uint64_t type) { - int ret = _kvm_device_check_attr(dev_fd, group, attr); + struct kvm_create_device create_dev = { + .type = type, + .flags = KVM_CREATE_DEVICE_TEST, + }; - TEST_ASSERT(!ret, "KVM_HAS_DEVICE_ATTR failed, rc: %i errno: %i", ret, errno); - return ret; + return __vm_ioctl(vm, KVM_CREATE_DEVICE, &create_dev); } -int _kvm_create_device(struct kvm_vm *vm, uint64_t type, bool test, int *fd) +int __kvm_create_device(struct kvm_vm *vm, uint64_t type) { - struct kvm_create_device create_dev; - int ret; + struct kvm_create_device create_dev = { + .type = type, + .fd = -1, + .flags = 0, + }; + int err; - create_dev.type = type; - create_dev.fd = -1; - create_dev.flags = test ? KVM_CREATE_DEVICE_TEST : 0; - ret = ioctl(vm_get_fd(vm), KVM_CREATE_DEVICE, &create_dev); - *fd = create_dev.fd; - return ret; + err = __vm_ioctl(vm, KVM_CREATE_DEVICE, &create_dev); + TEST_ASSERT(err <= 0, "KVM_CREATE_DEVICE shouldn't return a positive value"); + return err ? : create_dev.fd; } -int kvm_create_device(struct kvm_vm *vm, uint64_t type, bool test) -{ - int fd, ret; - - ret = _kvm_create_device(vm, type, test, &fd); - - if (!test) { - TEST_ASSERT(!ret, - "KVM_CREATE_DEVICE IOCTL failed, rc: %i errno: %i", ret, errno); - return fd; - } - return ret; -} - -int _kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, - void *val, bool write) +int __kvm_device_attr_get(int dev_fd, uint32_t group, uint64_t attr, void *val) { struct kvm_device_attr kvmattr = { .group = group, @@ -2122,58 +1538,20 @@ int _kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, .flags = 0, .addr = (uintptr_t)val, }; - int ret; - ret = ioctl(dev_fd, write ? KVM_SET_DEVICE_ATTR : KVM_GET_DEVICE_ATTR, - &kvmattr); - return ret; + return __kvm_ioctl(dev_fd, KVM_GET_DEVICE_ATTR, &kvmattr); } -int kvm_device_access(int dev_fd, uint32_t group, uint64_t attr, - void *val, bool write) +int __kvm_device_attr_set(int dev_fd, uint32_t group, uint64_t attr, void *val) { - int ret = _kvm_device_access(dev_fd, group, attr, val, write); + struct kvm_device_attr kvmattr = { + .group = group, + .attr = attr, + .flags = 0, + .addr = (uintptr_t)val, + }; - TEST_ASSERT(!ret, "KVM_SET|GET_DEVICE_ATTR IOCTL failed, rc: %i errno: %i", ret, errno); - return ret; -} - -int _vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - - TEST_ASSERT(vcpu, "nonexistent vcpu id: %d", vcpuid); - - return _kvm_device_check_attr(vcpu->fd, group, attr); -} - -int vcpu_has_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr) -{ - int ret = _vcpu_has_device_attr(vm, vcpuid, group, attr); - - TEST_ASSERT(!ret, "KVM_HAS_DEVICE_ATTR IOCTL failed, rc: %i errno: %i", ret, errno); - return ret; -} - -int _vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr, void *val, bool write) -{ - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - - TEST_ASSERT(vcpu, "nonexistent vcpu id: %d", vcpuid); - - return _kvm_device_access(vcpu->fd, group, attr, val, write); -} - -int vcpu_access_device_attr(struct kvm_vm *vm, uint32_t vcpuid, uint32_t group, - uint64_t attr, void *val, bool write) -{ - int ret = _vcpu_access_device_attr(vm, vcpuid, group, attr, val, write); - - TEST_ASSERT(!ret, "KVM_SET|GET_DEVICE_ATTR IOCTL failed, rc: %i errno: %i", ret, errno); - return ret; + return __kvm_ioctl(dev_fd, KVM_SET_DEVICE_ATTR, &kvmattr); } /* @@ -2187,14 +1565,14 @@ int _kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) .level = level, }; - return _vm_ioctl(vm, KVM_IRQ_LINE, &irq_level); + return __vm_ioctl(vm, KVM_IRQ_LINE, &irq_level); } void kvm_irq_line(struct kvm_vm *vm, uint32_t irq, int level) { int ret = _kvm_irq_line(vm, irq, level); - TEST_ASSERT(ret >= 0, "KVM_IRQ_LINE failed, rc: %i errno: %i", ret, errno); + TEST_ASSERT(ret >= 0, KVM_IOCTL_ERROR(KVM_IRQ_LINE, ret)); } struct kvm_irq_routing *kvm_gsi_routing_create(void) @@ -2233,7 +1611,7 @@ int _kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) int ret; assert(routing); - ret = ioctl(vm_get_fd(vm), KVM_SET_GSI_ROUTING, routing); + ret = __vm_ioctl(vm, KVM_SET_GSI_ROUTING, routing); free(routing); return ret; @@ -2244,8 +1622,7 @@ void kvm_gsi_routing_write(struct kvm_vm *vm, struct kvm_irq_routing *routing) int ret; ret = _kvm_gsi_routing_write(vm, routing); - TEST_ASSERT(ret == 0, "KVM_SET_GSI_ROUTING failed, rc: %i errno: %i", - ret, errno); + TEST_ASSERT(!ret, KVM_IOCTL_ERROR(KVM_SET_GSI_ROUTING, ret)); } /* @@ -2267,7 +1644,7 @@ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) { int ctr; struct userspace_mem_region *region; - struct vcpu *vcpu; + struct kvm_vcpu *vcpu; fprintf(stream, "%*smode: 0x%x\n", indent, "", vm->mode); fprintf(stream, "%*sfd: %i\n", indent, "", vm->fd); @@ -2292,8 +1669,9 @@ void vm_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) virt_dump(stream, vm, indent + 4); } fprintf(stream, "%*sVCPUs:\n", indent, ""); + list_for_each_entry(vcpu, &vm->vcpus, list) - vcpu_dump(stream, vm, vcpu->id, indent + 2); + vcpu_dump(stream, vcpu, indent + 2); } /* Known KVM exit reasons */ @@ -2447,64 +1825,11 @@ void *addr_gva2hva(struct kvm_vm *vm, vm_vaddr_t gva) return addr_gpa2hva(vm, addr_gva2gpa(vm, gva)); } -/* - * Is Unrestricted Guest - * - * Input Args: - * vm - Virtual Machine - * - * Output Args: None - * - * Return: True if the unrestricted guest is set to 'Y', otherwise return false. - * - * Check if the unrestricted guest flag is enabled. - */ -bool vm_is_unrestricted_guest(struct kvm_vm *vm) -{ - char val = 'N'; - size_t count; - FILE *f; - - if (vm == NULL) { - /* Ensure that the KVM vendor-specific module is loaded. */ - close(open_kvm_dev_path_or_exit()); - } - - f = fopen("/sys/module/kvm_intel/parameters/unrestricted_guest", "r"); - if (f) { - count = fread(&val, sizeof(char), 1, f); - TEST_ASSERT(count == 1, "Unable to read from param file."); - fclose(f); - } - - return val == 'Y'; -} - -unsigned int vm_get_page_size(struct kvm_vm *vm) -{ - return vm->page_size; -} - -unsigned int vm_get_page_shift(struct kvm_vm *vm) -{ - return vm->page_shift; -} - -unsigned long __attribute__((weak)) vm_compute_max_gfn(struct kvm_vm *vm) +unsigned long __weak vm_compute_max_gfn(struct kvm_vm *vm) { return ((1ULL << vm->pa_bits) >> vm->page_shift) - 1; } -uint64_t vm_get_max_gfn(struct kvm_vm *vm) -{ - return vm->max_gfn; -} - -int vm_get_fd(struct kvm_vm *vm) -{ - return vm->fd; -} - static unsigned int vm_calc_num_pages(unsigned int num_pages, unsigned int page_shift, unsigned int new_page_shift, @@ -2545,14 +1870,112 @@ unsigned int vm_calc_num_guest_pages(enum vm_guest_mode mode, size_t size) return vm_adjust_num_guest_pages(mode, n); } -int vm_get_stats_fd(struct kvm_vm *vm) +/* + * Read binary stats descriptors + * + * Input Args: + * stats_fd - the file descriptor for the binary stats file from which to read + * header - the binary stats metadata header corresponding to the given FD + * + * Output Args: None + * + * Return: + * A pointer to a newly allocated series of stat descriptors. + * Caller is responsible for freeing the returned kvm_stats_desc. + * + * Read the stats descriptors from the binary stats interface. + */ +struct kvm_stats_desc *read_stats_descriptors(int stats_fd, + struct kvm_stats_header *header) { - return ioctl(vm->fd, KVM_GET_STATS_FD, NULL); + struct kvm_stats_desc *stats_desc; + ssize_t desc_size, total_size, ret; + + desc_size = get_stats_descriptor_size(header); + total_size = header->num_desc * desc_size; + + stats_desc = calloc(header->num_desc, desc_size); + TEST_ASSERT(stats_desc, "Allocate memory for stats descriptors"); + + ret = pread(stats_fd, stats_desc, total_size, header->desc_offset); + TEST_ASSERT(ret == total_size, "Read KVM stats descriptors"); + + return stats_desc; } -int vcpu_get_stats_fd(struct kvm_vm *vm, uint32_t vcpuid) +/* + * Read stat data for a particular stat + * + * Input Args: + * stats_fd - the file descriptor for the binary stats file from which to read + * header - the binary stats metadata header corresponding to the given FD + * desc - the binary stat metadata for the particular stat to be read + * max_elements - the maximum number of 8-byte values to read into data + * + * Output Args: + * data - the buffer into which stat data should be read + * + * Read the data values of a specified stat from the binary stats interface. + */ +void read_stat_data(int stats_fd, struct kvm_stats_header *header, + struct kvm_stats_desc *desc, uint64_t *data, + size_t max_elements) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); + size_t nr_elements = min_t(ssize_t, desc->size, max_elements); + size_t size = nr_elements * sizeof(*data); + ssize_t ret; - return ioctl(vcpu->fd, KVM_GET_STATS_FD, NULL); + TEST_ASSERT(desc->size, "No elements in stat '%s'", desc->name); + TEST_ASSERT(max_elements, "Zero elements requested for stat '%s'", desc->name); + + ret = pread(stats_fd, data, size, + header->data_offset + desc->offset); + + TEST_ASSERT(ret >= 0, "pread() failed on stat '%s', errno: %i (%s)", + desc->name, errno, strerror(errno)); + TEST_ASSERT(ret == size, + "pread() on stat '%s' read %ld bytes, wanted %lu bytes", + desc->name, size, ret); +} + +/* + * Read the data of the named stat + * + * Input Args: + * vm - the VM for which the stat should be read + * stat_name - the name of the stat to read + * max_elements - the maximum number of 8-byte values to read into data + * + * Output Args: + * data - the buffer into which stat data should be read + * + * Read the data values of a specified stat from the binary stats interface. + */ +void __vm_get_stat(struct kvm_vm *vm, const char *stat_name, uint64_t *data, + size_t max_elements) +{ + struct kvm_stats_desc *desc; + size_t size_desc; + int i; + + if (!vm->stats_fd) { + vm->stats_fd = vm_get_stats_fd(vm); + read_stats_header(vm->stats_fd, &vm->stats_header); + vm->stats_desc = read_stats_descriptors(vm->stats_fd, + &vm->stats_header); + } + + size_desc = get_stats_descriptor_size(&vm->stats_header); + + for (i = 0; i < vm->stats_header.num_desc; ++i) { + desc = (void *)vm->stats_desc + (i * size_desc); + + if (strcmp(desc->name, stat_name)) + continue; + + read_stat_data(vm->stats_fd, &vm->stats_header, desc, + data, max_elements); + + break; + } } diff --git a/tools/testing/selftests/kvm/lib/kvm_util_internal.h b/tools/testing/selftests/kvm/lib/kvm_util_internal.h deleted file mode 100644 index a03febc24ba6..000000000000 --- a/tools/testing/selftests/kvm/lib/kvm_util_internal.h +++ /dev/null @@ -1,128 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * tools/testing/selftests/kvm/lib/kvm_util_internal.h - * - * Copyright (C) 2018, Google LLC. - */ - -#ifndef SELFTEST_KVM_UTIL_INTERNAL_H -#define SELFTEST_KVM_UTIL_INTERNAL_H - -#include "linux/hashtable.h" -#include "linux/rbtree.h" - -#include "sparsebit.h" - -struct userspace_mem_region { - struct kvm_userspace_memory_region region; - struct sparsebit *unused_phy_pages; - int fd; - off_t offset; - void *host_mem; - void *host_alias; - void *mmap_start; - void *mmap_alias; - size_t mmap_size; - struct rb_node gpa_node; - struct rb_node hva_node; - struct hlist_node slot_node; -}; - -struct vcpu { - struct list_head list; - uint32_t id; - int fd; - struct kvm_run *state; - struct kvm_dirty_gfn *dirty_gfns; - uint32_t fetch_index; - uint32_t dirty_gfns_count; -}; - -struct userspace_mem_regions { - struct rb_root gpa_tree; - struct rb_root hva_tree; - DECLARE_HASHTABLE(slot_hash, 9); -}; - -struct kvm_vm { - int mode; - unsigned long type; - int kvm_fd; - int fd; - unsigned int pgtable_levels; - unsigned int page_size; - unsigned int page_shift; - unsigned int pa_bits; - unsigned int va_bits; - uint64_t max_gfn; - struct list_head vcpus; - struct userspace_mem_regions regions; - struct sparsebit *vpages_valid; - struct sparsebit *vpages_mapped; - bool has_irqchip; - bool pgd_created; - vm_paddr_t pgd; - vm_vaddr_t gdt; - vm_vaddr_t tss; - vm_vaddr_t idt; - vm_vaddr_t handlers; - uint32_t dirty_ring_size; -}; - -struct vcpu *vcpu_find(struct kvm_vm *vm, uint32_t vcpuid); - -/* - * Virtual Translation Tables Dump - * - * Input Args: - * stream - Output FILE stream - * vm - Virtual Machine - * indent - Left margin indent amount - * - * Output Args: None - * - * Return: None - * - * Dumps to the FILE stream given by @stream, the contents of all the - * virtual translation tables for the VM given by @vm. - */ -void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent); - -/* - * Register Dump - * - * Input Args: - * stream - Output FILE stream - * regs - Registers - * indent - Left margin indent amount - * - * Output Args: None - * - * Return: None - * - * Dumps the state of the registers given by @regs, to the FILE stream - * given by @stream. - */ -void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent); - -/* - * System Register Dump - * - * Input Args: - * stream - Output FILE stream - * sregs - System registers - * indent - Left margin indent amount - * - * Output Args: None - * - * Return: None - * - * Dumps the state of the system registers given by @sregs, to the FILE stream - * given by @stream. - */ -void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent); - -struct userspace_mem_region * -memslot2region(struct kvm_vm *vm, uint32_t memslot); - -#endif /* SELFTEST_KVM_UTIL_INTERNAL_H */ diff --git a/tools/testing/selftests/kvm/lib/perf_test_util.c b/tools/testing/selftests/kvm/lib/perf_test_util.c index f989ff91f022..9618b37c66f7 100644 --- a/tools/testing/selftests/kvm/lib/perf_test_util.c +++ b/tools/testing/selftests/kvm/lib/perf_test_util.c @@ -17,8 +17,8 @@ struct perf_test_args perf_test_args; static uint64_t guest_test_virt_mem = DEFAULT_GUEST_TEST_MEM; struct vcpu_thread { - /* The id of the vCPU. */ - int vcpu_id; + /* The index of the vCPU. */ + int vcpu_idx; /* The pthread backing the vCPU. */ pthread_t thread; @@ -36,24 +36,26 @@ static void (*vcpu_thread_fn)(struct perf_test_vcpu_args *); /* Set to true once all vCPU threads are up and running. */ static bool all_vcpu_threads_running; +static struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; + /* * Continuously write to the first 8 bytes of each page in the * specified region. */ -void perf_test_guest_code(uint32_t vcpu_id) +void perf_test_guest_code(uint32_t vcpu_idx) { struct perf_test_args *pta = &perf_test_args; - struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_id]; + struct perf_test_vcpu_args *vcpu_args = &pta->vcpu_args[vcpu_idx]; uint64_t gva; uint64_t pages; int i; - /* Make sure vCPU args data structure is not corrupt. */ - GUEST_ASSERT(vcpu_args->vcpu_id == vcpu_id); - gva = vcpu_args->gva; pages = vcpu_args->pages; + /* Make sure vCPU args data structure is not corrupt. */ + GUEST_ASSERT(vcpu_args->vcpu_idx == vcpu_idx); + while (true) { for (i = 0; i < pages; i++) { uint64_t addr = gva + (i * pta->guest_page_size); @@ -68,47 +70,50 @@ void perf_test_guest_code(uint32_t vcpu_id) } } -void perf_test_setup_vcpus(struct kvm_vm *vm, int vcpus, +void perf_test_setup_vcpus(struct kvm_vm *vm, int nr_vcpus, + struct kvm_vcpu *vcpus[], uint64_t vcpu_memory_bytes, bool partition_vcpu_memory_access) { struct perf_test_args *pta = &perf_test_args; struct perf_test_vcpu_args *vcpu_args; - int vcpu_id; + int i; - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { - vcpu_args = &pta->vcpu_args[vcpu_id]; + for (i = 0; i < nr_vcpus; i++) { + vcpu_args = &pta->vcpu_args[i]; + + vcpu_args->vcpu = vcpus[i]; + vcpu_args->vcpu_idx = i; - vcpu_args->vcpu_id = vcpu_id; if (partition_vcpu_memory_access) { vcpu_args->gva = guest_test_virt_mem + - (vcpu_id * vcpu_memory_bytes); + (i * vcpu_memory_bytes); vcpu_args->pages = vcpu_memory_bytes / pta->guest_page_size; - vcpu_args->gpa = pta->gpa + (vcpu_id * vcpu_memory_bytes); + vcpu_args->gpa = pta->gpa + (i * vcpu_memory_bytes); } else { vcpu_args->gva = guest_test_virt_mem; - vcpu_args->pages = (vcpus * vcpu_memory_bytes) / + vcpu_args->pages = (nr_vcpus * vcpu_memory_bytes) / pta->guest_page_size; vcpu_args->gpa = pta->gpa; } - vcpu_args_set(vm, vcpu_id, 1, vcpu_id); + vcpu_args_set(vcpus[i], 1, i); pr_debug("Added VCPU %d with test mem gpa [%lx, %lx)\n", - vcpu_id, vcpu_args->gpa, vcpu_args->gpa + + i, vcpu_args->gpa, vcpu_args->gpa + (vcpu_args->pages * pta->guest_page_size)); } } -struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, +struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int nr_vcpus, uint64_t vcpu_memory_bytes, int slots, enum vm_mem_backing_src_type backing_src, bool partition_vcpu_memory_access) { struct perf_test_args *pta = &perf_test_args; struct kvm_vm *vm; - uint64_t guest_num_pages, slot0_pages = DEFAULT_GUEST_PHY_PAGES; + uint64_t guest_num_pages, slot0_pages = 0; uint64_t backing_src_pagesz = get_backing_src_pagesz(backing_src); uint64_t region_end_gfn; int i; @@ -125,7 +130,7 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, pta->guest_page_size = vm_guest_mode_params[mode].page_size; guest_num_pages = vm_adjust_num_guest_pages(mode, - (vcpus * vcpu_memory_bytes) / pta->guest_page_size); + (nr_vcpus * vcpu_memory_bytes) / pta->guest_page_size); TEST_ASSERT(vcpu_memory_bytes % getpagesize() == 0, "Guest memory size is not host page size aligned."); @@ -140,20 +145,20 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, * in-memory data structures. */ if (pta->nested) - slot0_pages += perf_test_nested_pages(vcpus); + slot0_pages += perf_test_nested_pages(nr_vcpus); /* * Pass guest_num_pages to populate the page tables for test memory. * The memory is also added to memslot 0, but that's a benign side * effect as KVM allows aliasing HVAs in meslots. */ - vm = vm_create_with_vcpus(mode, vcpus, slot0_pages, guest_num_pages, 0, - perf_test_guest_code, NULL); + vm = __vm_create_with_vcpus(mode, nr_vcpus, slot0_pages + guest_num_pages, + perf_test_guest_code, vcpus); pta->vm = vm; /* Put the test region at the top guest physical memory. */ - region_end_gfn = vm_get_max_gfn(vm) + 1; + region_end_gfn = vm->max_gfn + 1; #ifdef __x86_64__ /* @@ -170,11 +175,10 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, TEST_ASSERT(guest_num_pages < region_end_gfn, "Requested more guest memory than address space allows.\n" " guest pages: %" PRIx64 " max gfn: %" PRIx64 - " vcpus: %d wss: %" PRIx64 "]\n", - guest_num_pages, region_end_gfn - 1, vcpus, - vcpu_memory_bytes); + " nr_vcpus: %d wss: %" PRIx64 "]\n", + guest_num_pages, region_end_gfn - 1, nr_vcpus, vcpu_memory_bytes); - pta->gpa = (region_end_gfn - guest_num_pages) * pta->guest_page_size; + pta->gpa = (region_end_gfn - guest_num_pages - 1) * pta->guest_page_size; pta->gpa = align_down(pta->gpa, backing_src_pagesz); #ifdef __s390x__ /* Align to 1M (segment size) */ @@ -197,11 +201,12 @@ struct kvm_vm *perf_test_create_vm(enum vm_guest_mode mode, int vcpus, /* Do mapping for the demand paging memory slot */ virt_map(vm, guest_test_virt_mem, pta->gpa, guest_num_pages); - perf_test_setup_vcpus(vm, vcpus, vcpu_memory_bytes, partition_vcpu_memory_access); + perf_test_setup_vcpus(vm, nr_vcpus, vcpus, vcpu_memory_bytes, + partition_vcpu_memory_access); if (pta->nested) { pr_info("Configuring vCPUs to run in L2 (nested).\n"); - perf_test_setup_nested(vm, vcpus); + perf_test_setup_nested(vm, nr_vcpus, vcpus); } ucall_init(vm, NULL); @@ -229,7 +234,7 @@ uint64_t __weak perf_test_nested_pages(int nr_vcpus) return 0; } -void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) +void __weak perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu **vcpus) { pr_info("%s() not support on this architecture, skipping.\n", __func__); exit(KSFT_SKIP); @@ -250,39 +255,40 @@ static void *vcpu_thread_main(void *data) while (!READ_ONCE(all_vcpu_threads_running)) ; - vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_id]); + vcpu_thread_fn(&perf_test_args.vcpu_args[vcpu->vcpu_idx]); return NULL; } -void perf_test_start_vcpu_threads(int vcpus, void (*vcpu_fn)(struct perf_test_vcpu_args *)) +void perf_test_start_vcpu_threads(int nr_vcpus, + void (*vcpu_fn)(struct perf_test_vcpu_args *)) { - int vcpu_id; + int i; vcpu_thread_fn = vcpu_fn; WRITE_ONCE(all_vcpu_threads_running, false); - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { - struct vcpu_thread *vcpu = &vcpu_threads[vcpu_id]; + for (i = 0; i < nr_vcpus; i++) { + struct vcpu_thread *vcpu = &vcpu_threads[i]; - vcpu->vcpu_id = vcpu_id; + vcpu->vcpu_idx = i; WRITE_ONCE(vcpu->running, false); pthread_create(&vcpu->thread, NULL, vcpu_thread_main, vcpu); } - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) { - while (!READ_ONCE(vcpu_threads[vcpu_id].running)) + for (i = 0; i < nr_vcpus; i++) { + while (!READ_ONCE(vcpu_threads[i].running)) ; } WRITE_ONCE(all_vcpu_threads_running, true); } -void perf_test_join_vcpu_threads(int vcpus) +void perf_test_join_vcpu_threads(int nr_vcpus) { - int vcpu_id; + int i; - for (vcpu_id = 0; vcpu_id < vcpus; vcpu_id++) - pthread_join(vcpu_threads[vcpu_id].thread, NULL); + for (i = 0; i < nr_vcpus; i++) + pthread_join(vcpu_threads[i].thread, NULL); } diff --git a/tools/testing/selftests/kvm/lib/riscv/processor.c b/tools/testing/selftests/kvm/lib/riscv/processor.c index abc0ae5a4fe1..604478151212 100644 --- a/tools/testing/selftests/kvm/lib/riscv/processor.c +++ b/tools/testing/selftests/kvm/lib/riscv/processor.c @@ -9,7 +9,6 @@ #include #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "processor.h" #define DEFAULT_RISCV_GUEST_STACK_VADDR_MIN 0xac0000 @@ -54,7 +53,7 @@ static uint64_t pte_index(struct kvm_vm *vm, vm_vaddr_t gva, int level) return (gva & pte_index_mask[level]) >> pte_index_shift[level]; } -void virt_pgd_alloc(struct kvm_vm *vm) +void virt_arch_pgd_alloc(struct kvm_vm *vm) { if (!vm->pgd_created) { vm_paddr_t paddr = vm_phy_pages_alloc(vm, @@ -65,7 +64,7 @@ void virt_pgd_alloc(struct kvm_vm *vm) } } -void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) { uint64_t *ptep, next_ppn; int level = vm->pgtable_levels - 1; @@ -109,7 +108,7 @@ void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) PGTBL_PTE_PERM_MASK | PGTBL_PTE_VALID_MASK; } -vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) { uint64_t *ptep; int level = vm->pgtable_levels - 1; @@ -160,7 +159,7 @@ static void pte_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent, #endif } -void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) { int level = vm->pgtable_levels - 1; uint64_t pgd, *ptep; @@ -179,8 +178,9 @@ void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) } } -void riscv_vcpu_mmu_setup(struct kvm_vm *vm, int vcpuid) +void riscv_vcpu_mmu_setup(struct kvm_vcpu *vcpu) { + struct kvm_vm *vm = vcpu->vm; unsigned long satp; /* @@ -199,46 +199,46 @@ void riscv_vcpu_mmu_setup(struct kvm_vm *vm, int vcpuid) satp = (vm->pgd >> PGTBL_PAGE_SIZE_SHIFT) & SATP_PPN; satp |= SATP_MODE_48; - set_reg(vm, vcpuid, RISCV_CSR_REG(satp), satp); + vcpu_set_reg(vcpu, RISCV_CSR_REG(satp), satp); } -void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) { struct kvm_riscv_core core; - get_reg(vm, vcpuid, RISCV_CORE_REG(mode), &core.mode); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.pc), &core.regs.pc); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.ra), &core.regs.ra); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.sp), &core.regs.sp); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.gp), &core.regs.gp); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.tp), &core.regs.tp); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t0), &core.regs.t0); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t1), &core.regs.t1); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t2), &core.regs.t2); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s0), &core.regs.s0); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s1), &core.regs.s1); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a0), &core.regs.a0); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a1), &core.regs.a1); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a2), &core.regs.a2); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a3), &core.regs.a3); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a4), &core.regs.a4); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a5), &core.regs.a5); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a6), &core.regs.a6); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.a7), &core.regs.a7); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s2), &core.regs.s2); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s3), &core.regs.s3); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s4), &core.regs.s4); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s5), &core.regs.s5); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s6), &core.regs.s6); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s7), &core.regs.s7); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s8), &core.regs.s8); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s9), &core.regs.s9); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s10), &core.regs.s10); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.s11), &core.regs.s11); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t3), &core.regs.t3); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t4), &core.regs.t4); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t5), &core.regs.t5); - get_reg(vm, vcpuid, RISCV_CORE_REG(regs.t6), &core.regs.t6); + vcpu_get_reg(vcpu, RISCV_CORE_REG(mode), &core.mode); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.pc), &core.regs.pc); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.ra), &core.regs.ra); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.sp), &core.regs.sp); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.gp), &core.regs.gp); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.tp), &core.regs.tp); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t0), &core.regs.t0); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t1), &core.regs.t1); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t2), &core.regs.t2); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s0), &core.regs.s0); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s1), &core.regs.s1); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a0), &core.regs.a0); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a1), &core.regs.a1); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a2), &core.regs.a2); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a3), &core.regs.a3); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a4), &core.regs.a4); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a5), &core.regs.a5); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a6), &core.regs.a6); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.a7), &core.regs.a7); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s2), &core.regs.s2); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s3), &core.regs.s3); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s4), &core.regs.s4); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s5), &core.regs.s5); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s6), &core.regs.s6); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s7), &core.regs.s7); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s8), &core.regs.s8); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s9), &core.regs.s9); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s10), &core.regs.s10); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.s11), &core.regs.s11); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t3), &core.regs.t3); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t4), &core.regs.t4); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t5), &core.regs.t5); + vcpu_get_reg(vcpu, RISCV_CORE_REG(regs.t6), &core.regs.t6); fprintf(stream, " MODE: 0x%lx\n", core.mode); @@ -275,7 +275,8 @@ static void __aligned(16) guest_unexp_trap(void) 0, 0, 0, 0, 0, 0); } -void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + void *guest_code) { int r; size_t stack_size = vm->page_size == 4096 ? @@ -285,9 +286,10 @@ void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) DEFAULT_RISCV_GUEST_STACK_VADDR_MIN); unsigned long current_gp = 0; struct kvm_mp_state mps; + struct kvm_vcpu *vcpu; - vm_vcpu_add(vm, vcpuid); - riscv_vcpu_mmu_setup(vm, vcpuid); + vcpu = __vm_vcpu_add(vm, vcpu_id); + riscv_vcpu_mmu_setup(vcpu); /* * With SBI HSM support in KVM RISC-V, all secondary VCPUs are @@ -295,26 +297,25 @@ void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) * are powered-on using KVM_SET_MP_STATE ioctl(). */ mps.mp_state = KVM_MP_STATE_RUNNABLE; - r = _vcpu_ioctl(vm, vcpuid, KVM_SET_MP_STATE, &mps); + r = __vcpu_ioctl(vcpu, KVM_SET_MP_STATE, &mps); TEST_ASSERT(!r, "IOCTL KVM_SET_MP_STATE failed (error %d)", r); /* Setup global pointer of guest to be same as the host */ asm volatile ( "add %0, gp, zero" : "=r" (current_gp) : : "memory"); - set_reg(vm, vcpuid, RISCV_CORE_REG(regs.gp), current_gp); + vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.gp), current_gp); /* Setup stack pointer and program counter of guest */ - set_reg(vm, vcpuid, RISCV_CORE_REG(regs.sp), - stack_vaddr + stack_size); - set_reg(vm, vcpuid, RISCV_CORE_REG(regs.pc), - (unsigned long)guest_code); + vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.sp), stack_vaddr + stack_size); + vcpu_set_reg(vcpu, RISCV_CORE_REG(regs.pc), (unsigned long)guest_code); /* Setup default exception vector of guest */ - set_reg(vm, vcpuid, RISCV_CSR_REG(stvec), - (unsigned long)guest_unexp_trap); + vcpu_set_reg(vcpu, RISCV_CSR_REG(stvec), (unsigned long)guest_unexp_trap); + + return vcpu; } -void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) +void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) { va_list ap; uint64_t id = RISCV_CORE_REG(regs.a0); @@ -352,12 +353,12 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) id = RISCV_CORE_REG(regs.a7); break; } - set_reg(vm, vcpuid, id, va_arg(ap, uint64_t)); + vcpu_set_reg(vcpu, id, va_arg(ap, uint64_t)); } va_end(ap); } -void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) { } diff --git a/tools/testing/selftests/kvm/lib/riscv/ucall.c b/tools/testing/selftests/kvm/lib/riscv/ucall.c index 8550f424d093..087b9740bc8f 100644 --- a/tools/testing/selftests/kvm/lib/riscv/ucall.c +++ b/tools/testing/selftests/kvm/lib/riscv/ucall.c @@ -8,7 +8,6 @@ #include #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "processor.h" void ucall_init(struct kvm_vm *vm, void *arg) @@ -53,7 +52,7 @@ void ucall(uint64_t cmd, int nargs, ...) va_list va; int i; - nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS; + nargs = min(nargs, UCALL_MAX_ARGS); va_start(va, nargs); for (i = 0; i < nargs; ++i) @@ -65,9 +64,9 @@ void ucall(uint64_t cmd, int nargs, ...) (vm_vaddr_t)&uc, 0, 0, 0, 0, 0); } -uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) { - struct kvm_run *run = vcpu_state(vm, vcpu_id); + struct kvm_run *run = vcpu->run; struct ucall ucall = {}; if (uc) @@ -77,16 +76,17 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) run->riscv_sbi.extension_id == KVM_RISCV_SELFTESTS_SBI_EXT) { switch (run->riscv_sbi.function_id) { case KVM_RISCV_SELFTESTS_SBI_UCALL: - memcpy(&ucall, addr_gva2hva(vm, - run->riscv_sbi.args[0]), sizeof(ucall)); + memcpy(&ucall, + addr_gva2hva(vcpu->vm, run->riscv_sbi.args[0]), + sizeof(ucall)); - vcpu_run_complete_io(vm, vcpu_id); + vcpu_run_complete_io(vcpu); if (uc) memcpy(uc, &ucall, sizeof(ucall)); break; case KVM_RISCV_SELFTESTS_SBI_UNEXP: - vcpu_dump(stderr, vm, vcpu_id, 2); + vcpu_dump(stderr, vcpu, 2); TEST_ASSERT(0, "Unexpected trap taken by guest"); break; default: diff --git a/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c b/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c index 86b9e611ad87..cdb7daeed5fd 100644 --- a/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c +++ b/tools/testing/selftests/kvm/lib/s390x/diag318_test_handler.c @@ -8,8 +8,6 @@ #include "test_util.h" #include "kvm_util.h" -#define VCPU_ID 6 - #define ICPT_INSTRUCTION 0x04 #define IPA0_DIAG 0x8300 @@ -27,14 +25,15 @@ static void guest_code(void) */ static uint64_t diag318_handler(void) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; uint64_t reg; uint64_t diag318_info; - vm = vm_create_default(VCPU_ID, 0, guest_code); - vcpu_run(vm, VCPU_ID); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vcpu_run(vcpu); + run = vcpu->run; TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC, "DIAGNOSE 0x0318 instruction was not intercepted"); @@ -62,7 +61,7 @@ uint64_t get_diag318_info(void) * If KVM does not support diag318, then return 0 to * ensure tests do not break. */ - if (!kvm_check_cap(KVM_CAP_S390_DIAG318)) { + if (!kvm_has_cap(KVM_CAP_S390_DIAG318)) { if (!printed_skip) { fprintf(stdout, "KVM_CAP_S390_DIAG318 not supported. " "Skipping diag318 test.\n"); diff --git a/tools/testing/selftests/kvm/lib/s390x/processor.c b/tools/testing/selftests/kvm/lib/s390x/processor.c index f87c7137598e..89d7340d9cbd 100644 --- a/tools/testing/selftests/kvm/lib/s390x/processor.c +++ b/tools/testing/selftests/kvm/lib/s390x/processor.c @@ -7,11 +7,10 @@ #include "processor.h" #include "kvm_util.h" -#include "../kvm_util_internal.h" #define PAGES_PER_REGION 4 -void virt_pgd_alloc(struct kvm_vm *vm) +void virt_arch_pgd_alloc(struct kvm_vm *vm) { vm_paddr_t paddr; @@ -47,7 +46,7 @@ static uint64_t virt_alloc_region(struct kvm_vm *vm, int ri) | ((ri < 4 ? (PAGES_PER_REGION - 1) : 0) & REGION_ENTRY_LENGTH); } -void virt_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa) +void virt_arch_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa) { int ri, idx; uint64_t *entry; @@ -86,7 +85,7 @@ void virt_pg_map(struct kvm_vm *vm, uint64_t gva, uint64_t gpa) entry[idx] = gpa; } -vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) { int ri, idx; uint64_t *entry; @@ -147,7 +146,7 @@ static void virt_dump_region(FILE *stream, struct kvm_vm *vm, uint8_t indent, } } -void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) { if (!vm->pgd_created) return; @@ -155,12 +154,14 @@ void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) virt_dump_region(stream, vm, indent, vm->pgd); } -void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + void *guest_code) { size_t stack_size = DEFAULT_STACK_PGS * getpagesize(); uint64_t stack_vaddr; struct kvm_regs regs; struct kvm_sregs sregs; + struct kvm_vcpu *vcpu; struct kvm_run *run; TEST_ASSERT(vm->page_size == 4096, "Unsupported page size: 0x%x", @@ -169,24 +170,26 @@ void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) stack_vaddr = vm_vaddr_alloc(vm, stack_size, DEFAULT_GUEST_STACK_VADDR_MIN); - vm_vcpu_add(vm, vcpuid); + vcpu = __vm_vcpu_add(vm, vcpu_id); /* Setup guest registers */ - vcpu_regs_get(vm, vcpuid, ®s); + vcpu_regs_get(vcpu, ®s); regs.gprs[15] = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize()) - 160; - vcpu_regs_set(vm, vcpuid, ®s); + vcpu_regs_set(vcpu, ®s); - vcpu_sregs_get(vm, vcpuid, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.crs[0] |= 0x00040000; /* Enable floating point regs */ sregs.crs[1] = vm->pgd | 0xf; /* Primary region table */ - vcpu_sregs_set(vm, vcpuid, &sregs); + vcpu_sregs_set(vcpu, &sregs); - run = vcpu_state(vm, vcpuid); + run = vcpu->run; run->psw_mask = 0x0400000180000000ULL; /* DAT enabled + 64 bit mode */ run->psw_addr = (uintptr_t)guest_code; + + return vcpu; } -void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) +void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) { va_list ap; struct kvm_regs regs; @@ -197,26 +200,21 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) num); va_start(ap, num); - vcpu_regs_get(vm, vcpuid, ®s); + vcpu_regs_get(vcpu, ®s); for (i = 0; i < num; i++) regs.gprs[i + 2] = va_arg(ap, uint64_t); - vcpu_regs_set(vm, vcpuid, ®s); + vcpu_regs_set(vcpu, ®s); va_end(ap); } -void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - - if (!vcpu) - return; - fprintf(stream, "%*spstate: psw: 0x%.16llx:0x%.16llx\n", - indent, "", vcpu->state->psw_mask, vcpu->state->psw_addr); + indent, "", vcpu->run->psw_mask, vcpu->run->psw_addr); } -void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) { } diff --git a/tools/testing/selftests/kvm/lib/s390x/ucall.c b/tools/testing/selftests/kvm/lib/s390x/ucall.c index 9d3b0f15249a..73dc4e21190f 100644 --- a/tools/testing/selftests/kvm/lib/s390x/ucall.c +++ b/tools/testing/selftests/kvm/lib/s390x/ucall.c @@ -22,7 +22,7 @@ void ucall(uint64_t cmd, int nargs, ...) va_list va; int i; - nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS; + nargs = min(nargs, UCALL_MAX_ARGS); va_start(va, nargs); for (i = 0; i < nargs; ++i) @@ -33,9 +33,9 @@ void ucall(uint64_t cmd, int nargs, ...) asm volatile ("diag 0,%0,0x501" : : "a"(&uc) : "memory"); } -uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) { - struct kvm_run *run = vcpu_state(vm, vcpu_id); + struct kvm_run *run = vcpu->run; struct ucall ucall = {}; if (uc) @@ -47,10 +47,10 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) (run->s390_sieic.ipb >> 16) == 0x501) { int reg = run->s390_sieic.ipa & 0xf; - memcpy(&ucall, addr_gva2hva(vm, run->s.regs.gprs[reg]), + memcpy(&ucall, addr_gva2hva(vcpu->vm, run->s.regs.gprs[reg]), sizeof(ucall)); - vcpu_run_complete_io(vm, vcpu_id); + vcpu_run_complete_io(vcpu); if (uc) memcpy(uc, &ucall, sizeof(ucall)); } diff --git a/tools/testing/selftests/kvm/lib/x86_64/perf_test_util.c b/tools/testing/selftests/kvm/lib/x86_64/perf_test_util.c index e258524435a0..0f344a7c89c4 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/perf_test_util.c +++ b/tools/testing/selftests/kvm/lib/x86_64/perf_test_util.c @@ -12,7 +12,6 @@ #include "test_util.h" #include "kvm_util.h" #include "perf_test_util.h" -#include "../kvm_util_internal.h" #include "processor.h" #include "vmx.h" @@ -78,14 +77,14 @@ void perf_test_setup_ept(struct vmx_pages *vmx, struct kvm_vm *vm) nested_identity_map_1g(vmx, vm, start, end - start); } -void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) +void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus, struct kvm_vcpu *vcpus[]) { struct vmx_pages *vmx, *vmx0 = NULL; struct kvm_regs regs; vm_vaddr_t vmx_gva; int vcpu_id; - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); for (vcpu_id = 0; vcpu_id < nr_vcpus; vcpu_id++) { vmx = vcpu_alloc_vmx(vm, &vmx_gva); @@ -104,9 +103,9 @@ void perf_test_setup_nested(struct kvm_vm *vm, int nr_vcpus) * Override the vCPU to run perf_test_l1_guest_code() which will * bounce it into L2 before calling perf_test_guest_code(). */ - vcpu_regs_get(vm, vcpu_id, ®s); + vcpu_regs_get(vcpus[vcpu_id], ®s); regs.rip = (unsigned long) perf_test_l1_guest_code; - vcpu_regs_set(vm, vcpu_id, ®s); - vcpu_args_set(vm, vcpu_id, 2, vmx_gva, vcpu_id); + vcpu_regs_set(vcpus[vcpu_id], ®s); + vcpu_args_set(vcpus[vcpu_id], 2, vmx_gva, vcpu_id); } } diff --git a/tools/testing/selftests/kvm/lib/x86_64/processor.c b/tools/testing/selftests/kvm/lib/x86_64/processor.c index ead7011ee8f6..2e6e61bbe81b 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/processor.c +++ b/tools/testing/selftests/kvm/lib/x86_64/processor.c @@ -7,7 +7,6 @@ #include "test_util.h" #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "processor.h" #ifndef NUM_INTERRUPTS @@ -17,10 +16,11 @@ #define DEFAULT_CODE_SELECTOR 0x8 #define DEFAULT_DATA_SELECTOR 0x10 +#define MAX_NR_CPUID_ENTRIES 100 + vm_vaddr_t exception_handlers; -void regs_dump(FILE *stream, struct kvm_regs *regs, - uint8_t indent) +static void regs_dump(FILE *stream, struct kvm_regs *regs, uint8_t indent) { fprintf(stream, "%*srax: 0x%.16llx rbx: 0x%.16llx " "rcx: 0x%.16llx rdx: 0x%.16llx\n", @@ -43,21 +43,6 @@ void regs_dump(FILE *stream, struct kvm_regs *regs, regs->rip, regs->rflags); } -/* - * Segment Dump - * - * Input Args: - * stream - Output FILE stream - * segment - KVM segment - * indent - Left margin indent amount - * - * Output Args: None - * - * Return: None - * - * Dumps the state of the KVM segment given by @segment, to the FILE stream - * given by @stream. - */ static void segment_dump(FILE *stream, struct kvm_segment *segment, uint8_t indent) { @@ -75,21 +60,6 @@ static void segment_dump(FILE *stream, struct kvm_segment *segment, segment->unusable, segment->padding); } -/* - * dtable Dump - * - * Input Args: - * stream - Output FILE stream - * dtable - KVM dtable - * indent - Left margin indent amount - * - * Output Args: None - * - * Return: None - * - * Dumps the state of the KVM dtable given by @dtable, to the FILE stream - * given by @stream. - */ static void dtable_dump(FILE *stream, struct kvm_dtable *dtable, uint8_t indent) { @@ -99,8 +69,7 @@ static void dtable_dump(FILE *stream, struct kvm_dtable *dtable, dtable->padding[0], dtable->padding[1], dtable->padding[2]); } -void sregs_dump(FILE *stream, struct kvm_sregs *sregs, - uint8_t indent) +static void sregs_dump(FILE *stream, struct kvm_sregs *sregs, uint8_t indent) { unsigned int i; @@ -142,7 +111,7 @@ void sregs_dump(FILE *stream, struct kvm_sregs *sregs, } } -void virt_pgd_alloc(struct kvm_vm *vm) +void virt_arch_pgd_alloc(struct kvm_vm *vm) { TEST_ASSERT(vm->mode == VM_MODE_PXXV48_4K, "Attempt to use " "unknown or unsupported guest mode, mode: 0x%x", vm->mode); @@ -240,27 +209,24 @@ void __virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr, int level) *pte = PTE_PRESENT_MASK | PTE_WRITABLE_MASK | (paddr & PHYSICAL_PAGE_MASK); } -void virt_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) +void virt_arch_pg_map(struct kvm_vm *vm, uint64_t vaddr, uint64_t paddr) { __virt_pg_map(vm, vaddr, paddr, PG_LEVEL_4K); } -static uint64_t *_vm_get_page_table_entry(struct kvm_vm *vm, int vcpuid, - uint64_t vaddr) +static uint64_t *_vm_get_page_table_entry(struct kvm_vm *vm, + struct kvm_vcpu *vcpu, + uint64_t vaddr) { uint16_t index[4]; uint64_t *pml4e, *pdpe, *pde; uint64_t *pte; - struct kvm_cpuid_entry2 *entry; struct kvm_sregs sregs; - int max_phy_addr; uint64_t rsvd_mask = 0; - entry = kvm_get_supported_cpuid_index(0x80000008, 0); - max_phy_addr = entry->eax & 0x000000ff; /* Set the high bits in the reserved mask. */ - if (max_phy_addr < 52) - rsvd_mask = GENMASK_ULL(51, max_phy_addr); + if (vm->pa_bits < 52) + rsvd_mask = GENMASK_ULL(51, vm->pa_bits); /* * SDM vol 3, fig 4-11 "Formats of CR3 and Paging-Structure Entries @@ -268,7 +234,7 @@ static uint64_t *_vm_get_page_table_entry(struct kvm_vm *vm, int vcpuid, * If IA32_EFER.NXE = 0 and the P flag of a paging-structure entry is 1, * the XD flag (bit 63) is reserved. */ - vcpu_sregs_get(vm, vcpuid, &sregs); + vcpu_sregs_get(vcpu, &sregs); if ((sregs.efer & EFER_NX) == 0) { rsvd_mask |= PTE_NX_MASK; } @@ -320,22 +286,23 @@ static uint64_t *_vm_get_page_table_entry(struct kvm_vm *vm, int vcpuid, return &pte[index[0]]; } -uint64_t vm_get_page_table_entry(struct kvm_vm *vm, int vcpuid, uint64_t vaddr) +uint64_t vm_get_page_table_entry(struct kvm_vm *vm, struct kvm_vcpu *vcpu, + uint64_t vaddr) { - uint64_t *pte = _vm_get_page_table_entry(vm, vcpuid, vaddr); + uint64_t *pte = _vm_get_page_table_entry(vm, vcpu, vaddr); return *(uint64_t *)pte; } -void vm_set_page_table_entry(struct kvm_vm *vm, int vcpuid, uint64_t vaddr, - uint64_t pte) +void vm_set_page_table_entry(struct kvm_vm *vm, struct kvm_vcpu *vcpu, + uint64_t vaddr, uint64_t pte) { - uint64_t *new_pte = _vm_get_page_table_entry(vm, vcpuid, vaddr); + uint64_t *new_pte = _vm_get_page_table_entry(vm, vcpu, vaddr); *(uint64_t *)new_pte = pte; } -void virt_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) +void virt_arch_dump(FILE *stream, struct kvm_vm *vm, uint8_t indent) { uint64_t *pml4e, *pml4e_start; uint64_t *pdpe, *pdpe_start; @@ -516,7 +483,7 @@ static void kvm_seg_set_kernel_data_64bit(struct kvm_vm *vm, uint16_t selector, kvm_seg_fill_gdt_64bit(vm, segp); } -vm_paddr_t addr_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) +vm_paddr_t addr_arch_gva2gpa(struct kvm_vm *vm, vm_vaddr_t gva) { uint16_t index[4]; uint64_t *pml4e, *pdpe, *pde; @@ -579,12 +546,12 @@ static void kvm_setup_tss_64bit(struct kvm_vm *vm, struct kvm_segment *segp, kvm_seg_fill_gdt_64bit(vm, segp); } -static void vcpu_setup(struct kvm_vm *vm, int vcpuid) +static void vcpu_setup(struct kvm_vm *vm, struct kvm_vcpu *vcpu) { struct kvm_sregs sregs; /* Set mode specific system register values. */ - vcpu_sregs_get(vm, vcpuid, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.idt.limit = 0; @@ -608,25 +575,10 @@ static void vcpu_setup(struct kvm_vm *vm, int vcpuid) } sregs.cr3 = vm->pgd; - vcpu_sregs_set(vm, vcpuid, &sregs); + vcpu_sregs_set(vcpu, &sregs); } -#define CPUID_XFD_BIT (1 << 4) -static bool is_xfd_supported(void) -{ - int eax, ebx, ecx, edx; - const int leaf = 0xd, subleaf = 0x1; - - __asm__ __volatile__( - "cpuid" - : /* output */ "=a"(eax), "=b"(ebx), - "=c"(ecx), "=d"(edx) - : /* input */ "0"(leaf), "2"(subleaf)); - - return !!(eax & CPUID_XFD_BIT); -} - -void vm_xsave_req_perm(int bit) +void __vm_xsave_require_permission(int bit, const char *name) { int kvm_fd; u64 bitmask; @@ -637,26 +589,21 @@ void vm_xsave_req_perm(int bit) .addr = (unsigned long) &bitmask }; + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XFD)); + kvm_fd = open_kvm_dev_path_or_exit(); - rc = ioctl(kvm_fd, KVM_GET_DEVICE_ATTR, &attr); + rc = __kvm_ioctl(kvm_fd, KVM_GET_DEVICE_ATTR, &attr); close(kvm_fd); + if (rc == -1 && (errno == ENXIO || errno == EINVAL)) - exit(KSFT_SKIP); + __TEST_REQUIRE(0, "KVM_X86_XCOMP_GUEST_SUPP not supported"); + TEST_ASSERT(rc == 0, "KVM_GET_DEVICE_ATTR(0, KVM_X86_XCOMP_GUEST_SUPP) error: %ld", rc); - if (!(bitmask & (1ULL << bit))) - exit(KSFT_SKIP); - if (!is_xfd_supported()) - exit(KSFT_SKIP); + __TEST_REQUIRE(bitmask & (1ULL << bit), + "Required XSAVE feature '%s' not supported", name); - rc = syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, bit); - - /* - * The older kernel version(<5.15) can't support - * ARCH_REQ_XCOMP_GUEST_PERM and directly return. - */ - if (rc) - return; + TEST_REQUIRE(!syscall(SYS_arch_prctl, ARCH_REQ_XCOMP_GUEST_PERM, bit)); rc = syscall(SYS_arch_prctl, ARCH_GET_XCOMP_GUEST_PERM, &bitmask); TEST_ASSERT(rc == 0, "prctl(ARCH_GET_XCOMP_GUEST_PERM) error: %ld", rc); @@ -665,108 +612,89 @@ void vm_xsave_req_perm(int bit) bitmask); } -void vm_vcpu_add_default(struct kvm_vm *vm, uint32_t vcpuid, void *guest_code) +struct kvm_vcpu *vm_arch_vcpu_add(struct kvm_vm *vm, uint32_t vcpu_id, + void *guest_code) { struct kvm_mp_state mp_state; struct kvm_regs regs; vm_vaddr_t stack_vaddr; + struct kvm_vcpu *vcpu; + stack_vaddr = vm_vaddr_alloc(vm, DEFAULT_STACK_PGS * getpagesize(), DEFAULT_GUEST_STACK_VADDR_MIN); - /* Create VCPU */ - vm_vcpu_add(vm, vcpuid); - vcpu_set_cpuid(vm, vcpuid, kvm_get_supported_cpuid()); - vcpu_setup(vm, vcpuid); + vcpu = __vm_vcpu_add(vm, vcpu_id); + vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid()); + vcpu_setup(vm, vcpu); /* Setup guest general purpose registers */ - vcpu_regs_get(vm, vcpuid, ®s); + vcpu_regs_get(vcpu, ®s); regs.rflags = regs.rflags | 0x2; regs.rsp = stack_vaddr + (DEFAULT_STACK_PGS * getpagesize()); regs.rip = (unsigned long) guest_code; - vcpu_regs_set(vm, vcpuid, ®s); + vcpu_regs_set(vcpu, ®s); /* Setup the MP state */ mp_state.mp_state = 0; - vcpu_set_mp_state(vm, vcpuid, &mp_state); + vcpu_mp_state_set(vcpu, &mp_state); + + return vcpu; } -/* - * Allocate an instance of struct kvm_cpuid2 - * - * Input Args: None - * - * Output Args: None - * - * Return: A pointer to the allocated struct. The caller is responsible - * for freeing this struct. - * - * Since kvm_cpuid2 uses a 0-length array to allow a the size of the - * array to be decided at allocation time, allocation is slightly - * complicated. This function uses a reasonable default length for - * the array and performs the appropriate allocation. - */ -static struct kvm_cpuid2 *allocate_kvm_cpuid2(void) +struct kvm_vcpu *vm_arch_vcpu_recreate(struct kvm_vm *vm, uint32_t vcpu_id) { - struct kvm_cpuid2 *cpuid; - int nent = 100; - size_t size; + struct kvm_vcpu *vcpu = __vm_vcpu_add(vm, vcpu_id); - size = sizeof(*cpuid); - size += nent * sizeof(struct kvm_cpuid_entry2); - cpuid = malloc(size); - if (!cpuid) { - perror("malloc"); - abort(); - } + vcpu_init_cpuid(vcpu, kvm_get_supported_cpuid()); - cpuid->nent = nent; - - return cpuid; + return vcpu; } -/* - * KVM Supported CPUID Get - * - * Input Args: None - * - * Output Args: - * - * Return: The supported KVM CPUID - * - * Get the guest CPUID supported by KVM. - */ -struct kvm_cpuid2 *kvm_get_supported_cpuid(void) +void vcpu_arch_free(struct kvm_vcpu *vcpu) +{ + if (vcpu->cpuid) + free(vcpu->cpuid); +} + +const struct kvm_cpuid2 *kvm_get_supported_cpuid(void) { static struct kvm_cpuid2 *cpuid; - int ret; int kvm_fd; if (cpuid) return cpuid; - cpuid = allocate_kvm_cpuid2(); + cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES); kvm_fd = open_kvm_dev_path_or_exit(); - ret = ioctl(kvm_fd, KVM_GET_SUPPORTED_CPUID, cpuid); - TEST_ASSERT(ret == 0, "KVM_GET_SUPPORTED_CPUID failed %d %d\n", - ret, errno); + kvm_ioctl(kvm_fd, KVM_GET_SUPPORTED_CPUID, cpuid); close(kvm_fd); return cpuid; } -/* - * KVM Get MSR - * - * Input Args: - * msr_index - Index of MSR - * - * Output Args: None - * - * Return: On success, value of the MSR. On failure a TEST_ASSERT is produced. - * - * Get value of MSR for VCPU. - */ +bool kvm_cpuid_has(const struct kvm_cpuid2 *cpuid, + struct kvm_x86_cpu_feature feature) +{ + const struct kvm_cpuid_entry2 *entry; + int i; + + for (i = 0; i < cpuid->nent; i++) { + entry = &cpuid->entries[i]; + + /* + * The output registers in kvm_cpuid_entry2 are in alphabetical + * order, but kvm_x86_cpu_feature matches that mess, so yay + * pointer shenanigans! + */ + if (entry->function == feature.function && + entry->index == feature.index) + return (&entry->eax)[feature.reg] & BIT(feature.bit); + } + + return false; +} + uint64_t kvm_get_feature_msr(uint64_t msr_index) { struct { @@ -779,218 +707,98 @@ uint64_t kvm_get_feature_msr(uint64_t msr_index) buffer.entry.index = msr_index; kvm_fd = open_kvm_dev_path_or_exit(); - r = ioctl(kvm_fd, KVM_GET_MSRS, &buffer.header); - TEST_ASSERT(r == 1, "KVM_GET_MSRS IOCTL failed,\n" - " rc: %i errno: %i", r, errno); + r = __kvm_ioctl(kvm_fd, KVM_GET_MSRS, &buffer.header); + TEST_ASSERT(r == 1, KVM_IOCTL_ERROR(KVM_GET_MSRS, r)); close(kvm_fd); return buffer.entry.data; } -/* - * VM VCPU CPUID Set - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU id - * - * Output Args: None - * - * Return: KVM CPUID (KVM_GET_CPUID2) - * - * Set the VCPU's CPUID. - */ -struct kvm_cpuid2 *vcpu_get_cpuid(struct kvm_vm *vm, uint32_t vcpuid) +void vcpu_init_cpuid(struct kvm_vcpu *vcpu, const struct kvm_cpuid2 *cpuid) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - struct kvm_cpuid2 *cpuid; - int max_ent; - int rc = -1; + TEST_ASSERT(cpuid != vcpu->cpuid, "@cpuid can't be the vCPU's CPUID"); - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - cpuid = allocate_kvm_cpuid2(); - max_ent = cpuid->nent; - - for (cpuid->nent = 1; cpuid->nent <= max_ent; cpuid->nent++) { - rc = ioctl(vcpu->fd, KVM_GET_CPUID2, cpuid); - if (!rc) - break; - - TEST_ASSERT(rc == -1 && errno == E2BIG, - "KVM_GET_CPUID2 should either succeed or give E2BIG: %d %d", - rc, errno); + /* Allow overriding the default CPUID. */ + if (vcpu->cpuid && vcpu->cpuid->nent < cpuid->nent) { + free(vcpu->cpuid); + vcpu->cpuid = NULL; } - TEST_ASSERT(rc == 0, "KVM_GET_CPUID2 failed, rc: %i errno: %i", - rc, errno); + if (!vcpu->cpuid) + vcpu->cpuid = allocate_kvm_cpuid2(cpuid->nent); - return cpuid; + memcpy(vcpu->cpuid, cpuid, kvm_cpuid2_size(cpuid->nent)); + vcpu_set_cpuid(vcpu); } - - -/* - * Locate a cpuid entry. - * - * Input Args: - * function: The function of the cpuid entry to find. - * index: The index of the cpuid entry. - * - * Output Args: None - * - * Return: A pointer to the cpuid entry. Never returns NULL. - */ -struct kvm_cpuid_entry2 * -kvm_get_supported_cpuid_index(uint32_t function, uint32_t index) +void vcpu_set_cpuid_maxphyaddr(struct kvm_vcpu *vcpu, uint8_t maxphyaddr) { - struct kvm_cpuid2 *cpuid; - struct kvm_cpuid_entry2 *entry = NULL; - int i; + struct kvm_cpuid_entry2 *entry = vcpu_get_cpuid_entry(vcpu, 0x80000008); - cpuid = kvm_get_supported_cpuid(); - for (i = 0; i < cpuid->nent; i++) { - if (cpuid->entries[i].function == function && - cpuid->entries[i].index == index) { - entry = &cpuid->entries[i]; - break; - } - } - - TEST_ASSERT(entry, "Guest CPUID entry not found: (EAX=%x, ECX=%x).", - function, index); - return entry; + entry->eax = (entry->eax & ~0xff) | maxphyaddr; + vcpu_set_cpuid(vcpu); } - -int __vcpu_set_cpuid(struct kvm_vm *vm, uint32_t vcpuid, - struct kvm_cpuid2 *cpuid) +void vcpu_clear_cpuid_entry(struct kvm_vcpu *vcpu, uint32_t function) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); + struct kvm_cpuid_entry2 *entry = vcpu_get_cpuid_entry(vcpu, function); - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); - - return ioctl(vcpu->fd, KVM_SET_CPUID2, cpuid); + entry->eax = 0; + entry->ebx = 0; + entry->ecx = 0; + entry->edx = 0; + vcpu_set_cpuid(vcpu); } -/* - * VM VCPU CPUID Set - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU id - * cpuid - The CPUID values to set. - * - * Output Args: None - * - * Return: void - * - * Set the VCPU's CPUID. - */ -void vcpu_set_cpuid(struct kvm_vm *vm, - uint32_t vcpuid, struct kvm_cpuid2 *cpuid) +void vcpu_set_or_clear_cpuid_feature(struct kvm_vcpu *vcpu, + struct kvm_x86_cpu_feature feature, + bool set) { - int rc; + struct kvm_cpuid_entry2 *entry; + u32 *reg; - rc = __vcpu_set_cpuid(vm, vcpuid, cpuid); - TEST_ASSERT(rc == 0, "KVM_SET_CPUID2 failed, rc: %i errno: %i", - rc, errno); + entry = __vcpu_get_cpuid_entry(vcpu, feature.function, feature.index); + reg = (&entry->eax) + feature.reg; + if (set) + *reg |= BIT(feature.bit); + else + *reg &= ~BIT(feature.bit); + + vcpu_set_cpuid(vcpu); } -/* - * VCPU Get MSR - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * msr_index - Index of MSR - * - * Output Args: None - * - * Return: On success, value of the MSR. On failure a TEST_ASSERT is produced. - * - * Get value of MSR for VCPU. - */ -uint64_t vcpu_get_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index) +uint64_t vcpu_get_msr(struct kvm_vcpu *vcpu, uint64_t msr_index) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); struct { struct kvm_msrs header; struct kvm_msr_entry entry; } buffer = {}; - int r; - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); buffer.header.nmsrs = 1; buffer.entry.index = msr_index; - r = ioctl(vcpu->fd, KVM_GET_MSRS, &buffer.header); - TEST_ASSERT(r == 1, "KVM_GET_MSRS IOCTL failed,\n" - " rc: %i errno: %i", r, errno); + + vcpu_msrs_get(vcpu, &buffer.header); return buffer.entry.data; } -/* - * _VCPU Set MSR - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * msr_index - Index of MSR - * msr_value - New value of MSR - * - * Output Args: None - * - * Return: The result of KVM_SET_MSRS. - * - * Sets the value of an MSR for the given VCPU. - */ -int _vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, - uint64_t msr_value) +int _vcpu_set_msr(struct kvm_vcpu *vcpu, uint64_t msr_index, uint64_t msr_value) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); struct { struct kvm_msrs header; struct kvm_msr_entry entry; } buffer = {}; - int r; - TEST_ASSERT(vcpu != NULL, "vcpu not found, vcpuid: %u", vcpuid); memset(&buffer, 0, sizeof(buffer)); buffer.header.nmsrs = 1; buffer.entry.index = msr_index; buffer.entry.data = msr_value; - r = ioctl(vcpu->fd, KVM_SET_MSRS, &buffer.header); - return r; + + return __vcpu_ioctl(vcpu, KVM_SET_MSRS, &buffer.header); } -/* - * VCPU Set MSR - * - * Input Args: - * vm - Virtual Machine - * vcpuid - VCPU ID - * msr_index - Index of MSR - * msr_value - New value of MSR - * - * Output Args: None - * - * Return: On success, nothing. On failure a TEST_ASSERT is produced. - * - * Set value of MSR for VCPU. - */ -void vcpu_set_msr(struct kvm_vm *vm, uint32_t vcpuid, uint64_t msr_index, - uint64_t msr_value) -{ - int r; - - r = _vcpu_set_msr(vm, vcpuid, msr_index, msr_value); - TEST_ASSERT(r == 1, "KVM_SET_MSRS IOCTL failed,\n" - " rc: %i errno: %i", r, errno); -} - -void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) +void vcpu_args_set(struct kvm_vcpu *vcpu, unsigned int num, ...) { va_list ap; struct kvm_regs regs; @@ -1000,7 +808,7 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) num); va_start(ap, num); - vcpu_regs_get(vm, vcpuid, ®s); + vcpu_regs_get(vcpu, ®s); if (num >= 1) regs.rdi = va_arg(ap, uint64_t); @@ -1020,85 +828,112 @@ void vcpu_args_set(struct kvm_vm *vm, uint32_t vcpuid, unsigned int num, ...) if (num >= 6) regs.r9 = va_arg(ap, uint64_t); - vcpu_regs_set(vm, vcpuid, ®s); + vcpu_regs_set(vcpu, ®s); va_end(ap); } -void vcpu_dump(FILE *stream, struct kvm_vm *vm, uint32_t vcpuid, uint8_t indent) +void vcpu_arch_dump(FILE *stream, struct kvm_vcpu *vcpu, uint8_t indent) { struct kvm_regs regs; struct kvm_sregs sregs; - fprintf(stream, "%*scpuid: %u\n", indent, "", vcpuid); + fprintf(stream, "%*svCPU ID: %u\n", indent, "", vcpu->id); fprintf(stream, "%*sregs:\n", indent + 2, ""); - vcpu_regs_get(vm, vcpuid, ®s); + vcpu_regs_get(vcpu, ®s); regs_dump(stream, ®s, indent + 4); fprintf(stream, "%*ssregs:\n", indent + 2, ""); - vcpu_sregs_get(vm, vcpuid, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs_dump(stream, &sregs, indent + 4); } -static int kvm_get_num_msrs_fd(int kvm_fd) -{ - struct kvm_msr_list nmsrs; - int r; - - nmsrs.nmsrs = 0; - r = ioctl(kvm_fd, KVM_GET_MSR_INDEX_LIST, &nmsrs); - TEST_ASSERT(r == -1 && errno == E2BIG, "Unexpected result from KVM_GET_MSR_INDEX_LIST probe, r: %i", - r); - - return nmsrs.nmsrs; -} - -static int kvm_get_num_msrs(struct kvm_vm *vm) -{ - return kvm_get_num_msrs_fd(vm->kvm_fd); -} - -struct kvm_msr_list *kvm_get_msr_index_list(void) +static struct kvm_msr_list *__kvm_get_msr_index_list(bool feature_msrs) { struct kvm_msr_list *list; - int nmsrs, r, kvm_fd; + struct kvm_msr_list nmsrs; + int kvm_fd, r; kvm_fd = open_kvm_dev_path_or_exit(); - nmsrs = kvm_get_num_msrs_fd(kvm_fd); - list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0])); - list->nmsrs = nmsrs; - r = ioctl(kvm_fd, KVM_GET_MSR_INDEX_LIST, list); + nmsrs.nmsrs = 0; + if (!feature_msrs) + r = __kvm_ioctl(kvm_fd, KVM_GET_MSR_INDEX_LIST, &nmsrs); + else + r = __kvm_ioctl(kvm_fd, KVM_GET_MSR_FEATURE_INDEX_LIST, &nmsrs); + + TEST_ASSERT(r == -1 && errno == E2BIG, + "Expected -E2BIG, got rc: %i errno: %i (%s)", + r, errno, strerror(errno)); + + list = malloc(sizeof(*list) + nmsrs.nmsrs * sizeof(list->indices[0])); + TEST_ASSERT(list, "-ENOMEM when allocating MSR index list"); + list->nmsrs = nmsrs.nmsrs; + + if (!feature_msrs) + kvm_ioctl(kvm_fd, KVM_GET_MSR_INDEX_LIST, list); + else + kvm_ioctl(kvm_fd, KVM_GET_MSR_FEATURE_INDEX_LIST, list); close(kvm_fd); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MSR_INDEX_LIST, r: %i", - r); - + TEST_ASSERT(list->nmsrs == nmsrs.nmsrs, + "Number of MSRs in list changed, was %d, now %d", + nmsrs.nmsrs, list->nmsrs); return list; } -static int vcpu_save_xsave_state(struct kvm_vm *vm, struct vcpu *vcpu, - struct kvm_x86_state *state) +const struct kvm_msr_list *kvm_get_msr_index_list(void) { - int size; + static const struct kvm_msr_list *list; - size = vm_check_cap(vm, KVM_CAP_XSAVE2); - if (!size) - size = sizeof(struct kvm_xsave); - - state->xsave = malloc(size); - if (size == sizeof(struct kvm_xsave)) - return ioctl(vcpu->fd, KVM_GET_XSAVE, state->xsave); - else - return ioctl(vcpu->fd, KVM_GET_XSAVE2, state->xsave); + if (!list) + list = __kvm_get_msr_index_list(false); + return list; } -struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid) + +const struct kvm_msr_list *kvm_get_feature_msr_index_list(void) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - struct kvm_msr_list *list; + static const struct kvm_msr_list *list; + + if (!list) + list = __kvm_get_msr_index_list(true); + return list; +} + +bool kvm_msr_is_in_save_restore_list(uint32_t msr_index) +{ + const struct kvm_msr_list *list = kvm_get_msr_index_list(); + int i; + + for (i = 0; i < list->nmsrs; ++i) { + if (list->indices[i] == msr_index) + return true; + } + + return false; +} + +static void vcpu_save_xsave_state(struct kvm_vcpu *vcpu, + struct kvm_x86_state *state) +{ + int size = vm_check_cap(vcpu->vm, KVM_CAP_XSAVE2); + + if (size) { + state->xsave = malloc(size); + vcpu_xsave2_get(vcpu, state->xsave); + } else { + state->xsave = malloc(sizeof(struct kvm_xsave)); + vcpu_xsave_get(vcpu, state->xsave); + } +} + +struct kvm_x86_state *vcpu_save_state(struct kvm_vcpu *vcpu) +{ + const struct kvm_msr_list *msr_list = kvm_get_msr_index_list(); struct kvm_x86_state *state; - int nmsrs, r, i; + int i; + static int nested_size = -1; if (nested_size == -1) { @@ -1114,113 +949,57 @@ struct kvm_x86_state *vcpu_save_state(struct kvm_vm *vm, uint32_t vcpuid) * kernel with KVM_RUN. Complete IO prior to migrating state * to a new VM. */ - vcpu_run_complete_io(vm, vcpuid); + vcpu_run_complete_io(vcpu); - nmsrs = kvm_get_num_msrs(vm); - list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0])); - list->nmsrs = nmsrs; - r = ioctl(vm->kvm_fd, KVM_GET_MSR_INDEX_LIST, list); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MSR_INDEX_LIST, r: %i", - r); + state = malloc(sizeof(*state) + msr_list->nmsrs * sizeof(state->msrs.entries[0])); - state = malloc(sizeof(*state) + nmsrs * sizeof(state->msrs.entries[0])); - r = ioctl(vcpu->fd, KVM_GET_VCPU_EVENTS, &state->events); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_VCPU_EVENTS, r: %i", - r); + vcpu_events_get(vcpu, &state->events); + vcpu_mp_state_get(vcpu, &state->mp_state); + vcpu_regs_get(vcpu, &state->regs); + vcpu_save_xsave_state(vcpu, state); - r = ioctl(vcpu->fd, KVM_GET_MP_STATE, &state->mp_state); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_MP_STATE, r: %i", - r); + if (kvm_has_cap(KVM_CAP_XCRS)) + vcpu_xcrs_get(vcpu, &state->xcrs); - r = ioctl(vcpu->fd, KVM_GET_REGS, &state->regs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_REGS, r: %i", - r); - - r = vcpu_save_xsave_state(vm, vcpu, state); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XSAVE, r: %i", - r); - - if (kvm_check_cap(KVM_CAP_XCRS)) { - r = ioctl(vcpu->fd, KVM_GET_XCRS, &state->xcrs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_XCRS, r: %i", - r); - } - - r = ioctl(vcpu->fd, KVM_GET_SREGS, &state->sregs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_SREGS, r: %i", - r); + vcpu_sregs_get(vcpu, &state->sregs); if (nested_size) { state->nested.size = sizeof(state->nested_); - r = ioctl(vcpu->fd, KVM_GET_NESTED_STATE, &state->nested); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_NESTED_STATE, r: %i", - r); + + vcpu_nested_state_get(vcpu, &state->nested); TEST_ASSERT(state->nested.size <= nested_size, "Nested state size too big, %i (KVM_CHECK_CAP gave %i)", state->nested.size, nested_size); - } else + } else { state->nested.size = 0; + } - state->msrs.nmsrs = nmsrs; - for (i = 0; i < nmsrs; i++) - state->msrs.entries[i].index = list->indices[i]; - r = ioctl(vcpu->fd, KVM_GET_MSRS, &state->msrs); - TEST_ASSERT(r == nmsrs, "Unexpected result from KVM_GET_MSRS, r: %i (failed MSR was 0x%x)", - r, r == nmsrs ? -1 : list->indices[r]); + state->msrs.nmsrs = msr_list->nmsrs; + for (i = 0; i < msr_list->nmsrs; i++) + state->msrs.entries[i].index = msr_list->indices[i]; + vcpu_msrs_get(vcpu, &state->msrs); - r = ioctl(vcpu->fd, KVM_GET_DEBUGREGS, &state->debugregs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_GET_DEBUGREGS, r: %i", - r); + vcpu_debugregs_get(vcpu, &state->debugregs); - free(list); return state; } -void vcpu_load_state(struct kvm_vm *vm, uint32_t vcpuid, struct kvm_x86_state *state) +void vcpu_load_state(struct kvm_vcpu *vcpu, struct kvm_x86_state *state) { - struct vcpu *vcpu = vcpu_find(vm, vcpuid); - int r; + vcpu_sregs_set(vcpu, &state->sregs); + vcpu_msrs_set(vcpu, &state->msrs); - r = ioctl(vcpu->fd, KVM_SET_SREGS, &state->sregs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_SREGS, r: %i", - r); + if (kvm_has_cap(KVM_CAP_XCRS)) + vcpu_xcrs_set(vcpu, &state->xcrs); - r = ioctl(vcpu->fd, KVM_SET_MSRS, &state->msrs); - TEST_ASSERT(r == state->msrs.nmsrs, - "Unexpected result from KVM_SET_MSRS, r: %i (failed at %x)", - r, r == state->msrs.nmsrs ? -1 : state->msrs.entries[r].index); + vcpu_xsave_set(vcpu, state->xsave); + vcpu_events_set(vcpu, &state->events); + vcpu_mp_state_set(vcpu, &state->mp_state); + vcpu_debugregs_set(vcpu, &state->debugregs); + vcpu_regs_set(vcpu, &state->regs); - if (kvm_check_cap(KVM_CAP_XCRS)) { - r = ioctl(vcpu->fd, KVM_SET_XCRS, &state->xcrs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XCRS, r: %i", - r); - } - - r = ioctl(vcpu->fd, KVM_SET_XSAVE, state->xsave); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_XSAVE, r: %i", - r); - - r = ioctl(vcpu->fd, KVM_SET_VCPU_EVENTS, &state->events); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_VCPU_EVENTS, r: %i", - r); - - r = ioctl(vcpu->fd, KVM_SET_MP_STATE, &state->mp_state); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_MP_STATE, r: %i", - r); - - r = ioctl(vcpu->fd, KVM_SET_DEBUGREGS, &state->debugregs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_DEBUGREGS, r: %i", - r); - - r = ioctl(vcpu->fd, KVM_SET_REGS, &state->regs); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_REGS, r: %i", - r); - - if (state->nested.size) { - r = ioctl(vcpu->fd, KVM_SET_NESTED_STATE, &state->nested); - TEST_ASSERT(r == 0, "Unexpected result from KVM_SET_NESTED_STATE, r: %i", - r); - } + if (state->nested.size) + vcpu_nested_state_set(vcpu, &state->nested); } void kvm_x86_state_cleanup(struct kvm_x86_state *state) @@ -1232,15 +1011,9 @@ void kvm_x86_state_cleanup(struct kvm_x86_state *state) static bool cpu_vendor_string_is(const char *vendor) { const uint32_t *chunk = (const uint32_t *)vendor; - int eax, ebx, ecx, edx; - const int leaf = 0; - - __asm__ __volatile__( - "cpuid" - : /* output */ "=a"(eax), "=b"(ebx), - "=c"(ecx), "=d"(edx) - : /* input */ "0"(leaf), "2"(0)); + uint32_t eax, ebx, ecx, edx; + cpuid(0, &eax, &ebx, &ecx, &edx); return (ebx == chunk[0] && edx == chunk[1] && ecx == chunk[2]); } @@ -1257,19 +1030,9 @@ bool is_amd_cpu(void) return cpu_vendor_string_is("AuthenticAMD"); } -uint32_t kvm_get_cpuid_max_basic(void) -{ - return kvm_get_supported_cpuid_entry(0)->eax; -} - -uint32_t kvm_get_cpuid_max_extended(void) -{ - return kvm_get_supported_cpuid_entry(0x80000000)->eax; -} - void kvm_get_cpu_address_width(unsigned int *pa_bits, unsigned int *va_bits) { - struct kvm_cpuid_entry2 *entry; + const struct kvm_cpuid_entry2 *entry; bool pae; /* SDM 4.1.4 */ @@ -1315,6 +1078,20 @@ static void set_idt_entry(struct kvm_vm *vm, int vector, unsigned long addr, e->offset2 = addr >> 32; } + +static bool kvm_fixup_exception(struct ex_regs *regs) +{ + if (regs->r9 != KVM_EXCEPTION_MAGIC || regs->rip != regs->r10) + return false; + + if (regs->vector == DE_VECTOR) + return false; + + regs->rip = regs->r11; + regs->r9 = regs->vector; + return true; +} + void kvm_exit_unexpected_vector(uint32_t value) { ucall(UCALL_UNHANDLED, 1, value); @@ -1330,6 +1107,9 @@ void route_exception(struct ex_regs *regs) return; } + if (kvm_fixup_exception(regs)) + return; + kvm_exit_unexpected_vector(regs->vector); } @@ -1346,17 +1126,18 @@ void vm_init_descriptor_tables(struct kvm_vm *vm) DEFAULT_CODE_SELECTOR); } -void vcpu_init_descriptor_tables(struct kvm_vm *vm, uint32_t vcpuid) +void vcpu_init_descriptor_tables(struct kvm_vcpu *vcpu) { + struct kvm_vm *vm = vcpu->vm; struct kvm_sregs sregs; - vcpu_sregs_get(vm, vcpuid, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.idt.base = vm->idt; sregs.idt.limit = NUM_INTERRUPTS * sizeof(struct idt_entry) - 1; sregs.gdt.base = vm->gdt; sregs.gdt.limit = getpagesize() - 1; kvm_seg_set_kernel_data_64bit(NULL, DEFAULT_DATA_SELECTOR, &sregs.gs); - vcpu_sregs_set(vm, vcpuid, &sregs); + vcpu_sregs_set(vcpu, &sregs); *(vm_vaddr_t *)addr_gva2hva(vm, (vm_vaddr_t)(&exception_handlers)) = vm->handlers; } @@ -1368,11 +1149,11 @@ void vm_install_exception_handler(struct kvm_vm *vm, int vector, handlers[vector] = (vm_vaddr_t)handler; } -void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) +void assert_on_unhandled_exception(struct kvm_vcpu *vcpu) { struct ucall uc; - if (get_ucall(vm, vcpuid, &uc) == UCALL_UNHANDLED) { + if (get_ucall(vcpu, &uc) == UCALL_UNHANDLED) { uint64_t vector = uc.args[0]; TEST_FAIL("Unexpected vectored event in guest (vector:0x%lx)", @@ -1380,16 +1161,15 @@ void assert_on_unhandled_exception(struct kvm_vm *vm, uint32_t vcpuid) } } -struct kvm_cpuid_entry2 *get_cpuid(struct kvm_cpuid2 *cpuid, uint32_t function, - uint32_t index) +const struct kvm_cpuid_entry2 *get_cpuid_entry(const struct kvm_cpuid2 *cpuid, + uint32_t function, uint32_t index) { int i; for (i = 0; i < cpuid->nent; i++) { - struct kvm_cpuid_entry2 *cur = &cpuid->entries[i]; - - if (cur->function == function && cur->index == index) - return cur; + if (cpuid->entries[i].function == function && + cpuid->entries[i].index == index) + return &cpuid->entries[i]; } TEST_FAIL("CPUID function 0x%x index 0x%x not found ", function, index); @@ -1397,24 +1177,6 @@ struct kvm_cpuid_entry2 *get_cpuid(struct kvm_cpuid2 *cpuid, uint32_t function, return NULL; } -bool set_cpuid(struct kvm_cpuid2 *cpuid, - struct kvm_cpuid_entry2 *ent) -{ - int i; - - for (i = 0; i < cpuid->nent; i++) { - struct kvm_cpuid_entry2 *cur = &cpuid->entries[i]; - - if (cur->function != ent->function || cur->index != ent->index) - continue; - - memcpy(cur, ent, sizeof(struct kvm_cpuid_entry2)); - return true; - } - - return false; -} - uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, uint64_t a3) { @@ -1422,43 +1184,38 @@ uint64_t kvm_hypercall(uint64_t nr, uint64_t a0, uint64_t a1, uint64_t a2, asm volatile("vmcall" : "=a"(r) - : "b"(a0), "c"(a1), "d"(a2), "S"(a3)); + : "a"(nr), "b"(a0), "c"(a1), "d"(a2), "S"(a3)); return r; } -struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void) +const struct kvm_cpuid2 *kvm_get_supported_hv_cpuid(void) { static struct kvm_cpuid2 *cpuid; - int ret; int kvm_fd; if (cpuid) return cpuid; - cpuid = allocate_kvm_cpuid2(); + cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES); kvm_fd = open_kvm_dev_path_or_exit(); - ret = ioctl(kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, cpuid); - TEST_ASSERT(ret == 0, "KVM_GET_SUPPORTED_HV_CPUID failed %d %d\n", - ret, errno); + kvm_ioctl(kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, cpuid); close(kvm_fd); return cpuid; } -void vcpu_set_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid) +void vcpu_set_hv_cpuid(struct kvm_vcpu *vcpu) { static struct kvm_cpuid2 *cpuid_full; - struct kvm_cpuid2 *cpuid_sys, *cpuid_hv; + const struct kvm_cpuid2 *cpuid_sys, *cpuid_hv; int i, nent = 0; if (!cpuid_full) { cpuid_sys = kvm_get_supported_cpuid(); cpuid_hv = kvm_get_supported_hv_cpuid(); - cpuid_full = malloc(sizeof(*cpuid_full) + - (cpuid_sys->nent + cpuid_hv->nent) * - sizeof(struct kvm_cpuid_entry2)); + cpuid_full = allocate_kvm_cpuid2(cpuid_sys->nent + cpuid_hv->nent); if (!cpuid_full) { perror("malloc"); abort(); @@ -1478,16 +1235,14 @@ void vcpu_set_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid) cpuid_full->nent = nent + cpuid_hv->nent; } - vcpu_set_cpuid(vm, vcpuid, cpuid_full); + vcpu_init_cpuid(vcpu, cpuid_full); } -struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vm *vm, uint32_t vcpuid) +const struct kvm_cpuid2 *vcpu_get_supported_hv_cpuid(struct kvm_vcpu *vcpu) { - static struct kvm_cpuid2 *cpuid; + struct kvm_cpuid2 *cpuid = allocate_kvm_cpuid2(MAX_NR_CPUID_ENTRIES); - cpuid = allocate_kvm_cpuid2(); - - vcpu_ioctl(vm, vcpuid, KVM_GET_SUPPORTED_HV_CPUID, cpuid); + vcpu_ioctl(vcpu, KVM_GET_SUPPORTED_HV_CPUID, cpuid); return cpuid; } @@ -1510,9 +1265,7 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) /* Before family 17h, the HyperTransport area is just below 1T. */ ht_gfn = (1 << 28) - num_ht_pages; - eax = 1; - ecx = 0; - cpuid(&eax, &ebx, &ecx, &edx); + cpuid(1, &eax, &ebx, &ecx, &edx); if (x86_family(eax) < 0x17) goto done; @@ -1521,18 +1274,15 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) * reduced due to SME by bits 11:6 of CPUID[0x8000001f].EBX. Use * the old conservative value if MAXPHYADDR is not enumerated. */ - eax = 0x80000000; - cpuid(&eax, &ebx, &ecx, &edx); + cpuid(0x80000000, &eax, &ebx, &ecx, &edx); max_ext_leaf = eax; if (max_ext_leaf < 0x80000008) goto done; - eax = 0x80000008; - cpuid(&eax, &ebx, &ecx, &edx); + cpuid(0x80000008, &eax, &ebx, &ecx, &edx); max_pfn = (1ULL << ((eax & 0xff) - vm->page_shift)) - 1; if (max_ext_leaf >= 0x8000001f) { - eax = 0x8000001f; - cpuid(&eax, &ebx, &ecx, &edx); + cpuid(0x8000001f, &eax, &ebx, &ecx, &edx); max_pfn >>= (ebx >> 6) & 0x3f; } @@ -1540,3 +1290,24 @@ unsigned long vm_compute_max_gfn(struct kvm_vm *vm) done: return min(max_gfn, ht_gfn - 1); } + +/* Returns true if kvm_intel was loaded with unrestricted_guest=1. */ +bool vm_is_unrestricted_guest(struct kvm_vm *vm) +{ + char val = 'N'; + size_t count; + FILE *f; + + /* Ensure that a KVM vendor-specific module is loaded. */ + if (vm == NULL) + close(open_kvm_dev_path_or_exit()); + + f = fopen("/sys/module/kvm_intel/parameters/unrestricted_guest", "r"); + if (f) { + count = fread(&val, sizeof(char), 1, f); + TEST_ASSERT(count == 1, "Unable to read from param file."); + fclose(f); + } + + return val == 'Y'; +} diff --git a/tools/testing/selftests/kvm/lib/x86_64/svm.c b/tools/testing/selftests/kvm/lib/x86_64/svm.c index 736ee4a23df6..6d445886e16c 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/svm.c +++ b/tools/testing/selftests/kvm/lib/x86_64/svm.c @@ -9,7 +9,6 @@ #include "test_util.h" #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "processor.h" #include "svm_util.h" @@ -165,22 +164,6 @@ void run_guest(struct vmcb *vmcb, uint64_t vmcb_gpa) : "r15", "memory"); } -bool nested_svm_supported(void) -{ - struct kvm_cpuid_entry2 *entry = - kvm_get_supported_cpuid_entry(0x80000001); - - return entry->ecx & CPUID_SVM; -} - -void nested_svm_check_supported(void) -{ - if (!nested_svm_supported()) { - print_skip("nested SVM not enabled"); - exit(KSFT_SKIP); - } -} - /* * Open SEV_DEV_PATH if available, otherwise exit the entire program. * diff --git a/tools/testing/selftests/kvm/lib/x86_64/ucall.c b/tools/testing/selftests/kvm/lib/x86_64/ucall.c index a3489973e290..e5f0f9e0d3ee 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/ucall.c +++ b/tools/testing/selftests/kvm/lib/x86_64/ucall.c @@ -24,7 +24,7 @@ void ucall(uint64_t cmd, int nargs, ...) va_list va; int i; - nargs = nargs <= UCALL_MAX_ARGS ? nargs : UCALL_MAX_ARGS; + nargs = min(nargs, UCALL_MAX_ARGS); va_start(va, nargs); for (i = 0; i < nargs; ++i) @@ -35,9 +35,9 @@ void ucall(uint64_t cmd, int nargs, ...) : : [port] "d" (UCALL_PIO_PORT), "D" (&uc) : "rax", "memory"); } -uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) +uint64_t get_ucall(struct kvm_vcpu *vcpu, struct ucall *uc) { - struct kvm_run *run = vcpu_state(vm, vcpu_id); + struct kvm_run *run = vcpu->run; struct ucall ucall = {}; if (uc) @@ -46,11 +46,11 @@ uint64_t get_ucall(struct kvm_vm *vm, uint32_t vcpu_id, struct ucall *uc) if (run->exit_reason == KVM_EXIT_IO && run->io.port == UCALL_PIO_PORT) { struct kvm_regs regs; - vcpu_regs_get(vm, vcpu_id, ®s); - memcpy(&ucall, addr_gva2hva(vm, (vm_vaddr_t)regs.rdi), + vcpu_regs_get(vcpu, ®s); + memcpy(&ucall, addr_gva2hva(vcpu->vm, (vm_vaddr_t)regs.rdi), sizeof(ucall)); - vcpu_run_complete_io(vm, vcpu_id); + vcpu_run_complete_io(vcpu); if (uc) memcpy(uc, &ucall, sizeof(ucall)); } diff --git a/tools/testing/selftests/kvm/lib/x86_64/vmx.c b/tools/testing/selftests/kvm/lib/x86_64/vmx.c index b77a01d0a271..80a568c439b8 100644 --- a/tools/testing/selftests/kvm/lib/x86_64/vmx.c +++ b/tools/testing/selftests/kvm/lib/x86_64/vmx.c @@ -7,7 +7,6 @@ #include "test_util.h" #include "kvm_util.h" -#include "../kvm_util_internal.h" #include "processor.h" #include "vmx.h" @@ -43,16 +42,12 @@ struct eptPageTablePointer { uint64_t address:40; uint64_t reserved_63_52:12; }; -int vcpu_enable_evmcs(struct kvm_vm *vm, int vcpu_id) +int vcpu_enable_evmcs(struct kvm_vcpu *vcpu) { uint16_t evmcs_ver; - struct kvm_enable_cap enable_evmcs_cap = { - .cap = KVM_CAP_HYPERV_ENLIGHTENED_VMCS, - .args[0] = (unsigned long)&evmcs_ver - }; - - vcpu_ioctl(vm, vcpu_id, KVM_ENABLE_CAP, &enable_evmcs_cap); + vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENLIGHTENED_VMCS, + (unsigned long)&evmcs_ver); /* KVM should return supported EVMCS version range */ TEST_ASSERT(((evmcs_ver >> 8) >= (evmcs_ver & 0xff)) && @@ -387,21 +382,6 @@ void prepare_vmcs(struct vmx_pages *vmx, void *guest_rip, void *guest_rsp) init_vmcs_guest_state(guest_rip, guest_rsp); } -bool nested_vmx_supported(void) -{ - struct kvm_cpuid_entry2 *entry = kvm_get_supported_cpuid_entry(1); - - return entry->ecx & CPUID_VMX; -} - -void nested_vmx_check_supported(void) -{ - if (!nested_vmx_supported()) { - print_skip("nested VMX not enabled"); - exit(KSFT_SKIP); - } -} - static void nested_create_pte(struct kvm_vm *vm, struct eptPageTableEntry *pte, uint64_t nested_paddr, diff --git a/tools/testing/selftests/kvm/max_guest_memory_test.c b/tools/testing/selftests/kvm/max_guest_memory_test.c index 15f046e19cb2..9a6e4f3ad6b5 100644 --- a/tools/testing/selftests/kvm/max_guest_memory_test.c +++ b/tools/testing/selftests/kvm/max_guest_memory_test.c @@ -28,8 +28,7 @@ static void guest_code(uint64_t start_gpa, uint64_t end_gpa, uint64_t stride) } struct vcpu_info { - struct kvm_vm *vm; - uint32_t id; + struct kvm_vcpu *vcpu; uint64_t start_gpa; uint64_t end_gpa; }; @@ -52,45 +51,45 @@ static void rendezvous_with_boss(void) } } -static void run_vcpu(struct kvm_vm *vm, uint32_t vcpu_id) +static void run_vcpu(struct kvm_vcpu *vcpu) { - vcpu_run(vm, vcpu_id); - ASSERT_EQ(get_ucall(vm, vcpu_id, NULL), UCALL_DONE); + vcpu_run(vcpu); + ASSERT_EQ(get_ucall(vcpu, NULL), UCALL_DONE); } static void *vcpu_worker(void *data) { - struct vcpu_info *vcpu = data; + struct vcpu_info *info = data; + struct kvm_vcpu *vcpu = info->vcpu; struct kvm_vm *vm = vcpu->vm; struct kvm_sregs sregs; struct kvm_regs regs; - vcpu_args_set(vm, vcpu->id, 3, vcpu->start_gpa, vcpu->end_gpa, - vm_get_page_size(vm)); + vcpu_args_set(vcpu, 3, info->start_gpa, info->end_gpa, vm->page_size); /* Snapshot regs before the first run. */ - vcpu_regs_get(vm, vcpu->id, ®s); + vcpu_regs_get(vcpu, ®s); rendezvous_with_boss(); - run_vcpu(vm, vcpu->id); + run_vcpu(vcpu); rendezvous_with_boss(); - vcpu_regs_set(vm, vcpu->id, ®s); - vcpu_sregs_get(vm, vcpu->id, &sregs); + vcpu_regs_set(vcpu, ®s); + vcpu_sregs_get(vcpu, &sregs); #ifdef __x86_64__ /* Toggle CR0.WP to trigger a MMU context reset. */ sregs.cr0 ^= X86_CR0_WP; #endif - vcpu_sregs_set(vm, vcpu->id, &sregs); + vcpu_sregs_set(vcpu, &sregs); rendezvous_with_boss(); - run_vcpu(vm, vcpu->id); + run_vcpu(vcpu); rendezvous_with_boss(); return NULL; } -static pthread_t *spawn_workers(struct kvm_vm *vm, uint64_t start_gpa, - uint64_t end_gpa) +static pthread_t *spawn_workers(struct kvm_vm *vm, struct kvm_vcpu **vcpus, + uint64_t start_gpa, uint64_t end_gpa) { struct vcpu_info *info; uint64_t gpa, nr_bytes; @@ -104,12 +103,11 @@ static pthread_t *spawn_workers(struct kvm_vm *vm, uint64_t start_gpa, TEST_ASSERT(info, "Failed to allocate vCPU gpa ranges"); nr_bytes = ((end_gpa - start_gpa) / nr_vcpus) & - ~((uint64_t)vm_get_page_size(vm) - 1); + ~((uint64_t)vm->page_size - 1); TEST_ASSERT(nr_bytes, "C'mon, no way you have %d CPUs", nr_vcpus); for (i = 0, gpa = start_gpa; i < nr_vcpus; i++, gpa += nr_bytes) { - info[i].vm = vm; - info[i].id = i; + info[i].vcpu = vcpus[i]; info[i].start_gpa = gpa; info[i].end_gpa = gpa + nr_bytes; pthread_create(&threads[i], NULL, vcpu_worker, &info[i]); @@ -172,6 +170,7 @@ int main(int argc, char *argv[]) uint64_t max_gpa, gpa, slot_size, max_mem, i; int max_slots, slot, opt, fd; bool hugepages = false; + struct kvm_vcpu **vcpus; pthread_t *threads; struct kvm_vm *vm; void *mem; @@ -215,9 +214,12 @@ int main(int argc, char *argv[]) } } - vm = vm_create_default_with_vcpus(nr_vcpus, 0, 0, guest_code, NULL); + vcpus = malloc(nr_vcpus * sizeof(*vcpus)); + TEST_ASSERT(vcpus, "Failed to allocate vCPU array"); - max_gpa = vm_get_max_gfn(vm) << vm_get_page_shift(vm); + vm = vm_create_with_vcpus(nr_vcpus, guest_code, vcpus); + + max_gpa = vm->max_gfn << vm->page_shift; TEST_ASSERT(max_gpa > (4 * slot_size), "MAXPHYADDR <4gb "); fd = kvm_memfd_alloc(slot_size, hugepages); @@ -227,7 +229,7 @@ int main(int argc, char *argv[]) TEST_ASSERT(!madvise(mem, slot_size, MADV_NOHUGEPAGE), "madvise() failed"); /* Pre-fault the memory to avoid taking mmap_sem on guest page faults. */ - for (i = 0; i < slot_size; i += vm_get_page_size(vm)) + for (i = 0; i < slot_size; i += vm->page_size) ((uint8_t *)mem)[i] = 0xaa; gpa = 0; @@ -246,13 +248,16 @@ int main(int argc, char *argv[]) for (i = 0; i < slot_size; i += size_1gb) __virt_pg_map(vm, gpa + i, gpa + i, PG_LEVEL_1G); #else - for (i = 0; i < slot_size; i += vm_get_page_size(vm)) + for (i = 0; i < slot_size; i += vm->page_size) virt_pg_map(vm, gpa + i, gpa + i); #endif } atomic_set(&rendezvous, nr_vcpus + 1); - threads = spawn_workers(vm, start_gpa, gpa); + threads = spawn_workers(vm, vcpus, start_gpa, gpa); + + free(vcpus); + vcpus = NULL; pr_info("Running with %lugb of guest memory and %u vCPUs\n", (gpa - start_gpa) / size_1gb, nr_vcpus); diff --git a/tools/testing/selftests/kvm/memslot_modification_stress_test.c b/tools/testing/selftests/kvm/memslot_modification_stress_test.c index 1410d0a9141a..6ee7e1dde404 100644 --- a/tools/testing/selftests/kvm/memslot_modification_stress_test.c +++ b/tools/testing/selftests/kvm/memslot_modification_stress_test.c @@ -38,19 +38,18 @@ static bool run_vcpus = true; static void vcpu_worker(struct perf_test_vcpu_args *vcpu_args) { - int ret; - int vcpu_id = vcpu_args->vcpu_id; - struct kvm_vm *vm = perf_test_args.vm; + struct kvm_vcpu *vcpu = vcpu_args->vcpu; struct kvm_run *run; + int ret; - run = vcpu_state(vm, vcpu_id); + run = vcpu->run; /* Let the guest access its memory until a stop signal is received */ while (READ_ONCE(run_vcpus)) { - ret = _vcpu_run(vm, vcpu_id); + ret = _vcpu_run(vcpu); TEST_ASSERT(ret == 0, "vcpu_run failed: %d\n", ret); - if (get_ucall(vm, vcpu_id, NULL) == UCALL_SYNC) + if (get_ucall(vcpu, NULL) == UCALL_SYNC) continue; TEST_ASSERT(false, @@ -76,7 +75,7 @@ static void add_remove_memslot(struct kvm_vm *vm, useconds_t delay, * Add the dummy memslot just below the perf_test_util memslot, which is * at the top of the guest physical address space. */ - gpa = perf_test_args.gpa - pages * vm_get_page_size(vm); + gpa = perf_test_args.gpa - pages * vm->page_size; for (i = 0; i < nr_modifications; i++) { usleep(delay); diff --git a/tools/testing/selftests/kvm/memslot_perf_test.c b/tools/testing/selftests/kvm/memslot_perf_test.c index 1727f75e0c2c..44995446d942 100644 --- a/tools/testing/selftests/kvm/memslot_perf_test.c +++ b/tools/testing/selftests/kvm/memslot_perf_test.c @@ -25,8 +25,6 @@ #include #include -#define VCPU_ID 0 - #define MEM_SIZE ((512U << 20) + 4096) #define MEM_SIZE_PAGES (MEM_SIZE / 4096) #define MEM_GPA 0x10000000UL @@ -90,6 +88,7 @@ static_assert(MEM_TEST_MOVE_SIZE <= MEM_TEST_SIZE, struct vm_data { struct kvm_vm *vm; + struct kvm_vcpu *vcpu; pthread_t vcpu_thread; uint32_t nslots; uint64_t npages; @@ -127,29 +126,29 @@ static bool verbose; pr_info(__VA_ARGS__); \ } while (0) -static void check_mmio_access(struct vm_data *vm, struct kvm_run *run) +static void check_mmio_access(struct vm_data *data, struct kvm_run *run) { - TEST_ASSERT(vm->mmio_ok, "Unexpected mmio exit"); + TEST_ASSERT(data->mmio_ok, "Unexpected mmio exit"); TEST_ASSERT(run->mmio.is_write, "Unexpected mmio read"); TEST_ASSERT(run->mmio.len == 8, "Unexpected exit mmio size = %u", run->mmio.len); - TEST_ASSERT(run->mmio.phys_addr >= vm->mmio_gpa_min && - run->mmio.phys_addr <= vm->mmio_gpa_max, + TEST_ASSERT(run->mmio.phys_addr >= data->mmio_gpa_min && + run->mmio.phys_addr <= data->mmio_gpa_max, "Unexpected exit mmio address = 0x%llx", run->mmio.phys_addr); } -static void *vcpu_worker(void *data) +static void *vcpu_worker(void *__data) { - struct vm_data *vm = data; - struct kvm_run *run; + struct vm_data *data = __data; + struct kvm_vcpu *vcpu = data->vcpu; + struct kvm_run *run = vcpu->run; struct ucall uc; - run = vcpu_state(vm->vm, VCPU_ID); while (1) { - vcpu_run(vm->vm, VCPU_ID); + vcpu_run(vcpu); - switch (get_ucall(vm->vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: TEST_ASSERT(uc.args[1] == 0, "Unexpected sync ucall, got %lx", @@ -158,14 +157,12 @@ static void *vcpu_worker(void *data) continue; case UCALL_NONE: if (run->exit_reason == KVM_EXIT_MMIO) - check_mmio_access(vm, run); + check_mmio_access(data, run); else goto done; break; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld, val = %lu", - (const char *)uc.args[0], - __FILE__, uc.args[1], uc.args[2]); + REPORT_GUEST_ASSERT_1(uc, "val = %lu"); break; case UCALL_DONE: goto done; @@ -238,6 +235,7 @@ static struct vm_data *alloc_vm(void) TEST_ASSERT(data, "malloc(vmdata) failed"); data->vm = NULL; + data->vcpu = NULL; data->hva_slots = NULL; return data; @@ -278,7 +276,7 @@ static bool prepare_vm(struct vm_data *data, int nslots, uint64_t *maxslots, data->hva_slots = malloc(sizeof(*data->hva_slots) * data->nslots); TEST_ASSERT(data->hva_slots, "malloc() fail"); - data->vm = vm_create_default(VCPU_ID, mempages, guest_code); + data->vm = __vm_create_with_one_vcpu(&data->vcpu, mempages, guest_code); ucall_init(data->vm, NULL); pr_info_v("Adding slots 1..%i, each slot with %"PRIu64" pages + %"PRIu64" extra pages last\n", diff --git a/tools/testing/selftests/kvm/rseq_test.c b/tools/testing/selftests/kvm/rseq_test.c index 2237d1aac801..a54d4d05a058 100644 --- a/tools/testing/selftests/kvm/rseq_test.c +++ b/tools/testing/selftests/kvm/rseq_test.c @@ -20,8 +20,6 @@ #include "processor.h" #include "test_util.h" -#define VCPU_ID 0 - static __thread volatile struct rseq __rseq = { .cpu_id = RSEQ_CPU_ID_UNINITIALIZED, }; @@ -174,12 +172,11 @@ static void *migration_worker(void *__rseq_tid) return NULL; } -static int calc_min_max_cpu(void) +static void calc_min_max_cpu(void) { int i, cnt, nproc; - if (CPU_COUNT(&possible_mask) < 2) - return -EINVAL; + TEST_REQUIRE(CPU_COUNT(&possible_mask) >= 2); /* * CPU_SET doesn't provide a FOR_EACH helper, get the min/max CPU that @@ -201,13 +198,15 @@ static int calc_min_max_cpu(void) cnt++; } - return (cnt < 2) ? -EINVAL : 0; + __TEST_REQUIRE(cnt >= 2, + "Only one usable CPU, task migration not possible"); } int main(int argc, char *argv[]) { int r, i, snapshot; struct kvm_vm *vm; + struct kvm_vcpu *vcpu; u32 cpu, rseq_cpu; /* Tell stdout not to buffer its content */ @@ -217,10 +216,7 @@ int main(int argc, char *argv[]) TEST_ASSERT(!r, "sched_getaffinity failed, errno = %d (%s)", errno, strerror(errno)); - if (calc_min_max_cpu()) { - print_skip("Only one usable CPU, task migration not possible"); - exit(KSFT_SKIP); - } + calc_min_max_cpu(); sys_rseq(0); @@ -229,15 +225,15 @@ int main(int argc, char *argv[]) * GUEST_SYNC, while concurrently migrating the process by setting its * CPU affinity. */ - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); ucall_init(vm, NULL); pthread_create(&migration_thread, NULL, migration_worker, (void *)(unsigned long)gettid()); for (i = 0; !done; i++) { - vcpu_run(vm, VCPU_ID); - TEST_ASSERT(get_ucall(vm, VCPU_ID, NULL) == UCALL_SYNC, + vcpu_run(vcpu); + TEST_ASSERT(get_ucall(vcpu, NULL) == UCALL_SYNC, "Guest failed?"); /* diff --git a/tools/testing/selftests/kvm/s390x/memop.c b/tools/testing/selftests/kvm/s390x/memop.c index 49f26f544127..9113696d5178 100644 --- a/tools/testing/selftests/kvm/s390x/memop.c +++ b/tools/testing/selftests/kvm/s390x/memop.c @@ -14,6 +14,7 @@ #include "test_util.h" #include "kvm_util.h" +#include "kselftest.h" enum mop_target { LOGICAL, @@ -98,21 +99,18 @@ static struct kvm_s390_mem_op ksmo_from_desc(struct mop_desc desc) return ksmo; } -/* vcpu dummy id signifying that vm instead of vcpu ioctl is to occur */ -const uint32_t VM_VCPU_ID = (uint32_t)-1; - -struct test_vcpu { +struct test_info { struct kvm_vm *vm; - uint32_t id; + struct kvm_vcpu *vcpu; }; #define PRINT_MEMOP false -static void print_memop(uint32_t vcpu_id, const struct kvm_s390_mem_op *ksmo) +static void print_memop(struct kvm_vcpu *vcpu, const struct kvm_s390_mem_op *ksmo) { if (!PRINT_MEMOP) return; - if (vcpu_id == VM_VCPU_ID) + if (!vcpu) printf("vm memop("); else printf("vcpu memop("); @@ -147,25 +145,29 @@ static void print_memop(uint32_t vcpu_id, const struct kvm_s390_mem_op *ksmo) puts(")"); } -static void memop_ioctl(struct test_vcpu vcpu, struct kvm_s390_mem_op *ksmo) +static void memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo) { - if (vcpu.id == VM_VCPU_ID) - vm_ioctl(vcpu.vm, KVM_S390_MEM_OP, ksmo); + struct kvm_vcpu *vcpu = info.vcpu; + + if (!vcpu) + vm_ioctl(info.vm, KVM_S390_MEM_OP, ksmo); else - vcpu_ioctl(vcpu.vm, vcpu.id, KVM_S390_MEM_OP, ksmo); + vcpu_ioctl(vcpu, KVM_S390_MEM_OP, ksmo); } -static int err_memop_ioctl(struct test_vcpu vcpu, struct kvm_s390_mem_op *ksmo) +static int err_memop_ioctl(struct test_info info, struct kvm_s390_mem_op *ksmo) { - if (vcpu.id == VM_VCPU_ID) - return _vm_ioctl(vcpu.vm, KVM_S390_MEM_OP, ksmo); + struct kvm_vcpu *vcpu = info.vcpu; + + if (!vcpu) + return __vm_ioctl(info.vm, KVM_S390_MEM_OP, ksmo); else - return _vcpu_ioctl(vcpu.vm, vcpu.id, KVM_S390_MEM_OP, ksmo); + return __vcpu_ioctl(vcpu, KVM_S390_MEM_OP, ksmo); } -#define MEMOP(err, vcpu_p, mop_target_p, access_mode_p, buf_p, size_p, ...) \ +#define MEMOP(err, info_p, mop_target_p, access_mode_p, buf_p, size_p, ...) \ ({ \ - struct test_vcpu __vcpu = (vcpu_p); \ + struct test_info __info = (info_p); \ struct mop_desc __desc = { \ .target = (mop_target_p), \ .mode = (access_mode_p), \ @@ -177,13 +179,13 @@ static int err_memop_ioctl(struct test_vcpu vcpu, struct kvm_s390_mem_op *ksmo) \ if (__desc._gaddr_v) { \ if (__desc.target == ABSOLUTE) \ - __desc.gaddr = addr_gva2gpa(__vcpu.vm, __desc.gaddr_v); \ + __desc.gaddr = addr_gva2gpa(__info.vm, __desc.gaddr_v); \ else \ __desc.gaddr = __desc.gaddr_v; \ } \ __ksmo = ksmo_from_desc(__desc); \ - print_memop(__vcpu.id, &__ksmo); \ - err##memop_ioctl(__vcpu, &__ksmo); \ + print_memop(__info.vcpu, &__ksmo); \ + err##memop_ioctl(__info, &__ksmo); \ }) #define MOP(...) MEMOP(, __VA_ARGS__) @@ -200,7 +202,6 @@ static int err_memop_ioctl(struct test_vcpu vcpu, struct kvm_s390_mem_op *ksmo) #define CHECK_N_DO(f, ...) ({ f(__VA_ARGS__, CHECK_ONLY); f(__VA_ARGS__); }) -#define VCPU_ID 1 #define PAGE_SHIFT 12 #define PAGE_SIZE (1ULL << PAGE_SHIFT) #define PAGE_MASK (~(PAGE_SIZE - 1)) @@ -212,21 +213,22 @@ static uint8_t mem2[65536]; struct test_default { struct kvm_vm *kvm_vm; - struct test_vcpu vm; - struct test_vcpu vcpu; + struct test_info vm; + struct test_info vcpu; struct kvm_run *run; int size; }; static struct test_default test_default_init(void *guest_code) { + struct kvm_vcpu *vcpu; struct test_default t; t.size = min((size_t)kvm_check_cap(KVM_CAP_S390_MEM_OP), sizeof(mem1)); - t.kvm_vm = vm_create_default(VCPU_ID, 0, guest_code); - t.vm = (struct test_vcpu) { t.kvm_vm, VM_VCPU_ID }; - t.vcpu = (struct test_vcpu) { t.kvm_vm, VCPU_ID }; - t.run = vcpu_state(t.kvm_vm, VCPU_ID); + t.kvm_vm = vm_create_with_one_vcpu(&vcpu, guest_code); + t.vm = (struct test_info) { t.kvm_vm, NULL }; + t.vcpu = (struct test_info) { t.kvm_vm, vcpu }; + t.run = vcpu->run; return t; } @@ -241,14 +243,15 @@ enum stage { STAGE_COPIED, }; -#define HOST_SYNC(vcpu_p, stage) \ +#define HOST_SYNC(info_p, stage) \ ({ \ - struct test_vcpu __vcpu = (vcpu_p); \ + struct test_info __info = (info_p); \ + struct kvm_vcpu *__vcpu = __info.vcpu; \ struct ucall uc; \ int __stage = (stage); \ \ - vcpu_run(__vcpu.vm, __vcpu.id); \ - get_ucall(__vcpu.vm, __vcpu.id, &uc); \ + vcpu_run(__vcpu); \ + get_ucall(__vcpu, &uc); \ ASSERT_EQ(uc.cmd, UCALL_SYNC); \ ASSERT_EQ(uc.args[1], __stage); \ }) \ @@ -267,7 +270,7 @@ static void prepare_mem12(void) #define DEFAULT_WRITE_READ(copy_cpu, mop_cpu, mop_target_p, size, ...) \ ({ \ - struct test_vcpu __copy_cpu = (copy_cpu), __mop_cpu = (mop_cpu); \ + struct test_info __copy_cpu = (copy_cpu), __mop_cpu = (mop_cpu); \ enum mop_target __target = (mop_target_p); \ uint32_t __size = (size); \ \ @@ -282,7 +285,7 @@ static void prepare_mem12(void) #define DEFAULT_READ(copy_cpu, mop_cpu, mop_target_p, size, ...) \ ({ \ - struct test_vcpu __copy_cpu = (copy_cpu), __mop_cpu = (mop_cpu); \ + struct test_info __copy_cpu = (copy_cpu), __mop_cpu = (mop_cpu); \ enum mop_target __target = (mop_target_p); \ uint32_t __size = (size); \ \ @@ -623,34 +626,34 @@ static void guest_idle(void) GUEST_SYNC(STAGE_IDLED); } -static void _test_errors_common(struct test_vcpu vcpu, enum mop_target target, int size) +static void _test_errors_common(struct test_info info, enum mop_target target, int size) { int rv; /* Bad size: */ - rv = ERR_MOP(vcpu, target, WRITE, mem1, -1, GADDR_V(mem1)); + rv = ERR_MOP(info, target, WRITE, mem1, -1, GADDR_V(mem1)); TEST_ASSERT(rv == -1 && errno == E2BIG, "ioctl allows insane sizes"); /* Zero size: */ - rv = ERR_MOP(vcpu, target, WRITE, mem1, 0, GADDR_V(mem1)); + rv = ERR_MOP(info, target, WRITE, mem1, 0, GADDR_V(mem1)); TEST_ASSERT(rv == -1 && (errno == EINVAL || errno == ENOMEM), "ioctl allows 0 as size"); /* Bad flags: */ - rv = ERR_MOP(vcpu, target, WRITE, mem1, size, GADDR_V(mem1), SET_FLAGS(-1)); + rv = ERR_MOP(info, target, WRITE, mem1, size, GADDR_V(mem1), SET_FLAGS(-1)); TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows all flags"); /* Bad guest address: */ - rv = ERR_MOP(vcpu, target, WRITE, mem1, size, GADDR((void *)~0xfffUL), CHECK_ONLY); + rv = ERR_MOP(info, target, WRITE, mem1, size, GADDR((void *)~0xfffUL), CHECK_ONLY); TEST_ASSERT(rv > 0, "ioctl does not report bad guest memory access"); /* Bad host address: */ - rv = ERR_MOP(vcpu, target, WRITE, 0, size, GADDR_V(mem1)); + rv = ERR_MOP(info, target, WRITE, 0, size, GADDR_V(mem1)); TEST_ASSERT(rv == -1 && errno == EFAULT, "ioctl does not report bad host memory address"); /* Bad key: */ - rv = ERR_MOP(vcpu, target, WRITE, mem1, size, GADDR_V(mem1), KEY(17)); + rv = ERR_MOP(info, target, WRITE, mem1, size, GADDR_V(mem1), KEY(17)); TEST_ASSERT(rv == -1 && errno == EINVAL, "ioctl allows invalid key"); } @@ -691,34 +694,89 @@ static void test_errors(void) kvm_vm_free(t.kvm_vm); } +struct testdef { + const char *name; + void (*test)(void); + int extension; +} testlist[] = { + { + .name = "simple copy", + .test = test_copy, + }, + { + .name = "generic error checks", + .test = test_errors, + }, + { + .name = "copy with storage keys", + .test = test_copy_key, + .extension = 1, + }, + { + .name = "copy with key storage protection override", + .test = test_copy_key_storage_prot_override, + .extension = 1, + }, + { + .name = "copy with key fetch protection", + .test = test_copy_key_fetch_prot, + .extension = 1, + }, + { + .name = "copy with key fetch protection override", + .test = test_copy_key_fetch_prot_override, + .extension = 1, + }, + { + .name = "error checks with key", + .test = test_errors_key, + .extension = 1, + }, + { + .name = "termination", + .test = test_termination, + .extension = 1, + }, + { + .name = "error checks with key storage protection override", + .test = test_errors_key_storage_prot_override, + .extension = 1, + }, + { + .name = "error checks without key fetch prot override", + .test = test_errors_key_fetch_prot_override_not_enabled, + .extension = 1, + }, + { + .name = "error checks with key fetch prot override", + .test = test_errors_key_fetch_prot_override_enabled, + .extension = 1, + }, +}; + int main(int argc, char *argv[]) { - int memop_cap, extension_cap; + int extension_cap, idx; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_S390_MEM_OP)); setbuf(stdout, NULL); /* Tell stdout not to buffer its content */ - memop_cap = kvm_check_cap(KVM_CAP_S390_MEM_OP); + ksft_print_header(); + + ksft_set_plan(ARRAY_SIZE(testlist)); + extension_cap = kvm_check_cap(KVM_CAP_S390_MEM_OP_EXTENSION); - if (!memop_cap) { - print_skip("CAP_S390_MEM_OP not supported"); - exit(KSFT_SKIP); + for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { + if (extension_cap >= testlist[idx].extension) { + testlist[idx].test(); + ksft_test_result_pass("%s\n", testlist[idx].name); + } else { + ksft_test_result_skip("%s - extension level %d not supported\n", + testlist[idx].name, + testlist[idx].extension); + } } - test_copy(); - if (extension_cap > 0) { - test_copy_key(); - test_copy_key_storage_prot_override(); - test_copy_key_fetch_prot(); - test_copy_key_fetch_prot_override(); - test_errors_key(); - test_termination(); - test_errors_key_storage_prot_override(); - test_errors_key_fetch_prot_override_not_enabled(); - test_errors_key_fetch_prot_override_enabled(); - } else { - print_skip("storage key memop extension not supported"); - } - test_errors(); - - return 0; + ksft_finished(); /* Print results and exit() accordingly */ } diff --git a/tools/testing/selftests/kvm/s390x/resets.c b/tools/testing/selftests/kvm/s390x/resets.c index b143db6d8693..19486084eb30 100644 --- a/tools/testing/selftests/kvm/s390x/resets.c +++ b/tools/testing/selftests/kvm/s390x/resets.c @@ -12,15 +12,14 @@ #include "test_util.h" #include "kvm_util.h" +#include "kselftest.h" -#define VCPU_ID 3 #define LOCAL_IRQS 32 -struct kvm_s390_irq buf[VCPU_ID + LOCAL_IRQS]; +#define ARBITRARY_NON_ZERO_VCPU_ID 3 + +struct kvm_s390_irq buf[ARBITRARY_NON_ZERO_VCPU_ID + LOCAL_IRQS]; -struct kvm_vm *vm; -struct kvm_run *run; -struct kvm_sync_regs *sync_regs; static uint8_t regs_null[512]; static void guest_code_initial(void) @@ -58,25 +57,22 @@ static void guest_code_initial(void) ); } -static void test_one_reg(uint64_t id, uint64_t value) +static void test_one_reg(struct kvm_vcpu *vcpu, uint64_t id, uint64_t value) { - struct kvm_one_reg reg; uint64_t eval_reg; - reg.addr = (uintptr_t)&eval_reg; - reg.id = id; - vcpu_get_reg(vm, VCPU_ID, ®); + vcpu_get_reg(vcpu, id, &eval_reg); TEST_ASSERT(eval_reg == value, "value == 0x%lx", value); } -static void assert_noirq(void) +static void assert_noirq(struct kvm_vcpu *vcpu) { struct kvm_s390_irq_state irq_state; int irqs; irq_state.len = sizeof(buf); irq_state.buf = (unsigned long)buf; - irqs = _vcpu_ioctl(vm, VCPU_ID, KVM_S390_GET_IRQ_STATE, &irq_state); + irqs = __vcpu_ioctl(vcpu, KVM_S390_GET_IRQ_STATE, &irq_state); /* * irqs contains the number of retrieved interrupts. Any interrupt * (notably, the emergency call interrupt we have injected) should @@ -86,19 +82,20 @@ static void assert_noirq(void) TEST_ASSERT(!irqs, "IRQ pending"); } -static void assert_clear(void) +static void assert_clear(struct kvm_vcpu *vcpu) { + struct kvm_sync_regs *sync_regs = &vcpu->run->s.regs; struct kvm_sregs sregs; struct kvm_regs regs; struct kvm_fpu fpu; - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); TEST_ASSERT(!memcmp(®s.gprs, regs_null, sizeof(regs.gprs)), "grs == 0"); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); TEST_ASSERT(!memcmp(&sregs.acrs, regs_null, sizeof(sregs.acrs)), "acrs == 0"); - vcpu_fpu_get(vm, VCPU_ID, &fpu); + vcpu_fpu_get(vcpu, &fpu); TEST_ASSERT(!memcmp(&fpu.fprs, regs_null, sizeof(fpu.fprs)), "fprs == 0"); /* sync regs */ @@ -112,8 +109,10 @@ static void assert_clear(void) "vrs0-15 == 0 (sync_regs)"); } -static void assert_initial_noclear(void) +static void assert_initial_noclear(struct kvm_vcpu *vcpu) { + struct kvm_sync_regs *sync_regs = &vcpu->run->s.regs; + TEST_ASSERT(sync_regs->gprs[0] == 0xffff000000000000UL, "gpr0 == 0xffff000000000000 (sync_regs)"); TEST_ASSERT(sync_regs->gprs[1] == 0x0000555500000000UL, @@ -127,13 +126,14 @@ static void assert_initial_noclear(void) TEST_ASSERT(sync_regs->acrs[9] == 1, "ar9 == 1 (sync_regs)"); } -static void assert_initial(void) +static void assert_initial(struct kvm_vcpu *vcpu) { + struct kvm_sync_regs *sync_regs = &vcpu->run->s.regs; struct kvm_sregs sregs; struct kvm_fpu fpu; /* KVM_GET_SREGS */ - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); TEST_ASSERT(sregs.crs[0] == 0xE0UL, "cr0 == 0xE0 (KVM_GET_SREGS)"); TEST_ASSERT(sregs.crs[14] == 0xC2000000UL, "cr14 == 0xC2000000 (KVM_GET_SREGS)"); @@ -156,36 +156,38 @@ static void assert_initial(void) TEST_ASSERT(sync_regs->gbea == 1, "gbea == 1 (sync_regs)"); /* kvm_run */ - TEST_ASSERT(run->psw_addr == 0, "psw_addr == 0 (kvm_run)"); - TEST_ASSERT(run->psw_mask == 0, "psw_mask == 0 (kvm_run)"); + TEST_ASSERT(vcpu->run->psw_addr == 0, "psw_addr == 0 (kvm_run)"); + TEST_ASSERT(vcpu->run->psw_mask == 0, "psw_mask == 0 (kvm_run)"); - vcpu_fpu_get(vm, VCPU_ID, &fpu); + vcpu_fpu_get(vcpu, &fpu); TEST_ASSERT(!fpu.fpc, "fpc == 0"); - test_one_reg(KVM_REG_S390_GBEA, 1); - test_one_reg(KVM_REG_S390_PP, 0); - test_one_reg(KVM_REG_S390_TODPR, 0); - test_one_reg(KVM_REG_S390_CPU_TIMER, 0); - test_one_reg(KVM_REG_S390_CLOCK_COMP, 0); + test_one_reg(vcpu, KVM_REG_S390_GBEA, 1); + test_one_reg(vcpu, KVM_REG_S390_PP, 0); + test_one_reg(vcpu, KVM_REG_S390_TODPR, 0); + test_one_reg(vcpu, KVM_REG_S390_CPU_TIMER, 0); + test_one_reg(vcpu, KVM_REG_S390_CLOCK_COMP, 0); } -static void assert_normal_noclear(void) +static void assert_normal_noclear(struct kvm_vcpu *vcpu) { + struct kvm_sync_regs *sync_regs = &vcpu->run->s.regs; + TEST_ASSERT(sync_regs->crs[2] == 0x10, "cr2 == 10 (sync_regs)"); TEST_ASSERT(sync_regs->crs[8] == 1, "cr10 == 1 (sync_regs)"); TEST_ASSERT(sync_regs->crs[10] == 1, "cr10 == 1 (sync_regs)"); TEST_ASSERT(sync_regs->crs[11] == -1, "cr11 == -1 (sync_regs)"); } -static void assert_normal(void) +static void assert_normal(struct kvm_vcpu *vcpu) { - test_one_reg(KVM_REG_S390_PFTOKEN, KVM_S390_PFAULT_TOKEN_INVALID); - TEST_ASSERT(sync_regs->pft == KVM_S390_PFAULT_TOKEN_INVALID, + test_one_reg(vcpu, KVM_REG_S390_PFTOKEN, KVM_S390_PFAULT_TOKEN_INVALID); + TEST_ASSERT(vcpu->run->s.regs.pft == KVM_S390_PFAULT_TOKEN_INVALID, "pft == 0xff..... (sync_regs)"); - assert_noirq(); + assert_noirq(vcpu); } -static void inject_irq(int cpu_id) +static void inject_irq(struct kvm_vcpu *vcpu) { struct kvm_s390_irq_state irq_state; struct kvm_s390_irq *irq = &buf[0]; @@ -195,85 +197,119 @@ static void inject_irq(int cpu_id) irq_state.len = sizeof(struct kvm_s390_irq); irq_state.buf = (unsigned long)buf; irq->type = KVM_S390_INT_EMERGENCY; - irq->u.emerg.code = cpu_id; - irqs = _vcpu_ioctl(vm, cpu_id, KVM_S390_SET_IRQ_STATE, &irq_state); + irq->u.emerg.code = vcpu->id; + irqs = __vcpu_ioctl(vcpu, KVM_S390_SET_IRQ_STATE, &irq_state); TEST_ASSERT(irqs >= 0, "Error injecting EMERGENCY IRQ errno %d\n", errno); } +static struct kvm_vm *create_vm(struct kvm_vcpu **vcpu) +{ + struct kvm_vm *vm; + + vm = vm_create(1); + + *vcpu = vm_vcpu_add(vm, ARBITRARY_NON_ZERO_VCPU_ID, guest_code_initial); + + return vm; +} + static void test_normal(void) { - pr_info("Testing normal reset\n"); - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code_initial); - run = vcpu_state(vm, VCPU_ID); - sync_regs = &run->s.regs; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - vcpu_run(vm, VCPU_ID); + ksft_print_msg("Testing normal reset\n"); + vm = create_vm(&vcpu); - inject_irq(VCPU_ID); + vcpu_run(vcpu); - vcpu_ioctl(vm, VCPU_ID, KVM_S390_NORMAL_RESET, 0); + inject_irq(vcpu); + + vcpu_ioctl(vcpu, KVM_S390_NORMAL_RESET, NULL); /* must clears */ - assert_normal(); + assert_normal(vcpu); /* must not clears */ - assert_normal_noclear(); - assert_initial_noclear(); + assert_normal_noclear(vcpu); + assert_initial_noclear(vcpu); kvm_vm_free(vm); } static void test_initial(void) { - pr_info("Testing initial reset\n"); - vm = vm_create_default(VCPU_ID, 0, guest_code_initial); - run = vcpu_state(vm, VCPU_ID); - sync_regs = &run->s.regs; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - vcpu_run(vm, VCPU_ID); + ksft_print_msg("Testing initial reset\n"); + vm = create_vm(&vcpu); - inject_irq(VCPU_ID); + vcpu_run(vcpu); - vcpu_ioctl(vm, VCPU_ID, KVM_S390_INITIAL_RESET, 0); + inject_irq(vcpu); + + vcpu_ioctl(vcpu, KVM_S390_INITIAL_RESET, NULL); /* must clears */ - assert_normal(); - assert_initial(); + assert_normal(vcpu); + assert_initial(vcpu); /* must not clears */ - assert_initial_noclear(); + assert_initial_noclear(vcpu); kvm_vm_free(vm); } static void test_clear(void) { - pr_info("Testing clear reset\n"); - vm = vm_create_default(VCPU_ID, 0, guest_code_initial); - run = vcpu_state(vm, VCPU_ID); - sync_regs = &run->s.regs; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - vcpu_run(vm, VCPU_ID); + ksft_print_msg("Testing clear reset\n"); + vm = create_vm(&vcpu); - inject_irq(VCPU_ID); + vcpu_run(vcpu); - vcpu_ioctl(vm, VCPU_ID, KVM_S390_CLEAR_RESET, 0); + inject_irq(vcpu); + + vcpu_ioctl(vcpu, KVM_S390_CLEAR_RESET, NULL); /* must clears */ - assert_normal(); - assert_initial(); - assert_clear(); + assert_normal(vcpu); + assert_initial(vcpu); + assert_clear(vcpu); kvm_vm_free(vm); } +struct testdef { + const char *name; + void (*test)(void); + bool needs_cap; +} testlist[] = { + { "initial", test_initial, false }, + { "normal", test_normal, true }, + { "clear", test_clear, true }, +}; + int main(int argc, char *argv[]) { + bool has_s390_vcpu_resets = kvm_check_cap(KVM_CAP_S390_VCPU_RESETS); + int idx; + setbuf(stdout, NULL); /* Tell stdout not to buffer its content */ - test_initial(); - if (kvm_check_cap(KVM_CAP_S390_VCPU_RESETS)) { - test_normal(); - test_clear(); + ksft_print_header(); + ksft_set_plan(ARRAY_SIZE(testlist)); + + for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { + if (!testlist[idx].needs_cap || has_s390_vcpu_resets) { + testlist[idx].test(); + ksft_test_result_pass("%s\n", testlist[idx].name); + } else { + ksft_test_result_skip("%s - no VCPU_RESETS capability\n", + testlist[idx].name); + } } - return 0; + + ksft_finished(); /* Print results and exit() accordingly */ } diff --git a/tools/testing/selftests/kvm/s390x/sync_regs_test.c b/tools/testing/selftests/kvm/s390x/sync_regs_test.c index caf7b8859a94..3fdb6e2598eb 100644 --- a/tools/testing/selftests/kvm/s390x/sync_regs_test.c +++ b/tools/testing/selftests/kvm/s390x/sync_regs_test.c @@ -21,8 +21,7 @@ #include "test_util.h" #include "kvm_util.h" #include "diag318_test_handler.h" - -#define VCPU_ID 5 +#include "kselftest.h" static void guest_code(void) { @@ -74,61 +73,58 @@ static void compare_sregs(struct kvm_sregs *left, struct kvm_sync_regs *right) #define TEST_SYNC_FIELDS (KVM_SYNC_GPRS|KVM_SYNC_ACRS|KVM_SYNC_CRS|KVM_SYNC_DIAG318) #define INVALID_SYNC_FIELD 0x80000000 -int main(int argc, char *argv[]) +void test_read_invalid(struct kvm_vcpu *vcpu) { - struct kvm_vm *vm; - struct kvm_run *run; - struct kvm_regs regs; - struct kvm_sregs sregs; - int rv, cap; - - /* Tell stdout not to buffer its content */ - setbuf(stdout, NULL); - - cap = kvm_check_cap(KVM_CAP_SYNC_REGS); - if (!cap) { - print_skip("CAP_SYNC_REGS not supported"); - exit(KSFT_SKIP); - } - - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - - run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; + int rv; /* Request reading invalid register set from VCPU. */ run->kvm_valid_regs = INVALID_SYNC_FIELD; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0; + run->kvm_valid_regs = 0; run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0; + run->kvm_valid_regs = 0; +} + +void test_set_invalid(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + int rv; /* Request setting invalid register set into VCPU. */ run->kvm_dirty_regs = INVALID_SYNC_FIELD; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0; + run->kvm_dirty_regs = 0; run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0; + run->kvm_dirty_regs = 0; +} + +void test_req_and_verify_all_valid_regs(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + struct kvm_sregs sregs; + struct kvm_regs regs; + int rv; /* Request and verify all valid register sets. */ run->kvm_valid_regs = TEST_SYNC_FIELDS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv); TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC, "Unexpected exit reason: %u (%s)\n", @@ -141,11 +137,19 @@ int main(int argc, char *argv[]) run->s390_sieic.icptcode, run->s390_sieic.ipa, run->s390_sieic.ipb); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); compare_regs(®s, &run->s.regs); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); compare_sregs(&sregs, &run->s.regs); +} + +void test_set_and_verify_various_reg_values(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + struct kvm_sregs sregs; + struct kvm_regs regs; + int rv; /* Set and verify various register values */ run->s.regs.gprs[11] = 0xBAD1DEA; @@ -159,7 +163,7 @@ int main(int argc, char *argv[]) run->kvm_dirty_regs |= KVM_SYNC_DIAG318; } - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv); TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC, "Unexpected exit reason: %u (%s)\n", @@ -175,11 +179,17 @@ int main(int argc, char *argv[]) "diag318 sync regs value incorrect 0x%llx.", run->s.regs.diag318); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); compare_regs(®s, &run->s.regs); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); compare_sregs(&sregs, &run->s.regs); +} + +void test_clear_kvm_dirty_regs_bits(struct kvm_vcpu *vcpu) +{ + struct kvm_run *run = vcpu->run; + int rv; /* Clear kvm_dirty_regs bits, verify new s.regs values are * overwritten with existing guest values. @@ -188,7 +198,7 @@ int main(int argc, char *argv[]) run->kvm_dirty_regs = 0; run->s.regs.gprs[11] = 0xDEADBEEF; run->s.regs.diag318 = 0x4B1D; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv == 0, "vcpu_run failed: %d\n", rv); TEST_ASSERT(run->exit_reason == KVM_EXIT_S390_SIEIC, "Unexpected exit reason: %u (%s)\n", @@ -200,8 +210,43 @@ int main(int argc, char *argv[]) TEST_ASSERT(run->s.regs.diag318 != 0x4B1D, "diag318 sync regs value incorrect 0x%llx.", run->s.regs.diag318); +} + +struct testdef { + const char *name; + void (*test)(struct kvm_vcpu *vcpu); +} testlist[] = { + { "read invalid", test_read_invalid }, + { "set invalid", test_set_invalid }, + { "request+verify all valid regs", test_req_and_verify_all_valid_regs }, + { "set+verify various regs", test_set_and_verify_various_reg_values }, + { "clear kvm_dirty_regs bits", test_clear_kvm_dirty_regs_bits }, +}; + +int main(int argc, char *argv[]) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + int idx; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_SYNC_REGS)); + + /* Tell stdout not to buffer its content */ + setbuf(stdout, NULL); + + ksft_print_header(); + + ksft_set_plan(ARRAY_SIZE(testlist)); + + /* Create VM */ + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + + for (idx = 0; idx < ARRAY_SIZE(testlist); idx++) { + testlist[idx].test(vcpu); + ksft_test_result_pass("%s\n", testlist[idx].name); + } kvm_vm_free(vm); - return 0; + ksft_finished(); /* Print results and exit() accordingly */ } diff --git a/tools/testing/selftests/kvm/s390x/tprot.c b/tools/testing/selftests/kvm/s390x/tprot.c index c097b9db495e..a9a0b76e5fa4 100644 --- a/tools/testing/selftests/kvm/s390x/tprot.c +++ b/tools/testing/selftests/kvm/s390x/tprot.c @@ -8,14 +8,13 @@ #include #include "test_util.h" #include "kvm_util.h" +#include "kselftest.h" #define PAGE_SHIFT 12 #define PAGE_SIZE (1 << PAGE_SHIFT) #define CR0_FETCH_PROTECTION_OVERRIDE (1UL << (63 - 38)) #define CR0_STORAGE_PROTECTION_OVERRIDE (1UL << (63 - 39)) -#define VCPU_ID 1 - static __aligned(PAGE_SIZE) uint8_t pages[2][PAGE_SIZE]; static uint8_t *const page_store_prot = pages[0]; static uint8_t *const page_fetch_prot = pages[1]; @@ -63,12 +62,12 @@ static enum permission test_protection(void *addr, uint8_t key) } enum stage { - STAGE_END, STAGE_INIT_SIMPLE, TEST_SIMPLE, STAGE_INIT_FETCH_PROT_OVERRIDE, TEST_FETCH_PROT_OVERRIDE, TEST_STORAGE_PROT_OVERRIDE, + STAGE_END /* must be the last entry (it's the amount of tests) */ }; struct test { @@ -182,46 +181,63 @@ static void guest_code(void) GUEST_SYNC(perform_next_stage(&i, mapped_0)); } -#define HOST_SYNC(vmp, stage) \ -({ \ - struct kvm_vm *__vm = (vmp); \ - struct ucall uc; \ - int __stage = (stage); \ - \ - vcpu_run(__vm, VCPU_ID); \ - get_ucall(__vm, VCPU_ID, &uc); \ - if (uc.cmd == UCALL_ABORT) { \ - TEST_FAIL("line %lu: %s, hints: %lu, %lu", uc.args[1], \ - (const char *)uc.args[0], uc.args[2], uc.args[3]); \ - } \ - ASSERT_EQ(uc.cmd, UCALL_SYNC); \ - ASSERT_EQ(uc.args[1], __stage); \ +#define HOST_SYNC_NO_TAP(vcpup, stage) \ +({ \ + struct kvm_vcpu *__vcpu = (vcpup); \ + struct ucall uc; \ + int __stage = (stage); \ + \ + vcpu_run(__vcpu); \ + get_ucall(__vcpu, &uc); \ + if (uc.cmd == UCALL_ABORT) \ + REPORT_GUEST_ASSERT_2(uc, "hints: %lu, %lu"); \ + ASSERT_EQ(uc.cmd, UCALL_SYNC); \ + ASSERT_EQ(uc.args[1], __stage); \ +}) + +#define HOST_SYNC(vcpu, stage) \ +({ \ + HOST_SYNC_NO_TAP(vcpu, stage); \ + ksft_test_result_pass("" #stage "\n"); \ }) int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; vm_vaddr_t guest_0_page; - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); + ksft_print_header(); + ksft_set_plan(STAGE_END); - HOST_SYNC(vm, STAGE_INIT_SIMPLE); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; + + HOST_SYNC(vcpu, STAGE_INIT_SIMPLE); mprotect(addr_gva2hva(vm, (vm_vaddr_t)pages), PAGE_SIZE * 2, PROT_READ); - HOST_SYNC(vm, TEST_SIMPLE); + HOST_SYNC(vcpu, TEST_SIMPLE); guest_0_page = vm_vaddr_alloc(vm, PAGE_SIZE, 0); - if (guest_0_page != 0) - print_skip("Did not allocate page at 0 for fetch protection override tests"); - HOST_SYNC(vm, STAGE_INIT_FETCH_PROT_OVERRIDE); + if (guest_0_page != 0) { + /* Use NO_TAP so we don't get a PASS print */ + HOST_SYNC_NO_TAP(vcpu, STAGE_INIT_FETCH_PROT_OVERRIDE); + ksft_test_result_skip("STAGE_INIT_FETCH_PROT_OVERRIDE - " + "Did not allocate page at 0\n"); + } else { + HOST_SYNC(vcpu, STAGE_INIT_FETCH_PROT_OVERRIDE); + } if (guest_0_page == 0) mprotect(addr_gva2hva(vm, (vm_vaddr_t)0), PAGE_SIZE, PROT_READ); run->s.regs.crs[0] |= CR0_FETCH_PROTECTION_OVERRIDE; run->kvm_dirty_regs = KVM_SYNC_CRS; - HOST_SYNC(vm, TEST_FETCH_PROT_OVERRIDE); + HOST_SYNC(vcpu, TEST_FETCH_PROT_OVERRIDE); run->s.regs.crs[0] |= CR0_STORAGE_PROTECTION_OVERRIDE; run->kvm_dirty_regs = KVM_SYNC_CRS; - HOST_SYNC(vm, TEST_STORAGE_PROT_OVERRIDE); + HOST_SYNC(vcpu, TEST_STORAGE_PROT_OVERRIDE); + + kvm_vm_free(vm); + + ksft_finished(); /* Print results and exit() accordingly */ } diff --git a/tools/testing/selftests/kvm/set_memory_region_test.c b/tools/testing/selftests/kvm/set_memory_region_test.c index 73bc297dabe6..0d55f508d595 100644 --- a/tools/testing/selftests/kvm/set_memory_region_test.c +++ b/tools/testing/selftests/kvm/set_memory_region_test.c @@ -17,8 +17,6 @@ #include #include -#define VCPU_ID 0 - /* * s390x needs at least 1MB alignment, and the x86_64 MOVE/DELETE tests need a * 2MB sized and aligned region so that the initial region corresponds to @@ -54,8 +52,8 @@ static inline uint64_t guest_spin_on_val(uint64_t spin_val) static void *vcpu_worker(void *data) { - struct kvm_vm *vm = data; - struct kvm_run *run; + struct kvm_vcpu *vcpu = data; + struct kvm_run *run = vcpu->run; struct ucall uc; uint64_t cmd; @@ -64,13 +62,11 @@ static void *vcpu_worker(void *data) * which will occur if the guest attempts to access a memslot after it * has been deleted or while it is being moved . */ - run = vcpu_state(vm, VCPU_ID); - while (1) { - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); if (run->exit_reason == KVM_EXIT_IO) { - cmd = get_ucall(vm, VCPU_ID, &uc); + cmd = get_ucall(vcpu, &uc); if (cmd != UCALL_SYNC) break; @@ -92,8 +88,7 @@ static void *vcpu_worker(void *data) } if (run->exit_reason == KVM_EXIT_IO && cmd == UCALL_ABORT) - TEST_FAIL("%s at %s:%ld, val = %lu", (const char *)uc.args[0], - __FILE__, uc.args[1], uc.args[2]); + REPORT_GUEST_ASSERT_1(uc, "val = %lu"); return NULL; } @@ -113,13 +108,14 @@ static void wait_for_vcpu(void) usleep(100000); } -static struct kvm_vm *spawn_vm(pthread_t *vcpu_thread, void *guest_code) +static struct kvm_vm *spawn_vm(struct kvm_vcpu **vcpu, pthread_t *vcpu_thread, + void *guest_code) { struct kvm_vm *vm; uint64_t *hva; uint64_t gpa; - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(vcpu, guest_code); vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_THP, MEM_REGION_GPA, MEM_REGION_SLOT, @@ -138,7 +134,7 @@ static struct kvm_vm *spawn_vm(pthread_t *vcpu_thread, void *guest_code) hva = addr_gpa2hva(vm, MEM_REGION_GPA); memset(hva, 0, 2 * 4096); - pthread_create(vcpu_thread, NULL, vcpu_worker, vm); + pthread_create(vcpu_thread, NULL, vcpu_worker, *vcpu); /* Ensure the guest thread is spun up. */ wait_for_vcpu(); @@ -180,10 +176,11 @@ static void guest_code_move_memory_region(void) static void test_move_memory_region(void) { pthread_t vcpu_thread; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; uint64_t *hva; - vm = spawn_vm(&vcpu_thread, guest_code_move_memory_region); + vm = spawn_vm(&vcpu, &vcpu_thread, guest_code_move_memory_region); hva = addr_gpa2hva(vm, MEM_REGION_GPA); @@ -258,11 +255,12 @@ static void guest_code_delete_memory_region(void) static void test_delete_memory_region(void) { pthread_t vcpu_thread; + struct kvm_vcpu *vcpu; struct kvm_regs regs; struct kvm_run *run; struct kvm_vm *vm; - vm = spawn_vm(&vcpu_thread, guest_code_delete_memory_region); + vm = spawn_vm(&vcpu, &vcpu_thread, guest_code_delete_memory_region); /* Delete the memory region, the guest should not die. */ vm_mem_region_delete(vm, MEM_REGION_SLOT); @@ -286,13 +284,13 @@ static void test_delete_memory_region(void) pthread_join(vcpu_thread, NULL); - run = vcpu_state(vm, VCPU_ID); + run = vcpu->run; TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN || run->exit_reason == KVM_EXIT_INTERNAL_ERROR, "Unexpected exit reason = %d", run->exit_reason); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); /* * On AMD, after KVM_EXIT_SHUTDOWN the VMCB has been reinitialized already, @@ -309,19 +307,19 @@ static void test_delete_memory_region(void) static void test_zero_memory_regions(void) { + struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; pr_info("Testing KVM_RUN with zero added memory regions\n"); - vm = vm_create(VM_MODE_DEFAULT, 0, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); + vm = vm_create_barebones(); + vcpu = __vm_vcpu_add(vm, 0); - TEST_ASSERT(!ioctl(vm_get_fd(vm), KVM_SET_NR_MMU_PAGES, 64), - "KVM_SET_NR_MMU_PAGES failed, errno = %d\n", errno); - vcpu_run(vm, VCPU_ID); + vm_ioctl(vm, KVM_SET_NR_MMU_PAGES, (void *)64ul); + vcpu_run(vcpu); - run = vcpu_state(vm, VCPU_ID); + run = vcpu->run; TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR, "Unexpected exit_reason = %u\n", run->exit_reason); @@ -354,7 +352,7 @@ static void test_add_max_memory_regions(void) "KVM_CAP_NR_MEMSLOTS should be greater than 0"); pr_info("Allowed number of memory slots: %i\n", max_mem_slots); - vm = vm_create(VM_MODE_DEFAULT, 0, O_RDWR); + vm = vm_create_barebones(); /* Check it can be added memory slots up to the maximum allowed */ pr_info("Adding slots 0..%i, each memory region with %dK size\n", diff --git a/tools/testing/selftests/kvm/steal_time.c b/tools/testing/selftests/kvm/steal_time.c index 8c4e811bd586..db8967f1a17b 100644 --- a/tools/testing/selftests/kvm/steal_time.c +++ b/tools/testing/selftests/kvm/steal_time.c @@ -58,36 +58,32 @@ static void guest_code(int cpu) GUEST_DONE(); } -static void steal_time_init(struct kvm_vm *vm) +static bool is_steal_time_supported(struct kvm_vcpu *vcpu) { - int i; - - if (!(kvm_get_supported_cpuid_entry(KVM_CPUID_FEATURES)->eax & - KVM_FEATURE_STEAL_TIME)) { - print_skip("steal-time not supported"); - exit(KSFT_SKIP); - } - - for (i = 0; i < NR_VCPUS; ++i) { - int ret; - - /* ST_GPA_BASE is identity mapped */ - st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - sync_global_to_guest(vm, st_gva[i]); - - ret = _vcpu_set_msr(vm, i, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_STEAL_RESERVED_MASK); - TEST_ASSERT(ret == 0, "Bad GPA didn't fail"); - - vcpu_set_msr(vm, i, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED); - } + return kvm_cpu_has(X86_FEATURE_KVM_STEAL_TIME); } -static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpuid) +static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) { - struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpuid]); + int ret; + + /* ST_GPA_BASE is identity mapped */ + st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); + sync_global_to_guest(vcpu->vm, st_gva[i]); + + ret = _vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, + (ulong)st_gva[i] | KVM_STEAL_RESERVED_MASK); + TEST_ASSERT(ret == 0, "Bad GPA didn't fail"); + + vcpu_set_msr(vcpu, MSR_KVM_STEAL_TIME, (ulong)st_gva[i] | KVM_MSR_ENABLED); +} + +static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) +{ + struct kvm_steal_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); int i; - pr_info("VCPU%d:\n", vcpuid); + pr_info("VCPU%d:\n", vcpu_idx); pr_info(" steal: %lld\n", st->steal); pr_info(" version: %d\n", st->version); pr_info(" flags: %d\n", st->flags); @@ -158,49 +154,50 @@ static void guest_code(int cpu) GUEST_DONE(); } -static void steal_time_init(struct kvm_vm *vm) +static bool is_steal_time_supported(struct kvm_vcpu *vcpu) { struct kvm_device_attr dev = { .group = KVM_ARM_VCPU_PVTIME_CTRL, .attr = KVM_ARM_VCPU_PVTIME_IPA, }; - int i, ret; - ret = _vcpu_ioctl(vm, 0, KVM_HAS_DEVICE_ATTR, &dev); - if (ret != 0 && errno == ENXIO) { - print_skip("steal-time not supported"); - exit(KSFT_SKIP); - } - - for (i = 0; i < NR_VCPUS; ++i) { - uint64_t st_ipa; - - vcpu_ioctl(vm, i, KVM_HAS_DEVICE_ATTR, &dev); - - dev.addr = (uint64_t)&st_ipa; - - /* ST_GPA_BASE is identity mapped */ - st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); - sync_global_to_guest(vm, st_gva[i]); - - st_ipa = (ulong)st_gva[i] | 1; - ret = _vcpu_ioctl(vm, i, KVM_SET_DEVICE_ATTR, &dev); - TEST_ASSERT(ret == -1 && errno == EINVAL, "Bad IPA didn't report EINVAL"); - - st_ipa = (ulong)st_gva[i]; - vcpu_ioctl(vm, i, KVM_SET_DEVICE_ATTR, &dev); - - ret = _vcpu_ioctl(vm, i, KVM_SET_DEVICE_ATTR, &dev); - TEST_ASSERT(ret == -1 && errno == EEXIST, "Set IPA twice without EEXIST"); - - } + return !__vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev); } -static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpuid) +static void steal_time_init(struct kvm_vcpu *vcpu, uint32_t i) { - struct st_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpuid]); + struct kvm_vm *vm = vcpu->vm; + uint64_t st_ipa; + int ret; - pr_info("VCPU%d:\n", vcpuid); + struct kvm_device_attr dev = { + .group = KVM_ARM_VCPU_PVTIME_CTRL, + .attr = KVM_ARM_VCPU_PVTIME_IPA, + .addr = (uint64_t)&st_ipa, + }; + + vcpu_ioctl(vcpu, KVM_HAS_DEVICE_ATTR, &dev); + + /* ST_GPA_BASE is identity mapped */ + st_gva[i] = (void *)(ST_GPA_BASE + i * STEAL_TIME_SIZE); + sync_global_to_guest(vm, st_gva[i]); + + st_ipa = (ulong)st_gva[i] | 1; + ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); + TEST_ASSERT(ret == -1 && errno == EINVAL, "Bad IPA didn't report EINVAL"); + + st_ipa = (ulong)st_gva[i]; + vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); + + ret = __vcpu_ioctl(vcpu, KVM_SET_DEVICE_ATTR, &dev); + TEST_ASSERT(ret == -1 && errno == EEXIST, "Set IPA twice without EEXIST"); +} + +static void steal_time_dump(struct kvm_vm *vm, uint32_t vcpu_idx) +{ + struct st_time *st = addr_gva2hva(vm, (ulong)st_gva[vcpu_idx]); + + pr_info("VCPU%d:\n", vcpu_idx); pr_info(" rev: %d\n", st->rev); pr_info(" attr: %d\n", st->attr); pr_info(" st_time: %ld\n", st->st_time); @@ -224,29 +221,27 @@ static void *do_steal_time(void *arg) return NULL; } -static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid) +static void run_vcpu(struct kvm_vcpu *vcpu) { struct ucall uc; - vcpu_args_set(vm, vcpuid, 1, vcpuid); + vcpu_run(vcpu); - vcpu_ioctl(vm, vcpuid, KVM_RUN, NULL); - - switch (get_ucall(vm, vcpuid, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: case UCALL_DONE: break; case UCALL_ABORT: - TEST_ASSERT(false, "%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); default: TEST_ASSERT(false, "Unexpected exit: %s", - exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason)); + exit_reason_str(vcpu->run->exit_reason)); } } int main(int ac, char **av) { + struct kvm_vcpu *vcpus[NR_VCPUS]; struct kvm_vm *vm; pthread_attr_t attr; pthread_t thread; @@ -266,26 +261,26 @@ int main(int ac, char **av) pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset); pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - /* Create a one VCPU guest and an identity mapped memslot for the steal time structure */ - vm = vm_create_default(0, 0, guest_code); + /* Create a VM and an identity mapped memslot for the steal time structure */ + vm = vm_create_with_vcpus(NR_VCPUS, guest_code, vcpus); gpages = vm_calc_num_guest_pages(VM_MODE_DEFAULT, STEAL_TIME_SIZE * NR_VCPUS); vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, ST_GPA_BASE, 1, gpages, 0); virt_map(vm, ST_GPA_BASE, ST_GPA_BASE, gpages); ucall_init(vm, NULL); - /* Add the rest of the VCPUs */ - for (i = 1; i < NR_VCPUS; ++i) - vm_vcpu_add_default(vm, i, guest_code); - - steal_time_init(vm); + TEST_REQUIRE(is_steal_time_supported(vcpus[0])); /* Run test on each VCPU */ for (i = 0; i < NR_VCPUS; ++i) { + steal_time_init(vcpus[i], i); + + vcpu_args_set(vcpus[i], 1, i); + /* First VCPU run initializes steal-time */ - run_vcpu(vm, i); + run_vcpu(vcpus[i]); /* Second VCPU run, expect guest stolen time to be <= run_delay */ - run_vcpu(vm, i); + run_vcpu(vcpus[i]); sync_global_from_guest(vm, guest_stolen_time[i]); stolen_time = guest_stolen_time[i]; run_delay = get_run_delay(); @@ -306,7 +301,7 @@ int main(int ac, char **av) MIN_RUN_DELAY_NS, run_delay); /* Run VCPU again to confirm stolen time is consistent with run_delay */ - run_vcpu(vm, i); + run_vcpu(vcpus[i]); sync_global_from_guest(vm, guest_stolen_time[i]); stolen_time = guest_stolen_time[i] - stolen_time; TEST_ASSERT(stolen_time >= run_delay, diff --git a/tools/testing/selftests/kvm/system_counter_offset_test.c b/tools/testing/selftests/kvm/system_counter_offset_test.c index b337bbbfa41f..1c274933912b 100644 --- a/tools/testing/selftests/kvm/system_counter_offset_test.c +++ b/tools/testing/selftests/kvm/system_counter_offset_test.c @@ -14,8 +14,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 0 - #ifdef __x86_64__ struct test_case { @@ -28,19 +26,17 @@ static struct test_case test_cases[] = { { -180 * NSEC_PER_SEC }, }; -static void check_preconditions(struct kvm_vm *vm) +static void check_preconditions(struct kvm_vcpu *vcpu) { - if (!_vcpu_has_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET)) - return; - - print_skip("KVM_VCPU_TSC_OFFSET not supported; skipping test"); - exit(KSFT_SKIP); + __TEST_REQUIRE(!__vcpu_has_device_attr(vcpu, KVM_VCPU_TSC_CTRL, + KVM_VCPU_TSC_OFFSET), + "KVM_VCPU_TSC_OFFSET not supported; skipping test"); } -static void setup_system_counter(struct kvm_vm *vm, struct test_case *test) +static void setup_system_counter(struct kvm_vcpu *vcpu, struct test_case *test) { - vcpu_access_device_attr(vm, VCPU_ID, KVM_VCPU_TSC_CTRL, - KVM_VCPU_TSC_OFFSET, &test->tsc_offset, true); + vcpu_device_attr_set(vcpu, KVM_VCPU_TSC_CTRL, KVM_VCPU_TSC_OFFSET, + &test->tsc_offset); } static uint64_t guest_read_system_counter(struct test_case *test) @@ -87,11 +83,10 @@ static void handle_sync(struct ucall *uc, uint64_t start, uint64_t end) static void handle_abort(struct ucall *uc) { - TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], - __FILE__, uc->args[1]); + REPORT_GUEST_ASSERT(*uc); } -static void enter_guest(struct kvm_vm *vm) +static void enter_guest(struct kvm_vcpu *vcpu) { uint64_t start, end; struct ucall uc; @@ -100,12 +95,12 @@ static void enter_guest(struct kvm_vm *vm) for (i = 0; i < ARRAY_SIZE(test_cases); i++) { struct test_case *test = &test_cases[i]; - setup_system_counter(vm, test); + setup_system_counter(vcpu, test); start = host_read_guest_system_counter(test); - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); end = host_read_guest_system_counter(test); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: handle_sync(&uc, start, end); break; @@ -114,19 +109,20 @@ static void enter_guest(struct kvm_vm *vm) return; default: TEST_ASSERT(0, "unhandled ucall %ld\n", - get_ucall(vm, VCPU_ID, &uc)); + get_ucall(vcpu, &uc)); } } } int main(void) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm = vm_create_default(VCPU_ID, 0, guest_main); - check_preconditions(vm); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); + check_preconditions(vcpu); ucall_init(vm, NULL); - enter_guest(vm); + enter_guest(vcpu); kvm_vm_free(vm); } diff --git a/tools/testing/selftests/kvm/x86_64/amx_test.c b/tools/testing/selftests/kvm/x86_64/amx_test.c index 76f65c22796f..dadcbad10a1d 100644 --- a/tools/testing/selftests/kvm/x86_64/amx_test.c +++ b/tools/testing/selftests/kvm/x86_64/amx_test.c @@ -25,10 +25,6 @@ # error This test is 64-bit only #endif -#define VCPU_ID 0 -#define X86_FEATURE_XSAVE (1 << 26) -#define X86_FEATURE_OSXSAVE (1 << 27) - #define NUM_TILES 8 #define TILE_SIZE 1024 #define XSAVE_SIZE ((NUM_TILES * TILE_SIZE) + PAGE_SIZE) @@ -124,15 +120,8 @@ static inline void __xsavec(struct xsave_data *data, uint64_t rfbm) static inline void check_cpuid_xsave(void) { - uint32_t eax, ebx, ecx, edx; - - eax = 1; - ecx = 0; - cpuid(&eax, &ebx, &ecx, &edx); - if (!(ecx & X86_FEATURE_XSAVE)) - GUEST_ASSERT(!"cpuid: no CPU xsave support!"); - if (!(ecx & X86_FEATURE_OSXSAVE)) - GUEST_ASSERT(!"cpuid: no OS xsave support!"); + GUEST_ASSERT(this_cpu_has(X86_FEATURE_XSAVE)); + GUEST_ASSERT(this_cpu_has(X86_FEATURE_OSXSAVE)); } static bool check_xsave_supports_xtile(void) @@ -144,10 +133,7 @@ static bool enum_xtile_config(void) { u32 eax, ebx, ecx, edx; - eax = TILE_CPUID; - ecx = TILE_PALETTE_CPUID_SUBLEAVE; - - cpuid(&eax, &ebx, &ecx, &edx); + __cpuid(TILE_CPUID, TILE_PALETTE_CPUID_SUBLEAVE, &eax, &ebx, &ecx, &edx); if (!eax || !ebx || !ecx) return false; @@ -169,10 +155,7 @@ static bool enum_xsave_tile(void) { u32 eax, ebx, ecx, edx; - eax = XSTATE_CPUID; - ecx = XFEATURE_XTILEDATA; - - cpuid(&eax, &ebx, &ecx, &edx); + __cpuid(XSTATE_CPUID, XFEATURE_XTILEDATA, &eax, &ebx, &ecx, &edx); if (!eax || !ebx) return false; @@ -187,10 +170,7 @@ static bool check_xsave_size(void) u32 eax, ebx, ecx, edx; bool valid = false; - eax = XSTATE_CPUID; - ecx = XSTATE_USER_STATE_SUBLEAVE; - - cpuid(&eax, &ebx, &ecx, &edx); + __cpuid(XSTATE_CPUID, XSTATE_USER_STATE_SUBLEAVE, &eax, &ebx, &ecx, &edx); if (ebx && ebx <= XSAVE_SIZE) valid = true; @@ -316,46 +296,36 @@ void guest_nm_handler(struct ex_regs *regs) int main(int argc, char *argv[]) { - struct kvm_cpuid_entry2 *entry; struct kvm_regs regs1, regs2; - bool amx_supported = false; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct kvm_x86_state *state; - int xsave_restore_size = 0; + int xsave_restore_size; vm_vaddr_t amx_cfg, tiledata, xsavedata; struct ucall uc; u32 amx_offset; int stage, ret; - vm_xsave_req_perm(XSTATE_XTILE_DATA_BIT); + vm_xsave_require_permission(XSTATE_XTILE_DATA_BIT); /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - entry = kvm_get_supported_cpuid_entry(1); - if (!(entry->ecx & X86_FEATURE_XSAVE)) { - print_skip("XSAVE feature not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE)); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_AMX_TILE)); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILECFG)); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XTILEDATA)); - if (kvm_get_cpuid_max_basic() >= 0xd) { - entry = kvm_get_supported_cpuid_index(0xd, 0); - amx_supported = entry && !!(entry->eax & XFEATURE_MASK_XTILE); - if (!amx_supported) { - print_skip("AMX is not supported by the vCPU (eax=0x%x)", entry->eax); - exit(KSFT_SKIP); - } - /* Get xsave/restore max size */ - xsave_restore_size = entry->ecx; - } + /* Get xsave/restore max size */ + xsave_restore_size = kvm_get_supported_cpuid_entry(0xd)->ecx; - run = vcpu_state(vm, VCPU_ID); - vcpu_regs_get(vm, VCPU_ID, ®s1); + run = vcpu->run; + vcpu_regs_get(vcpu, ®s1); /* Register #NM handler */ vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); vm_install_exception_handler(vm, NM_VECTOR, guest_nm_handler); /* amx cfg for guest_code */ @@ -369,19 +339,18 @@ int main(int argc, char *argv[]) /* xsave data for guest_code */ xsavedata = vm_vaddr_alloc_pages(vm, 3); memset(addr_gva2hva(vm, xsavedata), 0, 3 * getpagesize()); - vcpu_args_set(vm, VCPU_ID, 3, amx_cfg, tiledata, xsavedata); + vcpu_args_set(vcpu, 3, amx_cfg, tiledata, xsavedata); for (stage = 1; ; stage++) { - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: switch (uc.args[1]) { @@ -403,7 +372,7 @@ int main(int argc, char *argv[]) * size subtract 8K amx size. */ amx_offset = xsave_restore_size - NUM_TILES*TILE_SIZE; - state = vcpu_save_state(vm, VCPU_ID); + state = vcpu_save_state(vcpu); void *amx_start = (void *)state->xsave + amx_offset; void *tiles_data = (void *)addr_gva2hva(vm, tiledata); /* Only check TMM0 register, 1 tile */ @@ -424,22 +393,20 @@ int main(int argc, char *argv[]) TEST_FAIL("Unknown ucall %lu", uc.cmd); } - state = vcpu_save_state(vm, VCPU_ID); + state = vcpu_save_state(vcpu); memset(®s1, 0, sizeof(regs1)); - vcpu_regs_get(vm, VCPU_ID, ®s1); + vcpu_regs_get(vcpu, ®s1); kvm_vm_release(vm); /* Restore state in a new VM. */ - kvm_vm_restart(vm, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); - vcpu_load_state(vm, VCPU_ID, state); - run = vcpu_state(vm, VCPU_ID); + vcpu = vm_recreate_with_one_vcpu(vm); + vcpu_load_state(vcpu, state); + run = vcpu->run; kvm_x86_state_cleanup(state); memset(®s2, 0, sizeof(regs2)); - vcpu_regs_get(vm, VCPU_ID, ®s2); + vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", (ulong) regs2.rdi, (ulong) regs2.rsi); diff --git a/tools/testing/selftests/kvm/x86_64/cpuid_test.c b/tools/testing/selftests/kvm/x86_64/cpuid_test.c index 16d2465c5634..a6aeee2e62e4 100644 --- a/tools/testing/selftests/kvm/x86_64/cpuid_test.c +++ b/tools/testing/selftests/kvm/x86_64/cpuid_test.c @@ -12,8 +12,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 0 - /* CPUIDs known to differ */ struct { u32 function; @@ -33,10 +31,9 @@ static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid) u32 eax, ebx, ecx, edx; for (i = 0; i < guest_cpuid->nent; i++) { - eax = guest_cpuid->entries[i].function; - ecx = guest_cpuid->entries[i].index; - - cpuid(&eax, &ebx, &ecx, &edx); + __cpuid(guest_cpuid->entries[i].function, + guest_cpuid->entries[i].index, + &eax, &ebx, &ecx, &edx); GUEST_ASSERT(eax == guest_cpuid->entries[i].eax && ebx == guest_cpuid->entries[i].ebx && @@ -48,9 +45,9 @@ static void test_guest_cpuids(struct kvm_cpuid2 *guest_cpuid) static void test_cpuid_40000000(struct kvm_cpuid2 *guest_cpuid) { - u32 eax = 0x40000000, ebx, ecx = 0, edx; + u32 eax, ebx, ecx, edx; - cpuid(&eax, &ebx, &ecx, &edx); + cpuid(0x40000000, &eax, &ebx, &ecx, &edx); GUEST_ASSERT(eax == 0x40000001); } @@ -68,7 +65,7 @@ static void guest_main(struct kvm_cpuid2 *guest_cpuid) GUEST_DONE(); } -static bool is_cpuid_mangled(struct kvm_cpuid_entry2 *entrie) +static bool is_cpuid_mangled(const struct kvm_cpuid_entry2 *entrie) { int i; @@ -81,50 +78,44 @@ static bool is_cpuid_mangled(struct kvm_cpuid_entry2 *entrie) return false; } -static void check_cpuid(struct kvm_cpuid2 *cpuid, struct kvm_cpuid_entry2 *entrie) +static void compare_cpuids(const struct kvm_cpuid2 *cpuid1, + const struct kvm_cpuid2 *cpuid2) { + const struct kvm_cpuid_entry2 *e1, *e2; int i; - for (i = 0; i < cpuid->nent; i++) { - if (cpuid->entries[i].function == entrie->function && - cpuid->entries[i].index == entrie->index) { - if (is_cpuid_mangled(entrie)) - return; + TEST_ASSERT(cpuid1->nent == cpuid2->nent, + "CPUID nent mismatch: %d vs. %d", cpuid1->nent, cpuid2->nent); - TEST_ASSERT(cpuid->entries[i].eax == entrie->eax && - cpuid->entries[i].ebx == entrie->ebx && - cpuid->entries[i].ecx == entrie->ecx && - cpuid->entries[i].edx == entrie->edx, - "CPUID 0x%x.%x differ: 0x%x:0x%x:0x%x:0x%x vs 0x%x:0x%x:0x%x:0x%x", - entrie->function, entrie->index, - cpuid->entries[i].eax, cpuid->entries[i].ebx, - cpuid->entries[i].ecx, cpuid->entries[i].edx, - entrie->eax, entrie->ebx, entrie->ecx, entrie->edx); - return; - } + for (i = 0; i < cpuid1->nent; i++) { + e1 = &cpuid1->entries[i]; + e2 = &cpuid2->entries[i]; + + TEST_ASSERT(e1->function == e2->function && + e1->index == e2->index && e1->flags == e2->flags, + "CPUID entries[%d] mismtach: 0x%x.%d.%x vs. 0x%x.%d.%x\n", + i, e1->function, e1->index, e1->flags, + e2->function, e2->index, e2->flags); + + if (is_cpuid_mangled(e1)) + continue; + + TEST_ASSERT(e1->eax == e2->eax && e1->ebx == e2->ebx && + e1->ecx == e2->ecx && e1->edx == e2->edx, + "CPUID 0x%x.%x differ: 0x%x:0x%x:0x%x:0x%x vs 0x%x:0x%x:0x%x:0x%x", + e1->function, e1->index, + e1->eax, e1->ebx, e1->ecx, e1->edx, + e2->eax, e2->ebx, e2->ecx, e2->edx); } - - TEST_ASSERT(false, "CPUID 0x%x.%x not found", entrie->function, entrie->index); } -static void compare_cpuids(struct kvm_cpuid2 *cpuid1, struct kvm_cpuid2 *cpuid2) -{ - int i; - - for (i = 0; i < cpuid1->nent; i++) - check_cpuid(cpuid2, &cpuid1->entries[i]); - - for (i = 0; i < cpuid2->nent; i++) - check_cpuid(cpuid1, &cpuid2->entries[i]); -} - -static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid, int stage) +static void run_vcpu(struct kvm_vcpu *vcpu, int stage) { struct ucall uc; - _vcpu_run(vm, vcpuid); + vcpu_run(vcpu); - switch (get_ucall(vm, vcpuid, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage + 1, @@ -134,11 +125,10 @@ static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid, int stage) case UCALL_DONE: return; case UCALL_ABORT: - TEST_ASSERT(false, "%s at %s:%ld\n\tvalues: %#lx, %#lx", (const char *)uc.args[0], - __FILE__, uc.args[1], uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); default: TEST_ASSERT(false, "Unexpected exit: %s", - exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason)); + exit_reason_str(vcpu->run->exit_reason)); } } @@ -154,56 +144,53 @@ struct kvm_cpuid2 *vcpu_alloc_cpuid(struct kvm_vm *vm, vm_vaddr_t *p_gva, struct return guest_cpuids; } -static void set_cpuid_after_run(struct kvm_vm *vm, struct kvm_cpuid2 *cpuid) +static void set_cpuid_after_run(struct kvm_vcpu *vcpu) { struct kvm_cpuid_entry2 *ent; int rc; u32 eax, ebx, x; /* Setting unmodified CPUID is allowed */ - rc = __vcpu_set_cpuid(vm, VCPU_ID, cpuid); + rc = __vcpu_set_cpuid(vcpu); TEST_ASSERT(!rc, "Setting unmodified CPUID after KVM_RUN failed: %d", rc); /* Changing CPU features is forbidden */ - ent = get_cpuid(cpuid, 0x7, 0); + ent = vcpu_get_cpuid_entry(vcpu, 0x7); ebx = ent->ebx; ent->ebx--; - rc = __vcpu_set_cpuid(vm, VCPU_ID, cpuid); + rc = __vcpu_set_cpuid(vcpu); TEST_ASSERT(rc, "Changing CPU features should fail"); ent->ebx = ebx; /* Changing MAXPHYADDR is forbidden */ - ent = get_cpuid(cpuid, 0x80000008, 0); + ent = vcpu_get_cpuid_entry(vcpu, 0x80000008); eax = ent->eax; x = eax & 0xff; ent->eax = (eax & ~0xffu) | (x - 1); - rc = __vcpu_set_cpuid(vm, VCPU_ID, cpuid); + rc = __vcpu_set_cpuid(vcpu); TEST_ASSERT(rc, "Changing MAXPHYADDR should fail"); ent->eax = eax; } int main(void) { - struct kvm_cpuid2 *supp_cpuid, *cpuid2; + struct kvm_vcpu *vcpu; vm_vaddr_t cpuid_gva; struct kvm_vm *vm; int stage; - vm = vm_create_default(VCPU_ID, 0, guest_main); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); - supp_cpuid = kvm_get_supported_cpuid(); - cpuid2 = vcpu_get_cpuid(vm, VCPU_ID); + compare_cpuids(kvm_get_supported_cpuid(), vcpu->cpuid); - compare_cpuids(supp_cpuid, cpuid2); + vcpu_alloc_cpuid(vm, &cpuid_gva, vcpu->cpuid); - vcpu_alloc_cpuid(vm, &cpuid_gva, cpuid2); - - vcpu_args_set(vm, VCPU_ID, 1, cpuid_gva); + vcpu_args_set(vcpu, 1, cpuid_gva); for (stage = 0; stage < 3; stage++) - run_vcpu(vm, VCPU_ID, stage); + run_vcpu(vcpu, stage); - set_cpuid_after_run(vm, cpuid2); + set_cpuid_after_run(vcpu); kvm_vm_free(vm); } diff --git a/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c b/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c index 6f6fd189dda3..4208487652f8 100644 --- a/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c +++ b/tools/testing/selftests/kvm/x86_64/cr4_cpuid_sync_test.c @@ -19,25 +19,11 @@ #include "kvm_util.h" #include "processor.h" -#define X86_FEATURE_XSAVE (1<<26) -#define X86_FEATURE_OSXSAVE (1<<27) -#define VCPU_ID 1 - static inline bool cr4_cpuid_is_sync(void) { - int func, subfunc; - uint32_t eax, ebx, ecx, edx; - uint64_t cr4; + uint64_t cr4 = get_cr4(); - func = 0x1; - subfunc = 0x0; - __asm__ __volatile__("cpuid" - : "=a"(eax), "=b"(ebx), "=c"(ecx), "=d"(edx) - : "a"(func), "c"(subfunc)); - - cr4 = get_cr4(); - - return (!!(ecx & X86_FEATURE_OSXSAVE)) == (!!(cr4 & X86_CR4_OSXSAVE)); + return (this_cpu_has(X86_FEATURE_OSXSAVE) == !!(cr4 & X86_CR4_OSXSAVE)); } static void guest_code(void) @@ -63,44 +49,37 @@ static void guest_code(void) int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; struct kvm_sregs sregs; - struct kvm_cpuid_entry2 *entry; struct ucall uc; - int rc; - entry = kvm_get_supported_cpuid_entry(1); - if (!(entry->ecx & X86_FEATURE_XSAVE)) { - print_skip("XSAVE feature not supported"); - return 0; - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVE)); /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; while (1) { - rc = _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); - TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: /* emulate hypervisor clearing CR4.OSXSAVE */ - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.cr4 &= ~X86_CR4_OSXSAVE; - vcpu_sregs_set(vm, VCPU_ID, &sregs); + vcpu_sregs_set(vcpu, &sregs); break; case UCALL_ABORT: - TEST_FAIL("Guest CR4 bit (OSXSAVE) unsynchronized with CPUID bit."); + REPORT_GUEST_ASSERT(uc); break; case UCALL_DONE: goto done; diff --git a/tools/testing/selftests/kvm/x86_64/debug_regs.c b/tools/testing/selftests/kvm/x86_64/debug_regs.c index 5f078db1bcba..7ef99c3359a0 100644 --- a/tools/testing/selftests/kvm/x86_64/debug_regs.c +++ b/tools/testing/selftests/kvm/x86_64/debug_regs.c @@ -10,8 +10,6 @@ #include "processor.h" #include "apic.h" -#define VCPU_ID 0 - #define DR6_BD (1 << 13) #define DR7_GD (1 << 13) @@ -66,21 +64,22 @@ static void guest_code(void) GUEST_DONE(); } -#define CLEAR_DEBUG() memset(&debug, 0, sizeof(debug)) -#define APPLY_DEBUG() vcpu_set_guest_debug(vm, VCPU_ID, &debug) #define CAST_TO_RIP(v) ((unsigned long long)&(v)) -#define SET_RIP(v) do { \ - vcpu_regs_get(vm, VCPU_ID, ®s); \ - regs.rip = (v); \ - vcpu_regs_set(vm, VCPU_ID, ®s); \ - } while (0) -#define MOVE_RIP(v) SET_RIP(regs.rip + (v)); + +static void vcpu_skip_insn(struct kvm_vcpu *vcpu, int insn_len) +{ + struct kvm_regs regs; + + vcpu_regs_get(vcpu, ®s); + regs.rip += insn_len; + vcpu_regs_set(vcpu, ®s); +} int main(void) { struct kvm_guest_debug debug; unsigned long long target_dr6, target_rip; - struct kvm_regs regs; + struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; struct ucall uc; @@ -96,35 +95,32 @@ int main(void) 1, /* cli */ }; - if (!kvm_check_cap(KVM_CAP_SET_GUEST_DEBUG)) { - print_skip("KVM_CAP_SET_GUEST_DEBUG not supported"); - return 0; - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_GUEST_DEBUG)); - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; /* Test software BPs - int3 */ - CLEAR_DEBUG(); + memset(&debug, 0, sizeof(debug)); debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP; - APPLY_DEBUG(); - vcpu_run(vm, VCPU_ID); + vcpu_guest_debug_set(vcpu, &debug); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && run->debug.arch.exception == BP_VECTOR && run->debug.arch.pc == CAST_TO_RIP(sw_bp), "INT3: exit %d exception %d rip 0x%llx (should be 0x%llx)", run->exit_reason, run->debug.arch.exception, run->debug.arch.pc, CAST_TO_RIP(sw_bp)); - MOVE_RIP(1); + vcpu_skip_insn(vcpu, 1); /* Test instruction HW BP over DR[0-3] */ for (i = 0; i < 4; i++) { - CLEAR_DEBUG(); + memset(&debug, 0, sizeof(debug)); debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; debug.arch.debugreg[i] = CAST_TO_RIP(hw_bp); debug.arch.debugreg[7] = 0x400 | (1UL << (2*i+1)); - APPLY_DEBUG(); - vcpu_run(vm, VCPU_ID); + vcpu_guest_debug_set(vcpu, &debug); + vcpu_run(vcpu); target_dr6 = 0xffff0ff0 | (1UL << i); TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && run->debug.arch.exception == DB_VECTOR && @@ -137,17 +133,17 @@ int main(void) run->debug.arch.dr6, target_dr6); } /* Skip "nop" */ - MOVE_RIP(1); + vcpu_skip_insn(vcpu, 1); /* Test data access HW BP over DR[0-3] */ for (i = 0; i < 4; i++) { - CLEAR_DEBUG(); + memset(&debug, 0, sizeof(debug)); debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; debug.arch.debugreg[i] = CAST_TO_RIP(guest_value); debug.arch.debugreg[7] = 0x00000400 | (1UL << (2*i+1)) | (0x000d0000UL << (4*i)); - APPLY_DEBUG(); - vcpu_run(vm, VCPU_ID); + vcpu_guest_debug_set(vcpu, &debug); + vcpu_run(vcpu); target_dr6 = 0xffff0ff0 | (1UL << i); TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && run->debug.arch.exception == DB_VECTOR && @@ -159,23 +155,22 @@ int main(void) run->debug.arch.pc, CAST_TO_RIP(write_data), run->debug.arch.dr6, target_dr6); /* Rollback the 4-bytes "mov" */ - MOVE_RIP(-7); + vcpu_skip_insn(vcpu, -7); } /* Skip the 4-bytes "mov" */ - MOVE_RIP(7); + vcpu_skip_insn(vcpu, 7); /* Test single step */ target_rip = CAST_TO_RIP(ss_start); target_dr6 = 0xffff4ff0ULL; - vcpu_regs_get(vm, VCPU_ID, ®s); for (i = 0; i < (sizeof(ss_size) / sizeof(ss_size[0])); i++) { target_rip += ss_size[i]; - CLEAR_DEBUG(); + memset(&debug, 0, sizeof(debug)); debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_SINGLESTEP | KVM_GUESTDBG_BLOCKIRQ; debug.arch.debugreg[7] = 0x00000400; - APPLY_DEBUG(); - vcpu_run(vm, VCPU_ID); + vcpu_guest_debug_set(vcpu, &debug); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && run->debug.arch.exception == DB_VECTOR && run->debug.arch.pc == target_rip && @@ -188,11 +183,11 @@ int main(void) } /* Finally test global disable */ - CLEAR_DEBUG(); + memset(&debug, 0, sizeof(debug)); debug.control = KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_HW_BP; debug.arch.debugreg[7] = 0x400 | DR7_GD; - APPLY_DEBUG(); - vcpu_run(vm, VCPU_ID); + vcpu_guest_debug_set(vcpu, &debug); + vcpu_run(vcpu); target_dr6 = 0xffff0ff0 | DR6_BD; TEST_ASSERT(run->exit_reason == KVM_EXIT_DEBUG && run->debug.arch.exception == DB_VECTOR && @@ -205,12 +200,12 @@ int main(void) target_dr6); /* Disable all debug controls, run to the end */ - CLEAR_DEBUG(); - APPLY_DEBUG(); + memset(&debug, 0, sizeof(debug)); + vcpu_guest_debug_set(vcpu, &debug); - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "KVM_EXIT_IO"); - cmd = get_ucall(vm, VCPU_ID, &uc); + cmd = get_ucall(vcpu, &uc); TEST_ASSERT(cmd == UCALL_DONE, "UCALL_DONE"); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/emulator_error_test.c b/tools/testing/selftests/kvm/x86_64/emulator_error_test.c index aeb3850f81bd..236e11755ba6 100644 --- a/tools/testing/selftests/kvm/x86_64/emulator_error_test.c +++ b/tools/testing/selftests/kvm/x86_64/emulator_error_test.c @@ -11,7 +11,6 @@ #include "kvm_util.h" #include "vmx.h" -#define VCPU_ID 1 #define MAXPHYADDR 36 #define MEM_REGION_GVA 0x0000123456789000 @@ -27,14 +26,6 @@ static void guest_code(void) GUEST_DONE(); } -static void run_guest(struct kvm_vm *vm) -{ - int rc; - - rc = _vcpu_run(vm, VCPU_ID); - TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc); -} - /* * Accessors to get R/M, REG, and Mod bits described in the SDM vol 2, * figure 2-2 "Table Interpretation of ModR/M Byte (C8H)". @@ -56,9 +47,9 @@ static bool is_flds(uint8_t *insn_bytes, uint8_t insn_size) GET_RM(insn_bytes[1]) != 0x5; } -static void process_exit_on_emulation_error(struct kvm_vm *vm) +static void process_exit_on_emulation_error(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct kvm_regs regs; uint8_t *insn_bytes; uint8_t insn_size; @@ -92,50 +83,48 @@ static void process_exit_on_emulation_error(struct kvm_vm *vm) * contained an flds instruction that is 2-bytes in * length (ie: no prefix, no SIB, no displacement). */ - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); regs.rip += 2; - vcpu_regs_set(vm, VCPU_ID, ®s); + vcpu_regs_set(vcpu, ®s); } } } -static void do_guest_assert(struct kvm_vm *vm, struct ucall *uc) +static void do_guest_assert(struct ucall *uc) { - TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], __FILE__, - uc->args[1]); + REPORT_GUEST_ASSERT(*uc); } -static void check_for_guest_assert(struct kvm_vm *vm) +static void check_for_guest_assert(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); struct ucall uc; - if (run->exit_reason == KVM_EXIT_IO && - get_ucall(vm, VCPU_ID, &uc) == UCALL_ABORT) { - do_guest_assert(vm, &uc); + if (vcpu->run->exit_reason == KVM_EXIT_IO && + get_ucall(vcpu, &uc) == UCALL_ABORT) { + do_guest_assert(&uc); } } -static void process_ucall_done(struct kvm_vm *vm) +static void process_ucall_done(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct ucall uc; - check_for_guest_assert(vm); + check_for_guest_assert(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - TEST_ASSERT(get_ucall(vm, VCPU_ID, &uc) == UCALL_DONE, + TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE, "Unexpected ucall command: %lu, expected UCALL_DONE (%d)", uc.cmd, UCALL_DONE); } -static uint64_t process_ucall(struct kvm_vm *vm) +static uint64_t process_ucall(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct ucall uc; TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, @@ -143,14 +132,14 @@ static uint64_t process_ucall(struct kvm_vm *vm) run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: break; case UCALL_ABORT: - do_guest_assert(vm, &uc); + do_guest_assert(&uc); break; case UCALL_DONE: - process_ucall_done(vm); + process_ucall_done(vcpu); break; default: TEST_ASSERT(false, "Unexpected ucall"); @@ -161,12 +150,7 @@ static uint64_t process_ucall(struct kvm_vm *vm) int main(int argc, char *argv[]) { - struct kvm_enable_cap emul_failure_cap = { - .cap = KVM_CAP_EXIT_ON_EMULATION_FAILURE, - .args[0] = 1, - }; - struct kvm_cpuid_entry2 *entry; - struct kvm_cpuid2 *cpuid; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; uint64_t gpa, pte; uint64_t *hva; @@ -175,24 +159,15 @@ int main(int argc, char *argv[]) /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - vm = vm_create_default(VCPU_ID, 0, guest_code); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_SMALLER_MAXPHYADDR)); - if (!kvm_check_cap(KVM_CAP_SMALLER_MAXPHYADDR)) { - printf("module parameter 'allow_smaller_maxphyaddr' is not set. Skipping test.\n"); - return 0; - } + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - cpuid = kvm_get_supported_cpuid(); - - entry = kvm_get_supported_cpuid_index(0x80000008, 0); - entry->eax = (entry->eax & 0xffffff00) | MAXPHYADDR; - set_cpuid(cpuid, entry); - - vcpu_set_cpuid(vm, VCPU_ID, cpuid); + vcpu_set_cpuid_maxphyaddr(vcpu, MAXPHYADDR); rc = kvm_check_cap(KVM_CAP_EXIT_ON_EMULATION_FAILURE); TEST_ASSERT(rc, "KVM_CAP_EXIT_ON_EMULATION_FAILURE is unavailable"); - vm_enable_cap(vm, &emul_failure_cap); + vm_enable_cap(vm, KVM_CAP_EXIT_ON_EMULATION_FAILURE, 1); vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, MEM_REGION_GPA, MEM_REGION_SLOT, @@ -203,14 +178,14 @@ int main(int argc, char *argv[]) virt_map(vm, MEM_REGION_GVA, MEM_REGION_GPA, 1); hva = addr_gpa2hva(vm, MEM_REGION_GPA); memset(hva, 0, PAGE_SIZE); - pte = vm_get_page_table_entry(vm, VCPU_ID, MEM_REGION_GVA); - vm_set_page_table_entry(vm, VCPU_ID, MEM_REGION_GVA, pte | (1ull << 36)); + pte = vm_get_page_table_entry(vm, vcpu, MEM_REGION_GVA); + vm_set_page_table_entry(vm, vcpu, MEM_REGION_GVA, pte | (1ull << 36)); - run_guest(vm); - process_exit_on_emulation_error(vm); - run_guest(vm); + vcpu_run(vcpu); + process_exit_on_emulation_error(vcpu); + vcpu_run(vcpu); - TEST_ASSERT(process_ucall(vm) == UCALL_DONE, "Expected UCALL_DONE"); + TEST_ASSERT(process_ucall(vcpu) == UCALL_DONE, "Expected UCALL_DONE"); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/evmcs_test.c b/tools/testing/selftests/kvm/x86_64/evmcs_test.c index d12e043aa2ee..99bc202243d2 100644 --- a/tools/testing/selftests/kvm/x86_64/evmcs_test.c +++ b/tools/testing/selftests/kvm/x86_64/evmcs_test.c @@ -18,9 +18,6 @@ #include "vmx.h" -#define VCPU_ID 5 -#define NMI_VECTOR 2 - static int ud_count; static void guest_ud_handler(struct ex_regs *regs) @@ -160,88 +157,86 @@ void guest_code(struct vmx_pages *vmx_pages) GUEST_DONE(); } -void inject_nmi(struct kvm_vm *vm) +void inject_nmi(struct kvm_vcpu *vcpu) { struct kvm_vcpu_events events; - vcpu_events_get(vm, VCPU_ID, &events); + vcpu_events_get(vcpu, &events); events.nmi.pending = 1; events.flags |= KVM_VCPUEVENT_VALID_NMI_PENDING; - vcpu_events_set(vm, VCPU_ID, &events); + vcpu_events_set(vcpu, &events); } -static void save_restore_vm(struct kvm_vm *vm) +static struct kvm_vcpu *save_restore_vm(struct kvm_vm *vm, + struct kvm_vcpu *vcpu) { struct kvm_regs regs1, regs2; struct kvm_x86_state *state; - state = vcpu_save_state(vm, VCPU_ID); + state = vcpu_save_state(vcpu); memset(®s1, 0, sizeof(regs1)); - vcpu_regs_get(vm, VCPU_ID, ®s1); + vcpu_regs_get(vcpu, ®s1); kvm_vm_release(vm); /* Restore state in a new VM. */ - kvm_vm_restart(vm, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); - vcpu_set_hv_cpuid(vm, VCPU_ID); - vcpu_enable_evmcs(vm, VCPU_ID); - vcpu_load_state(vm, VCPU_ID, state); + vcpu = vm_recreate_with_one_vcpu(vm); + vcpu_set_hv_cpuid(vcpu); + vcpu_enable_evmcs(vcpu); + vcpu_load_state(vcpu, state); kvm_x86_state_cleanup(state); memset(®s2, 0, sizeof(regs2)); - vcpu_regs_get(vm, VCPU_ID, ®s2); + vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", (ulong) regs2.rdi, (ulong) regs2.rsi); + return vcpu; } int main(int argc, char *argv[]) { vm_vaddr_t vmx_pages_gva = 0; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct ucall uc; int stage; - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - if (!nested_vmx_supported() || - !kvm_check_cap(KVM_CAP_NESTED_STATE) || - !kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) { - print_skip("Enlightened VMCS is unsupported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE)); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)); - vcpu_set_hv_cpuid(vm, VCPU_ID); - vcpu_enable_evmcs(vm, VCPU_ID); + vcpu_set_hv_cpuid(vcpu); + vcpu_enable_evmcs(vcpu); vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler); pr_info("Running L1 which uses EVMCS to run L2\n"); for (stage = 1;; stage++) { - run = vcpu_state(vm, VCPU_ID); - _vcpu_run(vm, VCPU_ID); + run = vcpu->run; + + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; @@ -256,12 +251,12 @@ int main(int argc, char *argv[]) uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", stage, (ulong)uc.args[1]); - save_restore_vm(vm); + vcpu = save_restore_vm(vm, vcpu); /* Force immediate L2->L1 exit before resuming */ if (stage == 8) { pr_info("Injecting NMI into L1 before L2 had a chance to run after restore\n"); - inject_nmi(vm); + inject_nmi(vcpu); } /* @@ -271,7 +266,7 @@ int main(int argc, char *argv[]) */ if (stage == 9) { pr_info("Trying extra KVM_GET_NESTED_STATE/KVM_SET_NESTED_STATE cycle\n"); - save_restore_vm(vm); + vcpu = save_restore_vm(vm, vcpu); } } diff --git a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c index 1f5c32146f3d..b1905d280ef5 100644 --- a/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c +++ b/tools/testing/selftests/kvm/x86_64/fix_hypercall_test.c @@ -14,8 +14,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 0 - static bool ud_expected; static void guest_ud_handler(struct ex_regs *regs) @@ -94,29 +92,27 @@ static void guest_main(void) GUEST_DONE(); } -static void setup_ud_vector(struct kvm_vm *vm) +static void setup_ud_vector(struct kvm_vcpu *vcpu) { - vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); - vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); + vm_init_descriptor_tables(vcpu->vm); + vcpu_init_descriptor_tables(vcpu); + vm_install_exception_handler(vcpu->vm, UD_VECTOR, guest_ud_handler); } -static void enter_guest(struct kvm_vm *vm) +static void enter_guest(struct kvm_vcpu *vcpu) { - struct kvm_run *run; + struct kvm_run *run = vcpu->run; struct ucall uc; - run = vcpu_state(vm, VCPU_ID); - - vcpu_run(vm, VCPU_ID); - switch (get_ucall(vm, VCPU_ID, &uc)) { + vcpu_run(vcpu); + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: pr_info("%s: %016lx\n", (const char *)uc.args[2], uc.args[3]); break; case UCALL_DONE: return; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); default: TEST_FAIL("Unhandled ucall: %ld\nexit_reason: %u (%s)", uc.cmd, run->exit_reason, exit_reason_str(run->exit_reason)); @@ -125,45 +121,42 @@ static void enter_guest(struct kvm_vm *vm) static void test_fix_hypercall(void) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm = vm_create_default(VCPU_ID, 0, guest_main); - setup_ud_vector(vm); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); + setup_ud_vector(vcpu); ud_expected = false; sync_global_to_guest(vm, ud_expected); virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); - enter_guest(vm); + enter_guest(vcpu); } static void test_fix_hypercall_disabled(void) { - struct kvm_enable_cap cap = {0}; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; - vm = vm_create_default(VCPU_ID, 0, guest_main); - setup_ud_vector(vm); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); + setup_ud_vector(vcpu); - cap.cap = KVM_CAP_DISABLE_QUIRKS2; - cap.args[0] = KVM_X86_QUIRK_FIX_HYPERCALL_INSN; - vm_enable_cap(vm, &cap); + vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, + KVM_X86_QUIRK_FIX_HYPERCALL_INSN); ud_expected = true; sync_global_to_guest(vm, ud_expected); virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); - enter_guest(vm); + enter_guest(vcpu); } int main(void) { - if (!(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) & KVM_X86_QUIRK_FIX_HYPERCALL_INSN)) { - print_skip("KVM_X86_QUIRK_HYPERCALL_INSN not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_check_cap(KVM_CAP_DISABLE_QUIRKS2) & KVM_X86_QUIRK_FIX_HYPERCALL_INSN); test_fix_hypercall(); test_fix_hypercall_disabled(); diff --git a/tools/testing/selftests/kvm/x86_64/get_msr_index_features.c b/tools/testing/selftests/kvm/x86_64/get_msr_index_features.c index 8aed0db1331d..d09b3cbcadc6 100644 --- a/tools/testing/selftests/kvm/x86_64/get_msr_index_features.c +++ b/tools/testing/selftests/kvm/x86_64/get_msr_index_features.c @@ -15,116 +15,21 @@ #include "kvm_util.h" #include "processor.h" -static int kvm_num_index_msrs(int kvm_fd, int nmsrs) -{ - struct kvm_msr_list *list; - int r; - - list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0])); - list->nmsrs = nmsrs; - r = ioctl(kvm_fd, KVM_GET_MSR_INDEX_LIST, list); - TEST_ASSERT(r == -1 && errno == E2BIG, - "Unexpected result from KVM_GET_MSR_INDEX_LIST probe, r: %i", - r); - - r = list->nmsrs; - free(list); - return r; -} - -static void test_get_msr_index(void) -{ - int old_res, res, kvm_fd, r; - struct kvm_msr_list *list; - - kvm_fd = open_kvm_dev_path_or_exit(); - - old_res = kvm_num_index_msrs(kvm_fd, 0); - TEST_ASSERT(old_res != 0, "Expecting nmsrs to be > 0"); - - if (old_res != 1) { - res = kvm_num_index_msrs(kvm_fd, 1); - TEST_ASSERT(res > 1, "Expecting nmsrs to be > 1"); - TEST_ASSERT(res == old_res, "Expecting nmsrs to be identical"); - } - - list = malloc(sizeof(*list) + old_res * sizeof(list->indices[0])); - list->nmsrs = old_res; - r = ioctl(kvm_fd, KVM_GET_MSR_INDEX_LIST, list); - - TEST_ASSERT(r == 0, - "Unexpected result from KVM_GET_MSR_FEATURE_INDEX_LIST, r: %i", - r); - TEST_ASSERT(list->nmsrs == old_res, "Expecting nmsrs to be identical"); - free(list); - - close(kvm_fd); -} - -static int kvm_num_feature_msrs(int kvm_fd, int nmsrs) -{ - struct kvm_msr_list *list; - int r; - - list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0])); - list->nmsrs = nmsrs; - r = ioctl(kvm_fd, KVM_GET_MSR_FEATURE_INDEX_LIST, list); - TEST_ASSERT(r == -1 && errno == E2BIG, - "Unexpected result from KVM_GET_MSR_FEATURE_INDEX_LIST probe, r: %i", - r); - - r = list->nmsrs; - free(list); - return r; -} - -struct kvm_msr_list *kvm_get_msr_feature_list(int kvm_fd, int nmsrs) -{ - struct kvm_msr_list *list; - int r; - - list = malloc(sizeof(*list) + nmsrs * sizeof(list->indices[0])); - list->nmsrs = nmsrs; - r = ioctl(kvm_fd, KVM_GET_MSR_FEATURE_INDEX_LIST, list); - - TEST_ASSERT(r == 0, - "Unexpected result from KVM_GET_MSR_FEATURE_INDEX_LIST, r: %i", - r); - - return list; -} - -static void test_get_msr_feature(void) -{ - int res, old_res, i, kvm_fd; - struct kvm_msr_list *feature_list; - - kvm_fd = open_kvm_dev_path_or_exit(); - - old_res = kvm_num_feature_msrs(kvm_fd, 0); - TEST_ASSERT(old_res != 0, "Expecting nmsrs to be > 0"); - - if (old_res != 1) { - res = kvm_num_feature_msrs(kvm_fd, 1); - TEST_ASSERT(res > 1, "Expecting nmsrs to be > 1"); - TEST_ASSERT(res == old_res, "Expecting nmsrs to be identical"); - } - - feature_list = kvm_get_msr_feature_list(kvm_fd, old_res); - TEST_ASSERT(old_res == feature_list->nmsrs, - "Unmatching number of msr indexes"); - - for (i = 0; i < feature_list->nmsrs; i++) - kvm_get_feature_msr(feature_list->indices[i]); - - free(feature_list); - close(kvm_fd); -} - int main(int argc, char *argv[]) { - if (kvm_check_cap(KVM_CAP_GET_MSR_FEATURES)) - test_get_msr_feature(); + const struct kvm_msr_list *feature_list; + int i; - test_get_msr_index(); + /* + * Skip the entire test if MSR_FEATURES isn't supported, other tests + * will cover the "regular" list of MSRs, the coverage here is purely + * opportunistic and not interesting on its own. + */ + TEST_REQUIRE(kvm_has_cap(KVM_CAP_GET_MSR_FEATURES)); + + (void)kvm_get_msr_index_list(); + + feature_list = kvm_get_feature_msr_index_list(); + for (i = 0; i < feature_list->nmsrs; i++) + kvm_get_feature_msr(feature_list->indices[i]); } diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_clock.c b/tools/testing/selftests/kvm/x86_64/hyperv_clock.c index 3330fb183c68..d576bc8ce823 100644 --- a/tools/testing/selftests/kvm/x86_64/hyperv_clock.c +++ b/tools/testing/selftests/kvm/x86_64/hyperv_clock.c @@ -173,23 +173,21 @@ static void guest_main(struct ms_hyperv_tsc_page *tsc_page, vm_paddr_t tsc_page_ GUEST_DONE(); } -#define VCPU_ID 0 - -static void host_check_tsc_msr_rdtsc(struct kvm_vm *vm) +static void host_check_tsc_msr_rdtsc(struct kvm_vcpu *vcpu) { u64 tsc_freq, r1, r2, t1, t2; s64 delta_ns; - tsc_freq = vcpu_get_msr(vm, VCPU_ID, HV_X64_MSR_TSC_FREQUENCY); + tsc_freq = vcpu_get_msr(vcpu, HV_X64_MSR_TSC_FREQUENCY); TEST_ASSERT(tsc_freq > 0, "TSC frequency must be nonzero"); /* For increased accuracy, take mean rdtsc() before and afrer ioctl */ r1 = rdtsc(); - t1 = vcpu_get_msr(vm, VCPU_ID, HV_X64_MSR_TIME_REF_COUNT); + t1 = vcpu_get_msr(vcpu, HV_X64_MSR_TIME_REF_COUNT); r1 = (r1 + rdtsc()) / 2; nop_loop(); r2 = rdtsc(); - t2 = vcpu_get_msr(vm, VCPU_ID, HV_X64_MSR_TIME_REF_COUNT); + t2 = vcpu_get_msr(vcpu, HV_X64_MSR_TIME_REF_COUNT); r2 = (r2 + rdtsc()) / 2; TEST_ASSERT(t2 > t1, "Time reference MSR is not monotonic (%ld <= %ld)", t1, t2); @@ -207,36 +205,36 @@ static void host_check_tsc_msr_rdtsc(struct kvm_vm *vm) int main(void) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct ucall uc; vm_vaddr_t tsc_page_gva; int stage; - vm = vm_create_default(VCPU_ID, 0, guest_main); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); + run = vcpu->run; - vcpu_set_hv_cpuid(vm, VCPU_ID); + vcpu_set_hv_cpuid(vcpu); tsc_page_gva = vm_vaddr_alloc_page(vm); memset(addr_gva2hva(vm, tsc_page_gva), 0x0, getpagesize()); TEST_ASSERT((addr_gva2gpa(vm, tsc_page_gva) & (getpagesize() - 1)) == 0, "TSC page has to be page aligned\n"); - vcpu_args_set(vm, VCPU_ID, 2, tsc_page_gva, addr_gva2gpa(vm, tsc_page_gva)); + vcpu_args_set(vcpu, 2, tsc_page_gva, addr_gva2gpa(vm, tsc_page_gva)); - host_check_tsc_msr_rdtsc(vm); + host_check_tsc_msr_rdtsc(vcpu); for (stage = 1;; stage++) { - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c b/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c index 8c245ab2d98a..e804eb08dff9 100644 --- a/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c +++ b/tools/testing/selftests/kvm/x86_64/hyperv_cpuid.c @@ -20,8 +20,6 @@ #include "processor.h" #include "vmx.h" -#define VCPU_ID 0 - static void guest_code(void) { } @@ -45,7 +43,7 @@ static bool smt_possible(void) return res; } -static void test_hv_cpuid(struct kvm_cpuid2 *hv_cpuid_entries, +static void test_hv_cpuid(const struct kvm_cpuid2 *hv_cpuid_entries, bool evmcs_expected) { int i; @@ -58,7 +56,7 @@ static void test_hv_cpuid(struct kvm_cpuid2 *hv_cpuid_entries, nent_expected, hv_cpuid_entries->nent); for (i = 0; i < hv_cpuid_entries->nent; i++) { - struct kvm_cpuid_entry2 *entry = &hv_cpuid_entries->entries[i]; + const struct kvm_cpuid_entry2 *entry = &hv_cpuid_entries->entries[i]; TEST_ASSERT((entry->function >= 0x40000000) && (entry->function <= 0x40000082), @@ -115,64 +113,62 @@ static void test_hv_cpuid(struct kvm_cpuid2 *hv_cpuid_entries, } } -void test_hv_cpuid_e2big(struct kvm_vm *vm, bool system) +void test_hv_cpuid_e2big(struct kvm_vm *vm, struct kvm_vcpu *vcpu) { static struct kvm_cpuid2 cpuid = {.nent = 0}; int ret; - if (!system) - ret = _vcpu_ioctl(vm, VCPU_ID, KVM_GET_SUPPORTED_HV_CPUID, &cpuid); + if (vcpu) + ret = __vcpu_ioctl(vcpu, KVM_GET_SUPPORTED_HV_CPUID, &cpuid); else - ret = _kvm_ioctl(vm, KVM_GET_SUPPORTED_HV_CPUID, &cpuid); + ret = __kvm_ioctl(vm->kvm_fd, KVM_GET_SUPPORTED_HV_CPUID, &cpuid); TEST_ASSERT(ret == -1 && errno == E2BIG, "%s KVM_GET_SUPPORTED_HV_CPUID didn't fail with -E2BIG when" - " it should have: %d %d", system ? "KVM" : "vCPU", ret, errno); + " it should have: %d %d", !vcpu ? "KVM" : "vCPU", ret, errno); } int main(int argc, char *argv[]) { struct kvm_vm *vm; - struct kvm_cpuid2 *hv_cpuid_entries; + const struct kvm_cpuid2 *hv_cpuid_entries; + struct kvm_vcpu *vcpu; /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - if (!kvm_check_cap(KVM_CAP_HYPERV_CPUID)) { - print_skip("KVM_CAP_HYPERV_CPUID not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_HYPERV_CPUID)); - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); /* Test vCPU ioctl version */ - test_hv_cpuid_e2big(vm, false); + test_hv_cpuid_e2big(vm, vcpu); - hv_cpuid_entries = vcpu_get_supported_hv_cpuid(vm, VCPU_ID); + hv_cpuid_entries = vcpu_get_supported_hv_cpuid(vcpu); test_hv_cpuid(hv_cpuid_entries, false); - free(hv_cpuid_entries); + free((void *)hv_cpuid_entries); - if (!nested_vmx_supported() || - !kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) { + if (!kvm_cpu_has(X86_FEATURE_VMX) || + !kvm_has_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS)) { print_skip("Enlightened VMCS is unsupported"); goto do_sys; } - vcpu_enable_evmcs(vm, VCPU_ID); - hv_cpuid_entries = vcpu_get_supported_hv_cpuid(vm, VCPU_ID); + vcpu_enable_evmcs(vcpu); + hv_cpuid_entries = vcpu_get_supported_hv_cpuid(vcpu); test_hv_cpuid(hv_cpuid_entries, true); - free(hv_cpuid_entries); + free((void *)hv_cpuid_entries); do_sys: /* Test system ioctl version */ - if (!kvm_check_cap(KVM_CAP_SYS_HYPERV_CPUID)) { + if (!kvm_has_cap(KVM_CAP_SYS_HYPERV_CPUID)) { print_skip("KVM_CAP_SYS_HYPERV_CPUID not supported"); goto out; } - test_hv_cpuid_e2big(vm, true); + test_hv_cpuid_e2big(vm, NULL); hv_cpuid_entries = kvm_get_supported_hv_cpuid(); - test_hv_cpuid(hv_cpuid_entries, nested_vmx_supported()); + test_hv_cpuid(hv_cpuid_entries, kvm_cpu_has(X86_FEATURE_VMX)); out: kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_features.c b/tools/testing/selftests/kvm/x86_64/hyperv_features.c index 672915ce73d8..79ab0152d281 100644 --- a/tools/testing/selftests/kvm/x86_64/hyperv_features.c +++ b/tools/testing/selftests/kvm/x86_64/hyperv_features.c @@ -13,78 +13,22 @@ #include "processor.h" #include "hyperv.h" -#define VCPU_ID 0 #define LINUX_OS_ID ((u64)0x8100 << 48) -extern unsigned char rdmsr_start; -extern unsigned char rdmsr_end; - -static u64 do_rdmsr(u32 idx) +static inline uint8_t hypercall(u64 control, vm_vaddr_t input_address, + vm_vaddr_t output_address, uint64_t *hv_status) { - u32 lo, hi; + uint8_t vector; - asm volatile("rdmsr_start: rdmsr;" - "rdmsr_end:" - : "=a"(lo), "=c"(hi) - : "c"(idx)); - - return (((u64) hi) << 32) | lo; -} - -extern unsigned char wrmsr_start; -extern unsigned char wrmsr_end; - -static void do_wrmsr(u32 idx, u64 val) -{ - u32 lo, hi; - - lo = val; - hi = val >> 32; - - asm volatile("wrmsr_start: wrmsr;" - "wrmsr_end:" - : : "a"(lo), "c"(idx), "d"(hi)); -} - -static int nr_gp; -static int nr_ud; - -static inline u64 hypercall(u64 control, vm_vaddr_t input_address, - vm_vaddr_t output_address) -{ - u64 hv_status; - - asm volatile("mov %3, %%r8\n" - "vmcall" - : "=a" (hv_status), - "+c" (control), "+d" (input_address) - : "r" (output_address) - : "cc", "memory", "r8", "r9", "r10", "r11"); - - return hv_status; -} - -static void guest_gp_handler(struct ex_regs *regs) -{ - unsigned char *rip = (unsigned char *)regs->rip; - bool r, w; - - r = rip == &rdmsr_start; - w = rip == &wrmsr_start; - GUEST_ASSERT(r || w); - - nr_gp++; - - if (r) - regs->rip = (uint64_t)&rdmsr_end; - else - regs->rip = (uint64_t)&wrmsr_end; -} - -static void guest_ud_handler(struct ex_regs *regs) -{ - nr_ud++; - regs->rip += 3; + /* Note both the hypercall and the "asm safe" clobber r9-r11. */ + asm volatile("mov %[output_address], %%r8\n\t" + KVM_ASM_SAFE("vmcall") + : "=a" (*hv_status), + "+c" (control), "+d" (input_address), + KVM_ASM_SAFE_OUTPUTS(vector) + : [output_address] "r"(output_address) + : "cc", "memory", "r8", KVM_ASM_SAFE_CLOBBERS); + return vector; } struct msr_data { @@ -102,111 +46,105 @@ struct hcall_data { static void guest_msr(struct msr_data *msr) { - int i = 0; + uint64_t ignored; + uint8_t vector; - while (msr->idx) { - WRITE_ONCE(nr_gp, 0); - if (!msr->write) - do_rdmsr(msr->idx); - else - do_wrmsr(msr->idx, msr->write_val); + GUEST_ASSERT(msr->idx); - if (msr->available) - GUEST_ASSERT(READ_ONCE(nr_gp) == 0); - else - GUEST_ASSERT(READ_ONCE(nr_gp) == 1); - - GUEST_SYNC(i++); - } + if (!msr->write) + vector = rdmsr_safe(msr->idx, &ignored); + else + vector = wrmsr_safe(msr->idx, msr->write_val); + if (msr->available) + GUEST_ASSERT_2(!vector, msr->idx, vector); + else + GUEST_ASSERT_2(vector == GP_VECTOR, msr->idx, vector); GUEST_DONE(); } static void guest_hcall(vm_vaddr_t pgs_gpa, struct hcall_data *hcall) { - int i = 0; u64 res, input, output; + uint8_t vector; + + GUEST_ASSERT(hcall->control); wrmsr(HV_X64_MSR_GUEST_OS_ID, LINUX_OS_ID); wrmsr(HV_X64_MSR_HYPERCALL, pgs_gpa); - while (hcall->control) { - nr_ud = 0; - if (!(hcall->control & HV_HYPERCALL_FAST_BIT)) { - input = pgs_gpa; - output = pgs_gpa + 4096; - } else { - input = output = 0; - } - - res = hypercall(hcall->control, input, output); - if (hcall->ud_expected) - GUEST_ASSERT(nr_ud == 1); - else - GUEST_ASSERT(res == hcall->expect); - - GUEST_SYNC(i++); + if (!(hcall->control & HV_HYPERCALL_FAST_BIT)) { + input = pgs_gpa; + output = pgs_gpa + 4096; + } else { + input = output = 0; } + vector = hypercall(hcall->control, input, output, &res); + if (hcall->ud_expected) + GUEST_ASSERT_2(vector == UD_VECTOR, hcall->control, vector); + else + GUEST_ASSERT_2(!vector, hcall->control, vector); + + GUEST_ASSERT_2(!hcall->ud_expected || res == hcall->expect, + hcall->expect, res); GUEST_DONE(); } -static void hv_set_cpuid(struct kvm_vm *vm, struct kvm_cpuid2 *cpuid, - struct kvm_cpuid_entry2 *feat, - struct kvm_cpuid_entry2 *recomm, - struct kvm_cpuid_entry2 *dbg) +static void vcpu_reset_hv_cpuid(struct kvm_vcpu *vcpu) { - TEST_ASSERT(set_cpuid(cpuid, feat), - "failed to set KVM_CPUID_FEATURES leaf"); - TEST_ASSERT(set_cpuid(cpuid, recomm), - "failed to set HYPERV_CPUID_ENLIGHTMENT_INFO leaf"); - TEST_ASSERT(set_cpuid(cpuid, dbg), - "failed to set HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES leaf"); - vcpu_set_cpuid(vm, VCPU_ID, cpuid); + /* + * Enable all supported Hyper-V features, then clear the leafs holding + * the features that will be tested one by one. + */ + vcpu_set_hv_cpuid(vcpu); + + vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES); + vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO); + vcpu_clear_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES); } static void guest_test_msrs_access(void) { + struct kvm_cpuid2 *prev_cpuid = NULL; + struct kvm_cpuid_entry2 *feat, *dbg; + struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; struct ucall uc; - int stage = 0, r; - struct kvm_cpuid_entry2 feat = { - .function = HYPERV_CPUID_FEATURES - }; - struct kvm_cpuid_entry2 recomm = { - .function = HYPERV_CPUID_ENLIGHTMENT_INFO - }; - struct kvm_cpuid_entry2 dbg = { - .function = HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES - }; - struct kvm_cpuid2 *best; + int stage = 0; vm_vaddr_t msr_gva; - struct kvm_enable_cap cap = { - .cap = KVM_CAP_HYPERV_ENFORCE_CPUID, - .args = {1} - }; struct msr_data *msr; while (true) { - vm = vm_create_default(VCPU_ID, 0, guest_msr); + vm = vm_create_with_one_vcpu(&vcpu, guest_msr); msr_gva = vm_vaddr_alloc_page(vm); memset(addr_gva2hva(vm, msr_gva), 0x0, getpagesize()); msr = addr_gva2hva(vm, msr_gva); - vcpu_args_set(vm, VCPU_ID, 1, msr_gva); - vcpu_enable_cap(vm, VCPU_ID, &cap); + vcpu_args_set(vcpu, 1, msr_gva); + vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENFORCE_CPUID, 1); - vcpu_set_hv_cpuid(vm, VCPU_ID); + if (!prev_cpuid) { + vcpu_reset_hv_cpuid(vcpu); - best = kvm_get_supported_hv_cpuid(); + prev_cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent); + } else { + vcpu_init_cpuid(vcpu, prev_cpuid); + } + + feat = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES); + dbg = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); - vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler); + vcpu_init_descriptor_tables(vcpu); - run = vcpu_state(vm, VCPU_ID); + run = vcpu->run; + + /* TODO: Make this entire test easier to maintain. */ + if (stage >= 21) + vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_SYNIC2, 0); switch (stage) { case 0: @@ -223,7 +161,7 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 2: - feat.eax |= HV_MSR_HYPERCALL_AVAILABLE; + feat->eax |= HV_MSR_HYPERCALL_AVAILABLE; /* * HV_X64_MSR_GUEST_OS_ID has to be written first to make * HV_X64_MSR_HYPERCALL available. @@ -250,12 +188,14 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 6: - feat.eax |= HV_MSR_VP_RUNTIME_AVAILABLE; + feat->eax |= HV_MSR_VP_RUNTIME_AVAILABLE; + msr->idx = HV_X64_MSR_VP_RUNTIME; msr->write = 0; msr->available = 1; break; case 7: /* Read only */ + msr->idx = HV_X64_MSR_VP_RUNTIME; msr->write = 1; msr->write_val = 1; msr->available = 0; @@ -267,12 +207,14 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 9: - feat.eax |= HV_MSR_TIME_REF_COUNT_AVAILABLE; + feat->eax |= HV_MSR_TIME_REF_COUNT_AVAILABLE; + msr->idx = HV_X64_MSR_TIME_REF_COUNT; msr->write = 0; msr->available = 1; break; case 10: /* Read only */ + msr->idx = HV_X64_MSR_TIME_REF_COUNT; msr->write = 1; msr->write_val = 1; msr->available = 0; @@ -284,12 +226,14 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 12: - feat.eax |= HV_MSR_VP_INDEX_AVAILABLE; + feat->eax |= HV_MSR_VP_INDEX_AVAILABLE; + msr->idx = HV_X64_MSR_VP_INDEX; msr->write = 0; msr->available = 1; break; case 13: /* Read only */ + msr->idx = HV_X64_MSR_VP_INDEX; msr->write = 1; msr->write_val = 1; msr->available = 0; @@ -301,11 +245,13 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 15: - feat.eax |= HV_MSR_RESET_AVAILABLE; + feat->eax |= HV_MSR_RESET_AVAILABLE; + msr->idx = HV_X64_MSR_RESET; msr->write = 0; msr->available = 1; break; case 16: + msr->idx = HV_X64_MSR_RESET; msr->write = 1; msr->write_val = 0; msr->available = 1; @@ -317,11 +263,13 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 18: - feat.eax |= HV_MSR_REFERENCE_TSC_AVAILABLE; + feat->eax |= HV_MSR_REFERENCE_TSC_AVAILABLE; + msr->idx = HV_X64_MSR_REFERENCE_TSC; msr->write = 0; msr->available = 1; break; case 19: + msr->idx = HV_X64_MSR_REFERENCE_TSC; msr->write = 1; msr->write_val = 0; msr->available = 1; @@ -337,16 +285,18 @@ static void guest_test_msrs_access(void) * Remains unavailable even with KVM_CAP_HYPERV_SYNIC2 * capability enabled and guest visible CPUID bit unset. */ - cap.cap = KVM_CAP_HYPERV_SYNIC2; - cap.args[0] = 0; - vcpu_enable_cap(vm, VCPU_ID, &cap); + msr->idx = HV_X64_MSR_EOM; + msr->write = 0; + msr->available = 0; break; case 22: - feat.eax |= HV_MSR_SYNIC_AVAILABLE; + feat->eax |= HV_MSR_SYNIC_AVAILABLE; + msr->idx = HV_X64_MSR_EOM; msr->write = 0; msr->available = 1; break; case 23: + msr->idx = HV_X64_MSR_EOM; msr->write = 1; msr->write_val = 0; msr->available = 1; @@ -358,23 +308,29 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 25: - feat.eax |= HV_MSR_SYNTIMER_AVAILABLE; + feat->eax |= HV_MSR_SYNTIMER_AVAILABLE; + msr->idx = HV_X64_MSR_STIMER0_CONFIG; msr->write = 0; msr->available = 1; break; case 26: + msr->idx = HV_X64_MSR_STIMER0_CONFIG; msr->write = 1; msr->write_val = 0; msr->available = 1; break; case 27: /* Direct mode test */ + msr->idx = HV_X64_MSR_STIMER0_CONFIG; msr->write = 1; msr->write_val = 1 << 12; msr->available = 0; break; case 28: - feat.edx |= HV_STIMER_DIRECT_MODE_AVAILABLE; + feat->edx |= HV_STIMER_DIRECT_MODE_AVAILABLE; + msr->idx = HV_X64_MSR_STIMER0_CONFIG; + msr->write = 1; + msr->write_val = 1 << 12; msr->available = 1; break; @@ -384,7 +340,8 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 30: - feat.eax |= HV_MSR_APIC_ACCESS_AVAILABLE; + feat->eax |= HV_MSR_APIC_ACCESS_AVAILABLE; + msr->idx = HV_X64_MSR_EOI; msr->write = 1; msr->write_val = 1; msr->available = 1; @@ -396,12 +353,14 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 32: - feat.eax |= HV_ACCESS_FREQUENCY_MSRS; + feat->eax |= HV_ACCESS_FREQUENCY_MSRS; + msr->idx = HV_X64_MSR_TSC_FREQUENCY; msr->write = 0; msr->available = 1; break; case 33: /* Read only */ + msr->idx = HV_X64_MSR_TSC_FREQUENCY; msr->write = 1; msr->write_val = 1; msr->available = 0; @@ -413,11 +372,13 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 35: - feat.eax |= HV_ACCESS_REENLIGHTENMENT; + feat->eax |= HV_ACCESS_REENLIGHTENMENT; + msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL; msr->write = 0; msr->available = 1; break; case 36: + msr->idx = HV_X64_MSR_REENLIGHTENMENT_CONTROL; msr->write = 1; msr->write_val = 1; msr->available = 1; @@ -436,11 +397,13 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 39: - feat.edx |= HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE; + feat->edx |= HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE; + msr->idx = HV_X64_MSR_CRASH_P0; msr->write = 0; msr->available = 1; break; case 40: + msr->idx = HV_X64_MSR_CRASH_P0; msr->write = 1; msr->write_val = 1; msr->available = 1; @@ -452,48 +415,44 @@ static void guest_test_msrs_access(void) msr->available = 0; break; case 42: - feat.edx |= HV_FEATURE_DEBUG_MSRS_AVAILABLE; - dbg.eax |= HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING; + feat->edx |= HV_FEATURE_DEBUG_MSRS_AVAILABLE; + dbg->eax |= HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING; + msr->idx = HV_X64_MSR_SYNDBG_STATUS; msr->write = 0; msr->available = 1; break; case 43: + msr->idx = HV_X64_MSR_SYNDBG_STATUS; msr->write = 1; msr->write_val = 0; msr->available = 1; break; case 44: - /* END */ - msr->idx = 0; - break; + kvm_vm_free(vm); + return; } - hv_set_cpuid(vm, best, &feat, &recomm, &dbg); + vcpu_set_cpuid(vcpu); - if (msr->idx) - pr_debug("Stage %d: testing msr: 0x%x for %s\n", stage, - msr->idx, msr->write ? "write" : "read"); - else - pr_debug("Stage %d: finish\n", stage); + memcpy(prev_cpuid, vcpu->cpuid, kvm_cpuid2_size(vcpu->cpuid->nent)); - r = _vcpu_run(vm, VCPU_ID); - TEST_ASSERT(!r, "vcpu_run failed: %d\n", r); + pr_debug("Stage %d: testing msr: 0x%x for %s\n", stage, + msr->idx, msr->write ? "write" : "read"); + + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { - case UCALL_SYNC: - TEST_ASSERT(uc.args[1] == 0, - "Unexpected stage: %ld (0 expected)\n", - uc.args[1]); - break; + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT_2(uc, "MSR = %lx, vector = %lx"); return; case UCALL_DONE: + break; + default: + TEST_FAIL("Unhandled ucall: %ld", uc.cmd); return; } @@ -504,54 +463,50 @@ static void guest_test_msrs_access(void) static void guest_test_hcalls_access(void) { + struct kvm_cpuid_entry2 *feat, *recomm, *dbg; + struct kvm_cpuid2 *prev_cpuid = NULL; + struct kvm_vcpu *vcpu; struct kvm_run *run; struct kvm_vm *vm; struct ucall uc; - int stage = 0, r; - struct kvm_cpuid_entry2 feat = { - .function = HYPERV_CPUID_FEATURES, - .eax = HV_MSR_HYPERCALL_AVAILABLE - }; - struct kvm_cpuid_entry2 recomm = { - .function = HYPERV_CPUID_ENLIGHTMENT_INFO - }; - struct kvm_cpuid_entry2 dbg = { - .function = HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES - }; - struct kvm_enable_cap cap = { - .cap = KVM_CAP_HYPERV_ENFORCE_CPUID, - .args = {1} - }; + int stage = 0; vm_vaddr_t hcall_page, hcall_params; struct hcall_data *hcall; - struct kvm_cpuid2 *best; while (true) { - vm = vm_create_default(VCPU_ID, 0, guest_hcall); + vm = vm_create_with_one_vcpu(&vcpu, guest_hcall); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); - vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); + vcpu_init_descriptor_tables(vcpu); /* Hypercall input/output */ hcall_page = vm_vaddr_alloc_pages(vm, 2); - hcall = addr_gva2hva(vm, hcall_page); memset(addr_gva2hva(vm, hcall_page), 0x0, 2 * getpagesize()); hcall_params = vm_vaddr_alloc_page(vm); memset(addr_gva2hva(vm, hcall_params), 0x0, getpagesize()); + hcall = addr_gva2hva(vm, hcall_params); - vcpu_args_set(vm, VCPU_ID, 2, addr_gva2gpa(vm, hcall_page), hcall_params); - vcpu_enable_cap(vm, VCPU_ID, &cap); + vcpu_args_set(vcpu, 2, addr_gva2gpa(vm, hcall_page), hcall_params); + vcpu_enable_cap(vcpu, KVM_CAP_HYPERV_ENFORCE_CPUID, 1); - vcpu_set_hv_cpuid(vm, VCPU_ID); + if (!prev_cpuid) { + vcpu_reset_hv_cpuid(vcpu); - best = kvm_get_supported_hv_cpuid(); + prev_cpuid = allocate_kvm_cpuid2(vcpu->cpuid->nent); + } else { + vcpu_init_cpuid(vcpu, prev_cpuid); + } - run = vcpu_state(vm, VCPU_ID); + feat = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_FEATURES); + recomm = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_ENLIGHTMENT_INFO); + dbg = vcpu_get_cpuid_entry(vcpu, HYPERV_CPUID_SYNDBG_PLATFORM_CAPABILITIES); + + run = vcpu->run; switch (stage) { case 0: + feat->eax |= HV_MSR_HYPERCALL_AVAILABLE; hcall->control = 0xdeadbeef; hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE; break; @@ -561,7 +516,8 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 2: - feat.ebx |= HV_POST_MESSAGES; + feat->ebx |= HV_POST_MESSAGES; + hcall->control = HVCALL_POST_MESSAGE; hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT; break; @@ -570,7 +526,8 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 4: - feat.ebx |= HV_SIGNAL_EVENTS; + feat->ebx |= HV_SIGNAL_EVENTS; + hcall->control = HVCALL_SIGNAL_EVENT; hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT; break; @@ -579,11 +536,13 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_INVALID_HYPERCALL_CODE; break; case 6: - dbg.eax |= HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING; + dbg->eax |= HV_X64_SYNDBG_CAP_ALLOW_KERNEL_DEBUGGING; + hcall->control = HVCALL_RESET_DEBUG_SESSION; hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 7: - feat.ebx |= HV_DEBUGGING; + feat->ebx |= HV_DEBUGGING; + hcall->control = HVCALL_RESET_DEBUG_SESSION; hcall->expect = HV_STATUS_OPERATION_DENIED; break; @@ -592,7 +551,8 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 9: - recomm.eax |= HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED; + recomm->eax |= HV_X64_REMOTE_TLB_FLUSH_RECOMMENDED; + hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE; hcall->expect = HV_STATUS_SUCCESS; break; case 10: @@ -600,7 +560,8 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 11: - recomm.eax |= HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED; + recomm->eax |= HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED; + hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX; hcall->expect = HV_STATUS_SUCCESS; break; @@ -609,7 +570,8 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 13: - recomm.eax |= HV_X64_CLUSTER_IPI_RECOMMENDED; + recomm->eax |= HV_X64_CLUSTER_IPI_RECOMMENDED; + hcall->control = HVCALL_SEND_IPI; hcall->expect = HV_STATUS_INVALID_HYPERCALL_INPUT; break; case 14: @@ -623,7 +585,8 @@ static void guest_test_hcalls_access(void) hcall->expect = HV_STATUS_ACCESS_DENIED; break; case 16: - recomm.ebx = 0xfff; + recomm->ebx = 0xfff; + hcall->control = HVCALL_NOTIFY_LONG_SPIN_WAIT; hcall->expect = HV_STATUS_SUCCESS; break; case 17: @@ -632,42 +595,35 @@ static void guest_test_hcalls_access(void) hcall->ud_expected = true; break; case 18: - feat.edx |= HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE; + feat->edx |= HV_X64_HYPERCALL_XMM_INPUT_AVAILABLE; + hcall->control = HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE | HV_HYPERCALL_FAST_BIT; hcall->ud_expected = false; hcall->expect = HV_STATUS_SUCCESS; break; - case 19: - /* END */ - hcall->control = 0; - break; + kvm_vm_free(vm); + return; } - hv_set_cpuid(vm, best, &feat, &recomm, &dbg); + vcpu_set_cpuid(vcpu); - if (hcall->control) - pr_debug("Stage %d: testing hcall: 0x%lx\n", stage, - hcall->control); - else - pr_debug("Stage %d: finish\n", stage); + memcpy(prev_cpuid, vcpu->cpuid, kvm_cpuid2_size(vcpu->cpuid->nent)); - r = _vcpu_run(vm, VCPU_ID); - TEST_ASSERT(!r, "vcpu_run failed: %d\n", r); + pr_debug("Stage %d: testing hcall: 0x%lx\n", stage, hcall->control); + + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { - case UCALL_SYNC: - TEST_ASSERT(uc.args[1] == 0, - "Unexpected stage: %ld (0 expected)\n", - uc.args[1]); - break; + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT_2(uc, "arg1 = %lx, arg2 = %lx"); return; case UCALL_DONE: + break; + default: + TEST_FAIL("Unhandled ucall: %ld", uc.cmd); return; } diff --git a/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c b/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c index 21f5ca9197da..a380ad7bb9b3 100644 --- a/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c +++ b/tools/testing/selftests/kvm/x86_64/hyperv_svm_test.c @@ -21,7 +21,6 @@ #include "svm_util.h" #include "hyperv.h" -#define VCPU_ID 1 #define L2_GUEST_STACK_SIZE 256 struct hv_enlightenments { @@ -42,11 +41,6 @@ struct hv_enlightenments { */ #define VMCB_HV_NESTED_ENLIGHTENMENTS (1U << 31) -static inline void vmmcall(void) -{ - __asm__ __volatile__("vmmcall"); -} - void l2_guest_code(void) { GUEST_SYNC(3); @@ -127,33 +121,31 @@ int main(int argc, char *argv[]) { vm_vaddr_t nested_gva = 0; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct ucall uc; int stage; - if (!nested_svm_supported()) { - print_skip("Nested SVM not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - vcpu_set_hv_cpuid(vm, VCPU_ID); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vcpu_set_hv_cpuid(vcpu); + run = vcpu->run; vcpu_alloc_svm(vm, &nested_gva); - vcpu_args_set(vm, VCPU_ID, 1, nested_gva); + vcpu_args_set(vcpu, 1, nested_gva); for (stage = 1;; stage++) { - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; diff --git a/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c b/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c index 97731454f3f3..813ce282cf56 100644 --- a/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvm_clock_test.c @@ -16,8 +16,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 0 - struct test_case { uint64_t kvmclock_base; int64_t realtime_offset; @@ -73,8 +71,7 @@ static void handle_sync(struct ucall *uc, struct kvm_clock_data *start, static void handle_abort(struct ucall *uc) { - TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], - __FILE__, uc->args[1]); + REPORT_GUEST_ASSERT(*uc); } static void setup_clock(struct kvm_vm *vm, struct test_case *test_case) @@ -105,29 +102,27 @@ static void setup_clock(struct kvm_vm *vm, struct test_case *test_case) vm_ioctl(vm, KVM_SET_CLOCK, &data); } -static void enter_guest(struct kvm_vm *vm) +static void enter_guest(struct kvm_vcpu *vcpu) { struct kvm_clock_data start, end; - struct kvm_run *run; + struct kvm_run *run = vcpu->run; + struct kvm_vm *vm = vcpu->vm; struct ucall uc; - int i, r; - - run = vcpu_state(vm, VCPU_ID); + int i; for (i = 0; i < ARRAY_SIZE(test_cases); i++) { setup_clock(vm, &test_cases[i]); vm_ioctl(vm, KVM_GET_CLOCK, &start); - r = _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); vm_ioctl(vm, KVM_GET_CLOCK, &end); - TEST_ASSERT(!r, "vcpu_run failed: %d\n", r); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: handle_sync(&uc, &start, &end); break; @@ -178,26 +173,23 @@ out: int main(void) { + struct kvm_vcpu *vcpu; vm_vaddr_t pvti_gva; vm_paddr_t pvti_gpa; struct kvm_vm *vm; int flags; flags = kvm_check_cap(KVM_CAP_ADJUST_CLOCK); - if (!(flags & KVM_CLOCK_REALTIME)) { - print_skip("KVM_CLOCK_REALTIME not supported; flags: %x", - flags); - exit(KSFT_SKIP); - } + TEST_REQUIRE(flags & KVM_CLOCK_REALTIME); check_clocksource(); - vm = vm_create_default(VCPU_ID, 0, guest_main); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); pvti_gva = vm_vaddr_alloc(vm, getpagesize(), 0x10000); pvti_gpa = addr_gva2gpa(vm, pvti_gva); - vcpu_args_set(vm, VCPU_ID, 2, pvti_gpa, pvti_gva); + vcpu_args_set(vcpu, 2, pvti_gpa, pvti_gva); - enter_guest(vm); + enter_guest(vcpu); kvm_vm_free(vm); } diff --git a/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c b/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c index 04ed975662c9..619655c1a1f3 100644 --- a/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c +++ b/tools/testing/selftests/kvm/x86_64/kvm_pv_test.c @@ -12,55 +12,6 @@ #include "kvm_util.h" #include "processor.h" -extern unsigned char rdmsr_start; -extern unsigned char rdmsr_end; - -static u64 do_rdmsr(u32 idx) -{ - u32 lo, hi; - - asm volatile("rdmsr_start: rdmsr;" - "rdmsr_end:" - : "=a"(lo), "=c"(hi) - : "c"(idx)); - - return (((u64) hi) << 32) | lo; -} - -extern unsigned char wrmsr_start; -extern unsigned char wrmsr_end; - -static void do_wrmsr(u32 idx, u64 val) -{ - u32 lo, hi; - - lo = val; - hi = val >> 32; - - asm volatile("wrmsr_start: wrmsr;" - "wrmsr_end:" - : : "a"(lo), "c"(idx), "d"(hi)); -} - -static int nr_gp; - -static void guest_gp_handler(struct ex_regs *regs) -{ - unsigned char *rip = (unsigned char *)regs->rip; - bool r, w; - - r = rip == &rdmsr_start; - w = rip == &wrmsr_start; - GUEST_ASSERT(r || w); - - nr_gp++; - - if (r) - regs->rip = (uint64_t)&rdmsr_end; - else - regs->rip = (uint64_t)&wrmsr_end; -} - struct msr_data { uint32_t idx; const char *name; @@ -89,14 +40,16 @@ static struct msr_data msrs_to_test[] = { static void test_msr(struct msr_data *msr) { - PR_MSR(msr); - do_rdmsr(msr->idx); - GUEST_ASSERT(READ_ONCE(nr_gp) == 1); + uint64_t ignored; + uint8_t vector; - nr_gp = 0; - do_wrmsr(msr->idx, 0); - GUEST_ASSERT(READ_ONCE(nr_gp) == 1); - nr_gp = 0; + PR_MSR(msr); + + vector = rdmsr_safe(msr->idx, &ignored); + GUEST_ASSERT_1(vector == GP_VECTOR, vector); + + vector = wrmsr_safe(msr->idx, 0); + GUEST_ASSERT_1(vector == GP_VECTOR, vector); } struct hcall_data { @@ -142,15 +95,6 @@ static void guest_main(void) GUEST_DONE(); } -static void clear_kvm_cpuid_features(struct kvm_cpuid2 *cpuid) -{ - struct kvm_cpuid_entry2 ent = {0}; - - ent.function = KVM_CPUID_FEATURES; - TEST_ASSERT(set_cpuid(cpuid, &ent), - "failed to clear KVM_CPUID_FEATURES leaf"); -} - static void pr_msr(struct ucall *uc) { struct msr_data *msr = (struct msr_data *)uc->args[0]; @@ -165,30 +109,18 @@ static void pr_hcall(struct ucall *uc) pr_info("testing hcall: %s (%lu)\n", hc->name, hc->nr); } -static void handle_abort(struct ucall *uc) +static void enter_guest(struct kvm_vcpu *vcpu) { - TEST_FAIL("%s at %s:%ld", (const char *)uc->args[0], - __FILE__, uc->args[1]); -} - -#define VCPU_ID 0 - -static void enter_guest(struct kvm_vm *vm) -{ - struct kvm_run *run; + struct kvm_run *run = vcpu->run; struct ucall uc; - int r; - - run = vcpu_state(vm, VCPU_ID); while (true) { - r = _vcpu_run(vm, VCPU_ID); - TEST_ASSERT(!r, "vcpu_run failed: %d\n", r); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_PR_MSR: pr_msr(&uc); break; @@ -196,7 +128,7 @@ static void enter_guest(struct kvm_vm *vm) pr_hcall(&uc); break; case UCALL_ABORT: - handle_abort(&uc); + REPORT_GUEST_ASSERT_1(uc, "vector = %lu"); return; case UCALL_DONE: return; @@ -206,29 +138,20 @@ static void enter_guest(struct kvm_vm *vm) int main(void) { - struct kvm_enable_cap cap = {0}; - struct kvm_cpuid2 *best; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; - if (!kvm_check_cap(KVM_CAP_ENFORCE_PV_FEATURE_CPUID)) { - print_skip("KVM_CAP_ENFORCE_PV_FEATURE_CPUID not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_ENFORCE_PV_FEATURE_CPUID)); - vm = vm_create_default(VCPU_ID, 0, guest_main); + vm = vm_create_with_one_vcpu(&vcpu, guest_main); - cap.cap = KVM_CAP_ENFORCE_PV_FEATURE_CPUID; - cap.args[0] = 1; - vcpu_enable_cap(vm, VCPU_ID, &cap); + vcpu_enable_cap(vcpu, KVM_CAP_ENFORCE_PV_FEATURE_CPUID, 1); - best = kvm_get_supported_cpuid(); - clear_kvm_cpuid_features(best); - vcpu_set_cpuid(vm, VCPU_ID, best); + vcpu_clear_cpuid_entry(vcpu, KVM_CPUID_FEATURES); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); - vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler); + vcpu_init_descriptor_tables(vcpu); - enter_guest(vm); + enter_guest(vcpu); kvm_vm_free(vm); } diff --git a/tools/testing/selftests/kvm/x86_64/max_vcpuid_cap_test.c b/tools/testing/selftests/kvm/x86_64/max_vcpuid_cap_test.c new file mode 100644 index 000000000000..3cc4b86832fe --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/max_vcpuid_cap_test.c @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * maximum APIC ID capability tests + * + * Copyright (C) 2022, Intel, Inc. + * + * Tests for getting/setting maximum APIC ID capability + */ + +#include "kvm_util.h" + +#define MAX_VCPU_ID 2 + +int main(int argc, char *argv[]) +{ + struct kvm_vm *vm; + int ret; + + vm = vm_create_barebones(); + + /* Get KVM_CAP_MAX_VCPU_ID cap supported in KVM */ + ret = vm_check_cap(vm, KVM_CAP_MAX_VCPU_ID); + + /* Try to set KVM_CAP_MAX_VCPU_ID beyond KVM cap */ + ret = __vm_enable_cap(vm, KVM_CAP_MAX_VCPU_ID, ret + 1); + TEST_ASSERT(ret < 0, + "Setting KVM_CAP_MAX_VCPU_ID beyond KVM cap should fail"); + + /* Set KVM_CAP_MAX_VCPU_ID */ + vm_enable_cap(vm, KVM_CAP_MAX_VCPU_ID, MAX_VCPU_ID); + + + /* Try to set KVM_CAP_MAX_VCPU_ID again */ + ret = __vm_enable_cap(vm, KVM_CAP_MAX_VCPU_ID, MAX_VCPU_ID + 1); + TEST_ASSERT(ret < 0, + "Setting KVM_CAP_MAX_VCPU_ID multiple times should fail"); + + /* Create vCPU with id beyond KVM_CAP_MAX_VCPU_ID cap*/ + ret = __vm_ioctl(vm, KVM_CREATE_VCPU, (void *)MAX_VCPU_ID); + TEST_ASSERT(ret < 0, "Creating vCPU with ID > MAX_VCPU_ID should fail"); + + kvm_vm_free(vm); + return 0; +} diff --git a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c index 9f55ccd169a1..fb02581953a3 100644 --- a/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c +++ b/tools/testing/selftests/kvm/x86_64/mmio_warning_test.c @@ -59,10 +59,10 @@ void test(void) kvm = open("/dev/kvm", O_RDWR); TEST_ASSERT(kvm != -1, "failed to open /dev/kvm"); - kvmvm = ioctl(kvm, KVM_CREATE_VM, 0); - TEST_ASSERT(kvmvm != -1, "KVM_CREATE_VM failed"); + kvmvm = __kvm_ioctl(kvm, KVM_CREATE_VM, NULL); + TEST_ASSERT(kvmvm > 0, KVM_IOCTL_ERROR(KVM_CREATE_VM, kvmvm)); kvmcpu = ioctl(kvmvm, KVM_CREATE_VCPU, 0); - TEST_ASSERT(kvmcpu != -1, "KVM_CREATE_VCPU failed"); + TEST_ASSERT(kvmcpu != -1, KVM_IOCTL_ERROR(KVM_CREATE_VCPU, kvmcpu)); run = (struct kvm_run *)mmap(0, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, kvmcpu, 0); tc.kvmcpu = kvmcpu; @@ -93,15 +93,9 @@ int main(void) { int warnings_before, warnings_after; - if (!is_intel_cpu()) { - print_skip("Must be run on an Intel CPU"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(is_intel_cpu()); - if (vm_is_unrestricted_guest(NULL)) { - print_skip("Unrestricted guest must be disabled"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(!vm_is_unrestricted_guest(NULL)); warnings_before = get_warnings_count(); diff --git a/tools/testing/selftests/kvm/x86_64/mmu_role_test.c b/tools/testing/selftests/kvm/x86_64/mmu_role_test.c deleted file mode 100644 index bdecd532f935..000000000000 --- a/tools/testing/selftests/kvm/x86_64/mmu_role_test.c +++ /dev/null @@ -1,147 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 - -#include "kvm_util.h" -#include "processor.h" - -#define VCPU_ID 1 - -#define MMIO_GPA 0x100000000ull - -static void guest_code(void) -{ - (void)READ_ONCE(*((uint64_t *)MMIO_GPA)); - (void)READ_ONCE(*((uint64_t *)MMIO_GPA)); - - GUEST_ASSERT(0); -} - -static void guest_pf_handler(struct ex_regs *regs) -{ - /* PFEC == RSVD | PRESENT (read, kernel). */ - GUEST_ASSERT(regs->error_code == 0x9); - GUEST_DONE(); -} - -static void mmu_role_test(u32 *cpuid_reg, u32 evil_cpuid_val) -{ - u32 good_cpuid_val = *cpuid_reg; - struct kvm_run *run; - struct kvm_vm *vm; - uint64_t cmd; - int r; - - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); - - /* Map 1gb page without a backing memlot. */ - __virt_pg_map(vm, MMIO_GPA, MMIO_GPA, PG_LEVEL_1G); - - r = _vcpu_run(vm, VCPU_ID); - - /* Guest access to the 1gb page should trigger MMIO. */ - TEST_ASSERT(r == 0, "vcpu_run failed: %d\n", r); - TEST_ASSERT(run->exit_reason == KVM_EXIT_MMIO, - "Unexpected exit reason: %u (%s), expected MMIO exit (1gb page w/o memslot)\n", - run->exit_reason, exit_reason_str(run->exit_reason)); - - TEST_ASSERT(run->mmio.len == 8, "Unexpected exit mmio size = %u", run->mmio.len); - - TEST_ASSERT(run->mmio.phys_addr == MMIO_GPA, - "Unexpected exit mmio address = 0x%llx", run->mmio.phys_addr); - - /* - * Effect the CPUID change for the guest and re-enter the guest. Its - * access should now #PF due to the PAGE_SIZE bit being reserved or - * the resulting GPA being invalid. Note, kvm_get_supported_cpuid() - * returns the struct that contains the entry being modified. Eww. - */ - *cpuid_reg = evil_cpuid_val; - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); - - /* - * Add a dummy memslot to coerce KVM into bumping the MMIO generation. - * KVM does not "officially" support mucking with CPUID after KVM_RUN, - * and will incorrectly reuse MMIO SPTEs. Don't delete the memslot! - * KVM x86 zaps all shadow pages on memslot deletion. - */ - vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, - MMIO_GPA << 1, 10, 1, 0); - - /* Set up a #PF handler to eat the RSVD #PF and signal all done! */ - vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); - vm_install_exception_handler(vm, PF_VECTOR, guest_pf_handler); - - r = _vcpu_run(vm, VCPU_ID); - TEST_ASSERT(r == 0, "vcpu_run failed: %d\n", r); - - cmd = get_ucall(vm, VCPU_ID, NULL); - TEST_ASSERT(cmd == UCALL_DONE, - "Unexpected guest exit, exit_reason=%s, ucall.cmd = %lu\n", - exit_reason_str(run->exit_reason), cmd); - - /* - * Restore the happy CPUID value for the next test. Yes, changes are - * indeed persistent across VM destruction. - */ - *cpuid_reg = good_cpuid_val; - - kvm_vm_free(vm); -} - -int main(int argc, char *argv[]) -{ - struct kvm_cpuid_entry2 *entry; - int opt; - - /* - * All tests are opt-in because TDP doesn't play nice with reserved #PF - * in the GVA->GPA translation. The hardware page walker doesn't let - * software change GBPAGES or MAXPHYADDR, and KVM doesn't manually walk - * the GVA on fault for performance reasons. - */ - bool do_gbpages = false; - bool do_maxphyaddr = false; - - setbuf(stdout, NULL); - - while ((opt = getopt(argc, argv, "gm")) != -1) { - switch (opt) { - case 'g': - do_gbpages = true; - break; - case 'm': - do_maxphyaddr = true; - break; - case 'h': - default: - printf("usage: %s [-g (GBPAGES)] [-m (MAXPHYADDR)]\n", argv[0]); - break; - } - } - - if (!do_gbpages && !do_maxphyaddr) { - print_skip("No sub-tests selected"); - return 0; - } - - entry = kvm_get_supported_cpuid_entry(0x80000001); - if (!(entry->edx & CPUID_GBPAGES)) { - print_skip("1gb hugepages not supported"); - return 0; - } - - if (do_gbpages) { - pr_info("Test MMIO after toggling CPUID.GBPAGES\n\n"); - mmu_role_test(&entry->edx, entry->edx & ~CPUID_GBPAGES); - } - - if (do_maxphyaddr) { - pr_info("Test MMIO after changing CPUID.MAXPHYADDR\n\n"); - entry = kvm_get_supported_cpuid_entry(0x80000008); - mmu_role_test(&entry->eax, (entry->eax & ~0xff) | 0x20); - } - - return 0; -} diff --git a/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c b/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c new file mode 100644 index 000000000000..016070cad36e --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/monitor_mwait_test.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0 +#include +#include +#include +#include +#include + +#include "kvm_util.h" +#include "processor.h" + +#define CPUID_MWAIT (1u << 3) + +enum monitor_mwait_testcases { + MWAIT_QUIRK_DISABLED = BIT(0), + MISC_ENABLES_QUIRK_DISABLED = BIT(1), + MWAIT_DISABLED = BIT(2), +}; + +static void guest_monitor_wait(int testcase) +{ + /* + * If both MWAIT and its quirk are disabled, MONITOR/MWAIT should #UD, + * in all other scenarios KVM should emulate them as nops. + */ + bool fault_wanted = (testcase & MWAIT_QUIRK_DISABLED) && + (testcase & MWAIT_DISABLED); + u8 vector; + + GUEST_SYNC(testcase); + + /* + * Arbitrarily MONITOR this function, SVM performs fault checks before + * intercept checks, so the inputs for MONITOR and MWAIT must be valid. + */ + vector = kvm_asm_safe("monitor", "a"(guest_monitor_wait), "c"(0), "d"(0)); + if (fault_wanted) + GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector); + else + GUEST_ASSERT_2(!vector, testcase, vector); + + vector = kvm_asm_safe("mwait", "a"(guest_monitor_wait), "c"(0), "d"(0)); + if (fault_wanted) + GUEST_ASSERT_2(vector == UD_VECTOR, testcase, vector); + else + GUEST_ASSERT_2(!vector, testcase, vector); +} + +static void guest_code(void) +{ + guest_monitor_wait(MWAIT_DISABLED); + + guest_monitor_wait(MWAIT_QUIRK_DISABLED | MWAIT_DISABLED); + + guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_DISABLED); + guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED); + + guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED | MWAIT_DISABLED); + guest_monitor_wait(MISC_ENABLES_QUIRK_DISABLED | MWAIT_QUIRK_DISABLED); + + GUEST_DONE(); +} + +int main(int argc, char *argv[]) +{ + uint64_t disabled_quirks; + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vm *vm; + struct ucall uc; + int testcase; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2)); + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_MWAIT); + + run = vcpu->run; + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vcpu); + + while (1) { + vcpu_run(vcpu); + + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Unexpected exit reason: %u (%s),\n", + run->exit_reason, + exit_reason_str(run->exit_reason)); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_SYNC: + testcase = uc.args[1]; + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT_2(uc, "testcase = %lx, vector = %ld"); + goto done; + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall %lu", uc.cmd); + goto done; + } + + disabled_quirks = 0; + if (testcase & MWAIT_QUIRK_DISABLED) + disabled_quirks |= KVM_X86_QUIRK_MWAIT_NEVER_UD_FAULTS; + if (testcase & MISC_ENABLES_QUIRK_DISABLED) + disabled_quirks |= KVM_X86_QUIRK_MISC_ENABLE_NO_MWAIT; + vm_enable_cap(vm, KVM_CAP_DISABLE_QUIRKS2, disabled_quirks); + + /* + * If the MISC_ENABLES quirk (KVM neglects to update CPUID to + * enable/disable MWAIT) is disabled, toggle the ENABLE_MWAIT + * bit in MISC_ENABLES accordingly. If the quirk is enabled, + * the only valid configuration is MWAIT disabled, as CPUID + * can't be manually changed after running the vCPU. + */ + if (!(testcase & MISC_ENABLES_QUIRK_DISABLED)) { + TEST_ASSERT(testcase & MWAIT_DISABLED, + "Can't toggle CPUID features after running vCPU"); + continue; + } + + vcpu_set_msr(vcpu, MSR_IA32_MISC_ENABLE, + (testcase & MWAIT_DISABLED) ? 0 : MSR_IA32_MISC_ENABLE_MWAIT); + } + +done: + kvm_vm_free(vm); + return 0; +} diff --git a/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c b/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c new file mode 100644 index 000000000000..cc6421716400 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.c @@ -0,0 +1,269 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * tools/testing/selftests/kvm/nx_huge_page_test.c + * + * Usage: to be run via nx_huge_page_test.sh, which does the necessary + * environment setup and teardown + * + * Copyright (C) 2022, Google LLC. + */ + +#define _GNU_SOURCE + +#include +#include +#include + +#include +#include "kvm_util.h" +#include "processor.h" + +#define HPAGE_SLOT 10 +#define HPAGE_GPA (4UL << 30) /* 4G prevents collision w/ slot 0 */ +#define HPAGE_GVA HPAGE_GPA /* GVA is arbitrary, so use GPA. */ +#define PAGES_PER_2MB_HUGE_PAGE 512 +#define HPAGE_SLOT_NPAGES (3 * PAGES_PER_2MB_HUGE_PAGE) + +/* + * Passed by nx_huge_pages_test.sh to provide an easy warning if this test is + * being run without it. + */ +#define MAGIC_TOKEN 887563923 + +/* + * x86 opcode for the return instruction. Used to call into, and then + * immediately return from, memory backed with hugepages. + */ +#define RETURN_OPCODE 0xC3 + +/* Call the specified memory address. */ +static void guest_do_CALL(uint64_t target) +{ + ((void (*)(void)) target)(); +} + +/* + * Exit the VM after each memory access so that the userspace component of the + * test can make assertions about the pages backing the VM. + * + * See the below for an explanation of how each access should affect the + * backing mappings. + */ +void guest_code(void) +{ + uint64_t hpage_1 = HPAGE_GVA; + uint64_t hpage_2 = hpage_1 + (PAGE_SIZE * 512); + uint64_t hpage_3 = hpage_2 + (PAGE_SIZE * 512); + + READ_ONCE(*(uint64_t *)hpage_1); + GUEST_SYNC(1); + + READ_ONCE(*(uint64_t *)hpage_2); + GUEST_SYNC(2); + + guest_do_CALL(hpage_1); + GUEST_SYNC(3); + + guest_do_CALL(hpage_3); + GUEST_SYNC(4); + + READ_ONCE(*(uint64_t *)hpage_1); + GUEST_SYNC(5); + + READ_ONCE(*(uint64_t *)hpage_3); + GUEST_SYNC(6); +} + +static void check_2m_page_count(struct kvm_vm *vm, int expected_pages_2m) +{ + int actual_pages_2m; + + actual_pages_2m = vm_get_stat(vm, "pages_2m"); + + TEST_ASSERT(actual_pages_2m == expected_pages_2m, + "Unexpected 2m page count. Expected %d, got %d", + expected_pages_2m, actual_pages_2m); +} + +static void check_split_count(struct kvm_vm *vm, int expected_splits) +{ + int actual_splits; + + actual_splits = vm_get_stat(vm, "nx_lpage_splits"); + + TEST_ASSERT(actual_splits == expected_splits, + "Unexpected NX huge page split count. Expected %d, got %d", + expected_splits, actual_splits); +} + +static void wait_for_reclaim(int reclaim_period_ms) +{ + long reclaim_wait_ms; + struct timespec ts; + + reclaim_wait_ms = reclaim_period_ms * 5; + ts.tv_sec = reclaim_wait_ms / 1000; + ts.tv_nsec = (reclaim_wait_ms - (ts.tv_sec * 1000)) * 1000000; + nanosleep(&ts, NULL); +} + +void run_test(int reclaim_period_ms, bool disable_nx_huge_pages, + bool reboot_permissions) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + void *hva; + int r; + + vm = vm_create(1); + + if (disable_nx_huge_pages) { + /* + * Cannot run the test without NX huge pages if the kernel + * does not support it. + */ + if (!kvm_check_cap(KVM_CAP_VM_DISABLE_NX_HUGE_PAGES)) + return; + + r = __vm_disable_nx_huge_pages(vm); + if (reboot_permissions) { + TEST_ASSERT(!r, "Disabling NX huge pages should succeed if process has reboot permissions"); + } else { + TEST_ASSERT(r == -1 && errno == EPERM, + "This process should not have permission to disable NX huge pages"); + return; + } + } + + vcpu = vm_vcpu_add(vm, 0, guest_code); + + vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS_HUGETLB, + HPAGE_GPA, HPAGE_SLOT, + HPAGE_SLOT_NPAGES, 0); + + virt_map(vm, HPAGE_GVA, HPAGE_GPA, HPAGE_SLOT_NPAGES); + + hva = addr_gpa2hva(vm, HPAGE_GPA); + memset(hva, RETURN_OPCODE, HPAGE_SLOT_NPAGES * PAGE_SIZE); + + check_2m_page_count(vm, 0); + check_split_count(vm, 0); + + /* + * The guest code will first read from the first hugepage, resulting + * in a huge page mapping being created. + */ + vcpu_run(vcpu); + check_2m_page_count(vm, 1); + check_split_count(vm, 0); + + /* + * Then the guest code will read from the second hugepage, resulting + * in another huge page mapping being created. + */ + vcpu_run(vcpu); + check_2m_page_count(vm, 2); + check_split_count(vm, 0); + + /* + * Next, the guest will execute from the first huge page, causing it + * to be remapped at 4k. + * + * If NX huge pages are disabled, this should have no effect. + */ + vcpu_run(vcpu); + check_2m_page_count(vm, disable_nx_huge_pages ? 2 : 1); + check_split_count(vm, disable_nx_huge_pages ? 0 : 1); + + /* + * Executing from the third huge page (previously unaccessed) will + * cause part to be mapped at 4k. + * + * If NX huge pages are disabled, it should be mapped at 2M. + */ + vcpu_run(vcpu); + check_2m_page_count(vm, disable_nx_huge_pages ? 3 : 1); + check_split_count(vm, disable_nx_huge_pages ? 0 : 2); + + /* Reading from the first huge page again should have no effect. */ + vcpu_run(vcpu); + check_2m_page_count(vm, disable_nx_huge_pages ? 3 : 1); + check_split_count(vm, disable_nx_huge_pages ? 0 : 2); + + /* Give recovery thread time to run. */ + wait_for_reclaim(reclaim_period_ms); + + /* + * Now that the reclaimer has run, all the split pages should be gone. + * + * If NX huge pages are disabled, the relaimer will not run, so + * nothing should change from here on. + */ + check_2m_page_count(vm, disable_nx_huge_pages ? 3 : 1); + check_split_count(vm, 0); + + /* + * The 4k mapping on hpage 3 should have been removed, so check that + * reading from it causes a huge page mapping to be installed. + */ + vcpu_run(vcpu); + check_2m_page_count(vm, disable_nx_huge_pages ? 3 : 2); + check_split_count(vm, 0); + + kvm_vm_free(vm); +} + +static void help(char *name) +{ + puts(""); + printf("usage: %s [-h] [-p period_ms] [-t token]\n", name); + puts(""); + printf(" -p: The NX reclaim period in miliseconds.\n"); + printf(" -t: The magic token to indicate environment setup is done.\n"); + printf(" -r: The test has reboot permissions and can disable NX huge pages.\n"); + puts(""); + exit(0); +} + +int main(int argc, char **argv) +{ + int reclaim_period_ms = 0, token = 0, opt; + bool reboot_permissions = false; + + while ((opt = getopt(argc, argv, "hp:t:r")) != -1) { + switch (opt) { + case 'p': + reclaim_period_ms = atoi(optarg); + break; + case 't': + token = atoi(optarg); + break; + case 'r': + reboot_permissions = true; + break; + case 'h': + default: + help(argv[0]); + break; + } + } + + if (token != MAGIC_TOKEN) { + print_skip("This test must be run with the magic token %d.\n" + "This is done by nx_huge_pages_test.sh, which\n" + "also handles environment setup for the test.", + MAGIC_TOKEN); + exit(KSFT_SKIP); + } + + if (!reclaim_period_ms) { + print_skip("The NX reclaim period must be specified and non-zero"); + exit(KSFT_SKIP); + } + + run_test(reclaim_period_ms, false, reboot_permissions); + run_test(reclaim_period_ms, true, reboot_permissions); + + return 0; +} + diff --git a/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.sh b/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.sh new file mode 100755 index 000000000000..0560149e66ed --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/nx_huge_pages_test.sh @@ -0,0 +1,59 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0-only */ +# +# Wrapper script which performs setup and cleanup for nx_huge_pages_test. +# Makes use of root privileges to set up huge pages and KVM module parameters. +# +# tools/testing/selftests/kvm/nx_huge_page_test.sh +# Copyright (C) 2022, Google LLC. + +set -e + +NX_HUGE_PAGES=$(cat /sys/module/kvm/parameters/nx_huge_pages) +NX_HUGE_PAGES_RECOVERY_RATIO=$(cat /sys/module/kvm/parameters/nx_huge_pages_recovery_ratio) +NX_HUGE_PAGES_RECOVERY_PERIOD=$(cat /sys/module/kvm/parameters/nx_huge_pages_recovery_period_ms) +HUGE_PAGES=$(cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages) + +set +e + +function sudo_echo () { + echo "$1" | sudo tee -a "$2" > /dev/null +} + +NXECUTABLE="$(dirname $0)/nx_huge_pages_test" + +sudo_echo test /dev/null || exit 4 # KSFT_SKIP=4 + +( + set -e + + sudo_echo 1 /sys/module/kvm/parameters/nx_huge_pages + sudo_echo 1 /sys/module/kvm/parameters/nx_huge_pages_recovery_ratio + sudo_echo 100 /sys/module/kvm/parameters/nx_huge_pages_recovery_period_ms + sudo_echo "$(( $HUGE_PAGES + 3 ))" /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages + + # Test with reboot permissions + if [ $(whoami) == "root" ] || sudo setcap cap_sys_boot+ep $NXECUTABLE 2> /dev/null; then + echo Running test with CAP_SYS_BOOT enabled + $NXECUTABLE -t 887563923 -p 100 -r + test $(whoami) == "root" || sudo setcap cap_sys_boot-ep $NXECUTABLE + else + echo setcap failed, skipping nx_huge_pages_test with CAP_SYS_BOOT enabled + fi + + # Test without reboot permissions + if [ $(whoami) != "root" ] ; then + echo Running test with CAP_SYS_BOOT disabled + $NXECUTABLE -t 887563923 -p 100 + else + echo Running as root, skipping nx_huge_pages_test with CAP_SYS_BOOT disabled + fi +) +RET=$? + +sudo_echo "$NX_HUGE_PAGES" /sys/module/kvm/parameters/nx_huge_pages +sudo_echo "$NX_HUGE_PAGES_RECOVERY_RATIO" /sys/module/kvm/parameters/nx_huge_pages_recovery_ratio +sudo_echo "$NX_HUGE_PAGES_RECOVERY_PERIOD" /sys/module/kvm/parameters/nx_huge_pages_recovery_period_ms +sudo_echo "$HUGE_PAGES" /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages + +exit $RET diff --git a/tools/testing/selftests/kvm/x86_64/platform_info_test.c b/tools/testing/selftests/kvm/x86_64/platform_info_test.c index 1e89688cbbbf..76417c7d687b 100644 --- a/tools/testing/selftests/kvm/x86_64/platform_info_test.c +++ b/tools/testing/selftests/kvm/x86_64/platform_info_test.c @@ -21,7 +21,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 0 #define MSR_PLATFORM_INFO_MAX_TURBO_RATIO 0xff00 static void guest_code(void) @@ -35,28 +34,18 @@ static void guest_code(void) } } -static void set_msr_platform_info_enabled(struct kvm_vm *vm, bool enable) +static void test_msr_platform_info_enabled(struct kvm_vcpu *vcpu) { - struct kvm_enable_cap cap = {}; - - cap.cap = KVM_CAP_MSR_PLATFORM_INFO; - cap.flags = 0; - cap.args[0] = (int)enable; - vm_enable_cap(vm, &cap); -} - -static void test_msr_platform_info_enabled(struct kvm_vm *vm) -{ - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct ucall uc; - set_msr_platform_info_enabled(vm, true); - vcpu_run(vm, VCPU_ID); + vm_enable_cap(vcpu->vm, KVM_CAP_MSR_PLATFORM_INFO, true); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Exit_reason other than KVM_EXIT_IO: %u (%s),\n", run->exit_reason, exit_reason_str(run->exit_reason)); - get_ucall(vm, VCPU_ID, &uc); + get_ucall(vcpu, &uc); TEST_ASSERT(uc.cmd == UCALL_SYNC, "Received ucall other than UCALL_SYNC: %lu\n", uc.cmd); TEST_ASSERT((uc.args[1] & MSR_PLATFORM_INFO_MAX_TURBO_RATIO) == @@ -65,12 +54,12 @@ static void test_msr_platform_info_enabled(struct kvm_vm *vm) MSR_PLATFORM_INFO_MAX_TURBO_RATIO); } -static void test_msr_platform_info_disabled(struct kvm_vm *vm) +static void test_msr_platform_info_disabled(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; - set_msr_platform_info_enabled(vm, false); - vcpu_run(vm, VCPU_ID); + vm_enable_cap(vcpu->vm, KVM_CAP_MSR_PLATFORM_INFO, false); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_SHUTDOWN, "Exit_reason other than KVM_EXIT_SHUTDOWN: %u (%s)\n", run->exit_reason, @@ -79,27 +68,23 @@ static void test_msr_platform_info_disabled(struct kvm_vm *vm) int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; - int rv; uint64_t msr_platform_info; /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - rv = kvm_check_cap(KVM_CAP_MSR_PLATFORM_INFO); - if (!rv) { - print_skip("KVM_CAP_MSR_PLATFORM_INFO not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_MSR_PLATFORM_INFO)); - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - msr_platform_info = vcpu_get_msr(vm, VCPU_ID, MSR_PLATFORM_INFO); - vcpu_set_msr(vm, VCPU_ID, MSR_PLATFORM_INFO, - msr_platform_info | MSR_PLATFORM_INFO_MAX_TURBO_RATIO); - test_msr_platform_info_enabled(vm); - test_msr_platform_info_disabled(vm); - vcpu_set_msr(vm, VCPU_ID, MSR_PLATFORM_INFO, msr_platform_info); + msr_platform_info = vcpu_get_msr(vcpu, MSR_PLATFORM_INFO); + vcpu_set_msr(vcpu, MSR_PLATFORM_INFO, + msr_platform_info | MSR_PLATFORM_INFO_MAX_TURBO_RATIO); + test_msr_platform_info_enabled(vcpu); + test_msr_platform_info_disabled(vcpu); + vcpu_set_msr(vcpu, MSR_PLATFORM_INFO, msr_platform_info); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c index 93d77574b255..ea4e259a1e2e 100644 --- a/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c +++ b/tools/testing/selftests/kvm/x86_64/pmu_event_filter_test.c @@ -49,7 +49,6 @@ union cpuid10_ebx { /* Oddly, this isn't in perf_event.h. */ #define ARCH_PERFMON_BRANCHES_RETIRED 5 -#define VCPU_ID 0 #define NUM_BRANCHES 42 /* @@ -173,17 +172,17 @@ static void amd_guest_code(void) * Run the VM to the next GUEST_SYNC(value), and return the value passed * to the sync. Any other exit from the guest is fatal. */ -static uint64_t run_vm_to_sync(struct kvm_vm *vm) +static uint64_t run_vcpu_to_sync(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - get_ucall(vm, VCPU_ID, &uc); + get_ucall(vcpu, &uc); TEST_ASSERT(uc.cmd == UCALL_SYNC, "Received ucall other than UCALL_SYNC: %lu", uc.cmd); return uc.args[1]; @@ -197,13 +196,13 @@ static uint64_t run_vm_to_sync(struct kvm_vm *vm) * a sanity check and then GUEST_SYNC(success). In the case of failure, * the behavior of the guest on resumption is undefined. */ -static bool sanity_check_pmu(struct kvm_vm *vm) +static bool sanity_check_pmu(struct kvm_vcpu *vcpu) { bool success; - vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler); - success = run_vm_to_sync(vm); - vm_install_exception_handler(vm, GP_VECTOR, NULL); + vm_install_exception_handler(vcpu->vm, GP_VECTOR, guest_gp_handler); + success = run_vcpu_to_sync(vcpu); + vm_install_exception_handler(vcpu->vm, GP_VECTOR, NULL); return success; } @@ -264,9 +263,9 @@ static struct kvm_pmu_event_filter *remove_event(struct kvm_pmu_event_filter *f, return f; } -static void test_without_filter(struct kvm_vm *vm) +static void test_without_filter(struct kvm_vcpu *vcpu) { - uint64_t count = run_vm_to_sync(vm); + uint64_t count = run_vcpu_to_sync(vcpu); if (count != NUM_BRANCHES) pr_info("%s: Branch instructions retired = %lu (expected %u)\n", @@ -274,21 +273,21 @@ static void test_without_filter(struct kvm_vm *vm) TEST_ASSERT(count, "Allowed PMU event is not counting"); } -static uint64_t test_with_filter(struct kvm_vm *vm, +static uint64_t test_with_filter(struct kvm_vcpu *vcpu, struct kvm_pmu_event_filter *f) { - vm_ioctl(vm, KVM_SET_PMU_EVENT_FILTER, (void *)f); - return run_vm_to_sync(vm); + vm_ioctl(vcpu->vm, KVM_SET_PMU_EVENT_FILTER, f); + return run_vcpu_to_sync(vcpu); } -static void test_amd_deny_list(struct kvm_vm *vm) +static void test_amd_deny_list(struct kvm_vcpu *vcpu) { uint64_t event = EVENT(0x1C2, 0); struct kvm_pmu_event_filter *f; uint64_t count; f = create_pmu_event_filter(&event, 1, KVM_PMU_EVENT_DENY); - count = test_with_filter(vm, f); + count = test_with_filter(vcpu, f); free(f); if (count != NUM_BRANCHES) @@ -297,10 +296,10 @@ static void test_amd_deny_list(struct kvm_vm *vm) TEST_ASSERT(count, "Allowed PMU event is not counting"); } -static void test_member_deny_list(struct kvm_vm *vm) +static void test_member_deny_list(struct kvm_vcpu *vcpu) { struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY); - uint64_t count = test_with_filter(vm, f); + uint64_t count = test_with_filter(vcpu, f); free(f); if (count) @@ -309,10 +308,10 @@ static void test_member_deny_list(struct kvm_vm *vm) TEST_ASSERT(!count, "Disallowed PMU Event is counting"); } -static void test_member_allow_list(struct kvm_vm *vm) +static void test_member_allow_list(struct kvm_vcpu *vcpu) { struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW); - uint64_t count = test_with_filter(vm, f); + uint64_t count = test_with_filter(vcpu, f); free(f); if (count != NUM_BRANCHES) @@ -321,14 +320,14 @@ static void test_member_allow_list(struct kvm_vm *vm) TEST_ASSERT(count, "Allowed PMU event is not counting"); } -static void test_not_member_deny_list(struct kvm_vm *vm) +static void test_not_member_deny_list(struct kvm_vcpu *vcpu) { struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_DENY); uint64_t count; remove_event(f, INTEL_BR_RETIRED); remove_event(f, AMD_ZEN_BR_RETIRED); - count = test_with_filter(vm, f); + count = test_with_filter(vcpu, f); free(f); if (count != NUM_BRANCHES) pr_info("%s: Branch instructions retired = %lu (expected %u)\n", @@ -336,14 +335,14 @@ static void test_not_member_deny_list(struct kvm_vm *vm) TEST_ASSERT(count, "Allowed PMU event is not counting"); } -static void test_not_member_allow_list(struct kvm_vm *vm) +static void test_not_member_allow_list(struct kvm_vcpu *vcpu) { struct kvm_pmu_event_filter *f = event_filter(KVM_PMU_EVENT_ALLOW); uint64_t count; remove_event(f, INTEL_BR_RETIRED); remove_event(f, AMD_ZEN_BR_RETIRED); - count = test_with_filter(vm, f); + count = test_with_filter(vcpu, f); free(f); if (count) pr_info("%s: Branch instructions retired = %lu (expected 0)\n", @@ -358,25 +357,23 @@ static void test_not_member_allow_list(struct kvm_vm *vm) */ static void test_pmu_config_disable(void (*guest_code)(void)) { + struct kvm_vcpu *vcpu; int r; struct kvm_vm *vm; - struct kvm_enable_cap cap = { 0 }; r = kvm_check_cap(KVM_CAP_PMU_CAPABILITY); if (!(r & KVM_PMU_CAP_DISABLE)) return; - vm = vm_create_without_vcpus(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES); + vm = vm_create(1); - cap.cap = KVM_CAP_PMU_CAPABILITY; - cap.args[0] = KVM_PMU_CAP_DISABLE; - TEST_ASSERT(!vm_enable_cap(vm, &cap), "Failed to set KVM_PMU_CAP_DISABLE."); + vm_enable_cap(vm, KVM_CAP_PMU_CAPABILITY, KVM_PMU_CAP_DISABLE); - vm_vcpu_add_default(vm, VCPU_ID, guest_code); + vcpu = vm_vcpu_add(vm, 0, guest_code); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); - TEST_ASSERT(!sanity_check_pmu(vm), + TEST_ASSERT(!sanity_check_pmu(vcpu), "Guest should not be able to use disabled PMU."); kvm_vm_free(vm); @@ -387,7 +384,7 @@ static void test_pmu_config_disable(void (*guest_code)(void)) * counter per logical processor, an EBX bit vector of length greater * than 5, and EBX[5] clear. */ -static bool check_intel_pmu_leaf(struct kvm_cpuid_entry2 *entry) +static bool check_intel_pmu_leaf(const struct kvm_cpuid_entry2 *entry) { union cpuid10_eax eax = { .full = entry->eax }; union cpuid10_ebx ebx = { .full = entry->ebx }; @@ -403,10 +400,10 @@ static bool check_intel_pmu_leaf(struct kvm_cpuid_entry2 *entry) */ static bool use_intel_pmu(void) { - struct kvm_cpuid_entry2 *entry; + const struct kvm_cpuid_entry2 *entry; - entry = kvm_get_supported_cpuid_index(0xa, 0); - return is_intel_cpu() && entry && check_intel_pmu_leaf(entry); + entry = kvm_get_supported_cpuid_entry(0xa); + return is_intel_cpu() && check_intel_pmu_leaf(entry); } static bool is_zen1(uint32_t eax) @@ -435,10 +432,10 @@ static bool is_zen3(uint32_t eax) */ static bool use_amd_pmu(void) { - struct kvm_cpuid_entry2 *entry; + const struct kvm_cpuid_entry2 *entry; - entry = kvm_get_supported_cpuid_index(1, 0); - return is_amd_cpu() && entry && + entry = kvm_get_supported_cpuid_entry(1); + return is_amd_cpu() && (is_zen1(entry->eax) || is_zen2(entry->eax) || is_zen3(entry->eax)); @@ -446,47 +443,33 @@ static bool use_amd_pmu(void) int main(int argc, char *argv[]) { - void (*guest_code)(void) = NULL; + void (*guest_code)(void); + struct kvm_vcpu *vcpu; struct kvm_vm *vm; - int r; /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - r = kvm_check_cap(KVM_CAP_PMU_EVENT_FILTER); - if (!r) { - print_skip("KVM_CAP_PMU_EVENT_FILTER not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_PMU_EVENT_FILTER)); - if (use_intel_pmu()) - guest_code = intel_guest_code; - else if (use_amd_pmu()) - guest_code = amd_guest_code; + TEST_REQUIRE(use_intel_pmu() || use_amd_pmu()); + guest_code = use_intel_pmu() ? intel_guest_code : amd_guest_code; - if (!guest_code) { - print_skip("Don't know how to test this guest PMU"); - exit(KSFT_SKIP); - } - - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); - if (!sanity_check_pmu(vm)) { - print_skip("Guest PMU is not functional"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(sanity_check_pmu(vcpu)); if (use_amd_pmu()) - test_amd_deny_list(vm); + test_amd_deny_list(vcpu); - test_without_filter(vm); - test_member_deny_list(vm); - test_member_allow_list(vm); - test_not_member_deny_list(vm); - test_not_member_allow_list(vm); + test_without_filter(vcpu); + test_member_deny_list(vcpu); + test_member_allow_list(vcpu); + test_not_member_deny_list(vcpu); + test_not_member_allow_list(vcpu); kvm_vm_free(vm); diff --git a/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c b/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c index ae76436af0cc..b25d7556b638 100644 --- a/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c +++ b/tools/testing/selftests/kvm/x86_64/set_boot_cpu_id.c @@ -16,10 +16,6 @@ #include "processor.h" #include "apic.h" -#define N_VCPU 2 -#define VCPU_ID0 0 -#define VCPU_ID1 1 - static void guest_bsp_vcpu(void *arg) { GUEST_SYNC(1); @@ -38,31 +34,30 @@ static void guest_not_bsp_vcpu(void *arg) GUEST_DONE(); } -static void test_set_boot_busy(struct kvm_vm *vm) +static void test_set_bsp_busy(struct kvm_vcpu *vcpu, const char *msg) { - int res; + int r = __vm_ioctl(vcpu->vm, KVM_SET_BOOT_CPU_ID, + (void *)(unsigned long)vcpu->id); - res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID0); - TEST_ASSERT(res == -1 && errno == EBUSY, - "KVM_SET_BOOT_CPU_ID set while running vm"); + TEST_ASSERT(r == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set %s", msg); } -static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid) +static void run_vcpu(struct kvm_vcpu *vcpu) { struct ucall uc; int stage; for (stage = 0; stage < 2; stage++) { - vcpu_run(vm, vcpuid); + vcpu_run(vcpu); - switch (get_ucall(vm, vcpuid, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx", stage + 1, (ulong)uc.args[1]); - test_set_boot_busy(vm); + test_set_bsp_busy(vcpu, "while running vm"); break; case UCALL_DONE: TEST_ASSERT(stage == 1, @@ -70,91 +65,67 @@ static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid) stage); break; case UCALL_ABORT: - TEST_ASSERT(false, "%s at %s:%ld\n\tvalues: %#lx, %#lx", - (const char *)uc.args[0], __FILE__, - uc.args[1], uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); default: TEST_ASSERT(false, "Unexpected exit: %s", - exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason)); + exit_reason_str(vcpu->run->exit_reason)); } } } -static struct kvm_vm *create_vm(void) +static struct kvm_vm *create_vm(uint32_t nr_vcpus, uint32_t bsp_vcpu_id, + struct kvm_vcpu *vcpus[]) { struct kvm_vm *vm; - uint64_t vcpu_pages = (DEFAULT_STACK_PGS) * 2; - uint64_t extra_pg_pages = vcpu_pages / PTES_PER_MIN_PAGE * N_VCPU; - uint64_t pages = DEFAULT_GUEST_PHY_PAGES + vcpu_pages + extra_pg_pages; + uint32_t i; - pages = vm_adjust_num_guest_pages(VM_MODE_DEFAULT, pages); - vm = vm_create(VM_MODE_DEFAULT, pages, O_RDWR); + vm = vm_create(nr_vcpus); - kvm_vm_elf_load(vm, program_invocation_name); - vm_create_irqchip(vm); + vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *)(unsigned long)bsp_vcpu_id); + for (i = 0; i < nr_vcpus; i++) + vcpus[i] = vm_vcpu_add(vm, i, i == bsp_vcpu_id ? guest_bsp_vcpu : + guest_not_bsp_vcpu); return vm; } -static void add_x86_vcpu(struct kvm_vm *vm, uint32_t vcpuid, bool bsp_code) -{ - if (bsp_code) - vm_vcpu_add_default(vm, vcpuid, guest_bsp_vcpu); - else - vm_vcpu_add_default(vm, vcpuid, guest_not_bsp_vcpu); -} - -static void run_vm_bsp(uint32_t bsp_vcpu) +static void run_vm_bsp(uint32_t bsp_vcpu_id) { + struct kvm_vcpu *vcpus[2]; struct kvm_vm *vm; - bool is_bsp_vcpu1 = bsp_vcpu == VCPU_ID1; - vm = create_vm(); + vm = create_vm(ARRAY_SIZE(vcpus), bsp_vcpu_id, vcpus); - if (is_bsp_vcpu1) - vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1); - - add_x86_vcpu(vm, VCPU_ID0, !is_bsp_vcpu1); - add_x86_vcpu(vm, VCPU_ID1, is_bsp_vcpu1); - - run_vcpu(vm, VCPU_ID0); - run_vcpu(vm, VCPU_ID1); + run_vcpu(vcpus[0]); + run_vcpu(vcpus[1]); kvm_vm_free(vm); } static void check_set_bsp_busy(void) { + struct kvm_vcpu *vcpus[2]; struct kvm_vm *vm; - int res; - vm = create_vm(); + vm = create_vm(ARRAY_SIZE(vcpus), 0, vcpus); - add_x86_vcpu(vm, VCPU_ID0, true); - add_x86_vcpu(vm, VCPU_ID1, false); + test_set_bsp_busy(vcpus[1], "after adding vcpu"); - res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1); - TEST_ASSERT(res == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set after adding vcpu"); + run_vcpu(vcpus[0]); + run_vcpu(vcpus[1]); - run_vcpu(vm, VCPU_ID0); - run_vcpu(vm, VCPU_ID1); - - res = _vm_ioctl(vm, KVM_SET_BOOT_CPU_ID, (void *) VCPU_ID1); - TEST_ASSERT(res == -1 && errno == EBUSY, "KVM_SET_BOOT_CPU_ID set to a terminated vcpu"); + test_set_bsp_busy(vcpus[1], "to a terminated vcpu"); kvm_vm_free(vm); } int main(int argc, char *argv[]) { - if (!kvm_check_cap(KVM_CAP_SET_BOOT_CPU_ID)) { - print_skip("set_boot_cpu_id not available"); - return 0; - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_SET_BOOT_CPU_ID)); - run_vm_bsp(VCPU_ID0); - run_vm_bsp(VCPU_ID1); - run_vm_bsp(VCPU_ID0); + run_vm_bsp(0); + run_vm_bsp(1); + run_vm_bsp(0); check_set_bsp_busy(); } diff --git a/tools/testing/selftests/kvm/x86_64/set_sregs_test.c b/tools/testing/selftests/kvm/x86_64/set_sregs_test.c index 318be0bf77ab..2bb08bf2125d 100644 --- a/tools/testing/selftests/kvm/x86_64/set_sregs_test.c +++ b/tools/testing/selftests/kvm/x86_64/set_sregs_test.c @@ -22,9 +22,7 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 5 - -static void test_cr4_feature_bit(struct kvm_vm *vm, struct kvm_sregs *orig, +static void test_cr4_feature_bit(struct kvm_vcpu *vcpu, struct kvm_sregs *orig, uint64_t feature_bit) { struct kvm_sregs sregs; @@ -37,44 +35,40 @@ static void test_cr4_feature_bit(struct kvm_vm *vm, struct kvm_sregs *orig, memcpy(&sregs, orig, sizeof(sregs)); sregs.cr4 |= feature_bit; - rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); + rc = _vcpu_sregs_set(vcpu, &sregs); TEST_ASSERT(rc, "KVM allowed unsupported CR4 bit (0x%lx)", feature_bit); /* Sanity check that KVM didn't change anything. */ - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); TEST_ASSERT(!memcmp(&sregs, orig, sizeof(sregs)), "KVM modified sregs"); } -static uint64_t calc_cr4_feature_bits(struct kvm_vm *vm) +static uint64_t calc_supported_cr4_feature_bits(void) { - struct kvm_cpuid_entry2 *cpuid_1, *cpuid_7; uint64_t cr4; - cpuid_1 = kvm_get_supported_cpuid_entry(1); - cpuid_7 = kvm_get_supported_cpuid_entry(7); - cr4 = X86_CR4_VME | X86_CR4_PVI | X86_CR4_TSD | X86_CR4_DE | X86_CR4_PSE | X86_CR4_PAE | X86_CR4_MCE | X86_CR4_PGE | X86_CR4_PCE | X86_CR4_OSFXSR | X86_CR4_OSXMMEXCPT; - if (cpuid_7->ecx & CPUID_UMIP) + if (kvm_cpu_has(X86_FEATURE_UMIP)) cr4 |= X86_CR4_UMIP; - if (cpuid_7->ecx & CPUID_LA57) + if (kvm_cpu_has(X86_FEATURE_LA57)) cr4 |= X86_CR4_LA57; - if (cpuid_1->ecx & CPUID_VMX) + if (kvm_cpu_has(X86_FEATURE_VMX)) cr4 |= X86_CR4_VMXE; - if (cpuid_1->ecx & CPUID_SMX) + if (kvm_cpu_has(X86_FEATURE_SMX)) cr4 |= X86_CR4_SMXE; - if (cpuid_7->ebx & CPUID_FSGSBASE) + if (kvm_cpu_has(X86_FEATURE_FSGSBASE)) cr4 |= X86_CR4_FSGSBASE; - if (cpuid_1->ecx & CPUID_PCID) + if (kvm_cpu_has(X86_FEATURE_PCID)) cr4 |= X86_CR4_PCIDE; - if (cpuid_1->ecx & CPUID_XSAVE) + if (kvm_cpu_has(X86_FEATURE_XSAVE)) cr4 |= X86_CR4_OSXSAVE; - if (cpuid_7->ebx & CPUID_SMEP) + if (kvm_cpu_has(X86_FEATURE_SMEP)) cr4 |= X86_CR4_SMEP; - if (cpuid_7->ebx & CPUID_SMAP) + if (kvm_cpu_has(X86_FEATURE_SMAP)) cr4 |= X86_CR4_SMAP; - if (cpuid_7->ecx & CPUID_PKU) + if (kvm_cpu_has(X86_FEATURE_PKU)) cr4 |= X86_CR4_PKE; return cr4; @@ -83,6 +77,7 @@ static uint64_t calc_cr4_feature_bits(struct kvm_vm *vm) int main(int argc, char *argv[]) { struct kvm_sregs sregs; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; uint64_t cr4; int rc; @@ -95,44 +90,44 @@ int main(int argc, char *argv[]) * use it to verify all supported CR4 bits can be set prior to defining * the vCPU model, i.e. without doing KVM_SET_CPUID2. */ - vm = vm_create(VM_MODE_DEFAULT, DEFAULT_GUEST_PHY_PAGES, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); + vm = vm_create_barebones(); + vcpu = __vm_vcpu_add(vm, 0); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); - sregs.cr4 |= calc_cr4_feature_bits(vm); + sregs.cr4 |= calc_supported_cr4_feature_bits(); cr4 = sregs.cr4; - rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); + rc = _vcpu_sregs_set(vcpu, &sregs); TEST_ASSERT(!rc, "Failed to set supported CR4 bits (0x%lx)", cr4); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); TEST_ASSERT(sregs.cr4 == cr4, "sregs.CR4 (0x%llx) != CR4 (0x%lx)", sregs.cr4, cr4); /* Verify all unsupported features are rejected by KVM. */ - test_cr4_feature_bit(vm, &sregs, X86_CR4_UMIP); - test_cr4_feature_bit(vm, &sregs, X86_CR4_LA57); - test_cr4_feature_bit(vm, &sregs, X86_CR4_VMXE); - test_cr4_feature_bit(vm, &sregs, X86_CR4_SMXE); - test_cr4_feature_bit(vm, &sregs, X86_CR4_FSGSBASE); - test_cr4_feature_bit(vm, &sregs, X86_CR4_PCIDE); - test_cr4_feature_bit(vm, &sregs, X86_CR4_OSXSAVE); - test_cr4_feature_bit(vm, &sregs, X86_CR4_SMEP); - test_cr4_feature_bit(vm, &sregs, X86_CR4_SMAP); - test_cr4_feature_bit(vm, &sregs, X86_CR4_PKE); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_UMIP); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_LA57); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_VMXE); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_SMXE); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_FSGSBASE); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_PCIDE); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_OSXSAVE); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_SMEP); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_SMAP); + test_cr4_feature_bit(vcpu, &sregs, X86_CR4_PKE); kvm_vm_free(vm); /* Create a "real" VM and verify APIC_BASE can be set. */ - vm = vm_create_default(VCPU_ID, 0, NULL); + vm = vm_create_with_one_vcpu(&vcpu, NULL); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.apic_base = 1 << 10; - rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); + rc = _vcpu_sregs_set(vcpu, &sregs); TEST_ASSERT(rc, "Set IA32_APIC_BASE to %llx (invalid)", sregs.apic_base); sregs.apic_base = 1 << 11; - rc = _vcpu_sregs_set(vm, VCPU_ID, &sregs); + rc = _vcpu_sregs_set(vcpu, &sregs); TEST_ASSERT(!rc, "Couldn't set IA32_APIC_BASE to %llx (valid)", sregs.apic_base); diff --git a/tools/testing/selftests/kvm/x86_64/sev_migrate_tests.c b/tools/testing/selftests/kvm/x86_64/sev_migrate_tests.c index d1dc1acf997c..c7ef97561038 100644 --- a/tools/testing/selftests/kvm/x86_64/sev_migrate_tests.c +++ b/tools/testing/selftests/kvm/x86_64/sev_migrate_tests.c @@ -12,7 +12,6 @@ #include "processor.h" #include "svm_util.h" #include "kselftest.h" -#include "../lib/kvm_util_internal.h" #define SEV_POLICY_ES 0b100 @@ -54,10 +53,10 @@ static struct kvm_vm *sev_vm_create(bool es) struct kvm_sev_launch_start start = { 0 }; int i; - vm = vm_create(VM_MODE_DEFAULT, 0, O_RDWR); + vm = vm_create_barebones(); sev_ioctl(vm->fd, es ? KVM_SEV_ES_INIT : KVM_SEV_INIT, NULL); for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i) - vm_vcpu_add(vm, i); + __vm_vcpu_add(vm, i); if (es) start.policy |= SEV_POLICY_ES; sev_ioctl(vm->fd, KVM_SEV_LAUNCH_START, &start); @@ -71,32 +70,27 @@ static struct kvm_vm *aux_vm_create(bool with_vcpus) struct kvm_vm *vm; int i; - vm = vm_create(VM_MODE_DEFAULT, 0, O_RDWR); + vm = vm_create_barebones(); if (!with_vcpus) return vm; for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i) - vm_vcpu_add(vm, i); + __vm_vcpu_add(vm, i); return vm; } -static int __sev_migrate_from(int dst_fd, int src_fd) +static int __sev_migrate_from(struct kvm_vm *dst, struct kvm_vm *src) { - struct kvm_enable_cap cap = { - .cap = KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM, - .args = { src_fd } - }; - - return ioctl(dst_fd, KVM_ENABLE_CAP, &cap); + return __vm_enable_cap(dst, KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM, src->fd); } -static void sev_migrate_from(int dst_fd, int src_fd) +static void sev_migrate_from(struct kvm_vm *dst, struct kvm_vm *src) { int ret; - ret = __sev_migrate_from(dst_fd, src_fd); + ret = __sev_migrate_from(dst, src); TEST_ASSERT(!ret, "Migration failed, ret: %d, errno: %d\n", ret, errno); } @@ -111,13 +105,13 @@ static void test_sev_migrate_from(bool es) dst_vms[i] = aux_vm_create(true); /* Initial migration from the src to the first dst. */ - sev_migrate_from(dst_vms[0]->fd, src_vm->fd); + sev_migrate_from(dst_vms[0], src_vm); for (i = 1; i < NR_MIGRATE_TEST_VMS; i++) - sev_migrate_from(dst_vms[i]->fd, dst_vms[i - 1]->fd); + sev_migrate_from(dst_vms[i], dst_vms[i - 1]); /* Migrate the guest back to the original VM. */ - ret = __sev_migrate_from(src_vm->fd, dst_vms[NR_MIGRATE_TEST_VMS - 1]->fd); + ret = __sev_migrate_from(src_vm, dst_vms[NR_MIGRATE_TEST_VMS - 1]); TEST_ASSERT(ret == -1 && errno == EIO, "VM that was migrated from should be dead. ret %d, errno: %d\n", ret, errno); @@ -129,7 +123,7 @@ static void test_sev_migrate_from(bool es) struct locking_thread_input { struct kvm_vm *vm; - int source_fds[NR_LOCK_TESTING_THREADS]; + struct kvm_vm *source_vms[NR_LOCK_TESTING_THREADS]; }; static void *locking_test_thread(void *arg) @@ -139,7 +133,7 @@ static void *locking_test_thread(void *arg) for (i = 0; i < NR_LOCK_TESTING_ITERATIONS; ++i) { j = i % NR_LOCK_TESTING_THREADS; - __sev_migrate_from(input->vm->fd, input->source_fds[j]); + __sev_migrate_from(input->vm, input->source_vms[j]); } return NULL; @@ -153,11 +147,11 @@ static void test_sev_migrate_locking(void) for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i) { input[i].vm = sev_vm_create(/* es= */ false); - input[0].source_fds[i] = input[i].vm->fd; + input[0].source_vms[i] = input[i].vm; } for (i = 1; i < NR_LOCK_TESTING_THREADS; ++i) - memcpy(input[i].source_fds, input[0].source_fds, - sizeof(input[i].source_fds)); + memcpy(input[i].source_vms, input[0].source_vms, + sizeof(input[i].source_vms)); for (i = 0; i < NR_LOCK_TESTING_THREADS; ++i) pthread_create(&pt[i], NULL, locking_test_thread, &input[i]); @@ -174,9 +168,9 @@ static void test_sev_migrate_parameters(void) *sev_es_vm_no_vmsa; int ret; - vm_no_vcpu = vm_create(VM_MODE_DEFAULT, 0, O_RDWR); + vm_no_vcpu = vm_create_barebones(); vm_no_sev = aux_vm_create(true); - ret = __sev_migrate_from(vm_no_vcpu->fd, vm_no_sev->fd); + ret = __sev_migrate_from(vm_no_vcpu, vm_no_sev); TEST_ASSERT(ret == -1 && errno == EINVAL, "Migrations require SEV enabled. ret %d, errno: %d\n", ret, errno); @@ -186,29 +180,29 @@ static void test_sev_migrate_parameters(void) sev_vm = sev_vm_create(/* es= */ false); sev_es_vm = sev_vm_create(/* es= */ true); - sev_es_vm_no_vmsa = vm_create(VM_MODE_DEFAULT, 0, O_RDWR); + sev_es_vm_no_vmsa = vm_create_barebones(); sev_ioctl(sev_es_vm_no_vmsa->fd, KVM_SEV_ES_INIT, NULL); - vm_vcpu_add(sev_es_vm_no_vmsa, 1); + __vm_vcpu_add(sev_es_vm_no_vmsa, 1); - ret = __sev_migrate_from(sev_vm->fd, sev_es_vm->fd); + ret = __sev_migrate_from(sev_vm, sev_es_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "Should not be able migrate to SEV enabled VM. ret: %d, errno: %d\n", ret, errno); - ret = __sev_migrate_from(sev_es_vm->fd, sev_vm->fd); + ret = __sev_migrate_from(sev_es_vm, sev_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "Should not be able migrate to SEV-ES enabled VM. ret: %d, errno: %d\n", ret, errno); - ret = __sev_migrate_from(vm_no_vcpu->fd, sev_es_vm->fd); + ret = __sev_migrate_from(vm_no_vcpu, sev_es_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "SEV-ES migrations require same number of vCPUS. ret: %d, errno: %d\n", ret, errno); - ret = __sev_migrate_from(vm_no_vcpu->fd, sev_es_vm_no_vmsa->fd); + ret = __sev_migrate_from(vm_no_vcpu, sev_es_vm_no_vmsa); TEST_ASSERT( ret == -1 && errno == EINVAL, "SEV-ES migrations require UPDATE_VMSA. ret %d, errno: %d\n", @@ -222,22 +216,17 @@ out: kvm_vm_free(vm_no_sev); } -static int __sev_mirror_create(int dst_fd, int src_fd) +static int __sev_mirror_create(struct kvm_vm *dst, struct kvm_vm *src) { - struct kvm_enable_cap cap = { - .cap = KVM_CAP_VM_COPY_ENC_CONTEXT_FROM, - .args = { src_fd } - }; - - return ioctl(dst_fd, KVM_ENABLE_CAP, &cap); + return __vm_enable_cap(dst, KVM_CAP_VM_COPY_ENC_CONTEXT_FROM, src->fd); } -static void sev_mirror_create(int dst_fd, int src_fd) +static void sev_mirror_create(struct kvm_vm *dst, struct kvm_vm *src) { int ret; - ret = __sev_mirror_create(dst_fd, src_fd); + ret = __sev_mirror_create(dst, src); TEST_ASSERT(!ret, "Copying context failed, ret: %d, errno: %d\n", ret, errno); } @@ -285,11 +274,11 @@ static void test_sev_mirror(bool es) src_vm = sev_vm_create(es); dst_vm = aux_vm_create(false); - sev_mirror_create(dst_vm->fd, src_vm->fd); + sev_mirror_create(dst_vm, src_vm); /* Check that we can complete creation of the mirror VM. */ for (i = 0; i < NR_MIGRATE_TEST_VCPUS; ++i) - vm_vcpu_add(dst_vm, i); + __vm_vcpu_add(dst_vm, i); if (es) sev_ioctl(dst_vm->fd, KVM_SEV_LAUNCH_UPDATE_VMSA, NULL); @@ -309,18 +298,18 @@ static void test_sev_mirror_parameters(void) vm_with_vcpu = aux_vm_create(true); vm_no_vcpu = aux_vm_create(false); - ret = __sev_mirror_create(sev_vm->fd, sev_vm->fd); + ret = __sev_mirror_create(sev_vm, sev_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "Should not be able copy context to self. ret: %d, errno: %d\n", ret, errno); - ret = __sev_mirror_create(vm_no_vcpu->fd, vm_with_vcpu->fd); + ret = __sev_mirror_create(vm_no_vcpu, vm_with_vcpu); TEST_ASSERT(ret == -1 && errno == EINVAL, "Copy context requires SEV enabled. ret %d, errno: %d\n", ret, errno); - ret = __sev_mirror_create(vm_with_vcpu->fd, sev_vm->fd); + ret = __sev_mirror_create(vm_with_vcpu, sev_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "SEV copy context requires no vCPUS on the destination. ret: %d, errno: %d\n", @@ -330,13 +319,13 @@ static void test_sev_mirror_parameters(void) goto out; sev_es_vm = sev_vm_create(/* es= */ true); - ret = __sev_mirror_create(sev_vm->fd, sev_es_vm->fd); + ret = __sev_mirror_create(sev_vm, sev_es_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "Should not be able copy context to SEV enabled VM. ret: %d, errno: %d\n", ret, errno); - ret = __sev_mirror_create(sev_es_vm->fd, sev_vm->fd); + ret = __sev_mirror_create(sev_es_vm, sev_vm); TEST_ASSERT( ret == -1 && errno == EINVAL, "Should not be able copy context to SEV-ES enabled VM. ret: %d, errno: %d\n", @@ -364,16 +353,16 @@ static void test_sev_move_copy(void) dst2_mirror_vm = aux_vm_create(false); dst3_mirror_vm = aux_vm_create(false); - sev_mirror_create(mirror_vm->fd, sev_vm->fd); + sev_mirror_create(mirror_vm, sev_vm); - sev_migrate_from(dst_mirror_vm->fd, mirror_vm->fd); - sev_migrate_from(dst_vm->fd, sev_vm->fd); + sev_migrate_from(dst_mirror_vm, mirror_vm); + sev_migrate_from(dst_vm, sev_vm); - sev_migrate_from(dst2_vm->fd, dst_vm->fd); - sev_migrate_from(dst2_mirror_vm->fd, dst_mirror_vm->fd); + sev_migrate_from(dst2_vm, dst_vm); + sev_migrate_from(dst2_mirror_vm, dst_mirror_vm); - sev_migrate_from(dst3_mirror_vm->fd, dst2_mirror_vm->fd); - sev_migrate_from(dst3_vm->fd, dst2_vm->fd); + sev_migrate_from(dst3_mirror_vm, dst2_mirror_vm); + sev_migrate_from(dst3_vm, dst2_vm); kvm_vm_free(dst_vm); kvm_vm_free(sev_vm); @@ -393,10 +382,10 @@ static void test_sev_move_copy(void) mirror_vm = aux_vm_create(false); dst_mirror_vm = aux_vm_create(false); - sev_mirror_create(mirror_vm->fd, sev_vm->fd); + sev_mirror_create(mirror_vm, sev_vm); - sev_migrate_from(dst_mirror_vm->fd, mirror_vm->fd); - sev_migrate_from(dst_vm->fd, sev_vm->fd); + sev_migrate_from(dst_mirror_vm, mirror_vm); + sev_migrate_from(dst_vm, sev_vm); kvm_vm_free(mirror_vm); kvm_vm_free(dst_mirror_vm); @@ -404,41 +393,25 @@ static void test_sev_move_copy(void) kvm_vm_free(sev_vm); } -#define X86_FEATURE_SEV (1 << 1) -#define X86_FEATURE_SEV_ES (1 << 3) - int main(int argc, char *argv[]) { - struct kvm_cpuid_entry2 *cpuid; + TEST_REQUIRE(kvm_has_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM)); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)); - if (!kvm_check_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM) && - !kvm_check_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) { - print_skip("Capabilities not available"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SEV)); - cpuid = kvm_get_supported_cpuid_entry(0x80000000); - if (cpuid->eax < 0x8000001f) { - print_skip("AMD memory encryption not available"); - exit(KSFT_SKIP); - } - cpuid = kvm_get_supported_cpuid_entry(0x8000001f); - if (!(cpuid->eax & X86_FEATURE_SEV)) { - print_skip("AMD SEV not available"); - exit(KSFT_SKIP); - } - have_sev_es = !!(cpuid->eax & X86_FEATURE_SEV_ES); + have_sev_es = kvm_cpu_has(X86_FEATURE_SEV_ES); - if (kvm_check_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM)) { + if (kvm_has_cap(KVM_CAP_VM_MOVE_ENC_CONTEXT_FROM)) { test_sev_migrate_from(/* es= */ false); if (have_sev_es) test_sev_migrate_from(/* es= */ true); test_sev_migrate_locking(); test_sev_migrate_parameters(); - if (kvm_check_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) + if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) test_sev_move_copy(); } - if (kvm_check_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) { + if (kvm_has_cap(KVM_CAP_VM_COPY_ENC_CONTEXT_FROM)) { test_sev_mirror(/* es= */ false); if (have_sev_es) test_sev_mirror(/* es= */ true); diff --git a/tools/testing/selftests/kvm/x86_64/smm_test.c b/tools/testing/selftests/kvm/x86_64/smm_test.c index b4e0c860769e..1f136a81858e 100644 --- a/tools/testing/selftests/kvm/x86_64/smm_test.c +++ b/tools/testing/selftests/kvm/x86_64/smm_test.c @@ -19,8 +19,6 @@ #include "vmx.h" #include "svm_util.h" -#define VCPU_ID 1 - #define SMRAM_SIZE 65536 #define SMRAM_MEMSLOT ((1 << 16) | 1) #define SMRAM_PAGES (SMRAM_SIZE / PAGE_SIZE) @@ -85,7 +83,7 @@ static void guest_code(void *arg) sync_with_host(4); if (arg) { - if (cpu_has_svm()) { + if (this_cpu_has(X86_FEATURE_SVM)) { generic_svm_setup(svm, l2_guest_code, &l2_guest_stack[L2_GUEST_STACK_SIZE]); } else { @@ -101,7 +99,7 @@ static void guest_code(void *arg) sync_with_host(7); - if (cpu_has_svm()) { + if (this_cpu_has(X86_FEATURE_SVM)) { run_guest(svm->vmcb, svm->vmcb_gpa); run_guest(svm->vmcb, svm->vmcb_gpa); } else { @@ -116,22 +114,23 @@ static void guest_code(void *arg) sync_with_host(DONE); } -void inject_smi(struct kvm_vm *vm) +void inject_smi(struct kvm_vcpu *vcpu) { struct kvm_vcpu_events events; - vcpu_events_get(vm, VCPU_ID, &events); + vcpu_events_get(vcpu, &events); events.smi.pending = 1; events.flags |= KVM_VCPUEVENT_VALID_SMM; - vcpu_events_set(vm, VCPU_ID, &events); + vcpu_events_set(vcpu, &events); } int main(int argc, char *argv[]) { vm_vaddr_t nested_gva = 0; + struct kvm_vcpu *vcpu; struct kvm_regs regs; struct kvm_vm *vm; struct kvm_run *run; @@ -139,9 +138,9 @@ int main(int argc, char *argv[]) int stage, stage_reported; /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - run = vcpu_state(vm, VCPU_ID); + run = vcpu->run; vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, SMRAM_GPA, SMRAM_MEMSLOT, SMRAM_PAGES, 0); @@ -152,29 +151,29 @@ int main(int argc, char *argv[]) memcpy(addr_gpa2hva(vm, SMRAM_GPA) + 0x8000, smi_handler, sizeof(smi_handler)); - vcpu_set_msr(vm, VCPU_ID, MSR_IA32_SMBASE, SMRAM_GPA); + vcpu_set_msr(vcpu, MSR_IA32_SMBASE, SMRAM_GPA); - if (kvm_check_cap(KVM_CAP_NESTED_STATE)) { - if (nested_svm_supported()) + if (kvm_has_cap(KVM_CAP_NESTED_STATE)) { + if (kvm_cpu_has(X86_FEATURE_SVM)) vcpu_alloc_svm(vm, &nested_gva); - else if (nested_vmx_supported()) + else if (kvm_cpu_has(X86_FEATURE_VMX)) vcpu_alloc_vmx(vm, &nested_gva); } if (!nested_gva) pr_info("will skip SMM test with VMX enabled\n"); - vcpu_args_set(vm, VCPU_ID, 1, nested_gva); + vcpu_args_set(vcpu, 1, nested_gva); for (stage = 1;; stage++) { - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); memset(®s, 0, sizeof(regs)); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); stage_reported = regs.rax & 0xff; @@ -191,7 +190,7 @@ int main(int argc, char *argv[]) * return from it. Do not perform save/restore while in SMM yet. */ if (stage == 8) { - inject_smi(vm); + inject_smi(vcpu); continue; } @@ -200,15 +199,14 @@ int main(int argc, char *argv[]) * during L2 execution. */ if (stage == 10) - inject_smi(vm); + inject_smi(vcpu); - state = vcpu_save_state(vm, VCPU_ID); + state = vcpu_save_state(vcpu); kvm_vm_release(vm); - kvm_vm_restart(vm, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); - vcpu_load_state(vm, VCPU_ID, state); - run = vcpu_state(vm, VCPU_ID); + + vcpu = vm_recreate_with_one_vcpu(vm); + vcpu_load_state(vcpu, state); + run = vcpu->run; kvm_x86_state_cleanup(state); } diff --git a/tools/testing/selftests/kvm/x86_64/state_test.c b/tools/testing/selftests/kvm/x86_64/state_test.c index 2e0a92da8ff5..ea578971fb9f 100644 --- a/tools/testing/selftests/kvm/x86_64/state_test.c +++ b/tools/testing/selftests/kvm/x86_64/state_test.c @@ -20,7 +20,6 @@ #include "vmx.h" #include "svm_util.h" -#define VCPU_ID 5 #define L2_GUEST_STACK_SIZE 256 void svm_l2_guest_code(void) @@ -143,7 +142,7 @@ static void __attribute__((__flatten__)) guest_code(void *arg) GUEST_SYNC(2); if (arg) { - if (cpu_has_svm()) + if (this_cpu_has(X86_FEATURE_SVM)) svm_l1_guest_code(arg); else vmx_l1_guest_code(arg); @@ -157,6 +156,7 @@ int main(int argc, char *argv[]) vm_vaddr_t nested_gva = 0; struct kvm_regs regs1, regs2; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct kvm_x86_state *state; @@ -164,34 +164,33 @@ int main(int argc, char *argv[]) int stage; /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; - vcpu_regs_get(vm, VCPU_ID, ®s1); + vcpu_regs_get(vcpu, ®s1); - if (kvm_check_cap(KVM_CAP_NESTED_STATE)) { - if (nested_svm_supported()) + if (kvm_has_cap(KVM_CAP_NESTED_STATE)) { + if (kvm_cpu_has(X86_FEATURE_SVM)) vcpu_alloc_svm(vm, &nested_gva); - else if (nested_vmx_supported()) + else if (kvm_cpu_has(X86_FEATURE_VMX)) vcpu_alloc_vmx(vm, &nested_gva); } if (!nested_gva) pr_info("will skip nested state checks\n"); - vcpu_args_set(vm, VCPU_ID, 1, nested_gva); + vcpu_args_set(vcpu, 1, nested_gva); for (stage = 1;; stage++) { - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; @@ -206,22 +205,20 @@ int main(int argc, char *argv[]) uc.args[1] == stage, "Stage %d: Unexpected register values vmexit, got %lx", stage, (ulong)uc.args[1]); - state = vcpu_save_state(vm, VCPU_ID); + state = vcpu_save_state(vcpu); memset(®s1, 0, sizeof(regs1)); - vcpu_regs_get(vm, VCPU_ID, ®s1); + vcpu_regs_get(vcpu, ®s1); kvm_vm_release(vm); /* Restore state in a new VM. */ - kvm_vm_restart(vm, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); - vcpu_load_state(vm, VCPU_ID, state); - run = vcpu_state(vm, VCPU_ID); + vcpu = vm_recreate_with_one_vcpu(vm); + vcpu_load_state(vcpu, state); + run = vcpu->run; kvm_x86_state_cleanup(state); memset(®s2, 0, sizeof(regs2)); - vcpu_regs_get(vm, VCPU_ID, ®s2); + vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", (ulong) regs2.rdi, (ulong) regs2.rsi); diff --git a/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c b/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c index 30a81038df46..4a07ba227b99 100644 --- a/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c +++ b/tools/testing/selftests/kvm/x86_64/svm_int_ctl_test.c @@ -13,10 +13,6 @@ #include "svm_util.h" #include "apic.h" -#define VCPU_ID 0 - -static struct kvm_vm *vm; - bool vintr_irq_called; bool intr_irq_called; @@ -88,33 +84,36 @@ static void l1_guest_code(struct svm_test_data *svm) int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; + struct kvm_run *run; vm_vaddr_t svm_gva; + struct kvm_vm *vm; + struct ucall uc; - nested_svm_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); vm_install_exception_handler(vm, VINTR_IRQ_NUMBER, vintr_irq_handler); vm_install_exception_handler(vm, INTR_IRQ_NUMBER, intr_irq_handler); vcpu_alloc_svm(vm, &svm_gva); - vcpu_args_set(vm, VCPU_ID, 1, svm_gva); + vcpu_args_set(vcpu, 1, svm_gva); - struct kvm_run *run = vcpu_state(vm, VCPU_ID); - struct ucall uc; + run = vcpu->run; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); break; /* NOT REACHED */ case UCALL_DONE: diff --git a/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c new file mode 100644 index 000000000000..e637d7736012 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/svm_nested_soft_inject_test.c @@ -0,0 +1,211 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (C) 2022 Oracle and/or its affiliates. + * + * Based on: + * svm_int_ctl_test + * + * Copyright (C) 2021, Red Hat, Inc. + * + */ + +#include +#include +#include +#include "apic.h" +#include "kvm_util.h" +#include "processor.h" +#include "svm_util.h" +#include "test_util.h" + +#define INT_NR 0x20 + +static_assert(ATOMIC_INT_LOCK_FREE == 2, "atomic int is not lockless"); + +static unsigned int bp_fired; +static void guest_bp_handler(struct ex_regs *regs) +{ + bp_fired++; +} + +static unsigned int int_fired; +static void l2_guest_code_int(void); + +static void guest_int_handler(struct ex_regs *regs) +{ + int_fired++; + GUEST_ASSERT_2(regs->rip == (unsigned long)l2_guest_code_int, + regs->rip, (unsigned long)l2_guest_code_int); +} + +static void l2_guest_code_int(void) +{ + GUEST_ASSERT_1(int_fired == 1, int_fired); + vmmcall(); + ud2(); + + GUEST_ASSERT_1(bp_fired == 1, bp_fired); + hlt(); +} + +static atomic_int nmi_stage; +#define nmi_stage_get() atomic_load_explicit(&nmi_stage, memory_order_acquire) +#define nmi_stage_inc() atomic_fetch_add_explicit(&nmi_stage, 1, memory_order_acq_rel) +static void guest_nmi_handler(struct ex_regs *regs) +{ + nmi_stage_inc(); + + if (nmi_stage_get() == 1) { + vmmcall(); + GUEST_ASSERT(false); + } else { + GUEST_ASSERT_1(nmi_stage_get() == 3, nmi_stage_get()); + GUEST_DONE(); + } +} + +static void l2_guest_code_nmi(void) +{ + ud2(); +} + +static void l1_guest_code(struct svm_test_data *svm, uint64_t is_nmi, uint64_t idt_alt) +{ + #define L2_GUEST_STACK_SIZE 64 + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + struct vmcb *vmcb = svm->vmcb; + + if (is_nmi) + x2apic_enable(); + + /* Prepare for L2 execution. */ + generic_svm_setup(svm, + is_nmi ? l2_guest_code_nmi : l2_guest_code_int, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + vmcb->control.intercept_exceptions |= BIT(PF_VECTOR) | BIT(UD_VECTOR); + vmcb->control.intercept |= BIT(INTERCEPT_NMI) | BIT(INTERCEPT_HLT); + + if (is_nmi) { + vmcb->control.event_inj = SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_NMI; + } else { + vmcb->control.event_inj = INT_NR | SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_SOFT; + /* The return address pushed on stack */ + vmcb->control.next_rip = vmcb->save.rip; + } + + run_guest(vmcb, svm->vmcb_gpa); + GUEST_ASSERT_3(vmcb->control.exit_code == SVM_EXIT_VMMCALL, + vmcb->control.exit_code, + vmcb->control.exit_info_1, vmcb->control.exit_info_2); + + if (is_nmi) { + clgi(); + x2apic_write_reg(APIC_ICR, APIC_DEST_SELF | APIC_INT_ASSERT | APIC_DM_NMI); + + GUEST_ASSERT_1(nmi_stage_get() == 1, nmi_stage_get()); + nmi_stage_inc(); + + stgi(); + /* self-NMI happens here */ + while (true) + cpu_relax(); + } + + /* Skip over VMMCALL */ + vmcb->save.rip += 3; + + /* Switch to alternate IDT to cause intervening NPF again */ + vmcb->save.idtr.base = idt_alt; + vmcb->control.clean = 0; /* &= ~BIT(VMCB_DT) would be enough */ + + vmcb->control.event_inj = BP_VECTOR | SVM_EVTINJ_VALID | SVM_EVTINJ_TYPE_EXEPT; + /* The return address pushed on stack, skip over UD2 */ + vmcb->control.next_rip = vmcb->save.rip + 2; + + run_guest(vmcb, svm->vmcb_gpa); + GUEST_ASSERT_3(vmcb->control.exit_code == SVM_EXIT_HLT, + vmcb->control.exit_code, + vmcb->control.exit_info_1, vmcb->control.exit_info_2); + + GUEST_DONE(); +} + +static void run_test(bool is_nmi) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + vm_vaddr_t svm_gva; + vm_vaddr_t idt_alt_vm; + struct kvm_guest_debug debug; + + pr_info("Running %s test\n", is_nmi ? "NMI" : "soft int"); + + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(vcpu); + + vm_install_exception_handler(vm, NMI_VECTOR, guest_nmi_handler); + vm_install_exception_handler(vm, BP_VECTOR, guest_bp_handler); + vm_install_exception_handler(vm, INT_NR, guest_int_handler); + + vcpu_alloc_svm(vm, &svm_gva); + + if (!is_nmi) { + void *idt, *idt_alt; + + idt_alt_vm = vm_vaddr_alloc_page(vm); + idt_alt = addr_gva2hva(vm, idt_alt_vm); + idt = addr_gva2hva(vm, vm->idt); + memcpy(idt_alt, idt, getpagesize()); + } else { + idt_alt_vm = 0; + } + vcpu_args_set(vcpu, 3, svm_gva, (uint64_t)is_nmi, (uint64_t)idt_alt_vm); + + memset(&debug, 0, sizeof(debug)); + vcpu_guest_debug_set(vcpu, &debug); + + struct kvm_run *run = vcpu->run; + struct ucall uc; + + alarm(2); + vcpu_run(vcpu); + alarm(0); + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", + run->exit_reason, + exit_reason_str(run->exit_reason)); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_ABORT: + REPORT_GUEST_ASSERT_3(uc, "vals = 0x%lx 0x%lx 0x%lx"); + break; + /* NOT REACHED */ + case UCALL_DONE: + goto done; + default: + TEST_FAIL("Unknown ucall 0x%lx.", uc.cmd); + } +done: + kvm_vm_free(vm); +} + +int main(int argc, char *argv[]) +{ + /* Tell stdout not to buffer its content */ + setbuf(stdout, NULL); + + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); + + TEST_ASSERT(kvm_cpu_has(X86_FEATURE_NRIPS), + "KVM with nSVM is supposed to unconditionally advertise nRIP Save"); + + atomic_init(&nmi_stage, 0); + + run_test(false); + run_test(true); + + return 0; +} diff --git a/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c b/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c index be2ca157485b..c3ac45df7483 100644 --- a/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c +++ b/tools/testing/selftests/kvm/x86_64/svm_vmcall_test.c @@ -12,10 +12,6 @@ #include "processor.h" #include "svm_util.h" -#define VCPU_ID 5 - -static struct kvm_vm *vm; - static void l2_guest_code(struct svm_test_data *svm) { __asm__ __volatile__("vmcall"); @@ -39,28 +35,30 @@ static void l1_guest_code(struct svm_test_data *svm) int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; vm_vaddr_t svm_gva; + struct kvm_vm *vm; - nested_svm_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_SVM)); - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); vcpu_alloc_svm(vm, &svm_gva); - vcpu_args_set(vm, VCPU_ID, 1, svm_gva); + vcpu_args_set(vcpu, 1, svm_gva); for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; diff --git a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c index fc03a150278d..9b6db0b0b13e 100644 --- a/tools/testing/selftests/kvm/x86_64/sync_regs_test.c +++ b/tools/testing/selftests/kvm/x86_64/sync_regs_test.c @@ -20,8 +20,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 5 - #define UCALL_PIO_PORT ((uint16_t)0x1000) struct ucall uc_none = { @@ -84,6 +82,7 @@ static void compare_vcpu_events(struct kvm_vcpu_events *left, int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct kvm_regs regs; @@ -95,66 +94,59 @@ int main(int argc, char *argv[]) setbuf(stdout, NULL); cap = kvm_check_cap(KVM_CAP_SYNC_REGS); - if ((cap & TEST_SYNC_FIELDS) != TEST_SYNC_FIELDS) { - print_skip("KVM_CAP_SYNC_REGS not supported"); - exit(KSFT_SKIP); - } - if ((cap & INVALID_SYNC_FIELD) != 0) { - print_skip("The \"invalid\" field is not invalid"); - exit(KSFT_SKIP); - } + TEST_REQUIRE((cap & TEST_SYNC_FIELDS) == TEST_SYNC_FIELDS); + TEST_REQUIRE(!(cap & INVALID_SYNC_FIELD)); - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - run = vcpu_state(vm, VCPU_ID); + run = vcpu->run; /* Request reading invalid register set from VCPU. */ run->kvm_valid_regs = INVALID_SYNC_FIELD; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0; + run->kvm_valid_regs = 0; run->kvm_valid_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_valid_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_valid_regs = 0; + run->kvm_valid_regs = 0; /* Request setting invalid register set into VCPU. */ run->kvm_dirty_regs = INVALID_SYNC_FIELD; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0; + run->kvm_dirty_regs = 0; run->kvm_dirty_regs = INVALID_SYNC_FIELD | TEST_SYNC_FIELDS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(rv < 0 && errno == EINVAL, "Invalid kvm_dirty_regs did not cause expected KVM_RUN error: %d\n", rv); - vcpu_state(vm, VCPU_ID)->kvm_dirty_regs = 0; + run->kvm_dirty_regs = 0; /* Request and verify all valid register sets. */ /* TODO: BUILD TIME CHECK: TEST_ASSERT(KVM_SYNC_X86_NUM_FIELDS != 3); */ run->kvm_valid_regs = TEST_SYNC_FIELDS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, exit_reason_str(run->exit_reason)); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); compare_regs(®s, &run->s.regs.regs); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); compare_sregs(&sregs, &run->s.regs.sregs); - vcpu_events_get(vm, VCPU_ID, &events); + vcpu_events_get(vcpu, &events); compare_vcpu_events(&events, &run->s.regs.events); /* Set and verify various register values. */ @@ -164,7 +156,7 @@ int main(int argc, char *argv[]) run->kvm_valid_regs = TEST_SYNC_FIELDS; run->kvm_dirty_regs = KVM_SYNC_X86_REGS | KVM_SYNC_X86_SREGS; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, @@ -176,13 +168,13 @@ int main(int argc, char *argv[]) "apic_base sync regs value incorrect 0x%llx.", run->s.regs.sregs.apic_base); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); compare_regs(®s, &run->s.regs.regs); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); compare_sregs(&sregs, &run->s.regs.sregs); - vcpu_events_get(vm, VCPU_ID, &events); + vcpu_events_get(vcpu, &events); compare_vcpu_events(&events, &run->s.regs.events); /* Clear kvm_dirty_regs bits, verify new s.regs values are @@ -191,7 +183,7 @@ int main(int argc, char *argv[]) run->kvm_valid_regs = TEST_SYNC_FIELDS; run->kvm_dirty_regs = 0; run->s.regs.regs.rbx = 0xDEADBEEF; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, @@ -208,8 +200,8 @@ int main(int argc, char *argv[]) run->kvm_dirty_regs = 0; run->s.regs.regs.rbx = 0xAAAA; regs.rbx = 0xBAC0; - vcpu_regs_set(vm, VCPU_ID, ®s); - rv = _vcpu_run(vm, VCPU_ID); + vcpu_regs_set(vcpu, ®s); + rv = _vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, @@ -217,7 +209,7 @@ int main(int argc, char *argv[]) TEST_ASSERT(run->s.regs.regs.rbx == 0xAAAA, "rbx sync regs value incorrect 0x%llx.", run->s.regs.regs.rbx); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); TEST_ASSERT(regs.rbx == 0xBAC0 + 1, "rbx guest value incorrect 0x%llx.", regs.rbx); @@ -229,7 +221,7 @@ int main(int argc, char *argv[]) run->kvm_valid_regs = 0; run->kvm_dirty_regs = TEST_SYNC_FIELDS; run->s.regs.regs.rbx = 0xBBBB; - rv = _vcpu_run(vm, VCPU_ID); + rv = _vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, @@ -237,7 +229,7 @@ int main(int argc, char *argv[]) TEST_ASSERT(run->s.regs.regs.rbx == 0xBBBB, "rbx sync regs value incorrect 0x%llx.", run->s.regs.regs.rbx); - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); TEST_ASSERT(regs.rbx == 0xBBBB + 1, "rbx guest value incorrect 0x%llx.", regs.rbx); diff --git a/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c b/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c new file mode 100644 index 000000000000..70b44f0b52fe --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/triple_fault_event_test.c @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: GPL-2.0-only +#include "test_util.h" +#include "kvm_util.h" +#include "processor.h" +#include "vmx.h" + +#include +#include + +#include "kselftest.h" + +#define ARBITRARY_IO_PORT 0x2000 + +/* The virtual machine object. */ +static struct kvm_vm *vm; + +static void l2_guest_code(void) +{ + asm volatile("inb %%dx, %%al" + : : [port] "d" (ARBITRARY_IO_PORT) : "rax"); +} + +void l1_guest_code(struct vmx_pages *vmx) +{ +#define L2_GUEST_STACK_SIZE 64 + unsigned long l2_guest_stack[L2_GUEST_STACK_SIZE]; + + GUEST_ASSERT(vmx->vmcs_gpa); + GUEST_ASSERT(prepare_for_vmx_operation(vmx)); + GUEST_ASSERT(load_vmcs(vmx)); + + prepare_vmcs(vmx, l2_guest_code, + &l2_guest_stack[L2_GUEST_STACK_SIZE]); + + GUEST_ASSERT(!vmlaunch()); + /* L2 should triple fault after a triple fault event injected. */ + GUEST_ASSERT(vmreadz(VM_EXIT_REASON) == EXIT_REASON_TRIPLE_FAULT); + GUEST_DONE(); +} + +int main(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_run *run; + struct kvm_vcpu_events events; + vm_vaddr_t vmx_pages_gva; + struct ucall uc; + + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_X86_TRIPLE_FAULT_EVENT)); + + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + vm_enable_cap(vm, KVM_CAP_X86_TRIPLE_FAULT_EVENT, 1); + + run = vcpu->run; + vcpu_alloc_vmx(vm, &vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); + vcpu_run(vcpu); + + TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, + "Expected KVM_EXIT_IO, got: %u (%s)\n", + run->exit_reason, exit_reason_str(run->exit_reason)); + TEST_ASSERT(run->io.port == ARBITRARY_IO_PORT, + "Expected IN from port %d from L2, got port %d", + ARBITRARY_IO_PORT, run->io.port); + vcpu_events_get(vcpu, &events); + events.flags |= KVM_VCPUEVENT_VALID_TRIPLE_FAULT; + events.triple_fault.pending = true; + vcpu_events_set(vcpu, &events); + run->immediate_exit = true; + vcpu_run_complete_io(vcpu); + + vcpu_events_get(vcpu, &events); + TEST_ASSERT(events.flags & KVM_VCPUEVENT_VALID_TRIPLE_FAULT, + "Triple fault event invalid"); + TEST_ASSERT(events.triple_fault.pending, + "No triple fault pending"); + vcpu_run(vcpu); + + switch (get_ucall(vcpu, &uc)) { + case UCALL_DONE: + break; + case UCALL_ABORT: + REPORT_GUEST_ASSERT(uc); + default: + TEST_FAIL("Unexpected ucall: %lu", uc.cmd); + } + +} diff --git a/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c b/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c index a426078b16a3..22d366c697f7 100644 --- a/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c +++ b/tools/testing/selftests/kvm/x86_64/tsc_msrs_test.c @@ -9,14 +9,12 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 0 - #define UNITY (1ull << 30) #define HOST_ADJUST (UNITY * 64) #define GUEST_STEP (UNITY * 4) #define ROUND(x) ((x + UNITY / 2) & -UNITY) #define rounded_rdmsr(x) ROUND(rdmsr(x)) -#define rounded_host_rdmsr(x) ROUND(vcpu_get_msr(vm, 0, x)) +#define rounded_host_rdmsr(x) ROUND(vcpu_get_msr(vcpu, x)) static void guest_code(void) { @@ -66,15 +64,13 @@ static void guest_code(void) GUEST_DONE(); } -static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid, int stage) +static void run_vcpu(struct kvm_vcpu *vcpu, int stage) { struct ucall uc; - vcpu_args_set(vm, vcpuid, 1, vcpuid); + vcpu_run(vcpu); - vcpu_ioctl(vm, vcpuid, KVM_RUN, NULL); - - switch (get_ucall(vm, vcpuid, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: TEST_ASSERT(!strcmp((const char *)uc.args[0], "hello") && uc.args[1] == stage + 1, "Stage %d: Unexpected register values vmexit, got %lx", @@ -83,34 +79,33 @@ static void run_vcpu(struct kvm_vm *vm, uint32_t vcpuid, int stage) case UCALL_DONE: return; case UCALL_ABORT: - TEST_ASSERT(false, "%s at %s:%ld\n" \ - "\tvalues: %#lx, %#lx", (const char *)uc.args[0], - __FILE__, uc.args[1], uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT_2(uc, "values: %#lx, %#lx"); default: TEST_ASSERT(false, "Unexpected exit: %s", - exit_reason_str(vcpu_state(vm, vcpuid)->exit_reason)); + exit_reason_str(vcpu->run->exit_reason)); } } int main(void) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; uint64_t val; - vm = vm_create_default(VCPU_ID, 0, guest_code); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); val = 0; ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* Guest: writes to MSR_IA32_TSC affect both MSRs. */ - run_vcpu(vm, VCPU_ID, 1); + run_vcpu(vcpu, 1); val = 1ull * GUEST_STEP; ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); /* Guest: writes to MSR_IA32_TSC_ADJUST affect both MSRs. */ - run_vcpu(vm, VCPU_ID, 2); + run_vcpu(vcpu, 2); val = 2ull * GUEST_STEP; ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); @@ -119,18 +114,18 @@ int main(void) * Host: writes to MSR_IA32_TSC set the host-side offset * and therefore do not change MSR_IA32_TSC_ADJUST. */ - vcpu_set_msr(vm, 0, MSR_IA32_TSC, HOST_ADJUST + val); + vcpu_set_msr(vcpu, MSR_IA32_TSC, HOST_ADJUST + val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); - run_vcpu(vm, VCPU_ID, 3); + run_vcpu(vcpu, 3); /* Host: writes to MSR_IA32_TSC_ADJUST do not modify the TSC. */ - vcpu_set_msr(vm, 0, MSR_IA32_TSC_ADJUST, UNITY * 123456); + vcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, UNITY * 123456); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); - ASSERT_EQ(vcpu_get_msr(vm, 0, MSR_IA32_TSC_ADJUST), UNITY * 123456); + ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_TSC_ADJUST), UNITY * 123456); /* Restore previous value. */ - vcpu_set_msr(vm, 0, MSR_IA32_TSC_ADJUST, val); + vcpu_set_msr(vcpu, MSR_IA32_TSC_ADJUST, val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); @@ -138,7 +133,7 @@ int main(void) * Guest: writes to MSR_IA32_TSC_ADJUST do not destroy the * host-side offset and affect both MSRs. */ - run_vcpu(vm, VCPU_ID, 4); + run_vcpu(vcpu, 4); val = 3ull * GUEST_STEP; ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), HOST_ADJUST + val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val); @@ -147,7 +142,7 @@ int main(void) * Guest: writes to MSR_IA32_TSC affect both MSRs, so the host-side * offset is now visible in MSR_IA32_TSC_ADJUST. */ - run_vcpu(vm, VCPU_ID, 5); + run_vcpu(vcpu, 5); val = 4ull * GUEST_STEP; ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC), val); ASSERT_EQ(rounded_host_rdmsr(MSR_IA32_TSC_ADJUST), val - HOST_ADJUST); diff --git a/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c b/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c index f0083d8cfe98..47139aab7408 100644 --- a/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c +++ b/tools/testing/selftests/kvm/x86_64/tsc_scaling_sync.c @@ -46,38 +46,40 @@ static void guest_code(void) static void *run_vcpu(void *_cpu_nr) { - unsigned long cpu = (unsigned long)_cpu_nr; + unsigned long vcpu_id = (unsigned long)_cpu_nr; unsigned long failures = 0; static bool first_cpu_done; + struct kvm_vcpu *vcpu; - /* The kernel is fine, but vm_vcpu_add_default() needs locking */ + /* The kernel is fine, but vm_vcpu_add() needs locking */ pthread_spin_lock(&create_lock); - vm_vcpu_add_default(vm, cpu, guest_code); + vcpu = vm_vcpu_add(vm, vcpu_id, guest_code); if (!first_cpu_done) { first_cpu_done = true; - vcpu_set_msr(vm, cpu, MSR_IA32_TSC, TEST_TSC_OFFSET); + vcpu_set_msr(vcpu, MSR_IA32_TSC, TEST_TSC_OFFSET); } pthread_spin_unlock(&create_lock); for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, cpu); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, cpu); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, cpu, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_DONE: goto out; case UCALL_SYNC: - printf("Guest %ld sync %lx %lx %ld\n", cpu, uc.args[2], uc.args[3], uc.args[2] - uc.args[3]); + printf("Guest %d sync %lx %lx %ld\n", vcpu->id, + uc.args[2], uc.args[3], uc.args[2] - uc.args[3]); failures++; break; @@ -91,12 +93,9 @@ static void *run_vcpu(void *_cpu_nr) int main(int argc, char *argv[]) { - if (!kvm_check_cap(KVM_CAP_VM_TSC_CONTROL)) { - print_skip("KVM_CAP_VM_TSC_CONTROL not available"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_VM_TSC_CONTROL)); - vm = vm_create_default_with_vcpus(0, DEFAULT_STACK_PGS * NR_TEST_VCPUS, 0, guest_code, NULL); + vm = vm_create(NR_TEST_VCPUS); vm_ioctl(vm, KVM_SET_TSC_KHZ, (void *) TEST_TSC_KHZ); pthread_spin_init(&create_lock, PTHREAD_PROCESS_PRIVATE); diff --git a/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c b/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c new file mode 100644 index 000000000000..a897c7fd8abe --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/ucna_injection_test.c @@ -0,0 +1,316 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * ucna_injection_test + * + * Copyright (C) 2022, Google LLC. + * + * This work is licensed under the terms of the GNU GPL, version 2. + * + * Test that user space can inject UnCorrectable No Action required (UCNA) + * memory errors to the guest. + * + * The test starts one vCPU with the MCG_CMCI_P enabled. It verifies that + * proper UCNA errors can be injected to a vCPU with MCG_CMCI_P and + * corresponding per-bank control register (MCI_CTL2) bit enabled. + * The test also checks that the UCNA errors get recorded in the + * Machine Check bank registers no matter the error signal interrupts get + * delivered into the guest or not. + * + */ + +#define _GNU_SOURCE /* for program_invocation_short_name */ +#include +#include +#include +#include + +#include "kvm_util_base.h" +#include "kvm_util.h" +#include "mce.h" +#include "processor.h" +#include "test_util.h" +#include "apic.h" + +#define SYNC_FIRST_UCNA 9 +#define SYNC_SECOND_UCNA 10 +#define SYNC_GP 11 +#define FIRST_UCNA_ADDR 0xdeadbeef +#define SECOND_UCNA_ADDR 0xcafeb0ba + +/* + * Vector for the CMCI interrupt. + * Value is arbitrary. Any value in 0x20-0xFF should work: + * https://wiki.osdev.org/Interrupt_Vector_Table + */ +#define CMCI_VECTOR 0xa9 + +#define UCNA_BANK 0x7 // IMC0 bank + +#define MCI_CTL2_RESERVED_BIT BIT_ULL(29) + +static uint64_t supported_mcg_caps; + +/* + * Record states about the injected UCNA. + * The variables started with the 'i_' prefixes are recorded in interrupt + * handler. Variables without the 'i_' prefixes are recorded in guest main + * execution thread. + */ +static volatile uint64_t i_ucna_rcvd; +static volatile uint64_t i_ucna_addr; +static volatile uint64_t ucna_addr; +static volatile uint64_t ucna_addr2; + +struct thread_params { + struct kvm_vcpu *vcpu; + uint64_t *p_i_ucna_rcvd; + uint64_t *p_i_ucna_addr; + uint64_t *p_ucna_addr; + uint64_t *p_ucna_addr2; +}; + +static void verify_apic_base_addr(void) +{ + uint64_t msr = rdmsr(MSR_IA32_APICBASE); + uint64_t base = GET_APIC_BASE(msr); + + GUEST_ASSERT(base == APIC_DEFAULT_GPA); +} + +static void ucna_injection_guest_code(void) +{ + uint64_t ctl2; + verify_apic_base_addr(); + xapic_enable(); + + /* Sets up the interrupt vector and enables per-bank CMCI sigaling. */ + xapic_write_reg(APIC_LVTCMCI, CMCI_VECTOR | APIC_DM_FIXED); + ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); + wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_CMCI_EN); + + /* Enables interrupt in guest. */ + asm volatile("sti"); + + /* Let user space inject the first UCNA */ + GUEST_SYNC(SYNC_FIRST_UCNA); + + ucna_addr = rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK)); + + /* Disables the per-bank CMCI signaling. */ + ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); + wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 & ~MCI_CTL2_CMCI_EN); + + /* Let the user space inject the second UCNA */ + GUEST_SYNC(SYNC_SECOND_UCNA); + + ucna_addr2 = rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK)); + GUEST_DONE(); +} + +static void cmci_disabled_guest_code(void) +{ + uint64_t ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); + wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_CMCI_EN); + + GUEST_DONE(); +} + +static void cmci_enabled_guest_code(void) +{ + uint64_t ctl2 = rdmsr(MSR_IA32_MCx_CTL2(UCNA_BANK)); + wrmsr(MSR_IA32_MCx_CTL2(UCNA_BANK), ctl2 | MCI_CTL2_RESERVED_BIT); + + GUEST_DONE(); +} + +static void guest_cmci_handler(struct ex_regs *regs) +{ + i_ucna_rcvd++; + i_ucna_addr = rdmsr(MSR_IA32_MCx_ADDR(UCNA_BANK)); + xapic_write_reg(APIC_EOI, 0); +} + +static void guest_gp_handler(struct ex_regs *regs) +{ + GUEST_SYNC(SYNC_GP); +} + +static void run_vcpu_expect_gp(struct kvm_vcpu *vcpu) +{ + unsigned int exit_reason; + struct ucall uc; + + vcpu_run(vcpu); + + exit_reason = vcpu->run->exit_reason; + TEST_ASSERT(exit_reason == KVM_EXIT_IO, + "exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO", + exit_reason, exit_reason_str(exit_reason)); + TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_SYNC, + "Expect UCALL_SYNC\n"); + TEST_ASSERT(uc.args[1] == SYNC_GP, "#GP is expected."); + printf("vCPU received GP in guest.\n"); +} + +static void inject_ucna(struct kvm_vcpu *vcpu, uint64_t addr) { + /* + * A UCNA error is indicated with VAL=1, UC=1, PCC=0, S=0 and AR=0 in + * the IA32_MCi_STATUS register. + * MSCOD=1 (BIT[16] - MscodDataRdErr). + * MCACOD=0x0090 (Memory controller error format, channel 0) + */ + uint64_t status = MCI_STATUS_VAL | MCI_STATUS_UC | MCI_STATUS_EN | + MCI_STATUS_MISCV | MCI_STATUS_ADDRV | 0x10090; + struct kvm_x86_mce mce = {}; + mce.status = status; + mce.mcg_status = 0; + /* + * MCM_ADDR_PHYS indicates the reported address is a physical address. + * Lowest 6 bits is the recoverable address LSB, i.e., the injected MCE + * is at 4KB granularity. + */ + mce.misc = (MCM_ADDR_PHYS << 6) | 0xc; + mce.addr = addr; + mce.bank = UCNA_BANK; + + vcpu_ioctl(vcpu, KVM_X86_SET_MCE, &mce); +} + +static void *run_ucna_injection(void *arg) +{ + struct thread_params *params = (struct thread_params *)arg; + struct ucall uc; + int old; + int r; + unsigned int exit_reason; + + r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); + TEST_ASSERT(r == 0, + "pthread_setcanceltype failed with errno=%d", + r); + + vcpu_run(params->vcpu); + + exit_reason = params->vcpu->run->exit_reason; + TEST_ASSERT(exit_reason == KVM_EXIT_IO, + "unexpected exit reason %u-%s, expected KVM_EXIT_IO", + exit_reason, exit_reason_str(exit_reason)); + TEST_ASSERT(get_ucall(params->vcpu, &uc) == UCALL_SYNC, + "Expect UCALL_SYNC\n"); + TEST_ASSERT(uc.args[1] == SYNC_FIRST_UCNA, "Injecting first UCNA."); + + printf("Injecting first UCNA at %#x.\n", FIRST_UCNA_ADDR); + + inject_ucna(params->vcpu, FIRST_UCNA_ADDR); + vcpu_run(params->vcpu); + + exit_reason = params->vcpu->run->exit_reason; + TEST_ASSERT(exit_reason == KVM_EXIT_IO, + "unexpected exit reason %u-%s, expected KVM_EXIT_IO", + exit_reason, exit_reason_str(exit_reason)); + TEST_ASSERT(get_ucall(params->vcpu, &uc) == UCALL_SYNC, + "Expect UCALL_SYNC\n"); + TEST_ASSERT(uc.args[1] == SYNC_SECOND_UCNA, "Injecting second UCNA."); + + printf("Injecting second UCNA at %#x.\n", SECOND_UCNA_ADDR); + + inject_ucna(params->vcpu, SECOND_UCNA_ADDR); + vcpu_run(params->vcpu); + + exit_reason = params->vcpu->run->exit_reason; + TEST_ASSERT(exit_reason == KVM_EXIT_IO, + "unexpected exit reason %u-%s, expected KVM_EXIT_IO", + exit_reason, exit_reason_str(exit_reason)); + if (get_ucall(params->vcpu, &uc) == UCALL_ABORT) { + TEST_ASSERT(false, "vCPU assertion failure: %s.\n", + (const char *)uc.args[0]); + } + + return NULL; +} + +static void test_ucna_injection(struct kvm_vcpu *vcpu, struct thread_params *params) +{ + struct kvm_vm *vm = vcpu->vm; + params->vcpu = vcpu; + params->p_i_ucna_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&i_ucna_rcvd); + params->p_i_ucna_addr = (uint64_t *)addr_gva2hva(vm, (uint64_t)&i_ucna_addr); + params->p_ucna_addr = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ucna_addr); + params->p_ucna_addr2 = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ucna_addr2); + + run_ucna_injection(params); + + TEST_ASSERT(*params->p_i_ucna_rcvd == 1, "Only first UCNA get signaled."); + TEST_ASSERT(*params->p_i_ucna_addr == FIRST_UCNA_ADDR, + "Only first UCNA reported addr get recorded via interrupt."); + TEST_ASSERT(*params->p_ucna_addr == FIRST_UCNA_ADDR, + "First injected UCNAs should get exposed via registers."); + TEST_ASSERT(*params->p_ucna_addr2 == SECOND_UCNA_ADDR, + "Second injected UCNAs should get exposed via registers."); + + printf("Test successful.\n" + "UCNA CMCI interrupts received: %ld\n" + "Last UCNA address received via CMCI: %lx\n" + "First UCNA address in vCPU thread: %lx\n" + "Second UCNA address in vCPU thread: %lx\n", + *params->p_i_ucna_rcvd, *params->p_i_ucna_addr, + *params->p_ucna_addr, *params->p_ucna_addr2); +} + +static void setup_mce_cap(struct kvm_vcpu *vcpu, bool enable_cmci_p) +{ + uint64_t mcg_caps = MCG_CTL_P | MCG_SER_P | MCG_LMCE_P | KVM_MAX_MCE_BANKS; + if (enable_cmci_p) + mcg_caps |= MCG_CMCI_P; + + mcg_caps &= supported_mcg_caps | MCG_CAP_BANKS_MASK; + vcpu_ioctl(vcpu, KVM_X86_SETUP_MCE, &mcg_caps); +} + +static struct kvm_vcpu *create_vcpu_with_mce_cap(struct kvm_vm *vm, uint32_t vcpuid, + bool enable_cmci_p, void *guest_code) +{ + struct kvm_vcpu *vcpu = vm_vcpu_add(vm, vcpuid, guest_code); + setup_mce_cap(vcpu, enable_cmci_p); + return vcpu; +} + +int main(int argc, char *argv[]) +{ + struct thread_params params; + struct kvm_vm *vm; + struct kvm_vcpu *ucna_vcpu; + struct kvm_vcpu *cmcidis_vcpu; + struct kvm_vcpu *cmci_vcpu; + + kvm_check_cap(KVM_CAP_MCE); + + vm = __vm_create(VM_MODE_DEFAULT, 3, 0); + + kvm_ioctl(vm->kvm_fd, KVM_X86_GET_MCE_CAP_SUPPORTED, + &supported_mcg_caps); + + if (!(supported_mcg_caps & MCG_CMCI_P)) { + print_skip("MCG_CMCI_P is not supported"); + exit(KSFT_SKIP); + } + + ucna_vcpu = create_vcpu_with_mce_cap(vm, 0, true, ucna_injection_guest_code); + cmcidis_vcpu = create_vcpu_with_mce_cap(vm, 1, false, cmci_disabled_guest_code); + cmci_vcpu = create_vcpu_with_mce_cap(vm, 2, true, cmci_enabled_guest_code); + + vm_init_descriptor_tables(vm); + vcpu_init_descriptor_tables(ucna_vcpu); + vcpu_init_descriptor_tables(cmcidis_vcpu); + vcpu_init_descriptor_tables(cmci_vcpu); + vm_install_exception_handler(vm, CMCI_VECTOR, guest_cmci_handler); + vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler); + + virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); + + test_ucna_injection(ucna_vcpu, ¶ms); + run_vcpu_expect_gp(cmcidis_vcpu); + run_vcpu_expect_gp(cmci_vcpu); + + kvm_vm_free(vm); +} diff --git a/tools/testing/selftests/kvm/x86_64/userspace_io_test.c b/tools/testing/selftests/kvm/x86_64/userspace_io_test.c index e4bef2e05686..7316521428f8 100644 --- a/tools/testing/selftests/kvm/x86_64/userspace_io_test.c +++ b/tools/testing/selftests/kvm/x86_64/userspace_io_test.c @@ -10,8 +10,6 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 1 - static void guest_ins_port80(uint8_t *buffer, unsigned int count) { unsigned long end; @@ -52,31 +50,29 @@ static void guest_code(void) int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_regs regs; struct kvm_run *run; struct kvm_vm *vm; struct ucall uc; - int rc; /* Tell stdout not to buffer its content */ setbuf(stdout, NULL); - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; memset(®s, 0, sizeof(regs)); while (1) { - rc = _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); - TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, exit_reason_str(run->exit_reason)); - if (get_ucall(vm, VCPU_ID, &uc)) + if (get_ucall(vcpu, &uc)) break; TEST_ASSERT(run->io.port == 0x80, @@ -89,22 +85,20 @@ int main(int argc, char *argv[]) * scope from a testing perspective as it's not ABI in any way, * i.e. it really is abusing internal KVM knowledge. */ - vcpu_regs_get(vm, VCPU_ID, ®s); + vcpu_regs_get(vcpu, ®s); if (regs.rcx == 2) regs.rcx = 1; if (regs.rcx == 3) regs.rcx = 8192; memset((void *)run + run->io.data_offset, 0xaa, 4096); - vcpu_regs_set(vm, VCPU_ID, ®s); + vcpu_regs_set(vcpu, ®s); } switch (uc.cmd) { case UCALL_DONE: break; case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld : argN+1 = 0x%lx, argN+2 = 0x%lx", - (const char *)uc.args[0], __FILE__, uc.args[1], - uc.args[2], uc.args[3]); + REPORT_GUEST_ASSERT_2(uc, "argN+1 = 0x%lx, argN+2 = 0x%lx"); default: TEST_FAIL("Unknown ucall %lu", uc.cmd); } diff --git a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c index e3e20e8848d0..a4f06370a245 100644 --- a/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c +++ b/tools/testing/selftests/kvm/x86_64/userspace_msr_exit_test.c @@ -17,7 +17,6 @@ #define KVM_FEP_LENGTH 5 static int fep_available = 1; -#define VCPU_ID 1 #define MSR_NON_EXISTENT 0x474f4f00 static u64 deny_bits = 0; @@ -395,31 +394,21 @@ static void guest_ud_handler(struct ex_regs *regs) regs->rip += KVM_FEP_LENGTH; } -static void run_guest(struct kvm_vm *vm) +static void check_for_guest_assert(struct kvm_vcpu *vcpu) { - int rc; - - rc = _vcpu_run(vm, VCPU_ID); - TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc); -} - -static void check_for_guest_assert(struct kvm_vm *vm) -{ - struct kvm_run *run = vcpu_state(vm, VCPU_ID); struct ucall uc; - if (run->exit_reason == KVM_EXIT_IO && - get_ucall(vm, VCPU_ID, &uc) == UCALL_ABORT) { - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + if (vcpu->run->exit_reason == KVM_EXIT_IO && + get_ucall(vcpu, &uc) == UCALL_ABORT) { + REPORT_GUEST_ASSERT(uc); } } -static void process_rdmsr(struct kvm_vm *vm, uint32_t msr_index) +static void process_rdmsr(struct kvm_vcpu *vcpu, uint32_t msr_index) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; - check_for_guest_assert(vm); + check_for_guest_assert(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_X86_RDMSR, "Unexpected exit reason: %u (%s),\n", @@ -450,11 +439,11 @@ static void process_rdmsr(struct kvm_vm *vm, uint32_t msr_index) } } -static void process_wrmsr(struct kvm_vm *vm, uint32_t msr_index) +static void process_wrmsr(struct kvm_vcpu *vcpu, uint32_t msr_index) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; - check_for_guest_assert(vm); + check_for_guest_assert(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_X86_WRMSR, "Unexpected exit reason: %u (%s),\n", @@ -481,43 +470,43 @@ static void process_wrmsr(struct kvm_vm *vm, uint32_t msr_index) } } -static void process_ucall_done(struct kvm_vm *vm) +static void process_ucall_done(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct ucall uc; - check_for_guest_assert(vm); + check_for_guest_assert(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - TEST_ASSERT(get_ucall(vm, VCPU_ID, &uc) == UCALL_DONE, + TEST_ASSERT(get_ucall(vcpu, &uc) == UCALL_DONE, "Unexpected ucall command: %lu, expected UCALL_DONE (%d)", uc.cmd, UCALL_DONE); } -static uint64_t process_ucall(struct kvm_vm *vm) +static uint64_t process_ucall(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; struct ucall uc = {}; - check_for_guest_assert(vm); + check_for_guest_assert(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s)", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_SYNC: break; case UCALL_ABORT: - check_for_guest_assert(vm); + check_for_guest_assert(vcpu); break; case UCALL_DONE: - process_ucall_done(vm); + process_ucall_done(vcpu); break; default: TEST_ASSERT(false, "Unexpected ucall"); @@ -526,45 +515,43 @@ static uint64_t process_ucall(struct kvm_vm *vm) return uc.cmd; } -static void run_guest_then_process_rdmsr(struct kvm_vm *vm, uint32_t msr_index) +static void run_guest_then_process_rdmsr(struct kvm_vcpu *vcpu, + uint32_t msr_index) { - run_guest(vm); - process_rdmsr(vm, msr_index); + vcpu_run(vcpu); + process_rdmsr(vcpu, msr_index); } -static void run_guest_then_process_wrmsr(struct kvm_vm *vm, uint32_t msr_index) +static void run_guest_then_process_wrmsr(struct kvm_vcpu *vcpu, + uint32_t msr_index) { - run_guest(vm); - process_wrmsr(vm, msr_index); + vcpu_run(vcpu); + process_wrmsr(vcpu, msr_index); } -static uint64_t run_guest_then_process_ucall(struct kvm_vm *vm) +static uint64_t run_guest_then_process_ucall(struct kvm_vcpu *vcpu) { - run_guest(vm); - return process_ucall(vm); + vcpu_run(vcpu); + return process_ucall(vcpu); } -static void run_guest_then_process_ucall_done(struct kvm_vm *vm) +static void run_guest_then_process_ucall_done(struct kvm_vcpu *vcpu) { - run_guest(vm); - process_ucall_done(vm); + vcpu_run(vcpu); + process_ucall_done(vcpu); } -static void test_msr_filter_allow(void) { - struct kvm_enable_cap cap = { - .cap = KVM_CAP_X86_USER_SPACE_MSR, - .args[0] = KVM_MSR_EXIT_REASON_FILTER, - }; +static void test_msr_filter_allow(void) +{ + struct kvm_vcpu *vcpu; struct kvm_vm *vm; int rc; - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code_filter_allow); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + vm = vm_create_with_one_vcpu(&vcpu, guest_code_filter_allow); rc = kvm_check_cap(KVM_CAP_X86_USER_SPACE_MSR); TEST_ASSERT(rc, "KVM_CAP_X86_USER_SPACE_MSR is available"); - vm_enable_cap(vm, &cap); + vm_enable_cap(vm, KVM_CAP_X86_USER_SPACE_MSR, KVM_MSR_EXIT_REASON_FILTER); rc = kvm_check_cap(KVM_CAP_X86_MSR_FILTER); TEST_ASSERT(rc, "KVM_CAP_X86_MSR_FILTER is available"); @@ -572,43 +559,43 @@ static void test_msr_filter_allow(void) { vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter_allow); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); vm_install_exception_handler(vm, GP_VECTOR, guest_gp_handler); /* Process guest code userspace exits. */ - run_guest_then_process_rdmsr(vm, MSR_IA32_XSS); - run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); - run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); + run_guest_then_process_rdmsr(vcpu, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_XSS); - run_guest_then_process_rdmsr(vm, MSR_IA32_FLUSH_CMD); - run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); - run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); + run_guest_then_process_rdmsr(vcpu, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_FLUSH_CMD); - run_guest_then_process_wrmsr(vm, MSR_NON_EXISTENT); - run_guest_then_process_rdmsr(vm, MSR_NON_EXISTENT); + run_guest_then_process_wrmsr(vcpu, MSR_NON_EXISTENT); + run_guest_then_process_rdmsr(vcpu, MSR_NON_EXISTENT); vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); - run_guest(vm); + vcpu_run(vcpu); vm_install_exception_handler(vm, UD_VECTOR, NULL); - if (process_ucall(vm) != UCALL_DONE) { + if (process_ucall(vcpu) != UCALL_DONE) { vm_install_exception_handler(vm, GP_VECTOR, guest_fep_gp_handler); /* Process emulated rdmsr and wrmsr instructions. */ - run_guest_then_process_rdmsr(vm, MSR_IA32_XSS); - run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); - run_guest_then_process_wrmsr(vm, MSR_IA32_XSS); + run_guest_then_process_rdmsr(vcpu, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_XSS); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_XSS); - run_guest_then_process_rdmsr(vm, MSR_IA32_FLUSH_CMD); - run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); - run_guest_then_process_wrmsr(vm, MSR_IA32_FLUSH_CMD); + run_guest_then_process_rdmsr(vcpu, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_FLUSH_CMD); + run_guest_then_process_wrmsr(vcpu, MSR_IA32_FLUSH_CMD); - run_guest_then_process_wrmsr(vm, MSR_NON_EXISTENT); - run_guest_then_process_rdmsr(vm, MSR_NON_EXISTENT); + run_guest_then_process_wrmsr(vcpu, MSR_NON_EXISTENT); + run_guest_then_process_rdmsr(vcpu, MSR_NON_EXISTENT); /* Confirm the guest completed without issues. */ - run_guest_then_process_ucall_done(vm); + run_guest_then_process_ucall_done(vcpu); } else { printf("To run the instruction emulated tests set the module parameter 'kvm.force_emulation_prefix=1'\n"); } @@ -616,16 +603,16 @@ static void test_msr_filter_allow(void) { kvm_vm_free(vm); } -static int handle_ucall(struct kvm_vm *vm) +static int handle_ucall(struct kvm_vcpu *vcpu) { struct ucall uc; - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("Guest assertion not met"); + REPORT_GUEST_ASSERT(uc); break; case UCALL_SYNC: - vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &no_filter_deny); + vm_ioctl(vcpu->vm, KVM_X86_SET_MSR_FILTER, &no_filter_deny); break; case UCALL_DONE: return 1; @@ -673,25 +660,21 @@ static void handle_wrmsr(struct kvm_run *run) } } -static void test_msr_filter_deny(void) { - struct kvm_enable_cap cap = { - .cap = KVM_CAP_X86_USER_SPACE_MSR, - .args[0] = KVM_MSR_EXIT_REASON_INVAL | - KVM_MSR_EXIT_REASON_UNKNOWN | - KVM_MSR_EXIT_REASON_FILTER, - }; +static void test_msr_filter_deny(void) +{ + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; int rc; - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code_filter_deny); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code_filter_deny); + run = vcpu->run; rc = kvm_check_cap(KVM_CAP_X86_USER_SPACE_MSR); TEST_ASSERT(rc, "KVM_CAP_X86_USER_SPACE_MSR is available"); - vm_enable_cap(vm, &cap); + vm_enable_cap(vm, KVM_CAP_X86_USER_SPACE_MSR, KVM_MSR_EXIT_REASON_INVAL | + KVM_MSR_EXIT_REASON_UNKNOWN | + KVM_MSR_EXIT_REASON_FILTER); rc = kvm_check_cap(KVM_CAP_X86_MSR_FILTER); TEST_ASSERT(rc, "KVM_CAP_X86_MSR_FILTER is available"); @@ -700,9 +683,7 @@ static void test_msr_filter_deny(void) { vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter_deny); while (1) { - rc = _vcpu_run(vm, VCPU_ID); - - TEST_ASSERT(rc == 0, "vcpu_run failed: %d\n", rc); + vcpu_run(vcpu); switch (run->exit_reason) { case KVM_EXIT_X86_RDMSR: @@ -712,7 +693,7 @@ static void test_msr_filter_deny(void) { handle_wrmsr(run); break; case KVM_EXIT_IO: - if (handle_ucall(vm)) + if (handle_ucall(vcpu)) goto done; break; } @@ -726,31 +707,28 @@ done: kvm_vm_free(vm); } -static void test_msr_permission_bitmap(void) { - struct kvm_enable_cap cap = { - .cap = KVM_CAP_X86_USER_SPACE_MSR, - .args[0] = KVM_MSR_EXIT_REASON_FILTER, - }; +static void test_msr_permission_bitmap(void) +{ + struct kvm_vcpu *vcpu; struct kvm_vm *vm; int rc; - /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code_permission_bitmap); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + vm = vm_create_with_one_vcpu(&vcpu, guest_code_permission_bitmap); rc = kvm_check_cap(KVM_CAP_X86_USER_SPACE_MSR); TEST_ASSERT(rc, "KVM_CAP_X86_USER_SPACE_MSR is available"); - vm_enable_cap(vm, &cap); + vm_enable_cap(vm, KVM_CAP_X86_USER_SPACE_MSR, KVM_MSR_EXIT_REASON_FILTER); rc = kvm_check_cap(KVM_CAP_X86_MSR_FILTER); TEST_ASSERT(rc, "KVM_CAP_X86_MSR_FILTER is available"); vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter_fs); - run_guest_then_process_rdmsr(vm, MSR_FS_BASE); - TEST_ASSERT(run_guest_then_process_ucall(vm) == UCALL_SYNC, "Expected ucall state to be UCALL_SYNC."); + run_guest_then_process_rdmsr(vcpu, MSR_FS_BASE); + TEST_ASSERT(run_guest_then_process_ucall(vcpu) == UCALL_SYNC, + "Expected ucall state to be UCALL_SYNC."); vm_ioctl(vm, KVM_X86_SET_MSR_FILTER, &filter_gs); - run_guest_then_process_rdmsr(vm, MSR_GS_BASE); - run_guest_then_process_ucall_done(vm); + run_guest_then_process_rdmsr(vcpu, MSR_GS_BASE); + run_guest_then_process_ucall_done(vcpu); kvm_vm_free(vm); } diff --git a/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c b/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c index d438c4d3228a..5abecf06329e 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_apic_access_test.c @@ -28,11 +28,6 @@ #include "kselftest.h" -#define VCPU_ID 0 - -/* The virtual machine object. */ -static struct kvm_vm *vm; - static void l2_guest_code(void) { /* Exit to L1 */ @@ -77,33 +72,29 @@ static void l1_guest_code(struct vmx_pages *vmx_pages, unsigned long high_gpa) int main(int argc, char *argv[]) { unsigned long apic_access_addr = ~0ul; - unsigned int paddr_width; - unsigned int vaddr_width; vm_vaddr_t vmx_pages_gva; unsigned long high_gpa; struct vmx_pages *vmx; bool done = false; - nested_vmx_check_supported(); + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - kvm_get_cpu_address_width(&paddr_width, &vaddr_width); - high_gpa = (1ul << paddr_width) - getpagesize(); - if ((unsigned long)DEFAULT_GUEST_PHY_PAGES * getpagesize() > high_gpa) { - print_skip("No unbacked physical page available"); - exit(KSFT_SKIP); - } + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); + + high_gpa = (vm->max_gfn - 1) << vm->page_shift; vmx = vcpu_alloc_vmx(vm, &vmx_pages_gva); prepare_virtualize_apic_accesses(vmx, vm); - vcpu_args_set(vm, VCPU_ID, 2, vmx_pages_gva, high_gpa); + vcpu_args_set(vcpu, 2, vmx_pages_gva, high_gpa); while (!done) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); if (apic_access_addr == high_gpa) { TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR, @@ -121,10 +112,9 @@ int main(int argc, char *argv[]) run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: apic_access_addr = uc.args[1]; diff --git a/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c b/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c index edac8839e717..d79651b02740 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_close_while_nested_test.c @@ -18,15 +18,10 @@ #include "kselftest.h" -#define VCPU_ID 5 - enum { PORT_L0_EXIT = 0x2000, }; -/* The virtual machine object. */ -static struct kvm_vm *vm; - static void l2_guest_code(void) { /* Exit to L0 */ @@ -53,20 +48,22 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) int main(int argc, char *argv[]) { vm_vaddr_t vmx_pages_gva; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); /* Allocate VMX pages and shared descriptors (vmx_pages). */ vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, @@ -75,9 +72,9 @@ int main(int argc, char *argv[]) if (run->io.port == PORT_L0_EXIT) break; - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ default: TEST_FAIL("Unknown ucall %lu", uc.cmd); diff --git a/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c b/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c index 68f26a8b4f42..2d8c23d639f7 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_dirty_log_test.c @@ -17,8 +17,6 @@ #include "processor.h" #include "vmx.h" -#define VCPU_ID 1 - /* The memory slot index to track dirty pages */ #define TEST_MEM_SLOT_INDEX 1 #define TEST_MEM_PAGES 3 @@ -73,18 +71,19 @@ int main(int argc, char *argv[]) unsigned long *bmap; uint64_t *host_test_mem; + struct kvm_vcpu *vcpu; struct kvm_vm *vm; struct kvm_run *run; struct ucall uc; bool done = false; - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); vmx = vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); - run = vcpu_state(vm, VCPU_ID); + vcpu_args_set(vcpu, 1, vmx_pages_gva); + run = vcpu->run; /* Add an extra memory slot for testing dirty logging */ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, @@ -116,16 +115,15 @@ int main(int argc, char *argv[]) while (!done) { memset(host_test_mem, 0xaa, TEST_MEM_PAGES * 4096); - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Unexpected exit reason: %u (%s),\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: /* diff --git a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c index 27a850f3d7ce..2641b286b4ed 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_exception_with_invalid_guest_state.c @@ -10,10 +10,6 @@ #include "kselftest.h" -#define VCPU_ID 0 - -static struct kvm_vm *vm; - static void guest_ud_handler(struct ex_regs *regs) { /* Loop on the ud2 until guest state is made invalid. */ @@ -24,11 +20,11 @@ static void guest_code(void) asm volatile("ud2"); } -static void __run_vcpu_with_invalid_state(void) +static void __run_vcpu_with_invalid_state(struct kvm_vcpu *vcpu) { - struct kvm_run *run = vcpu_state(vm, VCPU_ID); + struct kvm_run *run = vcpu->run; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_INTERNAL_ERROR, "Expected KVM_EXIT_INTERNAL_ERROR, got %d (%s)\n", @@ -38,15 +34,15 @@ static void __run_vcpu_with_invalid_state(void) run->emulation_failure.suberror); } -static void run_vcpu_with_invalid_state(void) +static void run_vcpu_with_invalid_state(struct kvm_vcpu *vcpu) { /* * Always run twice to verify KVM handles the case where _KVM_ queues * an exception with invalid state and then exits to userspace, i.e. * that KVM doesn't explode if userspace ignores the initial error. */ - __run_vcpu_with_invalid_state(); - __run_vcpu_with_invalid_state(); + __run_vcpu_with_invalid_state(vcpu); + __run_vcpu_with_invalid_state(vcpu); } static void set_timer(void) @@ -59,33 +55,43 @@ static void set_timer(void) ASSERT_EQ(setitimer(ITIMER_REAL, &timer, NULL), 0); } -static void set_or_clear_invalid_guest_state(bool set) +static void set_or_clear_invalid_guest_state(struct kvm_vcpu *vcpu, bool set) { static struct kvm_sregs sregs; if (!sregs.cr0) - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.tr.unusable = !!set; - vcpu_sregs_set(vm, VCPU_ID, &sregs); + vcpu_sregs_set(vcpu, &sregs); } -static void set_invalid_guest_state(void) +static void set_invalid_guest_state(struct kvm_vcpu *vcpu) { - set_or_clear_invalid_guest_state(true); + set_or_clear_invalid_guest_state(vcpu, true); } -static void clear_invalid_guest_state(void) +static void clear_invalid_guest_state(struct kvm_vcpu *vcpu) { - set_or_clear_invalid_guest_state(false); + set_or_clear_invalid_guest_state(vcpu, false); +} + +static struct kvm_vcpu *get_set_sigalrm_vcpu(struct kvm_vcpu *__vcpu) +{ + static struct kvm_vcpu *vcpu = NULL; + + if (__vcpu) + vcpu = __vcpu; + return vcpu; } static void sigalrm_handler(int sig) { + struct kvm_vcpu *vcpu = get_set_sigalrm_vcpu(NULL); struct kvm_vcpu_events events; TEST_ASSERT(sig == SIGALRM, "Unexpected signal = %d", sig); - vcpu_events_get(vm, VCPU_ID, &events); + vcpu_events_get(vcpu, &events); /* * If an exception is pending, attempt KVM_RUN with invalid guest, @@ -93,8 +99,8 @@ static void sigalrm_handler(int sig) * between KVM queueing an exception and re-entering the guest. */ if (events.exception.pending) { - set_invalid_guest_state(); - run_vcpu_with_invalid_state(); + set_invalid_guest_state(vcpu); + run_vcpu_with_invalid_state(vcpu); } else { set_timer(); } @@ -102,15 +108,17 @@ static void sigalrm_handler(int sig) int main(int argc, char *argv[]) { - if (!is_intel_cpu() || vm_is_unrestricted_guest(NULL)) { - print_skip("Must be run with kvm_intel.unrestricted_guest=0"); - exit(KSFT_SKIP); - } + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - vm = vm_create_default(VCPU_ID, 0, (void *)guest_code); + TEST_REQUIRE(is_intel_cpu()); + TEST_REQUIRE(!vm_is_unrestricted_guest(NULL)); + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + get_set_sigalrm_vcpu(vcpu); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); vm_install_exception_handler(vm, UD_VECTOR, guest_ud_handler); @@ -119,8 +127,8 @@ int main(int argc, char *argv[]) * KVM_RUN should induce a TRIPLE_FAULT in L2 as KVM doesn't support * emulating invalid guest state for L2. */ - set_invalid_guest_state(); - run_vcpu_with_invalid_state(); + set_invalid_guest_state(vcpu); + run_vcpu_with_invalid_state(vcpu); /* * Verify KVM also handles the case where userspace gains control while @@ -129,11 +137,11 @@ int main(int argc, char *argv[]) * guest with invalid state when the handler interrupts KVM with an * exception pending. */ - clear_invalid_guest_state(); + clear_invalid_guest_state(vcpu); TEST_ASSERT(signal(SIGALRM, sigalrm_handler) != SIG_ERR, "Failed to register SIGALRM handler, errno = %d (%s)", errno, strerror(errno)); set_timer(); - run_vcpu_with_invalid_state(); + run_vcpu_with_invalid_state(vcpu); } diff --git a/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c b/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c index 489fbed4ca6f..6bfb4bb471ca 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_invalid_nested_guest_state.c @@ -9,7 +9,6 @@ #include "kselftest.h" -#define VCPU_ID 0 #define ARBITRARY_IO_PORT 0x2000 static struct kvm_vm *vm; @@ -55,20 +54,21 @@ int main(int argc, char *argv[]) { vm_vaddr_t vmx_pages_gva; struct kvm_sregs sregs; + struct kvm_vcpu *vcpu; struct kvm_run *run; struct ucall uc; - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); /* Allocate VMX pages and shared descriptors (vmx_pages). */ vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); - run = vcpu_state(vm, VCPU_ID); + run = vcpu->run; /* * The first exit to L0 userspace should be an I/O access from L2. @@ -88,17 +88,17 @@ int main(int argc, char *argv[]) * emulating invalid guest state for L2. */ memset(&sregs, 0, sizeof(sregs)); - vcpu_sregs_get(vm, VCPU_ID, &sregs); + vcpu_sregs_get(vcpu, &sregs); sregs.tr.unusable = 1; - vcpu_sregs_set(vm, VCPU_ID, &sregs); + vcpu_sregs_set(vcpu, &sregs); - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_DONE: break; case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); default: TEST_FAIL("Unexpected ucall: %lu", uc.cmd); } diff --git a/tools/testing/selftests/kvm/x86_64/vmx_msrs_test.c b/tools/testing/selftests/kvm/x86_64/vmx_msrs_test.c new file mode 100644 index 000000000000..322d561b4260 --- /dev/null +++ b/tools/testing/selftests/kvm/x86_64/vmx_msrs_test.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * VMX control MSR test + * + * Copyright (C) 2022 Google LLC. + * + * Tests for KVM ownership of bits in the VMX entry/exit control MSRs. Checks + * that KVM will set owned bits where appropriate, and will not if + * KVM_X86_QUIRK_TWEAK_VMX_CTRL_MSRS is disabled. + */ +#include +#include "kvm_util.h" +#include "vmx.h" + +static void vmx_fixed1_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index, + uint64_t mask) +{ + uint64_t val = vcpu_get_msr(vcpu, msr_index); + uint64_t bit; + + mask &= val; + + for_each_set_bit(bit, &mask, 64) { + vcpu_set_msr(vcpu, msr_index, val & ~BIT_ULL(bit)); + vcpu_set_msr(vcpu, msr_index, val); + } +} + +static void vmx_fixed0_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index, + uint64_t mask) +{ + uint64_t val = vcpu_get_msr(vcpu, msr_index); + uint64_t bit; + + mask = ~mask | val; + + for_each_clear_bit(bit, &mask, 64) { + vcpu_set_msr(vcpu, msr_index, val | BIT_ULL(bit)); + vcpu_set_msr(vcpu, msr_index, val); + } +} + +static void vmx_fixed0and1_msr_test(struct kvm_vcpu *vcpu, uint32_t msr_index) +{ + vmx_fixed0_msr_test(vcpu, msr_index, GENMASK_ULL(31, 0)); + vmx_fixed1_msr_test(vcpu, msr_index, GENMASK_ULL(63, 32)); +} + +static void vmx_save_restore_msrs_test(struct kvm_vcpu *vcpu) +{ + vcpu_set_msr(vcpu, MSR_IA32_VMX_VMCS_ENUM, 0); + vcpu_set_msr(vcpu, MSR_IA32_VMX_VMCS_ENUM, -1ull); + + vmx_fixed1_msr_test(vcpu, MSR_IA32_VMX_BASIC, + BIT_ULL(49) | BIT_ULL(54) | BIT_ULL(55)); + + vmx_fixed1_msr_test(vcpu, MSR_IA32_VMX_MISC, + BIT_ULL(5) | GENMASK_ULL(8, 6) | BIT_ULL(14) | + BIT_ULL(15) | BIT_ULL(28) | BIT_ULL(29) | BIT_ULL(30)); + + vmx_fixed0and1_msr_test(vcpu, MSR_IA32_VMX_PROCBASED_CTLS2); + vmx_fixed1_msr_test(vcpu, MSR_IA32_VMX_EPT_VPID_CAP, -1ull); + vmx_fixed0and1_msr_test(vcpu, MSR_IA32_VMX_TRUE_PINBASED_CTLS); + vmx_fixed0and1_msr_test(vcpu, MSR_IA32_VMX_TRUE_PROCBASED_CTLS); + vmx_fixed0and1_msr_test(vcpu, MSR_IA32_VMX_TRUE_EXIT_CTLS); + vmx_fixed0and1_msr_test(vcpu, MSR_IA32_VMX_TRUE_ENTRY_CTLS); + vmx_fixed1_msr_test(vcpu, MSR_IA32_VMX_VMFUNC, -1ull); +} + +int main(void) +{ + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; + + TEST_REQUIRE(kvm_has_cap(KVM_CAP_DISABLE_QUIRKS2)); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); + + /* No need to actually do KVM_RUN, thus no guest code. */ + vm = vm_create_with_one_vcpu(&vcpu, NULL); + + vmx_save_restore_msrs_test(vcpu); + + kvm_vm_free(vm); +} diff --git a/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c b/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c index 280c01fd2412..465a9434d61c 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_nested_tsc_scaling_test.c @@ -15,9 +15,6 @@ #include "vmx.h" #include "kselftest.h" - -#define VCPU_ID 0 - /* L2 is scaled up (from L1's perspective) by this factor */ #define L2_SCALE_FACTOR 4ULL @@ -119,14 +116,6 @@ static void l1_guest_code(struct vmx_pages *vmx_pages) GUEST_DONE(); } -static void tsc_scaling_check_supported(void) -{ - if (!kvm_check_cap(KVM_CAP_TSC_CONTROL)) { - print_skip("TSC scaling not supported by the HW"); - exit(KSFT_SKIP); - } -} - static void stable_tsc_check_supported(void) { FILE *fp; @@ -150,6 +139,7 @@ skip_test: int main(int argc, char *argv[]) { + struct kvm_vcpu *vcpu; struct kvm_vm *vm; vm_vaddr_t vmx_pages_gva; @@ -160,8 +150,8 @@ int main(int argc, char *argv[]) uint64_t l1_tsc_freq = 0; uint64_t l2_tsc_freq = 0; - nested_vmx_check_supported(); - tsc_scaling_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); + TEST_REQUIRE(kvm_has_cap(KVM_CAP_TSC_CONTROL)); stable_tsc_check_supported(); /* @@ -182,30 +172,29 @@ int main(int argc, char *argv[]) l0_tsc_freq = tsc_end - tsc_start; printf("real TSC frequency is around: %"PRIu64"\n", l0_tsc_freq); - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, l1_guest_code); vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); - tsc_khz = _vcpu_ioctl(vm, VCPU_ID, KVM_GET_TSC_KHZ, NULL); + tsc_khz = __vcpu_ioctl(vcpu, KVM_GET_TSC_KHZ, NULL); TEST_ASSERT(tsc_khz != -1, "vcpu ioctl KVM_GET_TSC_KHZ failed"); /* scale down L1's TSC frequency */ - vcpu_ioctl(vm, VCPU_ID, KVM_SET_TSC_KHZ, - (void *) (tsc_khz / l1_scale_factor)); + vcpu_ioctl(vcpu, KVM_SET_TSC_KHZ, (void *) (tsc_khz / l1_scale_factor)); for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *) uc.args[0]); + REPORT_GUEST_ASSERT(uc); case UCALL_SYNC: switch (uc.args[0]) { case USLEEP: 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 97b7fd4a9a3d..6ec901dab61e 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 @@ -17,9 +17,6 @@ #include "kvm_util.h" #include "vmx.h" -#define VCPU_ID 0 - -#define X86_FEATURE_PDCM (1<<15) #define PMU_CAP_FW_WRITES (1ULL << 13) #define PMU_CAP_LBR_FMT 0x3f @@ -56,11 +53,9 @@ static void guest_code(void) int main(int argc, char *argv[]) { - struct kvm_cpuid2 *cpuid; - struct kvm_cpuid_entry2 *entry_1_0; - struct kvm_cpuid_entry2 *entry_a_0; - bool pdcm_supported = false; + const struct kvm_cpuid_entry2 *entry_a_0; struct kvm_vm *vm; + struct kvm_vcpu *vcpu; int ret; union cpuid10_eax eax; union perf_capabilities host_cap; @@ -69,46 +64,37 @@ int main(int argc, char *argv[]) host_cap.capabilities &= (PMU_CAP_FW_WRITES | PMU_CAP_LBR_FMT); /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - cpuid = kvm_get_supported_cpuid(); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); - if (kvm_get_cpuid_max_basic() >= 0xa) { - entry_1_0 = kvm_get_supported_cpuid_index(1, 0); - entry_a_0 = kvm_get_supported_cpuid_index(0xa, 0); - pdcm_supported = entry_1_0 && !!(entry_1_0->ecx & X86_FEATURE_PDCM); - eax.full = entry_a_0->eax; - } - if (!pdcm_supported) { - print_skip("MSR_IA32_PERF_CAPABILITIES is not supported by the vCPU"); - exit(KSFT_SKIP); - } - if (!eax.split.version_id) { - print_skip("PMU is not supported by the vCPU"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_PDCM)); + + TEST_REQUIRE(kvm_get_cpuid_max_basic() >= 0xa); + entry_a_0 = kvm_get_supported_cpuid_entry(0xa); + + eax.full = entry_a_0->eax; + __TEST_REQUIRE(eax.split.version_id, "PMU is not supported by the vCPU"); /* testcase 1, set capabilities when we have PDCM bit */ - vcpu_set_cpuid(vm, VCPU_ID, cpuid); - vcpu_set_msr(vm, 0, MSR_IA32_PERF_CAPABILITIES, PMU_CAP_FW_WRITES); + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, PMU_CAP_FW_WRITES); /* check capabilities can be retrieved with KVM_GET_MSR */ - ASSERT_EQ(vcpu_get_msr(vm, VCPU_ID, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); + ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); /* check whatever we write with KVM_SET_MSR is _not_ modified */ - vcpu_run(vm, VCPU_ID); - ASSERT_EQ(vcpu_get_msr(vm, VCPU_ID, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); + vcpu_run(vcpu); + ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), PMU_CAP_FW_WRITES); /* testcase 2, check valid LBR formats are accepted */ - vcpu_set_msr(vm, 0, MSR_IA32_PERF_CAPABILITIES, 0); - ASSERT_EQ(vcpu_get_msr(vm, VCPU_ID, MSR_IA32_PERF_CAPABILITIES), 0); + vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0); + ASSERT_EQ(vcpu_get_msr(vcpu, MSR_IA32_PERF_CAPABILITIES), 0); - vcpu_set_msr(vm, 0, MSR_IA32_PERF_CAPABILITIES, host_cap.lbr_format); - ASSERT_EQ(vcpu_get_msr(vm, VCPU_ID, MSR_IA32_PERF_CAPABILITIES), (u64)host_cap.lbr_format); + 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(vm, 0, MSR_IA32_PERF_CAPABILITIES, 0x30); + ret = _vcpu_set_msr(vcpu, MSR_IA32_PERF_CAPABILITIES, 0x30); TEST_ASSERT(ret == 0, "Bad PERF_CAPABILITIES didn't fail."); printf("Completed perf capability tests.\n"); diff --git a/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c b/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c index ff92e25b6f1e..0efdc05969a5 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_preemption_timer_test.c @@ -22,7 +22,6 @@ #include "processor.h" #include "vmx.h" -#define VCPU_ID 5 #define PREEMPTION_TIMER_VALUE 100000000ull #define PREEMPTION_TIMER_VALUE_THRESHOLD1 80000000ull @@ -159,6 +158,7 @@ int main(int argc, char *argv[]) struct kvm_regs regs1, regs2; struct kvm_vm *vm; struct kvm_run *run; + struct kvm_vcpu *vcpu; struct kvm_x86_state *state; struct ucall uc; int stage; @@ -167,33 +167,29 @@ int main(int argc, char *argv[]) * AMD currently does not implement any VMX features, so for now we * just early out. */ - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - if (!kvm_check_cap(KVM_CAP_NESTED_STATE)) { - print_skip("KVM_CAP_NESTED_STATE not supported"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE)); /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, guest_code); - run = vcpu_state(vm, VCPU_ID); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + run = vcpu->run; - vcpu_regs_get(vm, VCPU_ID, ®s1); + vcpu_regs_get(vcpu, ®s1); vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); for (stage = 1;; stage++) { - _vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Stage %d: unexpected exit reason: %u (%s),\n", stage, run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s at %s:%ld", (const char *)uc.args[0], - __FILE__, uc.args[1]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; @@ -232,22 +228,20 @@ int main(int argc, char *argv[]) stage, uc.args[4], uc.args[5]); } - state = vcpu_save_state(vm, VCPU_ID); + state = vcpu_save_state(vcpu); memset(®s1, 0, sizeof(regs1)); - vcpu_regs_get(vm, VCPU_ID, ®s1); + vcpu_regs_get(vcpu, ®s1); kvm_vm_release(vm); /* Restore state in a new VM. */ - kvm_vm_restart(vm, O_RDWR); - vm_vcpu_add(vm, VCPU_ID); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); - vcpu_load_state(vm, VCPU_ID, state); - run = vcpu_state(vm, VCPU_ID); + vcpu = vm_recreate_with_one_vcpu(vm); + vcpu_load_state(vcpu, state); + run = vcpu->run; kvm_x86_state_cleanup(state); memset(®s2, 0, sizeof(regs2)); - vcpu_regs_get(vm, VCPU_ID, ®s2); + vcpu_regs_get(vcpu, ®s2); TEST_ASSERT(!memcmp(®s1, ®s2, sizeof(regs2)), "Unexpected register values after vcpu_load_state; rdi: %lx rsi: %lx", (ulong) regs2.rdi, (ulong) regs2.rsi); diff --git a/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c b/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c index 5827b9bae468..41ea7028a1f8 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_set_nested_state_test.c @@ -23,38 +23,37 @@ * changes this should be updated. */ #define VMCS12_REVISION 0x11e57ed0 -#define VCPU_ID 5 bool have_evmcs; -void test_nested_state(struct kvm_vm *vm, struct kvm_nested_state *state) +void test_nested_state(struct kvm_vcpu *vcpu, struct kvm_nested_state *state) { - vcpu_nested_state_set(vm, VCPU_ID, state, false); + vcpu_nested_state_set(vcpu, state); } -void test_nested_state_expect_errno(struct kvm_vm *vm, +void test_nested_state_expect_errno(struct kvm_vcpu *vcpu, struct kvm_nested_state *state, int expected_errno) { int rv; - rv = vcpu_nested_state_set(vm, VCPU_ID, state, true); + rv = __vcpu_nested_state_set(vcpu, state); TEST_ASSERT(rv == -1 && errno == expected_errno, "Expected %s (%d) from vcpu_nested_state_set but got rv: %i errno: %s (%d)", strerror(expected_errno), expected_errno, rv, strerror(errno), errno); } -void test_nested_state_expect_einval(struct kvm_vm *vm, +void test_nested_state_expect_einval(struct kvm_vcpu *vcpu, struct kvm_nested_state *state) { - test_nested_state_expect_errno(vm, state, EINVAL); + test_nested_state_expect_errno(vcpu, state, EINVAL); } -void test_nested_state_expect_efault(struct kvm_vm *vm, +void test_nested_state_expect_efault(struct kvm_vcpu *vcpu, struct kvm_nested_state *state) { - test_nested_state_expect_errno(vm, state, EFAULT); + test_nested_state_expect_errno(vcpu, state, EFAULT); } void set_revision_id_for_vmcs12(struct kvm_nested_state *state, @@ -86,7 +85,7 @@ void set_default_vmx_state(struct kvm_nested_state *state, int size) set_revision_id_for_vmcs12(state, VMCS12_REVISION); } -void test_vmx_nested_state(struct kvm_vm *vm) +void test_vmx_nested_state(struct kvm_vcpu *vcpu) { /* Add a page for VMCS12. */ const int state_sz = sizeof(struct kvm_nested_state) + getpagesize(); @@ -96,14 +95,14 @@ void test_vmx_nested_state(struct kvm_vm *vm) /* The format must be set to 0. 0 for VMX, 1 for SVM. */ set_default_vmx_state(state, state_sz); state->format = 1; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* * We cannot virtualize anything if the guest does not have VMX * enabled. */ set_default_vmx_state(state, state_sz); - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* * We cannot virtualize anything if the guest does not have VMX @@ -112,17 +111,17 @@ void test_vmx_nested_state(struct kvm_vm *vm) */ set_default_vmx_state(state, state_sz); state->hdr.vmx.vmxon_pa = -1ull; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); state->hdr.vmx.vmcs12_pa = -1ull; state->flags = KVM_STATE_NESTED_EVMCS; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); state->flags = 0; - test_nested_state(vm, state); + test_nested_state(vcpu, state); /* Enable VMX in the guest CPUID. */ - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + vcpu_set_cpuid_feature(vcpu, X86_FEATURE_VMX); /* * Setting vmxon_pa == -1ull and vmcs_pa == -1ull exits early without @@ -133,34 +132,34 @@ void test_vmx_nested_state(struct kvm_vm *vm) set_default_vmx_state(state, state_sz); state->hdr.vmx.vmxon_pa = -1ull; state->hdr.vmx.vmcs12_pa = -1ull; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); state->flags &= KVM_STATE_NESTED_EVMCS; if (have_evmcs) { - test_nested_state_expect_einval(vm, state); - vcpu_enable_evmcs(vm, VCPU_ID); + test_nested_state_expect_einval(vcpu, state); + vcpu_enable_evmcs(vcpu); } - test_nested_state(vm, state); + test_nested_state(vcpu, state); /* It is invalid to have vmxon_pa == -1ull and SMM flags non-zero. */ state->hdr.vmx.smm.flags = 1; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* Invalid flags are rejected. */ set_default_vmx_state(state, state_sz); state->hdr.vmx.flags = ~0; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* It is invalid to have vmxon_pa == -1ull and vmcs_pa != -1ull. */ set_default_vmx_state(state, state_sz); state->hdr.vmx.vmxon_pa = -1ull; state->flags = 0; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* It is invalid to have vmxon_pa set to a non-page aligned address. */ set_default_vmx_state(state, state_sz); state->hdr.vmx.vmxon_pa = 1; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* * It is invalid to have KVM_STATE_NESTED_SMM_GUEST_MODE and @@ -170,7 +169,7 @@ void test_vmx_nested_state(struct kvm_vm *vm) state->flags = KVM_STATE_NESTED_GUEST_MODE | KVM_STATE_NESTED_RUN_PENDING; state->hdr.vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* * It is invalid to have any of the SMM flags set besides: @@ -180,13 +179,13 @@ void test_vmx_nested_state(struct kvm_vm *vm) set_default_vmx_state(state, state_sz); state->hdr.vmx.smm.flags = ~(KVM_STATE_NESTED_SMM_GUEST_MODE | KVM_STATE_NESTED_SMM_VMXON); - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* Outside SMM, SMM flags must be zero. */ set_default_vmx_state(state, state_sz); state->flags = 0; state->hdr.vmx.smm.flags = KVM_STATE_NESTED_SMM_GUEST_MODE; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* * Size must be large enough to fit kvm_nested_state and vmcs12 @@ -195,13 +194,13 @@ void test_vmx_nested_state(struct kvm_vm *vm) set_default_vmx_state(state, state_sz); state->size = sizeof(*state); state->flags = 0; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); set_default_vmx_state(state, state_sz); state->size = sizeof(*state); state->flags = 0; state->hdr.vmx.vmcs12_pa = -1; - test_nested_state(vm, state); + test_nested_state(vcpu, state); /* * KVM_SET_NESTED_STATE succeeds with invalid VMCS @@ -209,7 +208,7 @@ void test_vmx_nested_state(struct kvm_vm *vm) */ set_default_vmx_state(state, state_sz); state->flags = 0; - test_nested_state(vm, state); + test_nested_state(vcpu, state); /* Invalid flags are rejected, even if no VMCS loaded. */ set_default_vmx_state(state, state_sz); @@ -217,13 +216,13 @@ void test_vmx_nested_state(struct kvm_vm *vm) state->flags = 0; state->hdr.vmx.vmcs12_pa = -1; state->hdr.vmx.flags = ~0; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* vmxon_pa cannot be the same address as vmcs_pa. */ set_default_vmx_state(state, state_sz); state->hdr.vmx.vmxon_pa = 0; state->hdr.vmx.vmcs12_pa = 0; - test_nested_state_expect_einval(vm, state); + test_nested_state_expect_einval(vcpu, state); /* * Test that if we leave nesting the state reflects that when we get @@ -233,8 +232,8 @@ void test_vmx_nested_state(struct kvm_vm *vm) state->hdr.vmx.vmxon_pa = -1ull; state->hdr.vmx.vmcs12_pa = -1ull; state->flags = 0; - test_nested_state(vm, state); - vcpu_nested_state_get(vm, VCPU_ID, state); + test_nested_state(vcpu, state); + vcpu_nested_state_get(vcpu, state); TEST_ASSERT(state->size >= sizeof(*state) && state->size <= state_sz, "Size must be between %ld and %d. The size returned was %d.", sizeof(*state), state_sz, state->size); @@ -244,54 +243,36 @@ void test_vmx_nested_state(struct kvm_vm *vm) free(state); } -void disable_vmx(struct kvm_vm *vm) -{ - struct kvm_cpuid2 *cpuid = kvm_get_supported_cpuid(); - int i; - - for (i = 0; i < cpuid->nent; ++i) - if (cpuid->entries[i].function == 1 && - cpuid->entries[i].index == 0) - break; - TEST_ASSERT(i != cpuid->nent, "CPUID function 1 not found"); - - cpuid->entries[i].ecx &= ~CPUID_VMX; - vcpu_set_cpuid(vm, VCPU_ID, cpuid); - cpuid->entries[i].ecx |= CPUID_VMX; -} - int main(int argc, char *argv[]) { struct kvm_vm *vm; struct kvm_nested_state state; + struct kvm_vcpu *vcpu; have_evmcs = kvm_check_cap(KVM_CAP_HYPERV_ENLIGHTENED_VMCS); - if (!kvm_check_cap(KVM_CAP_NESTED_STATE)) { - print_skip("KVM_CAP_NESTED_STATE not available"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_has_cap(KVM_CAP_NESTED_STATE)); /* * AMD currently does not implement set_nested_state, so for now we * just early out. */ - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - vm = vm_create_default(VCPU_ID, 0, 0); + vm = vm_create_with_one_vcpu(&vcpu, NULL); /* * First run tests with VMX disabled to check error handling. */ - disable_vmx(vm); + vcpu_clear_cpuid_feature(vcpu, X86_FEATURE_VMX); /* Passing a NULL kvm_nested_state causes a EFAULT. */ - test_nested_state_expect_efault(vm, NULL); + test_nested_state_expect_efault(vcpu, NULL); /* 'size' cannot be smaller than sizeof(kvm_nested_state). */ set_default_state(&state); state.size = 0; - test_nested_state_expect_einval(vm, &state); + test_nested_state_expect_einval(vcpu, &state); /* * Setting the flags 0xf fails the flags check. The only flags that @@ -302,7 +283,7 @@ int main(int argc, char *argv[]) */ set_default_state(&state); state.flags = 0xf; - test_nested_state_expect_einval(vm, &state); + test_nested_state_expect_einval(vcpu, &state); /* * If KVM_STATE_NESTED_RUN_PENDING is set then @@ -310,9 +291,9 @@ int main(int argc, char *argv[]) */ set_default_state(&state); state.flags = KVM_STATE_NESTED_RUN_PENDING; - test_nested_state_expect_einval(vm, &state); + test_nested_state_expect_einval(vcpu, &state); - test_vmx_nested_state(vm); + test_vmx_nested_state(vcpu); kvm_vm_free(vm); return 0; diff --git a/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c b/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c index 19b35c607dc6..5943187e8594 100644 --- a/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c +++ b/tools/testing/selftests/kvm/x86_64/vmx_tsc_adjust_test.c @@ -32,8 +32,6 @@ #define MSR_IA32_TSC_ADJUST 0x3b #endif -#define VCPU_ID 5 - #define TSC_ADJUST_VALUE (1ll << 32) #define TSC_OFFSET_VALUE -(1ll << 48) @@ -127,28 +125,29 @@ static void report(int64_t val) int main(int argc, char *argv[]) { vm_vaddr_t vmx_pages_gva; + struct kvm_vcpu *vcpu; - nested_vmx_check_supported(); + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_VMX)); - vm = vm_create_default(VCPU_ID, 0, (void *) l1_guest_code); + vm = vm_create_with_one_vcpu(&vcpu, (void *) l1_guest_code); /* Allocate VMX pages and shared descriptors (vmx_pages). */ vcpu_alloc_vmx(vm, &vmx_pages_gva); - vcpu_args_set(vm, VCPU_ID, 1, vmx_pages_gva); + vcpu_args_set(vcpu, 1, vmx_pages_gva); for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: report(uc.args[1]); diff --git a/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c b/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c index afbbc40df884..3d272d7f961e 100644 --- a/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c +++ b/tools/testing/selftests/kvm/x86_64/xapic_ipi_test.c @@ -39,9 +39,6 @@ /* Default delay between migrate_pages calls (microseconds) */ #define DEFAULT_DELAY_USECS 500000 -#define HALTER_VCPU_ID 0 -#define SENDER_VCPU_ID 1 - /* * Vector for IPI from sender vCPU to halting vCPU. * Value is arbitrary and was chosen for the alternating bit pattern. Any @@ -79,8 +76,7 @@ struct test_data_page { struct thread_params { struct test_data_page *data; - struct kvm_vm *vm; - uint32_t vcpu_id; + struct kvm_vcpu *vcpu; uint64_t *pipis_rcvd; /* host address of ipis_rcvd global */ }; @@ -198,6 +194,7 @@ static void sender_guest_code(struct test_data_page *data) static void *vcpu_thread(void *arg) { struct thread_params *params = (struct thread_params *)arg; + struct kvm_vcpu *vcpu = params->vcpu; struct ucall uc; int old; int r; @@ -206,17 +203,17 @@ static void *vcpu_thread(void *arg) r = pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &old); TEST_ASSERT(r == 0, "pthread_setcanceltype failed on vcpu_id=%u with errno=%d", - params->vcpu_id, r); + vcpu->id, r); - fprintf(stderr, "vCPU thread running vCPU %u\n", params->vcpu_id); - vcpu_run(params->vm, params->vcpu_id); - exit_reason = vcpu_state(params->vm, params->vcpu_id)->exit_reason; + fprintf(stderr, "vCPU thread running vCPU %u\n", vcpu->id); + vcpu_run(vcpu); + exit_reason = vcpu->run->exit_reason; TEST_ASSERT(exit_reason == KVM_EXIT_IO, "vCPU %u exited with unexpected exit reason %u-%s, expected KVM_EXIT_IO", - params->vcpu_id, exit_reason, exit_reason_str(exit_reason)); + vcpu->id, exit_reason, exit_reason_str(exit_reason)); - if (get_ucall(params->vm, params->vcpu_id, &uc) == UCALL_ABORT) { + if (get_ucall(vcpu, &uc) == UCALL_ABORT) { TEST_ASSERT(false, "vCPU %u exited with error: %s.\n" "Sending vCPU sent %lu IPIs to halting vCPU\n" @@ -224,7 +221,7 @@ static void *vcpu_thread(void *arg) "Halter TPR=%#x PPR=%#x LVR=%#x\n" "Migrations attempted: %lu\n" "Migrations completed: %lu\n", - params->vcpu_id, (const char *)uc.args[0], + vcpu->id, (const char *)uc.args[0], params->data->ipis_sent, params->data->hlt_count, params->data->wake_count, *params->pipis_rcvd, params->data->halter_tpr, @@ -236,7 +233,7 @@ static void *vcpu_thread(void *arg) return NULL; } -static void cancel_join_vcpu_thread(pthread_t thread, uint32_t vcpu_id) +static void cancel_join_vcpu_thread(pthread_t thread, struct kvm_vcpu *vcpu) { void *retval; int r; @@ -244,12 +241,12 @@ static void cancel_join_vcpu_thread(pthread_t thread, uint32_t vcpu_id) r = pthread_cancel(thread); TEST_ASSERT(r == 0, "pthread_cancel on vcpu_id=%d failed with errno=%d", - vcpu_id, r); + vcpu->id, r); r = pthread_join(thread, &retval); TEST_ASSERT(r == 0, "pthread_join on vcpu_id=%d failed with errno=%d", - vcpu_id, r); + vcpu->id, r); TEST_ASSERT(retval == PTHREAD_CANCELED, "expected retval=%p, got %p", PTHREAD_CANCELED, retval); @@ -415,34 +412,30 @@ int main(int argc, char *argv[]) if (delay_usecs <= 0) delay_usecs = DEFAULT_DELAY_USECS; - vm = vm_create_default(HALTER_VCPU_ID, 0, halter_guest_code); - params[0].vm = vm; - params[1].vm = vm; + vm = vm_create_with_one_vcpu(¶ms[0].vcpu, halter_guest_code); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, HALTER_VCPU_ID); + vcpu_init_descriptor_tables(params[0].vcpu); vm_install_exception_handler(vm, IPI_VECTOR, guest_ipi_handler); virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); - vm_vcpu_add_default(vm, SENDER_VCPU_ID, sender_guest_code); + params[1].vcpu = vm_vcpu_add(vm, 1, sender_guest_code); test_data_page_vaddr = vm_vaddr_alloc_page(vm); - data = - (struct test_data_page *)addr_gva2hva(vm, test_data_page_vaddr); + data = addr_gva2hva(vm, test_data_page_vaddr); memset(data, 0, sizeof(*data)); params[0].data = data; params[1].data = data; - vcpu_args_set(vm, HALTER_VCPU_ID, 1, test_data_page_vaddr); - vcpu_args_set(vm, SENDER_VCPU_ID, 1, test_data_page_vaddr); + vcpu_args_set(params[0].vcpu, 1, test_data_page_vaddr); + vcpu_args_set(params[1].vcpu, 1, test_data_page_vaddr); pipis_rcvd = (uint64_t *)addr_gva2hva(vm, (uint64_t)&ipis_rcvd); params[0].pipis_rcvd = pipis_rcvd; params[1].pipis_rcvd = pipis_rcvd; /* Start halter vCPU thread and wait for it to execute first HLT. */ - params[0].vcpu_id = HALTER_VCPU_ID; r = pthread_create(&threads[0], NULL, vcpu_thread, ¶ms[0]); TEST_ASSERT(r == 0, "pthread_create halter failed errno=%d", errno); @@ -462,7 +455,6 @@ int main(int argc, char *argv[]) "Halter vCPU thread reported its APIC ID: %u after %d seconds.\n", data->halter_apic_id, wait_secs); - params[1].vcpu_id = SENDER_VCPU_ID; r = pthread_create(&threads[1], NULL, vcpu_thread, ¶ms[1]); TEST_ASSERT(r == 0, "pthread_create sender failed errno=%d", errno); @@ -478,8 +470,8 @@ int main(int argc, char *argv[]) /* * Cancel threads and wait for them to stop. */ - cancel_join_vcpu_thread(threads[0], HALTER_VCPU_ID); - cancel_join_vcpu_thread(threads[1], SENDER_VCPU_ID); + cancel_join_vcpu_thread(threads[0], params[0].vcpu); + cancel_join_vcpu_thread(threads[1], params[1].vcpu); fprintf(stderr, "Test successful after running for %d seconds.\n" diff --git a/tools/testing/selftests/kvm/x86_64/xapic_state_test.c b/tools/testing/selftests/kvm/x86_64/xapic_state_test.c index 0792334ba243..6f7a5ef66718 100644 --- a/tools/testing/selftests/kvm/x86_64/xapic_state_test.c +++ b/tools/testing/selftests/kvm/x86_64/xapic_state_test.c @@ -11,8 +11,8 @@ #include "processor.h" #include "test_util.h" -struct kvm_vcpu { - uint32_t id; +struct xapic_vcpu { + struct kvm_vcpu *vcpu; bool is_x2apic; }; @@ -47,8 +47,9 @@ static void x2apic_guest_code(void) } while (1); } -static void ____test_icr(struct kvm_vm *vm, struct kvm_vcpu *vcpu, uint64_t val) +static void ____test_icr(struct xapic_vcpu *x, uint64_t val) { + struct kvm_vcpu *vcpu = x->vcpu; struct kvm_lapic_state xapic; struct ucall uc; uint64_t icr; @@ -58,40 +59,55 @@ static void ____test_icr(struct kvm_vm *vm, struct kvm_vcpu *vcpu, uint64_t val) * all bits are valid and should not be modified by KVM (ignoring the * fact that vectors 0-15 are technically illegal). */ - vcpu_ioctl(vm, vcpu->id, KVM_GET_LAPIC, &xapic); + vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic); *((u32 *)&xapic.regs[APIC_IRR]) = val; *((u32 *)&xapic.regs[APIC_IRR + 0x10]) = val >> 32; - vcpu_ioctl(vm, vcpu->id, KVM_SET_LAPIC, &xapic); + vcpu_ioctl(vcpu, KVM_SET_LAPIC, &xapic); - vcpu_run(vm, vcpu->id); - ASSERT_EQ(get_ucall(vm, vcpu->id, &uc), UCALL_SYNC); + vcpu_run(vcpu); + ASSERT_EQ(get_ucall(vcpu, &uc), UCALL_SYNC); ASSERT_EQ(uc.args[1], val); - vcpu_ioctl(vm, vcpu->id, KVM_GET_LAPIC, &xapic); + vcpu_ioctl(vcpu, KVM_GET_LAPIC, &xapic); icr = (u64)(*((u32 *)&xapic.regs[APIC_ICR])) | (u64)(*((u32 *)&xapic.regs[APIC_ICR2])) << 32; - if (!vcpu->is_x2apic) + if (!x->is_x2apic) { val &= (-1u | (0xffull << (32 + 24))); - ASSERT_EQ(icr, val & ~APIC_ICR_BUSY); + ASSERT_EQ(icr, val & ~APIC_ICR_BUSY); + } else { + ASSERT_EQ(icr & ~APIC_ICR_BUSY, val & ~APIC_ICR_BUSY); + } } -static void __test_icr(struct kvm_vm *vm, struct kvm_vcpu *vcpu, uint64_t val) +#define X2APIC_RSVED_BITS_MASK (GENMASK_ULL(31,20) | \ + GENMASK_ULL(17,16) | \ + GENMASK_ULL(13,13)) + +static void __test_icr(struct xapic_vcpu *x, uint64_t val) { - ____test_icr(vm, vcpu, val | APIC_ICR_BUSY); - ____test_icr(vm, vcpu, val & ~(u64)APIC_ICR_BUSY); + if (x->is_x2apic) { + /* Hardware writing vICR register requires reserved bits 31:20, + * 17:16 and 13 kept as zero to avoid #GP exception. Data value + * written to vICR should mask out those bits above. + */ + val &= ~X2APIC_RSVED_BITS_MASK; + } + ____test_icr(x, val | APIC_ICR_BUSY); + ____test_icr(x, val & ~(u64)APIC_ICR_BUSY); } -static void test_icr(struct kvm_vm *vm, struct kvm_vcpu *vcpu) +static void test_icr(struct xapic_vcpu *x) { + struct kvm_vcpu *vcpu = x->vcpu; uint64_t icr, i, j; icr = APIC_DEST_SELF | APIC_INT_ASSERT | APIC_DM_FIXED; for (i = 0; i <= 0xff; i++) - __test_icr(vm, vcpu, icr | i); + __test_icr(x, icr | i); icr = APIC_INT_ASSERT | APIC_DM_FIXED; for (i = 0; i <= 0xff; i++) - __test_icr(vm, vcpu, icr | i); + __test_icr(x, icr | i); /* * Send all flavors of IPIs to non-existent vCPUs. TODO: use number of @@ -100,32 +116,30 @@ static void test_icr(struct kvm_vm *vm, struct kvm_vcpu *vcpu) icr = APIC_INT_ASSERT | 0xff; for (i = vcpu->id + 1; i < 0xff; i++) { for (j = 0; j < 8; j++) - __test_icr(vm, vcpu, i << (32 + 24) | APIC_INT_ASSERT | (j << 8)); + __test_icr(x, i << (32 + 24) | icr | (j << 8)); } /* And again with a shorthand destination for all types of IPIs. */ icr = APIC_DEST_ALLBUT | APIC_INT_ASSERT; for (i = 0; i < 8; i++) - __test_icr(vm, vcpu, icr | (i << 8)); + __test_icr(x, icr | (i << 8)); /* And a few garbage value, just make sure it's an IRQ (blocked). */ - __test_icr(vm, vcpu, 0xa5a5a5a5a5a5a5a5 & ~APIC_DM_FIXED_MASK); - __test_icr(vm, vcpu, 0x5a5a5a5a5a5a5a5a & ~APIC_DM_FIXED_MASK); - __test_icr(vm, vcpu, -1ull & ~APIC_DM_FIXED_MASK); + __test_icr(x, 0xa5a5a5a5a5a5a5a5 & ~APIC_DM_FIXED_MASK); + __test_icr(x, 0x5a5a5a5a5a5a5a5a & ~APIC_DM_FIXED_MASK); + __test_icr(x, -1ull & ~APIC_DM_FIXED_MASK); } int main(int argc, char *argv[]) { - struct kvm_vcpu vcpu = { - .id = 0, + struct xapic_vcpu x = { + .vcpu = NULL, .is_x2apic = true, }; - struct kvm_cpuid2 *cpuid; struct kvm_vm *vm; - int i; - vm = vm_create_default(vcpu.id, 0, x2apic_guest_code); - test_icr(vm, &vcpu); + vm = vm_create_with_one_vcpu(&x.vcpu, x2apic_guest_code); + test_icr(&x); kvm_vm_free(vm); /* @@ -133,18 +147,12 @@ int main(int argc, char *argv[]) * the guest in order to test AVIC. KVM disallows changing CPUID after * KVM_RUN and AVIC is disabled if _any_ vCPU is allowed to use x2APIC. */ - vm = vm_create_default(vcpu.id, 0, xapic_guest_code); - vcpu.is_x2apic = false; + vm = vm_create_with_one_vcpu(&x.vcpu, xapic_guest_code); + x.is_x2apic = false; - cpuid = vcpu_get_cpuid(vm, vcpu.id); - for (i = 0; i < cpuid->nent; i++) { - if (cpuid->entries[i].function == 1) - break; - } - cpuid->entries[i].ecx &= ~BIT(21); - vcpu_set_cpuid(vm, vcpu.id, cpuid); + vcpu_clear_cpuid_feature(x.vcpu, X86_FEATURE_X2APIC); virt_pg_map(vm, APIC_DEFAULT_GPA, APIC_DEFAULT_GPA); - test_icr(vm, &vcpu); + test_icr(&x); kvm_vm_free(vm); } diff --git a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c index 7a51bb648fbb..8a5cb800f50e 100644 --- a/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c +++ b/tools/testing/selftests/kvm/x86_64/xen_shinfo_test.c @@ -18,8 +18,6 @@ #include -#define VCPU_ID 5 - #define SHINFO_REGION_GVA 0xc0000000ULL #define SHINFO_REGION_GPA 0xc0000000ULL #define SHINFO_REGION_SLOT 10 @@ -42,8 +40,6 @@ #define EVTCHN_TEST2 66 #define EVTCHN_TIMER 13 -static struct kvm_vm *vm; - #define XEN_HYPERCALL_MSR 0x40000000 #define MIN_STEAL_TIME 50000 @@ -344,29 +340,29 @@ static int cmp_timespec(struct timespec *a, struct timespec *b) else return 0; } -struct vcpu_info *vinfo; + +static struct vcpu_info *vinfo; +static struct kvm_vcpu *vcpu; static void handle_alrm(int sig) { if (vinfo) printf("evtchn_upcall_pending 0x%x\n", vinfo->evtchn_upcall_pending); - vcpu_dump(stdout, vm, VCPU_ID, 0); + vcpu_dump(stdout, vcpu, 0); TEST_FAIL("IRQ delivery timed out"); } int main(int argc, char *argv[]) { struct timespec min_ts, max_ts, vm_ts; + struct kvm_vm *vm; bool verbose; verbose = argc > 1 && (!strncmp(argv[1], "-v", 3) || !strncmp(argv[1], "--verbose", 10)); int xen_caps = kvm_check_cap(KVM_CAP_XEN_HVM); - if (!(xen_caps & KVM_XEN_HVM_CONFIG_SHARED_INFO) ) { - print_skip("KVM_XEN_HVM_CONFIG_SHARED_INFO not available"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(xen_caps & KVM_XEN_HVM_CONFIG_SHARED_INFO); bool do_runstate_tests = !!(xen_caps & KVM_XEN_HVM_CONFIG_RUNSTATE); bool do_eventfd_tests = !!(xen_caps & KVM_XEN_HVM_CONFIG_EVTCHN_2LEVEL); @@ -374,8 +370,7 @@ int main(int argc, char *argv[]) clock_gettime(CLOCK_REALTIME, &min_ts); - vm = vm_create_default(VCPU_ID, 0, (void *) guest_code); - vcpu_set_cpuid(vm, VCPU_ID, kvm_get_supported_cpuid()); + vm = vm_create_with_one_vcpu(&vcpu, guest_code); /* Map a region for the shared_info page */ vm_userspace_mem_region_add(vm, VM_MEM_SRC_ANONYMOUS, @@ -425,13 +420,13 @@ int main(int argc, char *argv[]) .type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_INFO, .u.gpa = VCPU_INFO_ADDR, }; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &vi); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &vi); struct kvm_xen_vcpu_attr pvclock = { .type = KVM_XEN_VCPU_ATTR_TYPE_VCPU_TIME_INFO, .u.gpa = PVTIME_ADDR, }; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &pvclock); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &pvclock); struct kvm_xen_hvm_attr vec = { .type = KVM_XEN_ATTR_TYPE_UPCALL_VECTOR, @@ -440,7 +435,7 @@ int main(int argc, char *argv[]) vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &vec); vm_init_descriptor_tables(vm); - vcpu_init_descriptor_tables(vm, VCPU_ID); + vcpu_init_descriptor_tables(vcpu); vm_install_exception_handler(vm, EVTCHN_VECTOR, evtchn_handler); if (do_runstate_tests) { @@ -448,7 +443,7 @@ int main(int argc, char *argv[]) .type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_ADDR, .u.gpa = RUNSTATE_ADDR, }; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &st); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &st); } int irq_fd[2] = { -1, -1 }; @@ -468,16 +463,16 @@ int main(int argc, char *argv[]) irq_routes.entries[0].gsi = 32; irq_routes.entries[0].type = KVM_IRQ_ROUTING_XEN_EVTCHN; irq_routes.entries[0].u.xen_evtchn.port = EVTCHN_TEST1; - irq_routes.entries[0].u.xen_evtchn.vcpu = VCPU_ID; + irq_routes.entries[0].u.xen_evtchn.vcpu = vcpu->id; irq_routes.entries[0].u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; irq_routes.entries[1].gsi = 33; irq_routes.entries[1].type = KVM_IRQ_ROUTING_XEN_EVTCHN; irq_routes.entries[1].u.xen_evtchn.port = EVTCHN_TEST2; - irq_routes.entries[1].u.xen_evtchn.vcpu = VCPU_ID; + irq_routes.entries[1].u.xen_evtchn.vcpu = vcpu->id; irq_routes.entries[1].u.xen_evtchn.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; - vm_ioctl(vm, KVM_SET_GSI_ROUTING, &irq_routes); + vm_ioctl(vm, KVM_SET_GSI_ROUTING, &irq_routes.info); struct kvm_irqfd ifd = { }; @@ -508,14 +503,14 @@ int main(int argc, char *argv[]) .u.evtchn.type = EVTCHNSTAT_interdomain, .u.evtchn.flags = 0, .u.evtchn.deliver.port.port = EVTCHN_TEST1, - .u.evtchn.deliver.port.vcpu = VCPU_ID + 1, + .u.evtchn.deliver.port.vcpu = vcpu->id + 1, .u.evtchn.deliver.port.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL, }; vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &inj); /* Test migration to a different vCPU */ inj.u.evtchn.flags = KVM_XEN_EVTCHN_UPDATE; - inj.u.evtchn.deliver.port.vcpu = VCPU_ID; + inj.u.evtchn.deliver.port.vcpu = vcpu->id; vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &inj); inj.u.evtchn.send_port = 197; @@ -524,7 +519,7 @@ int main(int argc, char *argv[]) inj.u.evtchn.flags = 0; vm_ioctl(vm, KVM_XEN_HVM_SET_ATTR, &inj); - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &tmr); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); } vinfo = addr_gpa2hva(vm, VCPU_INFO_VADDR); vinfo->evtchn_upcall_pending = 0; @@ -535,19 +530,19 @@ int main(int argc, char *argv[]) bool evtchn_irq_expected = false; for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); TEST_ASSERT(run->exit_reason == KVM_EXIT_IO, "Got exit_reason other than KVM_EXIT_IO: %u (%s)\n", run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: { struct kvm_xen_vcpu_attr rst; @@ -574,7 +569,7 @@ int main(int argc, char *argv[]) printf("Testing runstate %s\n", runstate_names[uc.args[1]]); rst.type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_CURRENT; rst.u.runstate.state = uc.args[1]; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &rst); break; case 4: @@ -589,7 +584,7 @@ int main(int argc, char *argv[]) 0x6b6b - rs->time[RUNSTATE_offline]; rst.u.runstate.time_runnable = -rst.u.runstate.time_blocked - rst.u.runstate.time_offline; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &rst); break; case 5: @@ -601,7 +596,7 @@ int main(int argc, char *argv[]) rst.u.runstate.state_entry_time = 0x6b6b + 0x5a; rst.u.runstate.time_blocked = 0x6b6b; rst.u.runstate.time_offline = 0x5a; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &rst); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &rst); break; case 6: @@ -660,7 +655,7 @@ int main(int argc, char *argv[]) struct kvm_irq_routing_xen_evtchn e; e.port = EVTCHN_TEST2; - e.vcpu = VCPU_ID; + e.vcpu = vcpu->id; e.priority = KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL; vm_ioctl(vm, KVM_XEN_HVM_EVTCHN_SEND, &e); @@ -702,7 +697,7 @@ int main(int argc, char *argv[]) case 14: memset(&tmr, 0, sizeof(tmr)); tmr.type = KVM_XEN_VCPU_ATTR_TYPE_TIMER; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_GET_ATTR, &tmr); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr); TEST_ASSERT(tmr.u.timer.port == EVTCHN_TIMER, "Timer port not returned"); TEST_ASSERT(tmr.u.timer.priority == KVM_IRQ_ROUTING_XEN_EVTCHN_PRIO_2LEVEL, @@ -721,8 +716,8 @@ int main(int argc, char *argv[]) if (verbose) printf("Testing restored oneshot timer\n"); - tmr.u.timer.expires_ns = rs->state_entry_time + 100000000, - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &tmr); + tmr.u.timer.expires_ns = rs->state_entry_time + 100000000; + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); evtchn_irq_expected = true; alarm(1); break; @@ -748,8 +743,8 @@ int main(int argc, char *argv[]) if (verbose) printf("Testing SCHEDOP_poll wake on masked event\n"); - tmr.u.timer.expires_ns = rs->state_entry_time + 100000000, - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &tmr); + tmr.u.timer.expires_ns = rs->state_entry_time + 100000000; + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); alarm(1); break; @@ -760,11 +755,11 @@ int main(int argc, char *argv[]) evtchn_irq_expected = true; tmr.u.timer.expires_ns = rs->state_entry_time + 100000000; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &tmr); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); /* Read it back and check the pending time is reported correctly */ tmr.u.timer.expires_ns = 0; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_GET_ATTR, &tmr); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr); TEST_ASSERT(tmr.u.timer.expires_ns == rs->state_entry_time + 100000000, "Timer not reported pending"); alarm(1); @@ -774,7 +769,7 @@ int main(int argc, char *argv[]) TEST_ASSERT(!evtchn_irq_expected, "Expected event channel IRQ but it didn't happen"); /* Read timer and check it is no longer pending */ - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_GET_ATTR, &tmr); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &tmr); TEST_ASSERT(!tmr.u.timer.expires_ns, "Timer still reported pending"); shinfo->evtchn_pending[0] = 0; @@ -783,7 +778,7 @@ int main(int argc, char *argv[]) evtchn_irq_expected = true; tmr.u.timer.expires_ns = rs->state_entry_time - 100000000ULL; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_SET_ATTR, &tmr); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_SET_ATTR, &tmr); alarm(1); break; @@ -853,7 +848,7 @@ int main(int argc, char *argv[]) struct kvm_xen_vcpu_attr rst = { .type = KVM_XEN_VCPU_ATTR_TYPE_RUNSTATE_DATA, }; - vcpu_ioctl(vm, VCPU_ID, KVM_XEN_VCPU_GET_ATTR, &rst); + vcpu_ioctl(vcpu, KVM_XEN_VCPU_GET_ATTR, &rst); if (verbose) { printf("Runstate: %s(%d), entry %" PRIu64 " ns\n", diff --git a/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c b/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c index b30fe9de1d4f..88914d48c65e 100644 --- a/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c +++ b/tools/testing/selftests/kvm/x86_64/xen_vmcall_test.c @@ -11,13 +11,9 @@ #include "kvm_util.h" #include "processor.h" -#define VCPU_ID 5 - #define HCALL_REGION_GPA 0xc0000000ULL #define HCALL_REGION_SLOT 10 -static struct kvm_vm *vm; - #define INPUTVALUE 17 #define ARGVALUE(x) (0xdeadbeef5a5a0000UL + x) #define RETVALUE 0xcafef00dfbfbffffUL @@ -84,14 +80,15 @@ static void guest_code(void) int main(int argc, char *argv[]) { - if (!(kvm_check_cap(KVM_CAP_XEN_HVM) & - KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL) ) { - print_skip("KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL not available"); - exit(KSFT_SKIP); - } + unsigned int xen_caps; + struct kvm_vcpu *vcpu; + struct kvm_vm *vm; - vm = vm_create_default(VCPU_ID, 0, (void *) guest_code); - vcpu_set_hv_cpuid(vm, VCPU_ID); + xen_caps = kvm_check_cap(KVM_CAP_XEN_HVM); + TEST_REQUIRE(xen_caps & KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL); + + vm = vm_create_with_one_vcpu(&vcpu, guest_code); + vcpu_set_hv_cpuid(vcpu); struct kvm_xen_hvm_config hvmc = { .flags = KVM_XEN_HVM_CONFIG_INTERCEPT_HCALL, @@ -105,10 +102,10 @@ int main(int argc, char *argv[]) virt_map(vm, HCALL_REGION_GPA, HCALL_REGION_GPA, 2); for (;;) { - volatile struct kvm_run *run = vcpu_state(vm, VCPU_ID); + volatile struct kvm_run *run = vcpu->run; struct ucall uc; - vcpu_run(vm, VCPU_ID); + vcpu_run(vcpu); if (run->exit_reason == KVM_EXIT_XEN) { ASSERT_EQ(run->xen.type, KVM_EXIT_XEN_HCALL); @@ -130,9 +127,9 @@ int main(int argc, char *argv[]) run->exit_reason, exit_reason_str(run->exit_reason)); - switch (get_ucall(vm, VCPU_ID, &uc)) { + switch (get_ucall(vcpu, &uc)) { case UCALL_ABORT: - TEST_FAIL("%s", (const char *)uc.args[0]); + REPORT_GUEST_ASSERT(uc); /* NOT REACHED */ case UCALL_SYNC: break; diff --git a/tools/testing/selftests/kvm/x86_64/xss_msr_test.c b/tools/testing/selftests/kvm/x86_64/xss_msr_test.c index 3529376747c2..e0ddf47362e7 100644 --- a/tools/testing/selftests/kvm/x86_64/xss_msr_test.c +++ b/tools/testing/selftests/kvm/x86_64/xss_msr_test.c @@ -12,64 +12,44 @@ #include "kvm_util.h" #include "vmx.h" -#define VCPU_ID 1 #define MSR_BITS 64 -#define X86_FEATURE_XSAVES (1<<3) - -bool is_supported_msr(u32 msr_index) -{ - struct kvm_msr_list *list; - bool found = false; - int i; - - list = kvm_get_msr_index_list(); - for (i = 0; i < list->nmsrs; ++i) { - if (list->indices[i] == msr_index) { - found = true; - break; - } - } - - free(list); - return found; -} - int main(int argc, char *argv[]) { - struct kvm_cpuid_entry2 *entry; - bool xss_supported = false; + bool xss_in_msr_list; struct kvm_vm *vm; + struct kvm_vcpu *vcpu; uint64_t xss_val; int i, r; /* Create VM */ - vm = vm_create_default(VCPU_ID, 0, 0); + vm = vm_create_with_one_vcpu(&vcpu, NULL); - if (kvm_get_cpuid_max_basic() >= 0xd) { - entry = kvm_get_supported_cpuid_index(0xd, 1); - xss_supported = entry && !!(entry->eax & X86_FEATURE_XSAVES); - } - if (!xss_supported) { - print_skip("IA32_XSS is not supported by the vCPU"); - exit(KSFT_SKIP); - } + TEST_REQUIRE(kvm_cpu_has(X86_FEATURE_XSAVES)); - xss_val = vcpu_get_msr(vm, VCPU_ID, MSR_IA32_XSS); + xss_val = vcpu_get_msr(vcpu, MSR_IA32_XSS); TEST_ASSERT(xss_val == 0, "MSR_IA32_XSS should be initialized to zero\n"); - vcpu_set_msr(vm, VCPU_ID, MSR_IA32_XSS, xss_val); + vcpu_set_msr(vcpu, MSR_IA32_XSS, xss_val); + /* * At present, KVM only supports a guest IA32_XSS value of 0. Verify * that trying to set the guest IA32_XSS to an unsupported value fails. * Also, in the future when a non-zero value succeeds check that - * IA32_XSS is in the KVM_GET_MSR_INDEX_LIST. + * IA32_XSS is in the list of MSRs to save/restore. */ + xss_in_msr_list = kvm_msr_is_in_save_restore_list(MSR_IA32_XSS); for (i = 0; i < MSR_BITS; ++i) { - r = _vcpu_set_msr(vm, VCPU_ID, MSR_IA32_XSS, 1ull << i); - TEST_ASSERT(r == 0 || is_supported_msr(MSR_IA32_XSS), - "IA32_XSS was able to be set, but was not found in KVM_GET_MSR_INDEX_LIST.\n"); + r = _vcpu_set_msr(vcpu, MSR_IA32_XSS, 1ull << i); + + /* + * Setting a list of MSRs returns the entry that "faulted", or + * the last entry +1 if all MSRs were successfully written. + */ + TEST_ASSERT(!r || r == 1, KVM_IOCTL_ERROR(KVM_SET_MSRS, r)); + TEST_ASSERT(r != 1 || xss_in_msr_list, + "IA32_XSS was able to be set, but was not in save/restore list"); } kvm_vm_free(vm); diff --git a/tools/tracing/rtla/Makefile b/tools/tracing/rtla/Makefile index 3822f4ea5f49..1bea2d16d4c1 100644 --- a/tools/tracing/rtla/Makefile +++ b/tools/tracing/rtla/Makefile @@ -1,6 +1,6 @@ NAME := rtla # Follow the kernel version -VERSION := $(shell cat VERSION 2> /dev/null || make -sC ../../.. kernelversion) +VERSION := $(shell cat VERSION 2> /dev/null || make -sC ../../.. kernelversion | grep -v make) # From libtracefs: # Makefiles suck: This macro sets a default value of $(2) for the diff --git a/tools/tracing/rtla/src/trace.c b/tools/tracing/rtla/src/trace.c index 5784c9f9e570..e1ba6d9f4265 100644 --- a/tools/tracing/rtla/src/trace.c +++ b/tools/tracing/rtla/src/trace.c @@ -134,13 +134,18 @@ void trace_instance_destroy(struct trace_instance *trace) if (trace->inst) { disable_tracer(trace->inst); destroy_instance(trace->inst); + trace->inst = NULL; } - if (trace->seq) + if (trace->seq) { free(trace->seq); + trace->seq = NULL; + } - if (trace->tep) + if (trace->tep) { tep_free(trace->tep); + trace->tep = NULL; + } } /* diff --git a/tools/tracing/rtla/src/utils.c b/tools/tracing/rtla/src/utils.c index 5352167a1e75..663a047f794d 100644 --- a/tools/tracing/rtla/src/utils.c +++ b/tools/tracing/rtla/src/utils.c @@ -106,8 +106,9 @@ int parse_cpu_list(char *cpu_list, char **monitored_cpus) nr_cpus = sysconf(_SC_NPROCESSORS_CONF); - mon_cpus = malloc(nr_cpus * sizeof(char)); - memset(mon_cpus, 0, (nr_cpus * sizeof(char))); + mon_cpus = calloc(nr_cpus, sizeof(char)); + if (!mon_cpus) + goto err; for (p = cpu_list; *p; ) { cpu = atoi(p); @@ -224,7 +225,7 @@ long parse_ns_duration(char *val) #elif __arm__ # define __NR_sched_setattr 380 # define __NR_sched_getattr 381 -#elif __aarch64__ +#elif __aarch64__ || __riscv # define __NR_sched_setattr 274 # define __NR_sched_getattr 275 #elif __powerpc__ diff --git a/tools/usb/testusb.c b/tools/usb/testusb.c index 474bae868b35..cbaa1b9fdeac 100644 --- a/tools/usb/testusb.c +++ b/tools/usb/testusb.c @@ -96,7 +96,10 @@ struct usb_interface_descriptor { enum usb_device_speed { USB_SPEED_UNKNOWN = 0, /* enumerating */ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ - USB_SPEED_HIGH /* usb 2.0 */ + USB_SPEED_HIGH, /* usb 2.0 */ + USB_SPEED_WIRELESS, /* wireless (usb 2.5) */ + USB_SPEED_SUPER, /* usb 3.0 */ + USB_SPEED_SUPER_PLUS, /* usb 3.1 */ }; /*-------------------------------------------------------------------------*/ @@ -104,11 +107,14 @@ enum usb_device_speed { static char *speed (enum usb_device_speed s) { switch (s) { - case USB_SPEED_UNKNOWN: return "unknown"; - case USB_SPEED_LOW: return "low"; - case USB_SPEED_FULL: return "full"; - case USB_SPEED_HIGH: return "high"; - default: return "??"; + case USB_SPEED_UNKNOWN: return "unknown"; + case USB_SPEED_LOW: return "low"; + case USB_SPEED_FULL: return "full"; + case USB_SPEED_HIGH: return "high"; + case USB_SPEED_WIRELESS: return "wireless"; + case USB_SPEED_SUPER: return "super"; + case USB_SPEED_SUPER_PLUS: return "super-plus"; + default: return "??"; } } diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index a49df8988cd6..32896c845ffe 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -168,7 +168,7 @@ __weak void kvm_arch_guest_memory_reclaimed(struct kvm *kvm) { } -bool kvm_is_zone_device_pfn(kvm_pfn_t pfn) +bool kvm_is_zone_device_page(struct page *page) { /* * The metadata used by is_zone_device_page() to determine whether or @@ -176,25 +176,42 @@ bool kvm_is_zone_device_pfn(kvm_pfn_t pfn) * the device has been pinned, e.g. by get_user_pages(). WARN if the * page_count() is zero to help detect bad usage of this helper. */ - if (!pfn_valid(pfn) || WARN_ON_ONCE(!page_count(pfn_to_page(pfn)))) + if (WARN_ON_ONCE(!page_count(page))) return false; - return is_zone_device_page(pfn_to_page(pfn)); + return is_zone_device_page(page); } -bool kvm_is_reserved_pfn(kvm_pfn_t pfn) +/* + * Returns a 'struct page' if the pfn is "valid" and backed by a refcounted + * page, NULL otherwise. Note, the list of refcounted PG_reserved page types + * is likely incomplete, it has been compiled purely through people wanting to + * back guest with a certain type of memory and encountering issues. + */ +struct page *kvm_pfn_to_refcounted_page(kvm_pfn_t pfn) { + struct page *page; + + if (!pfn_valid(pfn)) + return NULL; + + page = pfn_to_page(pfn); + if (!PageReserved(page)) + return page; + + /* The ZERO_PAGE(s) is marked PG_reserved, but is refcounted. */ + if (is_zero_pfn(pfn)) + return page; + /* * ZONE_DEVICE pages currently set PG_reserved, but from a refcounting * perspective they are "normal" pages, albeit with slightly different * usage rules. */ - if (pfn_valid(pfn)) - return PageReserved(pfn_to_page(pfn)) && - !is_zero_pfn(pfn) && - !kvm_is_zone_device_pfn(pfn); + if (kvm_is_zone_device_page(page)) + return page; - return true; + return NULL; } /* @@ -239,7 +256,7 @@ static bool kvm_request_needs_ipi(struct kvm_vcpu *vcpu, unsigned req) return mode == IN_GUEST_MODE; } -static void ack_flush(void *_completed) +static void ack_kick(void *_completed) { } @@ -248,7 +265,7 @@ static inline bool kvm_kick_many_cpus(struct cpumask *cpus, bool wait) if (cpumask_empty(cpus)) return false; - smp_call_function_many(cpus, ack_flush, NULL, wait); + smp_call_function_many(cpus, ack_kick, NULL, wait); return true; } @@ -379,14 +396,31 @@ static inline void *mmu_memory_cache_alloc_obj(struct kvm_mmu_memory_cache *mc, return (void *)__get_free_page(gfp_flags); } -int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min) +int __kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int capacity, int min) { + gfp_t gfp = mc->gfp_custom ? mc->gfp_custom : GFP_KERNEL_ACCOUNT; void *obj; if (mc->nobjs >= min) return 0; - while (mc->nobjs < ARRAY_SIZE(mc->objects)) { - obj = mmu_memory_cache_alloc_obj(mc, GFP_KERNEL_ACCOUNT); + + if (unlikely(!mc->objects)) { + if (WARN_ON_ONCE(!capacity)) + return -EIO; + + mc->objects = kvmalloc_array(sizeof(void *), capacity, gfp); + if (!mc->objects) + return -ENOMEM; + + mc->capacity = capacity; + } + + /* It is illegal to request a different capacity across topups. */ + if (WARN_ON_ONCE(mc->capacity != capacity)) + return -EIO; + + while (mc->nobjs < mc->capacity) { + obj = mmu_memory_cache_alloc_obj(mc, gfp); if (!obj) return mc->nobjs >= min ? 0 : -ENOMEM; mc->objects[mc->nobjs++] = obj; @@ -394,6 +428,11 @@ int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min) return 0; } +int kvm_mmu_topup_memory_cache(struct kvm_mmu_memory_cache *mc, int min) +{ + return __kvm_mmu_topup_memory_cache(mc, KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE, min); +} + int kvm_mmu_memory_cache_nr_free_objects(struct kvm_mmu_memory_cache *mc) { return mc->nobjs; @@ -407,6 +446,11 @@ void kvm_mmu_free_memory_cache(struct kvm_mmu_memory_cache *mc) else free_page((unsigned long)mc->objects[--mc->nobjs]); } + + kvfree(mc->objects); + + mc->objects = NULL; + mc->capacity = 0; } void *kvm_mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc) @@ -724,6 +768,15 @@ static int kvm_mmu_notifier_invalidate_range_start(struct mmu_notifier *mn, kvm->mn_active_invalidate_count++; spin_unlock(&kvm->mn_invalidate_lock); + /* + * Invalidate pfn caches _before_ invalidating the secondary MMUs, i.e. + * before acquiring mmu_lock, to avoid holding mmu_lock while acquiring + * each cache's lock. There are relatively few caches in existence at + * any given time, and the caches themselves can check for hva overlap, + * i.e. don't need to rely on memslot overlap checks for performance. + * Because this runs without holding mmu_lock, the pfn caches must use + * mn_active_invalidate_count (see above) instead of mmu_notifier_count. + */ gfn_to_pfn_cache_invalidate_start(kvm, range->start, range->end, hva_range.may_block); @@ -2492,9 +2545,12 @@ static bool vma_is_valid(struct vm_area_struct *vma, bool write_fault) static int kvm_try_get_pfn(kvm_pfn_t pfn) { - if (kvm_is_reserved_pfn(pfn)) + struct page *page = kvm_pfn_to_refcounted_page(pfn); + + if (!page) return 1; - return get_page_unless_zero(pfn_to_page(pfn)); + + return get_page_unless_zero(page); } static int hva_to_pfn_remapped(struct vm_area_struct *vma, @@ -2580,7 +2636,7 @@ kvm_pfn_t hva_to_pfn(unsigned long addr, bool atomic, bool *async, bool write_fault, bool *writable) { struct vm_area_struct *vma; - kvm_pfn_t pfn = 0; + kvm_pfn_t pfn; int npages, r; /* we can do it either atomically or asynchronously, not both */ @@ -2711,34 +2767,32 @@ int gfn_to_page_many_atomic(struct kvm_memory_slot *slot, gfn_t gfn, } EXPORT_SYMBOL_GPL(gfn_to_page_many_atomic); -static struct page *kvm_pfn_to_page(kvm_pfn_t pfn) -{ - if (is_error_noslot_pfn(pfn)) - return KVM_ERR_PTR_BAD_PAGE; - - if (kvm_is_reserved_pfn(pfn)) { - WARN_ON(1); - return KVM_ERR_PTR_BAD_PAGE; - } - - return pfn_to_page(pfn); -} - +/* + * Do not use this helper unless you are absolutely certain the gfn _must_ be + * backed by 'struct page'. A valid example is if the backing memslot is + * controlled by KVM. Note, if the returned page is valid, it's refcount has + * been elevated by gfn_to_pfn(). + */ struct page *gfn_to_page(struct kvm *kvm, gfn_t gfn) { + struct page *page; kvm_pfn_t pfn; pfn = gfn_to_pfn(kvm, gfn); - return kvm_pfn_to_page(pfn); + if (is_error_noslot_pfn(pfn)) + return KVM_ERR_PTR_BAD_PAGE; + + page = kvm_pfn_to_refcounted_page(pfn); + if (!page) + return KVM_ERR_PTR_BAD_PAGE; + + return page; } EXPORT_SYMBOL_GPL(gfn_to_page); void kvm_release_pfn(kvm_pfn_t pfn, bool dirty) { - if (pfn == 0) - return; - if (dirty) kvm_release_pfn_dirty(pfn); else @@ -2804,28 +2858,48 @@ void kvm_vcpu_unmap(struct kvm_vcpu *vcpu, struct kvm_host_map *map, bool dirty) } EXPORT_SYMBOL_GPL(kvm_vcpu_unmap); -struct page *kvm_vcpu_gfn_to_page(struct kvm_vcpu *vcpu, gfn_t gfn) +static bool kvm_is_ad_tracked_page(struct page *page) { - kvm_pfn_t pfn; - - pfn = kvm_vcpu_gfn_to_pfn(vcpu, gfn); - - return kvm_pfn_to_page(pfn); + /* + * Per page-flags.h, pages tagged PG_reserved "should in general not be + * touched (e.g. set dirty) except by its owner". + */ + return !PageReserved(page); +} + +static void kvm_set_page_dirty(struct page *page) +{ + if (kvm_is_ad_tracked_page(page)) + SetPageDirty(page); +} + +static void kvm_set_page_accessed(struct page *page) +{ + if (kvm_is_ad_tracked_page(page)) + mark_page_accessed(page); } -EXPORT_SYMBOL_GPL(kvm_vcpu_gfn_to_page); void kvm_release_page_clean(struct page *page) { WARN_ON(is_error_page(page)); - kvm_release_pfn_clean(page_to_pfn(page)); + kvm_set_page_accessed(page); + put_page(page); } EXPORT_SYMBOL_GPL(kvm_release_page_clean); void kvm_release_pfn_clean(kvm_pfn_t pfn) { - if (!is_error_noslot_pfn(pfn) && !kvm_is_reserved_pfn(pfn)) - put_page(pfn_to_page(pfn)); + struct page *page; + + if (is_error_noslot_pfn(pfn)) + return; + + page = kvm_pfn_to_refcounted_page(pfn); + if (!page) + return; + + kvm_release_page_clean(page); } EXPORT_SYMBOL_GPL(kvm_release_pfn_clean); @@ -2833,28 +2907,48 @@ void kvm_release_page_dirty(struct page *page) { WARN_ON(is_error_page(page)); - kvm_release_pfn_dirty(page_to_pfn(page)); + kvm_set_page_dirty(page); + kvm_release_page_clean(page); } EXPORT_SYMBOL_GPL(kvm_release_page_dirty); void kvm_release_pfn_dirty(kvm_pfn_t pfn) { - kvm_set_pfn_dirty(pfn); - kvm_release_pfn_clean(pfn); + struct page *page; + + if (is_error_noslot_pfn(pfn)) + return; + + page = kvm_pfn_to_refcounted_page(pfn); + if (!page) + return; + + kvm_release_page_dirty(page); } EXPORT_SYMBOL_GPL(kvm_release_pfn_dirty); +/* + * Note, checking for an error/noslot pfn is the caller's responsibility when + * directly marking a page dirty/accessed. Unlike the "release" helpers, the + * "set" helpers are not to be used when the pfn might point at garbage. + */ void kvm_set_pfn_dirty(kvm_pfn_t pfn) { - if (!kvm_is_reserved_pfn(pfn) && !kvm_is_zone_device_pfn(pfn)) - SetPageDirty(pfn_to_page(pfn)); + if (WARN_ON(is_error_noslot_pfn(pfn))) + return; + + if (pfn_valid(pfn)) + kvm_set_page_dirty(pfn_to_page(pfn)); } EXPORT_SYMBOL_GPL(kvm_set_pfn_dirty); void kvm_set_pfn_accessed(kvm_pfn_t pfn) { - if (!kvm_is_reserved_pfn(pfn) && !kvm_is_zone_device_pfn(pfn)) - mark_page_accessed(pfn_to_page(pfn)); + if (WARN_ON(is_error_noslot_pfn(pfn))) + return; + + if (pfn_valid(pfn)) + kvm_set_page_accessed(pfn_to_page(pfn)); } EXPORT_SYMBOL_GPL(kvm_set_pfn_accessed); @@ -3728,9 +3822,18 @@ static int create_vcpu_fd(struct kvm_vcpu *vcpu) return anon_inode_getfd(name, &kvm_vcpu_fops, vcpu, O_RDWR | O_CLOEXEC); } +#ifdef __KVM_HAVE_ARCH_VCPU_DEBUGFS +static int vcpu_get_pid(void *data, u64 *val) +{ + struct kvm_vcpu *vcpu = (struct kvm_vcpu *) data; + *val = pid_nr(rcu_access_pointer(vcpu->pid)); + return 0; +} + +DEFINE_SIMPLE_ATTRIBUTE(vcpu_get_pid_fops, vcpu_get_pid, NULL, "%llu\n"); + static void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) { -#ifdef __KVM_HAVE_ARCH_VCPU_DEBUGFS struct dentry *debugfs_dentry; char dir_name[ITOA_MAX_LEN * 2]; @@ -3740,10 +3843,12 @@ static void kvm_create_vcpu_debugfs(struct kvm_vcpu *vcpu) snprintf(dir_name, sizeof(dir_name), "vcpu%d", vcpu->vcpu_id); debugfs_dentry = debugfs_create_dir(dir_name, vcpu->kvm->debugfs_dentry); + debugfs_create_file("pid", 0444, debugfs_dentry, vcpu, + &vcpu_get_pid_fops); kvm_arch_create_vcpu_debugfs(vcpu, debugfs_dentry); -#endif } +#endif /* * Creates some virtual cpus. Good luck creating more than one. @@ -3763,13 +3868,15 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) return -EINVAL; } + r = kvm_arch_vcpu_precreate(kvm, id); + if (r) { + mutex_unlock(&kvm->lock); + return r; + } + kvm->created_vcpus++; mutex_unlock(&kvm->lock); - r = kvm_arch_vcpu_precreate(kvm, id); - if (r) - goto vcpu_decrement; - vcpu = kmem_cache_zalloc(kvm_vcpu_cache, GFP_KERNEL_ACCOUNT); if (!vcpu) { r = -ENOMEM; diff --git a/virt/kvm/pfncache.c b/virt/kvm/pfncache.c index dd84676615f1..ab519f72f2cd 100644 --- a/virt/kvm/pfncache.c +++ b/virt/kvm/pfncache.c @@ -95,48 +95,143 @@ bool kvm_gfn_to_pfn_cache_check(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, } EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_check); -static void __release_gpc(struct kvm *kvm, kvm_pfn_t pfn, void *khva, gpa_t gpa) +static void gpc_unmap_khva(struct kvm *kvm, kvm_pfn_t pfn, void *khva) { - /* Unmap the old page if it was mapped before, and release it */ - if (!is_error_noslot_pfn(pfn)) { - if (khva) { - if (pfn_valid(pfn)) - kunmap(pfn_to_page(pfn)); + /* Unmap the old pfn/page if it was mapped before. */ + if (!is_error_noslot_pfn(pfn) && khva) { + if (pfn_valid(pfn)) + kunmap(pfn_to_page(pfn)); #ifdef CONFIG_HAS_IOMEM - else - memunmap(khva); + else + memunmap(khva); #endif - } - - kvm_release_pfn(pfn, false); } } -static kvm_pfn_t hva_to_pfn_retry(struct kvm *kvm, unsigned long uhva) +static inline bool mmu_notifier_retry_cache(struct kvm *kvm, unsigned long mmu_seq) { + /* + * mn_active_invalidate_count acts for all intents and purposes + * like mmu_notifier_count here; but the latter cannot be used + * here because the invalidation of caches in the mmu_notifier + * event occurs _before_ mmu_notifier_count is elevated. + * + * Note, it does not matter that mn_active_invalidate_count + * is not protected by gpc->lock. It is guaranteed to + * be elevated before the mmu_notifier acquires gpc->lock, and + * isn't dropped until after mmu_notifier_seq is updated. + */ + if (kvm->mn_active_invalidate_count) + return true; + + /* + * Ensure mn_active_invalidate_count is read before + * mmu_notifier_seq. This pairs with the smp_wmb() in + * mmu_notifier_invalidate_range_end() to guarantee either the + * old (non-zero) value of mn_active_invalidate_count or the + * new (incremented) value of mmu_notifier_seq is observed. + */ + smp_rmb(); + return kvm->mmu_notifier_seq != mmu_seq; +} + +static kvm_pfn_t hva_to_pfn_retry(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) +{ + /* Note, the new page offset may be different than the old! */ + void *old_khva = gpc->khva - offset_in_page(gpc->khva); + kvm_pfn_t new_pfn = KVM_PFN_ERR_FAULT; + void *new_khva = NULL; unsigned long mmu_seq; - kvm_pfn_t new_pfn; - int retry; + + lockdep_assert_held(&gpc->refresh_lock); + + lockdep_assert_held_write(&gpc->lock); + + /* + * Invalidate the cache prior to dropping gpc->lock, the gpa=>uhva + * assets have already been updated and so a concurrent check() from a + * different task may not fail the gpa/uhva/generation checks. + */ + gpc->valid = false; do { mmu_seq = kvm->mmu_notifier_seq; smp_rmb(); + write_unlock_irq(&gpc->lock); + + /* + * If the previous iteration "failed" due to an mmu_notifier + * event, release the pfn and unmap the kernel virtual address + * from the previous attempt. Unmapping might sleep, so this + * needs to be done after dropping the lock. Opportunistically + * check for resched while the lock isn't held. + */ + if (new_pfn != KVM_PFN_ERR_FAULT) { + /* + * Keep the mapping if the previous iteration reused + * the existing mapping and didn't create a new one. + */ + if (new_khva != old_khva) + gpc_unmap_khva(kvm, new_pfn, new_khva); + + kvm_release_pfn_clean(new_pfn); + + cond_resched(); + } + /* We always request a writeable mapping */ - new_pfn = hva_to_pfn(uhva, false, NULL, true, NULL); + new_pfn = hva_to_pfn(gpc->uhva, false, NULL, true, NULL); if (is_error_noslot_pfn(new_pfn)) - break; + goto out_error; - KVM_MMU_READ_LOCK(kvm); - retry = mmu_notifier_retry_hva(kvm, mmu_seq, uhva); - KVM_MMU_READ_UNLOCK(kvm); - if (!retry) - break; + /* + * Obtain a new kernel mapping if KVM itself will access the + * pfn. Note, kmap() and memremap() can both sleep, so this + * too must be done outside of gpc->lock! + */ + if (gpc->usage & KVM_HOST_USES_PFN) { + if (new_pfn == gpc->pfn) { + new_khva = old_khva; + } else if (pfn_valid(new_pfn)) { + new_khva = kmap(pfn_to_page(new_pfn)); +#ifdef CONFIG_HAS_IOMEM + } else { + new_khva = memremap(pfn_to_hpa(new_pfn), PAGE_SIZE, MEMREMAP_WB); +#endif + } + if (!new_khva) { + kvm_release_pfn_clean(new_pfn); + goto out_error; + } + } - cond_resched(); - } while (1); + write_lock_irq(&gpc->lock); - return new_pfn; + /* + * Other tasks must wait for _this_ refresh to complete before + * attempting to refresh. + */ + WARN_ON_ONCE(gpc->valid); + } while (mmu_notifier_retry_cache(kvm, mmu_seq)); + + gpc->valid = true; + gpc->pfn = new_pfn; + gpc->khva = new_khva + (gpc->gpa & ~PAGE_MASK); + + /* + * Put the reference to the _new_ pfn. The pfn is now tracked by the + * cache and can be safely migrated, swapped, etc... as the cache will + * invalidate any mappings in response to relevant mmu_notifier events. + */ + kvm_release_pfn_clean(new_pfn); + + return 0; + +out_error: + write_lock_irq(&gpc->lock); + + return -EFAULT; } int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, @@ -146,9 +241,7 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, unsigned long page_offset = gpa & ~PAGE_MASK; kvm_pfn_t old_pfn, new_pfn; unsigned long old_uhva; - gpa_t old_gpa; void *old_khva; - bool old_valid; int ret = 0; /* @@ -158,13 +251,18 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, if (page_offset + len > PAGE_SIZE) return -EINVAL; + /* + * If another task is refreshing the cache, wait for it to complete. + * There is no guarantee that concurrent refreshes will see the same + * gpa, memslots generation, etc..., so they must be fully serialized. + */ + mutex_lock(&gpc->refresh_lock); + write_lock_irq(&gpc->lock); - old_gpa = gpc->gpa; old_pfn = gpc->pfn; old_khva = gpc->khva - offset_in_page(gpc->khva); old_uhva = gpc->uhva; - old_valid = gpc->valid; /* If the userspace HVA is invalid, refresh that first */ if (gpc->gpa != gpa || gpc->generation != slots->generation || @@ -177,64 +275,17 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, gpc->uhva = gfn_to_hva_memslot(gpc->memslot, gfn); if (kvm_is_error_hva(gpc->uhva)) { - gpc->pfn = KVM_PFN_ERR_FAULT; ret = -EFAULT; goto out; } - - gpc->uhva += page_offset; } /* * If the userspace HVA changed or the PFN was already invalid, * drop the lock and do the HVA to PFN lookup again. */ - if (!old_valid || old_uhva != gpc->uhva) { - unsigned long uhva = gpc->uhva; - void *new_khva = NULL; - - /* Placeholders for "hva is valid but not yet mapped" */ - gpc->pfn = KVM_PFN_ERR_FAULT; - gpc->khva = NULL; - gpc->valid = true; - - write_unlock_irq(&gpc->lock); - - new_pfn = hva_to_pfn_retry(kvm, uhva); - if (is_error_noslot_pfn(new_pfn)) { - ret = -EFAULT; - goto map_done; - } - - if (gpc->usage & KVM_HOST_USES_PFN) { - if (new_pfn == old_pfn) { - new_khva = old_khva; - old_pfn = KVM_PFN_ERR_FAULT; - old_khva = NULL; - } else if (pfn_valid(new_pfn)) { - new_khva = kmap(pfn_to_page(new_pfn)); -#ifdef CONFIG_HAS_IOMEM - } else { - new_khva = memremap(pfn_to_hpa(new_pfn), PAGE_SIZE, MEMREMAP_WB); -#endif - } - if (new_khva) - new_khva += page_offset; - else - ret = -EFAULT; - } - - map_done: - write_lock_irq(&gpc->lock); - if (ret) { - gpc->valid = false; - gpc->pfn = KVM_PFN_ERR_FAULT; - gpc->khva = NULL; - } else { - /* At this point, gpc->valid may already have been cleared */ - gpc->pfn = new_pfn; - gpc->khva = new_khva; - } + if (!gpc->valid || old_uhva != gpc->uhva) { + ret = hva_to_pfn_retry(kvm, gpc); } else { /* If the HVA→PFN mapping was already valid, don't unmap it. */ old_pfn = KVM_PFN_ERR_FAULT; @@ -242,9 +293,26 @@ int kvm_gfn_to_pfn_cache_refresh(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, } out: + /* + * Invalidate the cache and purge the pfn/khva if the refresh failed. + * Some/all of the uhva, gpa, and memslot generation info may still be + * valid, leave it as is. + */ + if (ret) { + gpc->valid = false; + gpc->pfn = KVM_PFN_ERR_FAULT; + gpc->khva = NULL; + } + + /* Snapshot the new pfn before dropping the lock! */ + new_pfn = gpc->pfn; + write_unlock_irq(&gpc->lock); - __release_gpc(kvm, old_pfn, old_khva, old_gpa); + mutex_unlock(&gpc->refresh_lock); + + if (old_pfn != new_pfn) + gpc_unmap_khva(kvm, old_pfn, old_khva); return ret; } @@ -254,14 +322,13 @@ void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) { void *old_khva; kvm_pfn_t old_pfn; - gpa_t old_gpa; + mutex_lock(&gpc->refresh_lock); write_lock_irq(&gpc->lock); gpc->valid = false; old_khva = gpc->khva - offset_in_page(gpc->khva); - old_gpa = gpc->gpa; old_pfn = gpc->pfn; /* @@ -272,8 +339,9 @@ void kvm_gfn_to_pfn_cache_unmap(struct kvm *kvm, struct gfn_to_pfn_cache *gpc) gpc->pfn = KVM_PFN_ERR_FAULT; write_unlock_irq(&gpc->lock); + mutex_unlock(&gpc->refresh_lock); - __release_gpc(kvm, old_pfn, old_khva, old_gpa); + gpc_unmap_khva(kvm, old_pfn, old_khva); } EXPORT_SYMBOL_GPL(kvm_gfn_to_pfn_cache_unmap); @@ -286,6 +354,7 @@ int kvm_gfn_to_pfn_cache_init(struct kvm *kvm, struct gfn_to_pfn_cache *gpc, if (!gpc->active) { rwlock_init(&gpc->lock); + mutex_init(&gpc->refresh_lock); gpc->khva = NULL; gpc->pfn = KVM_PFN_ERR_FAULT;