diff options
476 files changed, 9580 insertions, 5291 deletions
diff --git a/.get_maintainer.ignore b/.get_maintainer.ignore new file mode 100644 index 000000000000..cca6d870f7a5 --- /dev/null +++ b/.get_maintainer.ignore @@ -0,0 +1 @@ +Christoph Hellwig <hch@lst.de> @@ -17,6 +17,7 @@ Aleksey Gorelov <aleksey_gorelov@phoenix.com> Al Viro <viro@ftp.linux.org.uk> Al Viro <viro@zenIV.linux.org.uk> Andreas Herrmann <aherrman@de.ibm.com> +Andrey Ryabinin <ryabinin.a.a@gmail.com> <a.ryabinin@samsung.com> Andrew Morton <akpm@linux-foundation.org> Andrew Vasquez <andrew.vasquez@qlogic.com> Andy Adamson <andros@citi.umich.edu> diff --git a/Documentation/ABI/stable/sysfs-bus-vmbus b/Documentation/ABI/stable/sysfs-bus-vmbus new file mode 100644 index 000000000000..636e938d5e33 --- /dev/null +++ b/Documentation/ABI/stable/sysfs-bus-vmbus @@ -0,0 +1,29 @@ +What: /sys/bus/vmbus/devices/vmbus_*/id +Date: Jul 2009 +KernelVersion: 2.6.31 +Contact: K. Y. Srinivasan <kys@microsoft.com> +Description: The VMBus child_relid of the device's primary channel +Users: tools/hv/lsvmbus + +What: /sys/bus/vmbus/devices/vmbus_*/class_id +Date: Jul 2009 +KernelVersion: 2.6.31 +Contact: K. Y. Srinivasan <kys@microsoft.com> +Description: The VMBus interface type GUID of the device +Users: tools/hv/lsvmbus + +What: /sys/bus/vmbus/devices/vmbus_*/device_id +Date: Jul 2009 +KernelVersion: 2.6.31 +Contact: K. Y. Srinivasan <kys@microsoft.com> +Description: The VMBus interface instance GUID of the device +Users: tools/hv/lsvmbus + +What: /sys/bus/vmbus/devices/vmbus_*/channel_vp_mapping +Date: Jul 2015 +KernelVersion: 4.2.0 +Contact: K. Y. Srinivasan <kys@microsoft.com> +Description: The mapping of which primary/sub channels are bound to which + Virtual Processors. + Format: <channel's child_relid:the bound cpu's number> +Users: tools/hv/lsvmbus diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x index b4d0b99afffb..d72ca1736ba4 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm3x @@ -112,7 +112,7 @@ KernelVersion: 3.19 Contact: Mathieu Poirier <mathieu.poirier@linaro.org> Description: (RW) Mask to apply to all the context ID comparator. -What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/ctxid_val +What: /sys/bus/coresight/devices/<memory_map>.[etm|ptm]/ctxid_pid Date: November 2014 KernelVersion: 3.19 Contact: Mathieu Poirier <mathieu.poirier@linaro.org> diff --git a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x index 2fe2e3dae487..2355ed8ae31f 100644 --- a/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x +++ b/Documentation/ABI/testing/sysfs-bus-coresight-devices-etm4x @@ -249,7 +249,7 @@ KernelVersion: 4.01 Contact: Mathieu Poirier <mathieu.poirier@linaro.org> Description: (RW) Select which context ID comparator to work with. -What: /sys/bus/coresight/devices/<memory_map>.etm/ctxid_val +What: /sys/bus/coresight/devices/<memory_map>.etm/ctxid_pid Date: April 2015 KernelVersion: 4.01 Contact: Mathieu Poirier <mathieu.poirier@linaro.org> diff --git a/Documentation/ABI/testing/sysfs-driver-sunxi-sid b/Documentation/ABI/testing/sysfs-driver-sunxi-sid deleted file mode 100644 index ffb9536f6ecc..000000000000 --- a/Documentation/ABI/testing/sysfs-driver-sunxi-sid +++ /dev/null @@ -1,22 +0,0 @@ -What: /sys/devices/*/<our-device>/eeprom -Date: August 2013 -Contact: Oliver Schinagl <oliver@schinagl.nl> -Description: read-only access to the SID (Security-ID) on current - A-series SoC's from Allwinner. Currently supports A10, A10s, A13 - and A20 CPU's. The earlier A1x series of SoCs exports 16 bytes, - whereas the newer A20 SoC exposes 512 bytes split into sections. - Besides the 16 bytes of SID, there's also an SJTAG area, - HDMI-HDCP key and some custom keys. Below a quick overview, for - details see the user manual: - 0x000 128 bit root-key (sun[457]i) - 0x010 128 bit boot-key (sun7i) - 0x020 64 bit security-jtag-key (sun7i) - 0x028 16 bit key configuration (sun7i) - 0x02b 16 bit custom-vendor-key (sun7i) - 0x02c 320 bit low general key (sun7i) - 0x040 32 bit read-control access (sun7i) - 0x064 224 bit low general key (sun7i) - 0x080 2304 bit HDCP-key (sun7i) - 0x1a0 768 bit high general key (sun7i) -Users: any user space application which wants to read the SID on - Allwinner's A-series of CPU's. diff --git a/Documentation/devicetree/bindings/arm/coresight.txt b/Documentation/devicetree/bindings/arm/coresight.txt index 65a6db2271a2..62938eb9697f 100644 --- a/Documentation/devicetree/bindings/arm/coresight.txt +++ b/Documentation/devicetree/bindings/arm/coresight.txt @@ -17,6 +17,7 @@ its hardware characteristcs. - "arm,coresight-tmc", "arm,primecell"; - "arm,coresight-funnel", "arm,primecell"; - "arm,coresight-etm3x", "arm,primecell"; + - "arm,coresight-etm4x", "arm,primecell"; - "qcom,coresight-replicator1x", "arm,primecell"; * reg: physical base address and length of the register diff --git a/Documentation/devicetree/bindings/arm/cpus.txt b/Documentation/devicetree/bindings/arm/cpus.txt index d6b794cef0b8..91e6e5c478d0 100644 --- a/Documentation/devicetree/bindings/arm/cpus.txt +++ b/Documentation/devicetree/bindings/arm/cpus.txt @@ -199,6 +199,7 @@ nodes to be present and contain the properties described below. "qcom,kpss-acc-v1" "qcom,kpss-acc-v2" "rockchip,rk3066-smp" + "ste,dbx500-smp" - cpu-release-addr Usage: required for systems that have an "enable-method" diff --git a/Documentation/devicetree/bindings/extcon/extcon-palmas.txt b/Documentation/devicetree/bindings/extcon/extcon-palmas.txt index 45414bbcd945..f61d5af44a27 100644 --- a/Documentation/devicetree/bindings/extcon/extcon-palmas.txt +++ b/Documentation/devicetree/bindings/extcon/extcon-palmas.txt @@ -10,8 +10,11 @@ Required Properties: Optional Properties: - ti,wakeup : To enable the wakeup comparator in probe - - ti,enable-id-detection: Perform ID detection. + - ti,enable-id-detection: Perform ID detection. If id-gpio is specified + it performs id-detection using GPIO else using OTG core. - ti,enable-vbus-detection: Perform VBUS detection. + - id-gpio: gpio for GPIO ID detection. See gpio binding. + - debounce-delay-ms: debounce delay for GPIO ID pin in milliseconds. palmas-usb { compatible = "ti,twl6035-usb", "ti,palmas-usb"; diff --git a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt index fabdf64a5737..d543ed3f5363 100644 --- a/Documentation/devicetree/bindings/misc/allwinner,sunxi-sid.txt +++ b/Documentation/devicetree/bindings/nvmem/allwinner,sunxi-sid.txt @@ -4,6 +4,10 @@ Required properties: - compatible: "allwinner,sun4i-a10-sid" or "allwinner,sun7i-a20-sid" - reg: Should contain registers location and length += Data cells = +Are child nodes of qfprom, bindings of which as described in +bindings/nvmem/nvmem.txt + Example for sun4i: sid@01c23800 { compatible = "allwinner,sun4i-a10-sid"; diff --git a/Documentation/devicetree/bindings/nvmem/nvmem.txt b/Documentation/devicetree/bindings/nvmem/nvmem.txt new file mode 100644 index 000000000000..b52bc11e9597 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/nvmem.txt @@ -0,0 +1,80 @@ += NVMEM(Non Volatile Memory) Data Device Tree Bindings = + +This binding is intended to represent the location of hardware +configuration data stored in NVMEMs like eeprom, efuses and so on. + +On a significant proportion of boards, the manufacturer has stored +some data on NVMEM, for the OS to be able to retrieve these information +and act upon it. Obviously, the OS has to know about where to retrieve +these data from, and where they are stored on the storage device. + +This document is here to document this. + += Data providers = +Contains bindings specific to provider drivers and data cells as children +of this node. + +Optional properties: + read-only: Mark the provider as read only. + += Data cells = +These are the child nodes of the provider which contain data cell +information like offset and size in nvmem provider. + +Required properties: +reg: specifies the offset in byte within the storage device. + +Optional properties: + +bits: Is pair of bit location and number of bits, which specifies offset + in bit and number of bits within the address range specified by reg property. + Offset takes values from 0-7. + +For example: + + /* Provider */ + qfprom: qfprom@00700000 { + ... + + /* Data cells */ + tsens_calibration: calib@404 { + reg = <0x404 0x10>; + }; + + tsens_calibration_bckp: calib_bckp@504 { + reg = <0x504 0x11>; + bits = <6 128> + }; + + pvs_version: pvs-version@6 { + reg = <0x6 0x2> + bits = <7 2> + }; + + speed_bin: speed-bin@c{ + reg = <0xc 0x1>; + bits = <2 3>; + + }; + ... + }; + += Data consumers = +Are device nodes which consume nvmem data cells/providers. + +Required-properties: +nvmem-cells: list of phandle to the nvmem data cells. +nvmem-cell-names: names for the each nvmem-cells specified. Required if + nvmem-cells is used. + +Optional-properties: +nvmem : list of phandles to nvmem providers. +nvmem-names: names for the each nvmem provider. required if nvmem is used. + +For example: + + tsens { + ... + nvmem-cells = <&tsens_calibration>; + nvmem-cell-names = "calibration"; + }; diff --git a/Documentation/devicetree/bindings/nvmem/qfprom.txt b/Documentation/devicetree/bindings/nvmem/qfprom.txt new file mode 100644 index 000000000000..4ad68b7f5c18 --- /dev/null +++ b/Documentation/devicetree/bindings/nvmem/qfprom.txt @@ -0,0 +1,35 @@ += Qualcomm QFPROM device tree bindings = + +This binding is intended to represent QFPROM which is found in most QCOM SOCs. + +Required properties: +- compatible: should be "qcom,qfprom" +- reg: Should contain registers location and length + += Data cells = +Are child nodes of qfprom, bindings of which as described in +bindings/nvmem/nvmem.txt + +Example: + + qfprom: qfprom@00700000 { + compatible = "qcom,qfprom"; + reg = <0x00700000 0x8000>; + ... + /* Data cells */ + tsens_calibration: calib@404 { + reg = <0x4404 0x10>; + }; + }; + + += Data consumers = +Are device nodes which consume nvmem data cells. + +For example: + + tsens { + ... + nvmem-cells = <&tsens_calibration>; + nvmem-cell-names = "calibration"; + }; diff --git a/Documentation/devicetree/bindings/power/qcom,coincell-charger.txt b/Documentation/devicetree/bindings/power/qcom,coincell-charger.txt new file mode 100644 index 000000000000..0e6d8754e7ec --- /dev/null +++ b/Documentation/devicetree/bindings/power/qcom,coincell-charger.txt @@ -0,0 +1,48 @@ +Qualcomm Coincell Charger: + +The hardware block controls charging for a coincell or capacitor that is +used to provide power backup for certain features of the power management +IC (PMIC) + +- compatible: + Usage: required + Value type: <string> + Definition: must be: "qcom,pm8941-coincell" + +- reg: + Usage: required + Value type: <u32> + Definition: base address of the coincell charger registers + +- qcom,rset-ohms: + Usage: required + Value type: <u32> + Definition: resistance (in ohms) for current-limiting resistor + must be one of: 800, 1200, 1700, 2100 + +- qcom,vset-millivolts: + Usage: required + Value type: <u32> + Definition: voltage (in millivolts) to apply for charging + must be one of: 2500, 3000, 3100, 3200 + +- qcom,charger-disable: + Usage: optional + Value type: <boolean> + Definition: definining this property disables charging + +This charger is a sub-node of one of the 8941 PMIC blocks, and is specified +as a child node in DTS of that node. See ../mfd/qcom,spmi-pmic.txt and +../mfd/qcom-pm8xxx.txt + +Example: + + pm8941@0 { + coincell@2800 { + compatible = "qcom,pm8941-coincell"; + reg = <0x2800>; + + qcom,rset-ohms = <2100>; + qcom,vset-millivolts = <3000>; + }; + }; diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt index b35a64b82f9e..9494afb9476a 100644 --- a/Documentation/filesystems/sysfs.txt +++ b/Documentation/filesystems/sysfs.txt @@ -212,7 +212,10 @@ Other notes: - show() methods should return the number of bytes printed into the buffer. This is the return value of scnprintf(). -- show() should always use scnprintf(). +- show() must not use snprintf() when formatting the value to be + returned to user space. If you can guarantee that an overflow + will never happen you can use sprintf() otherwise you must use + scnprintf(). - store() should return the number of bytes used from the buffer. If the entire buffer has been used, just return the count argument. diff --git a/Documentation/ioctl/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index 611c52267d24..141f847c7648 100644 --- a/Documentation/ioctl/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt @@ -124,6 +124,8 @@ Code Seq#(hex) Include File Comments 'H' 00-7F linux/hiddev.h conflict! 'H' 00-0F linux/hidraw.h conflict! 'H' 01 linux/mei.h conflict! +'H' 02 linux/mei.h conflict! +'H' 03 linux/mei.h conflict! 'H' 00-0F sound/asound.h conflict! 'H' 20-40 sound/asound_fm.h conflict! 'H' 80-8F sound/sfnt_info.h conflict! diff --git a/Documentation/misc-devices/mei/mei.txt b/Documentation/misc-devices/mei/mei.txt index 8d47501bba0a..91c1fa34f48b 100644 --- a/Documentation/misc-devices/mei/mei.txt +++ b/Documentation/misc-devices/mei/mei.txt @@ -96,7 +96,7 @@ A code snippet for an application communicating with Intel AMTHI client: IOCTL ===== -The Intel MEI Driver supports the following IOCTL command: +The Intel MEI Driver supports the following IOCTL commands: IOCTL_MEI_CONNECT_CLIENT Connect to firmware Feature (client). usage: @@ -125,6 +125,49 @@ The Intel MEI Driver supports the following IOCTL command: data that can be sent or received. (e.g. if MTU=2K, can send requests up to bytes 2k and received responses up to 2k bytes). + IOCTL_MEI_NOTIFY_SET: enable or disable event notifications + + Usage: + uint32_t enable; + ioctl(fd, IOCTL_MEI_NOTIFY_SET, &enable); + + Inputs: + uint32_t enable = 1; + or + uint32_t enable[disable] = 0; + + Error returns: + EINVAL Wrong IOCTL Number + ENODEV Device is not initialized or the client not connected + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EOPNOTSUPP if the device doesn't support the feature + + Notes: + The client must be connected in order to enable notification events + + + IOCTL_MEI_NOTIFY_GET : retrieve event + + Usage: + uint32_t event; + ioctl(fd, IOCTL_MEI_NOTIFY_GET, &event); + + Outputs: + 1 - if an event is pending + 0 - if there is no even pending + + Error returns: + EINVAL Wrong IOCTL Number + ENODEV Device is not initialized or the client not connected + ENOMEM Unable to allocate memory to client internal data. + EFAULT Fatal Error (e.g. Unable to access user input data) + EOPNOTSUPP if the device doesn't support the feature + + Notes: + The client must be connected and event notification has to be enabled + in order to receive an event + Intel ME Applications ===================== diff --git a/Documentation/nvmem/nvmem.txt b/Documentation/nvmem/nvmem.txt new file mode 100644 index 000000000000..dbd40d879239 --- /dev/null +++ b/Documentation/nvmem/nvmem.txt @@ -0,0 +1,152 @@ + NVMEM SUBSYSTEM + Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + +This document explains the NVMEM Framework along with the APIs provided, +and how to use it. + +1. Introduction +=============== +*NVMEM* is the abbreviation for Non Volatile Memory layer. It is used to +retrieve configuration of SOC or Device specific data from non volatile +memories like eeprom, efuses and so on. + +Before this framework existed, NVMEM drivers like eeprom were stored in +drivers/misc, where they all had to duplicate pretty much the same code to +register a sysfs file, allow in-kernel users to access the content of the +devices they were driving, etc. + +This was also a problem as far as other in-kernel users were involved, since +the solutions used were pretty much different from one driver to another, there +was a rather big abstraction leak. + +This framework aims at solve these problems. It also introduces DT +representation for consumer devices to go get the data they require (MAC +Addresses, SoC/Revision ID, part numbers, and so on) from the NVMEMs. This +framework is based on regmap, so that most of the abstraction available in +regmap can be reused, across multiple types of buses. + +NVMEM Providers ++++++++++++++++ + +NVMEM provider refers to an entity that implements methods to initialize, read +and write the non-volatile memory. + +2. Registering/Unregistering the NVMEM provider +=============================================== + +A NVMEM provider can register with NVMEM core by supplying relevant +nvmem configuration to nvmem_register(), on success core would return a valid +nvmem_device pointer. + +nvmem_unregister(nvmem) is used to unregister a previously registered provider. + +For example, a simple qfprom case: + +static struct nvmem_config econfig = { + .name = "qfprom", + .owner = THIS_MODULE, +}; + +static int qfprom_probe(struct platform_device *pdev) +{ + ... + econfig.dev = &pdev->dev; + nvmem = nvmem_register(&econfig); + ... +} + +It is mandatory that the NVMEM provider has a regmap associated with its +struct device. Failure to do would return error code from nvmem_register(). + +NVMEM Consumers ++++++++++++++++ + +NVMEM consumers are the entities which make use of the NVMEM provider to +read from and to NVMEM. + +3. NVMEM cell based consumer APIs +================================= + +NVMEM cells are the data entries/fields in the NVMEM. +The NVMEM framework provides 3 APIs to read/write NVMEM cells. + +struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name); +struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name); + +void nvmem_cell_put(struct nvmem_cell *cell); +void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); + +void *nvmem_cell_read(struct nvmem_cell *cell, ssize_t *len); +int nvmem_cell_write(struct nvmem_cell *cell, void *buf, ssize_t len); + +*nvmem_cell_get() apis will get a reference to nvmem cell for a given id, +and nvmem_cell_read/write() can then read or write to the cell. +Once the usage of the cell is finished the consumer should call *nvmem_cell_put() +to free all the allocation memory for the cell. + +4. Direct NVMEM device based consumer APIs +========================================== + +In some instances it is necessary to directly read/write the NVMEM. +To facilitate such consumers NVMEM framework provides below apis. + +struct nvmem_device *nvmem_device_get(struct device *dev, const char *name); +struct nvmem_device *devm_nvmem_device_get(struct device *dev, + const char *name); +void nvmem_device_put(struct nvmem_device *nvmem); +int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset, + size_t bytes, void *buf); +int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, + size_t bytes, void *buf); +int nvmem_device_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf); +int nvmem_device_cell_write(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf); + +Before the consumers can read/write NVMEM directly, it should get hold +of nvmem_controller from one of the *nvmem_device_get() api. + +The difference between these apis and cell based apis is that these apis always +take nvmem_device as parameter. + +5. Releasing a reference to the NVMEM +===================================== + +When a consumers no longer needs the NVMEM, it has to release the reference +to the NVMEM it has obtained using the APIs mentioned in the above section. +The NVMEM framework provides 2 APIs to release a reference to the NVMEM. + +void nvmem_cell_put(struct nvmem_cell *cell); +void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); +void nvmem_device_put(struct nvmem_device *nvmem); +void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem); + +Both these APIs are used to release a reference to the NVMEM and +devm_nvmem_cell_put and devm_nvmem_device_put destroys the devres associated +with this NVMEM. + +Userspace ++++++++++ + +6. Userspace binary interface +============================== + +Userspace can read/write the raw NVMEM file located at +/sys/bus/nvmem/devices/*/nvmem + +ex: + +hexdump /sys/bus/nvmem/devices/qfprom0/nvmem + +0000000 0000 0000 0000 0000 0000 0000 0000 0000 +* +00000a0 db10 2240 0000 e000 0c00 0c00 0000 0c00 +0000000 0000 0000 0000 0000 0000 0000 0000 0000 +... +* +0001000 + +7. DeviceTree Binding +===================== + +See Documentation/devicetree/bindings/nvmem/nvmem.txt diff --git a/Documentation/power/suspend-and-cpuhotplug.txt b/Documentation/power/suspend-and-cpuhotplug.txt index 2850df3bf957..2fc909502db5 100644 --- a/Documentation/power/suspend-and-cpuhotplug.txt +++ b/Documentation/power/suspend-and-cpuhotplug.txt @@ -72,7 +72,7 @@ More details follow: | v Disable regular cpu hotplug - by setting cpu_hotplug_disabled=1 + by increasing cpu_hotplug_disabled | v Release cpu_add_remove_lock @@ -89,7 +89,7 @@ Resuming back is likewise, with the counterparts being (in the order of execution during resume): * enable_nonboot_cpus() which involves: | Acquire cpu_add_remove_lock - | Reset cpu_hotplug_disabled to 0, thereby enabling regular cpu hotplug + | Decrease cpu_hotplug_disabled, thereby enabling regular cpu hotplug | Call _cpu_up() [for all those cpus in the frozen_cpus mask, in a loop] | Release cpu_add_remove_lock v @@ -120,7 +120,7 @@ after the entire cycle is complete (i.e., suspend + resume). Acquire cpu_add_remove_lock | v - If cpu_hotplug_disabled is 1 + If cpu_hotplug_disabled > 0 return gracefully | | diff --git a/Documentation/s390/00-INDEX b/Documentation/s390/00-INDEX index 10c874ebdfe5..9189535f6cd2 100644 --- a/Documentation/s390/00-INDEX +++ b/Documentation/s390/00-INDEX @@ -16,8 +16,6 @@ Debugging390.txt - hints for debugging on s390 systems. driver-model.txt - information on s390 devices and the driver model. -kvm.txt - - ioctl calls to /dev/kvm on s390. monreader.txt - information on accessing the z/VM monitor stream from Linux. qeth.txt diff --git a/Documentation/s390/kvm.txt b/Documentation/s390/kvm.txt deleted file mode 100644 index 85f3280d7ef6..000000000000 --- a/Documentation/s390/kvm.txt +++ /dev/null @@ -1,125 +0,0 @@ -*** BIG FAT WARNING *** -The kvm module is currently in EXPERIMENTAL state for s390. This means that -the interface to the module is not yet considered to remain stable. Thus, be -prepared that we keep breaking your userspace application and guest -compatibility over and over again until we feel happy with the result. Make sure -your guest kernel, your host kernel, and your userspace launcher are in a -consistent state. - -This Documentation describes the unique ioctl calls to /dev/kvm, the resulting -kvm-vm file descriptors, and the kvm-vcpu file descriptors that differ from x86. - -1. ioctl calls to /dev/kvm -KVM does support the following ioctls on s390 that are common with other -architectures and do behave the same: -KVM_GET_API_VERSION -KVM_CREATE_VM (*) see note -KVM_CHECK_EXTENSION -KVM_GET_VCPU_MMAP_SIZE - -Notes: -* KVM_CREATE_VM may fail on s390, if the calling process has multiple -threads and has not called KVM_S390_ENABLE_SIE before. - -In addition, on s390 the following architecture specific ioctls are supported: -ioctl: KVM_S390_ENABLE_SIE -args: none -see also: include/linux/kvm.h -This call causes the kernel to switch on PGSTE in the user page table. This -operation is needed in order to run a virtual machine, and it requires the -calling process to be single-threaded. Note that the first call to KVM_CREATE_VM -will implicitly try to switch on PGSTE if the user process has not called -KVM_S390_ENABLE_SIE before. User processes that want to launch multiple threads -before creating a virtual machine have to call KVM_S390_ENABLE_SIE, or will -observe an error calling KVM_CREATE_VM. Switching on PGSTE is a one-time -operation, is not reversible, and will persist over the entire lifetime of -the calling process. It does not have any user-visible effect other than a small -performance penalty. - -2. ioctl calls to the kvm-vm file descriptor -KVM does support the following ioctls on s390 that are common with other -architectures and do behave the same: -KVM_CREATE_VCPU -KVM_SET_USER_MEMORY_REGION (*) see note -KVM_GET_DIRTY_LOG (**) see note - -Notes: -* kvm does only allow exactly one memory slot on s390, which has to start - at guest absolute address zero and at a user address that is aligned on any - page boundary. This hardware "limitation" allows us to have a few unique - optimizations. The memory slot doesn't have to be filled - with memory actually, it may contain sparse holes. That said, with different - user memory layout this does still allow a large flexibility when - doing the guest memory setup. -** KVM_GET_DIRTY_LOG doesn't work properly yet. The user will receive an empty -log. This ioctl call is only needed for guest migration, and we intend to -implement this one in the future. - -In addition, on s390 the following architecture specific ioctls for the kvm-vm -file descriptor are supported: -ioctl: KVM_S390_INTERRUPT -args: struct kvm_s390_interrupt * -see also: include/linux/kvm.h -This ioctl is used to submit a floating interrupt for a virtual machine. -Floating interrupts may be delivered to any virtual cpu in the configuration. -Only some interrupt types defined in include/linux/kvm.h make sense when -submitted as floating interrupts. The following interrupts are not considered -to be useful as floating interrupts, and a call to inject them will result in --EINVAL error code: program interrupts and interprocessor signals. Valid -floating interrupts are: -KVM_S390_INT_VIRTIO -KVM_S390_INT_SERVICE - -3. ioctl calls to the kvm-vcpu file descriptor -KVM does support the following ioctls on s390 that are common with other -architectures and do behave the same: -KVM_RUN -KVM_GET_REGS -KVM_SET_REGS -KVM_GET_SREGS -KVM_SET_SREGS -KVM_GET_FPU -KVM_SET_FPU - -In addition, on s390 the following architecture specific ioctls for the -kvm-vcpu file descriptor are supported: -ioctl: KVM_S390_INTERRUPT -args: struct kvm_s390_interrupt * -see also: include/linux/kvm.h -This ioctl is used to submit an interrupt for a specific virtual cpu. -Only some interrupt types defined in include/linux/kvm.h make sense when -submitted for a specific cpu. The following interrupts are not considered -to be useful, and a call to inject them will result in -EINVAL error code: -service processor calls and virtio interrupts. Valid interrupt types are: -KVM_S390_PROGRAM_INT -KVM_S390_SIGP_STOP -KVM_S390_RESTART -KVM_S390_SIGP_SET_PREFIX -KVM_S390_INT_EMERGENCY - -ioctl: KVM_S390_STORE_STATUS -args: unsigned long -see also: include/linux/kvm.h -This ioctl stores the state of the cpu at the guest real address given as -argument, unless one of the following values defined in include/linux/kvm.h -is given as argument: -KVM_S390_STORE_STATUS_NOADDR - the CPU stores its status to the save area in -absolute lowcore as defined by the principles of operation -KVM_S390_STORE_STATUS_PREFIXED - the CPU stores its status to the save area in -its prefix page just like the dump tool that comes with zipl. This is useful -to create a system dump for use with lkcdutils or crash. - -ioctl: KVM_S390_SET_INITIAL_PSW -args: struct kvm_s390_psw * -see also: include/linux/kvm.h -This ioctl can be used to set the processor status word (psw) of a stopped cpu -prior to running it with KVM_RUN. Note that this call is not required to modify -the psw during sie intercepts that fall back to userspace because struct kvm_run -does contain the psw, and this value is evaluated during reentry of KVM_RUN -after the intercept exit was recognized. - -ioctl: KVM_S390_INITIAL_RESET -args: none -see also: include/linux/kvm.h -This ioctl can be used to perform an initial cpu reset as defined by the -principles of operation. The target cpu has to be in stopped state. diff --git a/Documentation/trace/coresight.txt b/Documentation/trace/coresight.txt index 77d14d51a670..0a5c3290e732 100644 --- a/Documentation/trace/coresight.txt +++ b/Documentation/trace/coresight.txt @@ -15,7 +15,7 @@ HW assisted tracing is becoming increasingly useful when dealing with systems that have many SoCs and other components like GPU and DMA engines. ARM has developed a HW assisted tracing solution by means of different components, each being added to a design at synthesis time to cater to specific tracing needs. -Compoments are generally categorised as source, link and sinks and are +Components are generally categorised as source, link and sinks and are (usually) discovered using the AMBA bus. "Sources" generate a compressed stream representing the processor instruction @@ -138,7 +138,7 @@ void coresight_unregister(struct coresight_device *csdev); The registering function is taking a "struct coresight_device *csdev" and register the device with the core framework. The unregister function takes -a reference to a "strut coresight_device", obtained at registration time. +a reference to a "struct coresight_device", obtained at registration time. If everything goes well during the registration process the new devices will show up under /sys/bus/coresight/devices, as showns here for a TC2 platform: diff --git a/Documentation/virtual/kvm/api.txt b/Documentation/virtual/kvm/api.txt index a7926a90156f..a4ebcb712375 100644 --- a/Documentation/virtual/kvm/api.txt +++ b/Documentation/virtual/kvm/api.txt @@ -3277,6 +3277,7 @@ should put the acknowledged interrupt vector into the 'epr' field. struct { #define KVM_SYSTEM_EVENT_SHUTDOWN 1 #define KVM_SYSTEM_EVENT_RESET 2 +#define KVM_SYSTEM_EVENT_CRASH 3 __u32 type; __u64 flags; } system_event; @@ -3296,6 +3297,10 @@ Valid values for 'type' are: KVM_SYSTEM_EVENT_RESET -- the guest has requested a reset of the VM. As with SHUTDOWN, userspace can choose to ignore the request, or to schedule the reset to occur in the future and may call KVM_RUN again. + KVM_SYSTEM_EVENT_CRASH -- the guest crash occurred and the guest + has requested a crash condition maintenance. Userspace can choose + to ignore the request, or to gather VM memory core dump and/or + reset/shutdown of the VM. /* Fix the size of the union. */ char padding[256]; diff --git a/MAINTAINERS b/MAINTAINERS index fb7d3b6acd70..973002719b80 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3587,6 +3587,15 @@ S: Maintained F: drivers/gpu/drm/rockchip/ F: Documentation/devicetree/bindings/video/rockchip* +DRM DRIVERS FOR STI +M: Benjamin Gaignard <benjamin.gaignard@linaro.org> +M: Vincent Abriou <vincent.abriou@st.com> +L: dri-devel@lists.freedesktop.org +T: git http://git.linaro.org/people/benjamin.gaignard/kernel.git +S: Maintained +F: drivers/gpu/drm/sti +F: Documentation/devicetree/bindings/gpu/st,stih4xx.txt + DSBR100 USB FM RADIO DRIVER M: Alexey Klimov <klimov.linux@gmail.com> L: linux-media@vger.kernel.org @@ -4957,6 +4966,7 @@ F: drivers/scsi/storvsc_drv.c F: drivers/video/fbdev/hyperv_fb.c F: include/linux/hyperv.h F: tools/hv/ +F: Documentation/ABI/stable/sysfs-bus-vmbus I2C OVER PARALLEL PORT M: Jean Delvare <jdelvare@suse.com> @@ -5840,6 +5850,7 @@ S: Odd Fixes KERNEL NFSD, SUNRPC, AND LOCKD SERVERS M: "J. Bruce Fields" <bfields@fieldses.org> +M: Jeff Layton <jlayton@poochiereds.net> L: linux-nfs@vger.kernel.org W: http://nfs.sourceforge.net/ S: Supported @@ -7288,6 +7299,15 @@ S: Supported F: drivers/block/nvme* F: include/linux/nvme.h +NVMEM FRAMEWORK +M: Srinivas Kandagatla <srinivas.kandagatla@linaro.org> +M: Maxime Ripard <maxime.ripard@free-electrons.com> +S: Maintained +F: drivers/nvmem/ +F: Documentation/devicetree/bindings/nvmem/ +F: include/linux/nvmem-consumer.h +F: include/linux/nvmem-provider.h + NXP-NCI NFC DRIVER M: Clément Perrochaud <clement.perrochaud@effinnov.com> R: Charles Gorand <charles.gorand@effinnov.com> @@ -1,7 +1,7 @@ VERSION = 4 PATCHLEVEL = 2 SUBLEVEL = 0 -EXTRAVERSION = -rc6 +EXTRAVERSION = NAME = Hurr durr I'ma sheep # *DOCUMENTATION* diff --git a/arch/arm/Makefile b/arch/arm/Makefile index 07ab3d203916..7451b447cc2d 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -312,6 +312,9 @@ INSTALL_TARGETS = zinstall uinstall install PHONY += bzImage $(BOOT_TARGETS) $(INSTALL_TARGETS) +bootpImage uImage: zImage +zImage: Image + $(BOOT_TARGETS): vmlinux $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@ diff --git a/arch/arm/boot/dts/dra7.dtsi b/arch/arm/boot/dts/dra7.dtsi index 4a0718ccf68e..1e29ccf77ea2 100644 --- a/arch/arm/boot/dts/dra7.dtsi +++ b/arch/arm/boot/dts/dra7.dtsi @@ -116,7 +116,7 @@ ranges = <0 0x2000 0x2000>; scm_conf: scm_conf@0 { - compatible = "syscon"; + compatible = "syscon", "simple-bus"; reg = <0x0 0x1400>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi index e6d13592080d..b57033e8c633 100644 --- a/arch/arm/boot/dts/imx6qdl.dtsi +++ b/arch/arm/boot/dts/imx6qdl.dtsi @@ -181,10 +181,10 @@ interrupt-names = "msi"; #interrupt-cells = <1>; interrupt-map-mask = <0 0 0 0x7>; - interrupt-map = <0 0 0 1 &intc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 2 &intc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 3 &intc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>, - <0 0 0 4 &intc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>; + interrupt-map = <0 0 0 1 &gpc GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 2 &gpc GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 3 &gpc GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>, + <0 0 0 4 &gpc GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>; clocks = <&clks IMX6QDL_CLK_PCIE_AXI>, <&clks IMX6QDL_CLK_LVDS1_GATE>, <&clks IMX6QDL_CLK_PCIE_REF_125M>; diff --git a/arch/arm/boot/dts/k2e.dtsi b/arch/arm/boot/dts/k2e.dtsi index 1b6494fbdb91..675fb8e492c6 100644 --- a/arch/arm/boot/dts/k2e.dtsi +++ b/arch/arm/boot/dts/k2e.dtsi @@ -131,10 +131,17 @@ <GIC_SPI 376 IRQ_TYPE_EDGE_RISING>; }; }; + + mdio: mdio@24200f00 { + compatible = "ti,keystone_mdio", "ti,davinci_mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x24200f00 0x100>; + status = "disabled"; + clocks = <&clkcpgmac>; + clock-names = "fck"; + bus_freq = <2500000>; + }; /include/ "k2e-netcp.dtsi" }; }; - -&mdio { - reg = <0x24200f00 0x100>; -}; diff --git a/arch/arm/boot/dts/k2hk.dtsi b/arch/arm/boot/dts/k2hk.dtsi index ae6472407b22..d0810a5f2968 100644 --- a/arch/arm/boot/dts/k2hk.dtsi +++ b/arch/arm/boot/dts/k2hk.dtsi @@ -98,6 +98,17 @@ #gpio-cells = <2>; gpio,syscon-dev = <&devctrl 0x25c>; }; + + mdio: mdio@02090300 { + compatible = "ti,keystone_mdio", "ti,davinci_mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x02090300 0x100>; + status = "disabled"; + clocks = <&clkcpgmac>; + clock-names = "fck"; + bus_freq = <2500000>; + }; /include/ "k2hk-netcp.dtsi" }; }; diff --git a/arch/arm/boot/dts/k2l.dtsi b/arch/arm/boot/dts/k2l.dtsi index 0e007483615e..49fd414f680c 100644 --- a/arch/arm/boot/dts/k2l.dtsi +++ b/arch/arm/boot/dts/k2l.dtsi @@ -29,7 +29,6 @@ }; soc { - /include/ "k2l-clocks.dtsi" uart2: serial@02348400 { @@ -79,6 +78,17 @@ #gpio-cells = <2>; gpio,syscon-dev = <&devctrl 0x24c>; }; + + mdio: mdio@26200f00 { + compatible = "ti,keystone_mdio", "ti,davinci_mdio"; + #address-cells = <1>; + #size-cells = <0>; + reg = <0x26200f00 0x100>; + status = "disabled"; + clocks = <&clkcpgmac>; + clock-names = "fck"; + bus_freq = <2500000>; + }; /include/ "k2l-netcp.dtsi" }; }; @@ -96,7 +106,3 @@ /* Pin muxed. Enabled and configured by Bootloader */ status = "disabled"; }; - -&mdio { - reg = <0x26200f00 0x100>; -}; diff --git a/arch/arm/boot/dts/keystone.dtsi b/arch/arm/boot/dts/keystone.dtsi index e7a6f6deabb6..72816d65f7ec 100644 --- a/arch/arm/boot/dts/keystone.dtsi +++ b/arch/arm/boot/dts/keystone.dtsi @@ -267,17 +267,6 @@ 1 0 0x21000A00 0x00000100>; }; - mdio: mdio@02090300 { - compatible = "ti,keystone_mdio", "ti,davinci_mdio"; - #address-cells = <1>; - #size-cells = <0>; - reg = <0x02090300 0x100>; - status = "disabled"; - clocks = <&clkpa>; - clock-names = "fck"; - bus_freq = <2500000>; - }; - kirq0: keystone_irq@26202a0 { compatible = "ti,keystone-irq"; interrupts = <GIC_SPI 4 IRQ_TYPE_EDGE_RISING>; diff --git a/arch/arm/boot/dts/omap2430.dtsi b/arch/arm/boot/dts/omap2430.dtsi index 11a7963be003..2390f387c271 100644 --- a/arch/arm/boot/dts/omap2430.dtsi +++ b/arch/arm/boot/dts/omap2430.dtsi @@ -51,7 +51,8 @@ }; scm_conf: scm_conf@270 { - compatible = "syscon"; + compatible = "syscon", + "simple-bus"; reg = <0x270 0x240>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/omap4.dtsi b/arch/arm/boot/dts/omap4.dtsi index 7d31c6ff246f..abc4473e6f8a 100644 --- a/arch/arm/boot/dts/omap4.dtsi +++ b/arch/arm/boot/dts/omap4.dtsi @@ -191,7 +191,8 @@ }; omap4_padconf_global: omap4_padconf_global@5a0 { - compatible = "syscon"; + compatible = "syscon", + "simple-bus"; reg = <0x5a0 0x170>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/omap5.dtsi b/arch/arm/boot/dts/omap5.dtsi index c8fd648a7108..b1a1263e6001 100644 --- a/arch/arm/boot/dts/omap5.dtsi +++ b/arch/arm/boot/dts/omap5.dtsi @@ -180,7 +180,8 @@ }; omap5_padconf_global: omap5_padconf_global@5a0 { - compatible = "syscon"; + compatible = "syscon", + "simple-bus"; reg = <0x5a0 0xec>; #address-cells = <1>; #size-cells = <1>; diff --git a/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts b/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts index bd35b0674ff6..9bc72a3356e4 100644 --- a/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts +++ b/arch/arm/boot/dts/qcom-msm8974-sony-xperia-honami.dts @@ -17,3 +17,13 @@ status = "ok"; }; }; + +&spmi_bus { + pm8941@0 { + coincell@2800 { + status = "ok"; + qcom,rset-ohms = <2100>; + qcom,vset-millivolts = <3000>; + }; + }; +}; diff --git a/arch/arm/boot/dts/qcom-pm8941.dtsi b/arch/arm/boot/dts/qcom-pm8941.dtsi index aa774e685018..968f1043d4f5 100644 --- a/arch/arm/boot/dts/qcom-pm8941.dtsi +++ b/arch/arm/boot/dts/qcom-pm8941.dtsi @@ -125,6 +125,12 @@ interrupts = <0x0 0x36 0x0 IRQ_TYPE_EDGE_RISING>; qcom,external-resistor-micro-ohms = <10000>; }; + + coincell@2800 { + compatible = "qcom,pm8941-coincell"; + reg = <0x2800>; + status = "disabled"; + }; }; usid1: pm8941@1 { diff --git a/arch/arm/boot/dts/ste-dbx5x0.dtsi b/arch/arm/boot/dts/ste-dbx5x0.dtsi index a75f3289e653..b8f81fb418ce 100644 --- a/arch/arm/boot/dts/ste-dbx5x0.dtsi +++ b/arch/arm/boot/dts/ste-dbx5x0.dtsi @@ -15,6 +15,33 @@ #include "skeleton.dtsi" / { + cpus { + #address-cells = <1>; + #size-cells = <0>; + enable-method = "ste,dbx500-smp"; + + cpu-map { + cluster0 { + core0 { + cpu = <&CPU0>; + }; + core1 { + cpu = <&CPU1>; + }; + }; + }; + CPU0: cpu@300 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <0x300>; + }; + CPU1: cpu@301 { + device_type = "cpu"; + compatible = "arm,cortex-a9"; + reg = <0x301>; + }; + }; + soc { #address-cells = <1>; #size-cells = <1>; @@ -22,32 +49,6 @@ interrupt-parent = <&intc>; ranges; - cpus { - #address-cells = <1>; - #size-cells = <0>; - - cpu-map { - cluster0 { - core0 { - cpu = <&CPU0>; - }; - core1 { - cpu = <&CPU1>; - }; - }; - }; - CPU0: cpu@0 { - device_type = "cpu"; - compatible = "arm,cortex-a9"; - reg = <0>; - }; - CPU1: cpu@1 { - device_type = "cpu"; - compatible = "arm,cortex-a9"; - reg = <1>; - }; - }; - ptm@801ae000 { compatible = "arm,coresight-etm3x", "arm,primecell"; reg = <0x801ae000 0x1000>; diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index 92828a1dec80..b48dd4f37f80 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -61,6 +61,7 @@ work_pending: movlt scno, #(__NR_restart_syscall - __NR_SYSCALL_BASE) ldmia sp, {r0 - r6} @ have to reload r0 - r6 b local_restart @ ... and off we go +ENDPROC(ret_fast_syscall) /* * "slow" syscall return path. "why" tells us if this was a real syscall. diff --git a/arch/arm/kernel/head.S b/arch/arm/kernel/head.S index bd755d97e459..29e2991465cb 100644 --- a/arch/arm/kernel/head.S +++ b/arch/arm/kernel/head.S @@ -399,6 +399,9 @@ ENTRY(secondary_startup) sub lr, r4, r5 @ mmu has been enabled add r3, r7, lr ldrd r4, [r3, #0] @ get secondary_data.pgdir +ARM_BE8(eor r4, r4, r5) @ Swap r5 and r4 in BE: +ARM_BE8(eor r5, r4, r5) @ it can be done in 3 steps +ARM_BE8(eor r4, r4, r5) @ without using a temp reg. ldr r8, [r3, #8] @ get secondary_data.swapper_pg_dir badr lr, __enable_mmu @ return address mov r13, r12 @ __secondary_switched address diff --git a/arch/arm/kernel/vdso.c b/arch/arm/kernel/vdso.c index efe17dd9b921..54a5aeab988d 100644 --- a/arch/arm/kernel/vdso.c +++ b/arch/arm/kernel/vdso.c @@ -296,7 +296,6 @@ static bool tk_is_cntvct(const struct timekeeper *tk) */ void update_vsyscall(struct timekeeper *tk) { - struct timespec xtime_coarse; struct timespec64 *wtm = &tk->wall_to_monotonic; if (!cntvct_ok) { @@ -308,10 +307,10 @@ void update_vsyscall(struct timekeeper *tk) vdso_write_begin(vdso_data); - xtime_coarse = __current_kernel_time(); vdso_data->tk_is_cntvct = tk_is_cntvct(tk); - vdso_data->xtime_coarse_sec = xtime_coarse.tv_sec; - vdso_data->xtime_coarse_nsec = xtime_coarse.tv_nsec; + vdso_data->xtime_coarse_sec = tk->xtime_sec; + vdso_data->xtime_coarse_nsec = (u32)(tk->tkr_mono.xtime_nsec >> + tk->tkr_mono.shift); vdso_data->wtm_clock_sec = wtm->tv_sec; vdso_data->wtm_clock_nsec = wtm->tv_nsec; diff --git a/arch/arm/lib/uaccess_with_memcpy.c b/arch/arm/lib/uaccess_with_memcpy.c index 3e58d710013c..4b39af2dfda9 100644 --- a/arch/arm/lib/uaccess_with_memcpy.c +++ b/arch/arm/lib/uaccess_with_memcpy.c @@ -96,7 +96,7 @@ __copy_to_user_memcpy(void __user *to, const void *from, unsigned long n) } /* the mmap semaphore is taken only if not in an atomic context */ - atomic = in_atomic(); + atomic = faulthandler_disabled(); if (!atomic) down_read(¤t->mm->mmap_sem); diff --git a/arch/arm/mach-exynos/pm_domains.c b/arch/arm/mach-exynos/pm_domains.c index 6001f1c9d136..4a87e86dec45 100644 --- a/arch/arm/mach-exynos/pm_domains.c +++ b/arch/arm/mach-exynos/pm_domains.c @@ -146,9 +146,8 @@ static __init int exynos4_pm_init_power_domain(void) pd->base = of_iomap(np, 0); if (!pd->base) { pr_warn("%s: failed to map memory\n", __func__); - kfree(pd->pd.name); + kfree_const(pd->pd.name); kfree(pd); - of_node_put(np); continue; } diff --git a/arch/arm/mach-omap2/omap-wakeupgen.c b/arch/arm/mach-omap2/omap-wakeupgen.c index 8e52621b5a6b..e1d2e991d17a 100644 --- a/arch/arm/mach-omap2/omap-wakeupgen.c +++ b/arch/arm/mach-omap2/omap-wakeupgen.c @@ -392,6 +392,7 @@ static struct irq_chip wakeupgen_chip = { .irq_mask = wakeupgen_mask, .irq_unmask = wakeupgen_unmask, .irq_retrigger = irq_chip_retrigger_hierarchy, + .irq_set_type = irq_chip_set_type_parent, .flags = IRQCHIP_SKIP_SET_WAKE | IRQCHIP_MASK_ON_SUSPEND, #ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile index 9d259d94e429..1160434eece0 100644 --- a/arch/arm/vdso/Makefile +++ b/arch/arm/vdso/Makefile @@ -14,7 +14,7 @@ VDSO_LDFLAGS += -Wl,-z,max-page-size=4096 -Wl,-z,common-page-size=4096 VDSO_LDFLAGS += -nostdlib -shared VDSO_LDFLAGS += $(call cc-ldoption, -Wl$(comma)--hash-style=sysv) VDSO_LDFLAGS += $(call cc-ldoption, -Wl$(comma)--build-id) -VDSO_LDFLAGS += $(call cc-option, -fuse-ld=bfd) +VDSO_LDFLAGS += $(call cc-ldoption, -fuse-ld=bfd) obj-$(CONFIG_VDSO) += vdso.o extra-$(CONFIG_VDSO) += vdso.lds diff --git a/arch/arm64/kernel/vdso.c b/arch/arm64/kernel/vdso.c index ec37ab3f524f..97bc68f4c689 100644 --- a/arch/arm64/kernel/vdso.c +++ b/arch/arm64/kernel/vdso.c @@ -199,16 +199,15 @@ up_fail: */ void update_vsyscall(struct timekeeper *tk) { - struct timespec xtime_coarse; u32 use_syscall = strcmp(tk->tkr_mono.clock->name, "arch_sys_counter"); ++vdso_data->tb_seq_count; smp_wmb(); - xtime_coarse = __current_kernel_time(); vdso_data->use_syscall = use_syscall; - vdso_data->xtime_coarse_sec = xtime_coarse.tv_sec; - vdso_data->xtime_coarse_nsec = xtime_coarse.tv_nsec; + vdso_data->xtime_coarse_sec = tk->xtime_sec; + vdso_data->xtime_coarse_nsec = tk->tkr_mono.xtime_nsec >> + tk->tkr_mono.shift; vdso_data->wtm_clock_sec = tk->wall_to_monotonic.tv_sec; vdso_data->wtm_clock_nsec = tk->wall_to_monotonic.tv_nsec; diff --git a/arch/arm64/kvm/inject_fault.c b/arch/arm64/kvm/inject_fault.c index f02530e726f6..85c57158dcd9 100644 --- a/arch/arm64/kvm/inject_fault.c +++ b/arch/arm64/kvm/inject_fault.c @@ -168,8 +168,8 @@ void kvm_inject_dabt(struct kvm_vcpu *vcpu, unsigned long addr) { if (!(vcpu->arch.hcr_el2 & HCR_RW)) inject_abt32(vcpu, false, addr); - - inject_abt64(vcpu, false, addr); + else + inject_abt64(vcpu, false, addr); } /** @@ -184,8 +184,8 @@ void kvm_inject_pabt(struct kvm_vcpu *vcpu, unsigned long addr) { if (!(vcpu->arch.hcr_el2 & HCR_RW)) inject_abt32(vcpu, true, addr); - - inject_abt64(vcpu, true, addr); + else + inject_abt64(vcpu, true, addr); } /** @@ -198,6 +198,6 @@ void kvm_inject_undefined(struct kvm_vcpu *vcpu) { if (!(vcpu->arch.hcr_el2 & HCR_RW)) inject_undef32(vcpu); - - inject_undef64(vcpu); + else + inject_undef64(vcpu); } diff --git a/arch/mips/kernel/genex.S b/arch/mips/kernel/genex.S index af42e7003f12..baa7b6fc0a60 100644 --- a/arch/mips/kernel/genex.S +++ b/arch/mips/kernel/genex.S @@ -407,7 +407,7 @@ NESTED(nmi_handler, PT_SIZE, sp) .set noat SAVE_ALL FEXPORT(handle_\exception\ext) - __BUILD_clear_\clear + __build_clear_\clear .set at __BUILD_\verbose \exception move a0, sp diff --git a/arch/mips/kernel/scall64-64.S b/arch/mips/kernel/scall64-64.S index ad4d44635c76..a6f6b762c47a 100644 --- a/arch/mips/kernel/scall64-64.S +++ b/arch/mips/kernel/scall64-64.S @@ -80,7 +80,7 @@ syscall_trace_entry: SAVE_STATIC move s0, t2 move a0, sp - daddiu a1, v0, __NR_64_Linux + move a1, v0 jal syscall_trace_enter bltz v0, 2f # seccomp failed? Skip syscall diff --git a/arch/mips/kernel/scall64-n32.S b/arch/mips/kernel/scall64-n32.S index 446cc654da56..4b2010654c46 100644 --- a/arch/mips/kernel/scall64-n32.S +++ b/arch/mips/kernel/scall64-n32.S @@ -72,7 +72,7 @@ n32_syscall_trace_entry: SAVE_STATIC move s0, t2 move a0, sp - daddiu a1, v0, __NR_N32_Linux + move a1, v0 jal syscall_trace_enter bltz v0, 2f # seccomp failed? Skip syscall diff --git a/arch/powerpc/kernel/pci_of_scan.c b/arch/powerpc/kernel/pci_of_scan.c index 42e02a2d570b..efc3fa54c90b 100644 --- a/arch/powerpc/kernel/pci_of_scan.c +++ b/arch/powerpc/kernel/pci_of_scan.c @@ -191,6 +191,9 @@ struct pci_dev *of_create_pci_dev(struct device_node *node, pci_device_add(dev, bus); + /* Setup MSI caps & disable MSI/MSI-X interrupts */ + pci_msi_setup_pci_dev(dev); + return dev; } EXPORT_SYMBOL(of_create_pci_dev); diff --git a/arch/s390/include/asm/etr.h b/arch/s390/include/asm/etr.h index 629b79a93165..f7e5c36688c3 100644 --- a/arch/s390/include/asm/etr.h +++ b/arch/s390/include/asm/etr.h @@ -214,6 +214,9 @@ static inline int etr_ptff(void *ptff_block, unsigned int func) void etr_switch_to_local(void); void etr_sync_check(void); +/* notifier for syncs */ +extern struct atomic_notifier_head s390_epoch_delta_notifier; + /* STP interruption parameter */ struct stp_irq_parm { unsigned int _pad0 : 14; diff --git a/arch/s390/include/asm/kvm_host.h b/arch/s390/include/asm/kvm_host.h index 3024acbe1f9d..df4db81254d3 100644 --- a/arch/s390/include/asm/kvm_host.h +++ b/arch/s390/include/asm/kvm_host.h @@ -258,6 +258,9 @@ struct kvm_vcpu_stat { u32 diagnose_10; u32 diagnose_44; u32 diagnose_9c; + u32 diagnose_258; + u32 diagnose_308; + u32 diagnose_500; }; #define PGM_OPERATION 0x01 @@ -630,7 +633,6 @@ extern char sie_exit; static inline void kvm_arch_hardware_disable(void) {} static inline void kvm_arch_check_processor_compat(void *rtn) {} -static inline void kvm_arch_exit(void) {} static inline void kvm_arch_sync_events(struct kvm *kvm) {} static inline void kvm_arch_vcpu_uninit(struct kvm_vcpu *vcpu) {} static inline void kvm_arch_sched_in(struct kvm_vcpu *vcpu, int cpu) {} diff --git a/arch/s390/kernel/time.c b/arch/s390/kernel/time.c index 9e733d965e08..627887b075a7 100644 --- a/arch/s390/kernel/time.c +++ b/arch/s390/kernel/time.c @@ -58,6 +58,9 @@ EXPORT_SYMBOL_GPL(sched_clock_base_cc); static DEFINE_PER_CPU(struct clock_event_device, comparators); +ATOMIC_NOTIFIER_HEAD(s390_epoch_delta_notifier); +EXPORT_SYMBOL(s390_epoch_delta_notifier); + /* * Scheduler clock - returns current time in nanosec units. */ @@ -752,7 +755,7 @@ static void clock_sync_cpu(struct clock_sync_data *sync) static int etr_sync_clock(void *data) { static int first; - unsigned long long clock, old_clock, delay, delta; + unsigned long long clock, old_clock, clock_delta, delay, delta; struct clock_sync_data *etr_sync; struct etr_aib *sync_port, *aib; int port; @@ -789,6 +792,9 @@ static int etr_sync_clock(void *data) delay = (unsigned long long) (aib->edf2.etv - sync_port->edf2.etv) << 32; delta = adjust_time(old_clock, clock, delay); + clock_delta = clock - old_clock; + atomic_notifier_call_chain(&s390_epoch_delta_notifier, 0, + &clock_delta); etr_sync->fixup_cc = delta; fixup_clock_comparator(delta); /* Verify that the clock is properly set. */ @@ -1526,7 +1532,7 @@ void stp_island_check(void) static int stp_sync_clock(void *data) { static int first; - unsigned long long old_clock, delta; + unsigned long long old_clock, delta, new_clock, clock_delta; struct clock_sync_data *stp_sync; int rc; @@ -1551,7 +1557,11 @@ static int stp_sync_clock(void *data) old_clock = get_tod_clock(); rc = chsc_sstpc(stp_page, STP_OP_SYNC, 0); if (rc == 0) { - delta = adjust_time(old_clock, get_tod_clock(), 0); + new_clock = get_tod_clock(); + delta = adjust_time(old_clock, new_clock, 0); + clock_delta = new_clock - old_clock; + atomic_notifier_call_chain(&s390_epoch_delta_notifier, + 0, &clock_delta); fixup_clock_comparator(delta); rc = chsc_sstpi(stp_page, &stp_info, sizeof(struct stp_sstpi)); diff --git a/arch/s390/kvm/diag.c b/arch/s390/kvm/diag.c index fc7ec95848c3..5fbfb88f8477 100644 --- a/arch/s390/kvm/diag.c +++ b/arch/s390/kvm/diag.c @@ -27,13 +27,13 @@ static int diag_release_pages(struct kvm_vcpu *vcpu) start = vcpu->run->s.regs.gprs[(vcpu->arch.sie_block->ipa & 0xf0) >> 4]; end = vcpu->run->s.regs.gprs[vcpu->arch.sie_block->ipa & 0xf] + 4096; + vcpu->stat.diagnose_10++; if (start & ~PAGE_MASK || end & ~PAGE_MASK || start >= end || start < 2 * PAGE_SIZE) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); VCPU_EVENT(vcpu, 5, "diag release pages %lX %lX", start, end); - vcpu->stat.diagnose_10++; /* * We checked for start >= end above, so lets check for the @@ -75,6 +75,9 @@ static int __diag_page_ref_service(struct kvm_vcpu *vcpu) u16 rx = (vcpu->arch.sie_block->ipa & 0xf0) >> 4; u16 ry = (vcpu->arch.sie_block->ipa & 0x0f); + VCPU_EVENT(vcpu, 3, "diag page reference parameter block at 0x%llx", + vcpu->run->s.regs.gprs[rx]); + vcpu->stat.diagnose_258++; if (vcpu->run->s.regs.gprs[rx] & 7) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); rc = read_guest(vcpu, vcpu->run->s.regs.gprs[rx], rx, &parm, sizeof(parm)); @@ -85,6 +88,9 @@ static int __diag_page_ref_service(struct kvm_vcpu *vcpu) switch (parm.subcode) { case 0: /* TOKEN */ + VCPU_EVENT(vcpu, 3, "pageref token addr 0x%llx " + "select mask 0x%llx compare mask 0x%llx", + parm.token_addr, parm.select_mask, parm.compare_mask); if (vcpu->arch.pfault_token != KVM_S390_PFAULT_TOKEN_INVALID) { /* * If the pagefault handshake is already activated, @@ -114,6 +120,7 @@ static int __diag_page_ref_service(struct kvm_vcpu *vcpu) * the cancel, therefore to reduce code complexity, we assume * all outstanding tokens are already pending. */ + VCPU_EVENT(vcpu, 3, "pageref cancel addr 0x%llx", parm.token_addr); if (parm.token_addr || parm.select_mask || parm.compare_mask || parm.zarch) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); @@ -174,7 +181,8 @@ static int __diag_ipl_functions(struct kvm_vcpu *vcpu) unsigned int reg = vcpu->arch.sie_block->ipa & 0xf; unsigned long subcode = vcpu->run->s.regs.gprs[reg] & 0xffff; - VCPU_EVENT(vcpu, 5, "diag ipl functions, subcode %lx", subcode); + VCPU_EVENT(vcpu, 3, "diag ipl functions, subcode %lx", subcode); + vcpu->stat.diagnose_308++; switch (subcode) { case 3: vcpu->run->s390_reset_flags = KVM_S390_RESET_CLEAR; @@ -202,6 +210,7 @@ static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu) { int ret; + vcpu->stat.diagnose_500++; /* No virtio-ccw notification? Get out quickly. */ if (!vcpu->kvm->arch.css_support || (vcpu->run->s.regs.gprs[1] != KVM_S390_VIRTIO_CCW_NOTIFY)) diff --git a/arch/s390/kvm/guestdbg.c b/arch/s390/kvm/guestdbg.c index e97b3455d7e6..47518a324d75 100644 --- a/arch/s390/kvm/guestdbg.c +++ b/arch/s390/kvm/guestdbg.c @@ -473,10 +473,45 @@ static void filter_guest_per_event(struct kvm_vcpu *vcpu) vcpu->arch.sie_block->iprcc &= ~PGM_PER; } +#define pssec(vcpu) (vcpu->arch.sie_block->gcr[1] & _ASCE_SPACE_SWITCH) +#define hssec(vcpu) (vcpu->arch.sie_block->gcr[13] & _ASCE_SPACE_SWITCH) +#define old_ssec(vcpu) ((vcpu->arch.sie_block->tecmc >> 31) & 0x1) +#define old_as_is_home(vcpu) !(vcpu->arch.sie_block->tecmc & 0xffff) + void kvm_s390_handle_per_event(struct kvm_vcpu *vcpu) { + int new_as; + if (debug_exit_required(vcpu)) vcpu->guest_debug |= KVM_GUESTDBG_EXIT_PENDING; filter_guest_per_event(vcpu); + + /* + * Only RP, SAC, SACF, PT, PTI, PR, PC instructions can trigger + * a space-switch event. PER events enforce space-switch events + * for these instructions. So if no PER event for the guest is left, + * we might have to filter the space-switch element out, too. + */ + if (vcpu->arch.sie_block->iprcc == PGM_SPACE_SWITCH) { + vcpu->arch.sie_block->iprcc = 0; + new_as = psw_bits(vcpu->arch.sie_block->gpsw).as; + + /* + * If the AS changed from / to home, we had RP, SAC or SACF + * instruction. Check primary and home space-switch-event + * controls. (theoretically home -> home produced no event) + */ + if (((new_as == PSW_AS_HOME) ^ old_as_is_home(vcpu)) && + (pssec(vcpu) || hssec(vcpu))) + vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH; + + /* + * PT, PTI, PR, PC instruction operate on primary AS only. Check + * if the primary-space-switch-event control was or got set. + */ + if (new_as == PSW_AS_PRIMARY && !old_as_is_home(vcpu) && + (pssec(vcpu) || old_ssec(vcpu))) + vcpu->arch.sie_block->iprcc = PGM_SPACE_SWITCH; + } } diff --git a/arch/s390/kvm/interrupt.c b/arch/s390/kvm/interrupt.c index c98d89708e99..b277d50dcf76 100644 --- a/arch/s390/kvm/interrupt.c +++ b/arch/s390/kvm/interrupt.c @@ -30,7 +30,6 @@ #define IOINT_SCHID_MASK 0x0000ffff #define IOINT_SSID_MASK 0x00030000 #define IOINT_CSSID_MASK 0x03fc0000 -#define IOINT_AI_MASK 0x04000000 #define PFAULT_INIT 0x0600 #define PFAULT_DONE 0x0680 #define VIRTIO_PARAM 0x0d00 @@ -72,9 +71,13 @@ static int ckc_interrupts_enabled(struct kvm_vcpu *vcpu) static int ckc_irq_pending(struct kvm_vcpu *vcpu) { + preempt_disable(); if (!(vcpu->arch.sie_block->ckc < - get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) + get_tod_clock_fast() + vcpu->arch.sie_block->epoch)) { + preempt_enable(); return 0; + } + preempt_enable(); return ckc_interrupts_enabled(vcpu); } @@ -311,8 +314,8 @@ static int __must_check __deliver_pfault_init(struct kvm_vcpu *vcpu) li->irq.ext.ext_params2 = 0; spin_unlock(&li->lock); - VCPU_EVENT(vcpu, 4, "interrupt: pfault init parm:%x,parm64:%llx", - 0, ext.ext_params2); + VCPU_EVENT(vcpu, 4, "deliver: pfault init token 0x%llx", + ext.ext_params2); trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_PFAULT_INIT, 0, ext.ext_params2); @@ -368,7 +371,7 @@ static int __must_check __deliver_machine_check(struct kvm_vcpu *vcpu) spin_unlock(&fi->lock); if (deliver) { - VCPU_EVENT(vcpu, 4, "interrupt: machine check mcic=%llx", + VCPU_EVENT(vcpu, 3, "deliver: machine check mcic 0x%llx", mchk.mcic); trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_MCHK, @@ -403,7 +406,7 @@ static int __must_check __deliver_restart(struct kvm_vcpu *vcpu) struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; int rc; - VCPU_EVENT(vcpu, 4, "%s", "interrupt: cpu restart"); + VCPU_EVENT(vcpu, 3, "%s", "deliver: cpu restart"); vcpu->stat.deliver_restart_signal++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_RESTART, 0, 0); @@ -427,7 +430,6 @@ static int __must_check __deliver_set_prefix(struct kvm_vcpu *vcpu) clear_bit(IRQ_PEND_SET_PREFIX, &li->pending_irqs); spin_unlock(&li->lock); - VCPU_EVENT(vcpu, 4, "interrupt: set prefix to %x", prefix.address); vcpu->stat.deliver_prefix_signal++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_SIGP_SET_PREFIX, @@ -450,7 +452,7 @@ static int __must_check __deliver_emergency_signal(struct kvm_vcpu *vcpu) clear_bit(IRQ_PEND_EXT_EMERGENCY, &li->pending_irqs); spin_unlock(&li->lock); - VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp emerg"); + VCPU_EVENT(vcpu, 4, "%s", "deliver: sigp emerg"); vcpu->stat.deliver_emergency_signal++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_EMERGENCY, cpu_addr, 0); @@ -477,7 +479,7 @@ static int __must_check __deliver_external_call(struct kvm_vcpu *vcpu) clear_bit(IRQ_PEND_EXT_EXTERNAL, &li->pending_irqs); spin_unlock(&li->lock); - VCPU_EVENT(vcpu, 4, "%s", "interrupt: sigp ext call"); + VCPU_EVENT(vcpu, 4, "%s", "deliver: sigp ext call"); vcpu->stat.deliver_external_call++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_EXTERNAL_CALL, @@ -506,7 +508,7 @@ static int __must_check __deliver_prog(struct kvm_vcpu *vcpu) memset(&li->irq.pgm, 0, sizeof(pgm_info)); spin_unlock(&li->lock); - VCPU_EVENT(vcpu, 4, "interrupt: pgm check code:%x, ilc:%x", + VCPU_EVENT(vcpu, 3, "deliver: program irq code 0x%x, ilc:%d", pgm_info.code, ilc); vcpu->stat.deliver_program_int++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, @@ -622,7 +624,7 @@ static int __must_check __deliver_service(struct kvm_vcpu *vcpu) clear_bit(IRQ_PEND_EXT_SERVICE, &fi->pending_irqs); spin_unlock(&fi->lock); - VCPU_EVENT(vcpu, 4, "interrupt: sclp parm:%x", + VCPU_EVENT(vcpu, 4, "deliver: sclp parameter 0x%x", ext.ext_params); vcpu->stat.deliver_service_signal++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, KVM_S390_INT_SERVICE, @@ -651,9 +653,6 @@ static int __must_check __deliver_pfault_done(struct kvm_vcpu *vcpu) struct kvm_s390_interrupt_info, list); if (inti) { - trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, - KVM_S390_INT_PFAULT_DONE, 0, - inti->ext.ext_params2); list_del(&inti->list); fi->counters[FIRQ_CNTR_PFAULT] -= 1; } @@ -662,6 +661,12 @@ static int __must_check __deliver_pfault_done(struct kvm_vcpu *vcpu) spin_unlock(&fi->lock); if (inti) { + trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, + KVM_S390_INT_PFAULT_DONE, 0, + inti->ext.ext_params2); + VCPU_EVENT(vcpu, 4, "deliver: pfault done token 0x%llx", + inti->ext.ext_params2); + rc = put_guest_lc(vcpu, EXT_IRQ_CP_SERVICE, (u16 *)__LC_EXT_INT_CODE); rc |= put_guest_lc(vcpu, PFAULT_DONE, @@ -691,7 +696,7 @@ static int __must_check __deliver_virtio(struct kvm_vcpu *vcpu) list); if (inti) { VCPU_EVENT(vcpu, 4, - "interrupt: virtio parm:%x,parm64:%llx", + "deliver: virtio parm: 0x%x,parm64: 0x%llx", inti->ext.ext_params, inti->ext.ext_params2); vcpu->stat.deliver_virtio_interrupt++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, @@ -741,7 +746,7 @@ static int __must_check __deliver_io(struct kvm_vcpu *vcpu, struct kvm_s390_interrupt_info, list); if (inti) { - VCPU_EVENT(vcpu, 4, "interrupt: I/O %llx", inti->type); + VCPU_EVENT(vcpu, 4, "deliver: I/O 0x%llx", inti->type); vcpu->stat.deliver_io_int++; trace_kvm_s390_deliver_interrupt(vcpu->vcpu_id, inti->type, @@ -855,7 +860,9 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) goto no_timer; } + preempt_disable(); now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; + preempt_enable(); sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); /* underflow */ @@ -864,7 +871,7 @@ int kvm_s390_handle_wait(struct kvm_vcpu *vcpu) __set_cpu_idle(vcpu); hrtimer_start(&vcpu->arch.ckc_timer, ktime_set (0, sltime) , HRTIMER_MODE_REL); - VCPU_EVENT(vcpu, 5, "enabled wait via clock comparator: %llx ns", sltime); + VCPU_EVENT(vcpu, 4, "enabled wait via clock comparator: %llu ns", sltime); no_timer: srcu_read_unlock(&vcpu->kvm->srcu, vcpu->srcu_idx); kvm_vcpu_block(vcpu); @@ -894,7 +901,9 @@ enum hrtimer_restart kvm_s390_idle_wakeup(struct hrtimer *timer) u64 now, sltime; vcpu = container_of(timer, struct kvm_vcpu, arch.ckc_timer); + preempt_disable(); now = get_tod_clock_fast() + vcpu->arch.sie_block->epoch; + preempt_enable(); sltime = tod_to_ns(vcpu->arch.sie_block->ckc - now); /* @@ -968,6 +977,10 @@ static int __inject_prog(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; + VCPU_EVENT(vcpu, 3, "inject: program irq code 0x%x", irq->u.pgm.code); + trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, + irq->u.pgm.code, 0); + li->irq.pgm = irq->u.pgm; set_bit(IRQ_PEND_PROG, &li->pending_irqs); return 0; @@ -978,9 +991,6 @@ int kvm_s390_inject_program_int(struct kvm_vcpu *vcpu, u16 code) struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_irq irq; - VCPU_EVENT(vcpu, 3, "inject: program check %d (from kernel)", code); - trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, code, - 0, 1); spin_lock(&li->lock); irq.u.pgm.code = code; __inject_prog(vcpu, &irq); @@ -996,10 +1006,6 @@ int kvm_s390_inject_prog_irq(struct kvm_vcpu *vcpu, struct kvm_s390_irq irq; int rc; - VCPU_EVENT(vcpu, 3, "inject: prog irq %d (from kernel)", - pgm_info->code); - trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_PROGRAM_INT, - pgm_info->code, 0, 1); spin_lock(&li->lock); irq.u.pgm = *pgm_info; rc = __inject_prog(vcpu, &irq); @@ -1012,11 +1018,11 @@ static int __inject_pfault_init(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; - VCPU_EVENT(vcpu, 3, "inject: external irq params:%x, params2:%llx", - irq->u.ext.ext_params, irq->u.ext.ext_params2); + VCPU_EVENT(vcpu, 4, "inject: pfault init parameter block at 0x%llx", + irq->u.ext.ext_params2); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_PFAULT_INIT, irq->u.ext.ext_params, - irq->u.ext.ext_params2, 2); + irq->u.ext.ext_params2); li->irq.ext = irq->u.ext; set_bit(IRQ_PEND_PFAULT_INIT, &li->pending_irqs); @@ -1045,10 +1051,10 @@ static int __inject_extcall(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) struct kvm_s390_extcall_info *extcall = &li->irq.extcall; uint16_t src_id = irq->u.extcall.code; - VCPU_EVENT(vcpu, 3, "inject: external call source-cpu:%u", + VCPU_EVENT(vcpu, 4, "inject: external call source-cpu:%u", src_id); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_EXTERNAL_CALL, - src_id, 0, 2); + src_id, 0); /* sending vcpu invalid */ if (src_id >= KVM_MAX_VCPUS || @@ -1070,10 +1076,10 @@ static int __inject_set_prefix(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_prefix_info *prefix = &li->irq.prefix; - VCPU_EVENT(vcpu, 3, "inject: set prefix to %x (from user)", + VCPU_EVENT(vcpu, 3, "inject: set prefix to %x", irq->u.prefix.address); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_SIGP_SET_PREFIX, - irq->u.prefix.address, 0, 2); + irq->u.prefix.address, 0); if (!is_vcpu_stopped(vcpu)) return -EBUSY; @@ -1090,7 +1096,7 @@ static int __inject_sigp_stop(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) struct kvm_s390_stop_info *stop = &li->irq.stop; int rc = 0; - trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_SIGP_STOP, 0, 0, 2); + trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_SIGP_STOP, 0, 0); if (irq->u.stop.flags & ~KVM_S390_STOP_SUPP_FLAGS) return -EINVAL; @@ -1114,8 +1120,8 @@ static int __inject_sigp_restart(struct kvm_vcpu *vcpu, { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; - VCPU_EVENT(vcpu, 3, "inject: restart type %llx", irq->type); - trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_RESTART, 0, 0, 2); + VCPU_EVENT(vcpu, 3, "%s", "inject: restart int"); + trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_RESTART, 0, 0); set_bit(IRQ_PEND_RESTART, &li->pending_irqs); return 0; @@ -1126,10 +1132,10 @@ static int __inject_sigp_emergency(struct kvm_vcpu *vcpu, { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; - VCPU_EVENT(vcpu, 3, "inject: emergency %u\n", + VCPU_EVENT(vcpu, 4, "inject: emergency from cpu %u", irq->u.emerg.code); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_EMERGENCY, - irq->u.emerg.code, 0, 2); + irq->u.emerg.code, 0); set_bit(irq->u.emerg.code, li->sigp_emerg_pending); set_bit(IRQ_PEND_EXT_EMERGENCY, &li->pending_irqs); @@ -1142,10 +1148,10 @@ static int __inject_mchk(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; struct kvm_s390_mchk_info *mchk = &li->irq.mchk; - VCPU_EVENT(vcpu, 5, "inject: machine check parm64:%llx", + VCPU_EVENT(vcpu, 3, "inject: machine check mcic 0x%llx", irq->u.mchk.mcic); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_MCHK, 0, - irq->u.mchk.mcic, 2); + irq->u.mchk.mcic); /* * Because repressible machine checks can be indicated along with @@ -1172,9 +1178,9 @@ static int __inject_ckc(struct kvm_vcpu *vcpu) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; - VCPU_EVENT(vcpu, 3, "inject: type %x", KVM_S390_INT_CLOCK_COMP); + VCPU_EVENT(vcpu, 3, "%s", "inject: clock comparator external"); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_CLOCK_COMP, - 0, 0, 2); + 0, 0); set_bit(IRQ_PEND_EXT_CLOCK_COMP, &li->pending_irqs); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); @@ -1185,9 +1191,9 @@ static int __inject_cpu_timer(struct kvm_vcpu *vcpu) { struct kvm_s390_local_interrupt *li = &vcpu->arch.local_int; - VCPU_EVENT(vcpu, 3, "inject: type %x", KVM_S390_INT_CPU_TIMER); + VCPU_EVENT(vcpu, 3, "%s", "inject: cpu timer external"); trace_kvm_s390_inject_vcpu(vcpu->vcpu_id, KVM_S390_INT_CPU_TIMER, - 0, 0, 2); + 0, 0); set_bit(IRQ_PEND_EXT_CPU_TIMER, &li->pending_irqs); atomic_set_mask(CPUSTAT_EXT_INT, li->cpuflags); @@ -1435,20 +1441,20 @@ int kvm_s390_inject_vm(struct kvm *kvm, inti->ext.ext_params2 = s390int->parm64; break; case KVM_S390_INT_SERVICE: - VM_EVENT(kvm, 5, "inject: sclp parm:%x", s390int->parm); + VM_EVENT(kvm, 4, "inject: sclp parm:%x", s390int->parm); inti->ext.ext_params = s390int->parm; break; case KVM_S390_INT_PFAULT_DONE: inti->ext.ext_params2 = s390int->parm64; break; case KVM_S390_MCHK: - VM_EVENT(kvm, 5, "inject: machine check parm64:%llx", + VM_EVENT(kvm, 3, "inject: machine check mcic 0x%llx", s390int->parm64); inti->mchk.cr14 = s390int->parm; /* upper bits are not used */ inti->mchk.mcic = s390int->parm64; break; case KVM_S390_INT_IO_MIN...KVM_S390_INT_IO_MAX: - if (inti->type & IOINT_AI_MASK) + if (inti->type & KVM_S390_INT_IO_AI_MASK) VM_EVENT(kvm, 5, "%s", "inject: I/O (AI)"); else VM_EVENT(kvm, 5, "inject: I/O css %x ss %x schid %04x", @@ -1535,8 +1541,6 @@ static int do_inject_vcpu(struct kvm_vcpu *vcpu, struct kvm_s390_irq *irq) switch (irq->type) { case KVM_S390_PROGRAM_INT: - VCPU_EVENT(vcpu, 3, "inject: program check %d (from user)", - irq->u.pgm.code); rc = __inject_prog(vcpu, irq); break; case KVM_S390_SIGP_SET_PREFIX: diff --git a/arch/s390/kvm/kvm-s390.c b/arch/s390/kvm/kvm-s390.c index f32f843a3631..6861b74649ae 100644 --- a/arch/s390/kvm/kvm-s390.c +++ b/arch/s390/kvm/kvm-s390.c @@ -28,6 +28,7 @@ #include <linux/vmalloc.h> #include <asm/asm-offsets.h> #include <asm/lowcore.h> +#include <asm/etr.h> #include <asm/pgtable.h> #include <asm/nmi.h> #include <asm/switch_to.h> @@ -108,6 +109,9 @@ struct kvm_stats_debugfs_item debugfs_entries[] = { { "diagnose_10", VCPU_STAT(diagnose_10) }, { "diagnose_44", VCPU_STAT(diagnose_44) }, { "diagnose_9c", VCPU_STAT(diagnose_9c) }, + { "diagnose_258", VCPU_STAT(diagnose_258) }, + { "diagnose_308", VCPU_STAT(diagnose_308) }, + { "diagnose_500", VCPU_STAT(diagnose_500) }, { NULL } }; @@ -124,6 +128,7 @@ unsigned long kvm_s390_fac_list_mask_size(void) } static struct gmap_notifier gmap_notifier; +debug_info_t *kvm_s390_dbf; /* Section: not file related */ int kvm_arch_hardware_enable(void) @@ -134,24 +139,69 @@ int kvm_arch_hardware_enable(void) static void kvm_gmap_notifier(struct gmap *gmap, unsigned long address); +/* + * This callback is executed during stop_machine(). All CPUs are therefore + * temporarily stopped. In order not to change guest behavior, we have to + * disable preemption whenever we touch the epoch of kvm and the VCPUs, + * so a CPU won't be stopped while calculating with the epoch. + */ +static int kvm_clock_sync(struct notifier_block *notifier, unsigned long val, + void *v) +{ + struct kvm *kvm; + struct kvm_vcpu *vcpu; + int i; + unsigned long long *delta = v; + + list_for_each_entry(kvm, &vm_list, vm_list) { + kvm->arch.epoch -= *delta; + kvm_for_each_vcpu(i, vcpu, kvm) { + vcpu->arch.sie_block->epoch -= *delta; + } + } + return NOTIFY_OK; +} + +static struct notifier_block kvm_clock_notifier = { + .notifier_call = kvm_clock_sync, +}; + int kvm_arch_hardware_setup(void) { gmap_notifier.notifier_call = kvm_gmap_notifier; gmap_register_ipte_notifier(&gmap_notifier); + atomic_notifier_chain_register(&s390_epoch_delta_notifier, + &kvm_clock_notifier); return 0; } void kvm_arch_hardware_unsetup(void) { gmap_unregister_ipte_notifier(&gmap_notifier); + atomic_notifier_chain_unregister(&s390_epoch_delta_notifier, + &kvm_clock_notifier); } int kvm_arch_init(void *opaque) { + kvm_s390_dbf = debug_register("kvm-trace", 32, 1, 7 * sizeof(long)); + if (!kvm_s390_dbf) + return -ENOMEM; + + if (debug_register_view(kvm_s390_dbf, &debug_sprintf_view)) { + debug_unregister(kvm_s390_dbf); + return -ENOMEM; + } + /* Register floating interrupt controller interface. */ return kvm_register_device_ops(&kvm_flic_ops, KVM_DEV_TYPE_FLIC); } +void kvm_arch_exit(void) +{ + debug_unregister(kvm_s390_dbf); +} + /* Section: device related */ long kvm_arch_dev_ioctl(struct file *filp, unsigned int ioctl, unsigned long arg) @@ -281,10 +331,12 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) switch (cap->cap) { case KVM_CAP_S390_IRQCHIP: + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_IRQCHIP"); kvm->arch.use_irqchip = 1; r = 0; break; case KVM_CAP_S390_USER_SIGP: + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_SIGP"); kvm->arch.user_sigp = 1; r = 0; break; @@ -295,8 +347,11 @@ static int kvm_vm_ioctl_enable_cap(struct kvm *kvm, struct kvm_enable_cap *cap) r = 0; } else r = -EINVAL; + VM_EVENT(kvm, 3, "ENABLE: CAP_S390_VECTOR_REGISTERS %s", + r ? "(not available)" : "(success)"); break; case KVM_CAP_S390_USER_STSI: + VM_EVENT(kvm, 3, "%s", "ENABLE: CAP_S390_USER_STSI"); kvm->arch.user_stsi = 1; r = 0; break; @@ -314,6 +369,8 @@ static int kvm_s390_get_mem_control(struct kvm *kvm, struct kvm_device_attr *att switch (attr->attr) { case KVM_S390_VM_MEM_LIMIT_SIZE: ret = 0; + VM_EVENT(kvm, 3, "QUERY: max guest memory: %lu bytes", + kvm->arch.gmap->asce_end); if (put_user(kvm->arch.gmap->asce_end, (u64 __user *)attr->addr)) ret = -EFAULT; break; @@ -330,7 +387,13 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att unsigned int idx; switch (attr->attr) { case KVM_S390_VM_MEM_ENABLE_CMMA: + /* enable CMMA only for z10 and later (EDAT_1) */ + ret = -EINVAL; + if (!MACHINE_IS_LPAR || !MACHINE_HAS_EDAT1) + break; + ret = -EBUSY; + VM_EVENT(kvm, 3, "%s", "ENABLE: CMMA support"); mutex_lock(&kvm->lock); if (atomic_read(&kvm->online_vcpus) == 0) { kvm->arch.use_cmma = 1; @@ -339,6 +402,11 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att mutex_unlock(&kvm->lock); break; case KVM_S390_VM_MEM_CLR_CMMA: + ret = -EINVAL; + if (!kvm->arch.use_cmma) + break; + + VM_EVENT(kvm, 3, "%s", "RESET: CMMA states"); mutex_lock(&kvm->lock); idx = srcu_read_lock(&kvm->srcu); s390_reset_cmma(kvm->arch.gmap->mm); @@ -374,6 +442,7 @@ static int kvm_s390_set_mem_control(struct kvm *kvm, struct kvm_device_attr *att } } mutex_unlock(&kvm->lock); + VM_EVENT(kvm, 3, "SET: max guest memory: %lu bytes", new_limit); break; } default: @@ -400,22 +469,26 @@ static int kvm_s390_vm_set_crypto(struct kvm *kvm, struct kvm_device_attr *attr) kvm->arch.crypto.crycb->aes_wrapping_key_mask, sizeof(kvm->arch.crypto.crycb->aes_wrapping_key_mask)); kvm->arch.crypto.aes_kw = 1; + VM_EVENT(kvm, 3, "%s", "ENABLE: AES keywrapping support"); break; case KVM_S390_VM_CRYPTO_ENABLE_DEA_KW: get_random_bytes( kvm->arch.crypto.crycb->dea_wrapping_key_mask, sizeof(kvm->arch.crypto.crycb->dea_wrapping_key_mask)); kvm->arch.crypto.dea_kw = 1; + VM_EVENT(kvm, 3, "%s", "ENABLE: DEA keywrapping support"); break; case KVM_S390_VM_CRYPTO_DISABLE_AES_KW: kvm->arch.crypto.aes_kw = 0; memset(kvm->arch.crypto.crycb->aes_wrapping_key_mask, 0, sizeof(kvm->arch.crypto.crycb->aes_wrapping_key_mask)); + VM_EVENT(kvm, 3, "%s", "DISABLE: AES keywrapping support"); break; case KVM_S390_VM_CRYPTO_DISABLE_DEA_KW: kvm->arch.crypto.dea_kw = 0; memset(kvm->arch.crypto.crycb->dea_wrapping_key_mask, 0, sizeof(kvm->arch.crypto.crycb->dea_wrapping_key_mask)); + VM_EVENT(kvm, 3, "%s", "DISABLE: DEA keywrapping support"); break; default: mutex_unlock(&kvm->lock); @@ -440,6 +513,7 @@ static int kvm_s390_set_tod_high(struct kvm *kvm, struct kvm_device_attr *attr) if (gtod_high != 0) return -EINVAL; + VM_EVENT(kvm, 3, "SET: TOD extension: 0x%x\n", gtod_high); return 0; } @@ -459,12 +533,15 @@ static int kvm_s390_set_tod_low(struct kvm *kvm, struct kvm_device_attr *attr) return r; mutex_lock(&kvm->lock); + preempt_disable(); kvm->arch.epoch = gtod - host_tod; kvm_s390_vcpu_block_all(kvm); kvm_for_each_vcpu(vcpu_idx, cur_vcpu, kvm) cur_vcpu->arch.sie_block->epoch = kvm->arch.epoch; kvm_s390_vcpu_unblock_all(kvm); + preempt_enable(); mutex_unlock(&kvm->lock); + VM_EVENT(kvm, 3, "SET: TOD base: 0x%llx\n", gtod); return 0; } @@ -496,6 +573,7 @@ static int kvm_s390_get_tod_high(struct kvm *kvm, struct kvm_device_attr *attr) if (copy_to_user((void __user *)attr->addr, >od_high, sizeof(gtod_high))) return -EFAULT; + VM_EVENT(kvm, 3, "QUERY: TOD extension: 0x%x\n", gtod_high); return 0; } @@ -509,9 +587,12 @@ static int kvm_s390_get_tod_low(struct kvm *kvm, struct kvm_device_attr *attr) if (r) return r; + preempt_disable(); gtod = host_tod + kvm->arch.epoch; + preempt_enable(); if (copy_to_user((void __user *)attr->addr, >od, sizeof(gtod))) return -EFAULT; + VM_EVENT(kvm, 3, "QUERY: TOD base: 0x%llx\n", gtod); return 0; } @@ -821,7 +902,9 @@ static long kvm_s390_set_skeys(struct kvm *kvm, struct kvm_s390_skeys *args) } /* Enable storage key handling for the guest */ - s390_enable_skey(); + r = s390_enable_skey(); + if (r) + goto out; for (i = 0; i < args->count; i++) { hva = gfn_to_hva(kvm, args->start_gfn + i); @@ -879,8 +962,7 @@ long kvm_arch_vm_ioctl(struct file *filp, if (kvm->arch.use_irqchip) { /* Set up dummy routing. */ memset(&routing, 0, sizeof(routing)); - kvm_set_irq_routing(kvm, &routing, 0, 0); - r = 0; + r = kvm_set_irq_routing(kvm, &routing, 0, 0); } break; } @@ -1043,7 +1125,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) sprintf(debug_name, "kvm-%u", current->pid); - kvm->arch.dbf = debug_register(debug_name, 8, 2, 8 * sizeof(long)); + kvm->arch.dbf = debug_register(debug_name, 32, 1, 7 * sizeof(long)); if (!kvm->arch.dbf) goto out_err; @@ -1086,7 +1168,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) mutex_init(&kvm->arch.ipte_mutex); debug_register_view(kvm->arch.dbf, &debug_sprintf_view); - VM_EVENT(kvm, 3, "%s", "vm created"); + VM_EVENT(kvm, 3, "vm created with type %lu", type); if (type & KVM_VM_S390_UCONTROL) { kvm->arch.gmap = NULL; @@ -1103,6 +1185,7 @@ int kvm_arch_init_vm(struct kvm *kvm, unsigned long type) kvm->arch.epoch = 0; spin_lock_init(&kvm->arch.start_stop_lock); + KVM_EVENT(3, "vm 0x%p created by pid %u", kvm, current->pid); return 0; out_err: @@ -1110,6 +1193,7 @@ out_err: free_page((unsigned long)kvm->arch.model.fac); debug_unregister(kvm->arch.dbf); free_page((unsigned long)(kvm->arch.sca)); + KVM_EVENT(3, "creation of vm failed: %d", rc); return rc; } @@ -1131,7 +1215,7 @@ void kvm_arch_vcpu_destroy(struct kvm_vcpu *vcpu) if (kvm_is_ucontrol(vcpu->kvm)) gmap_free(vcpu->arch.gmap); - if (kvm_s390_cmma_enabled(vcpu->kvm)) + if (vcpu->kvm->arch.use_cmma) kvm_s390_vcpu_unsetup_cmma(vcpu); free_page((unsigned long)(vcpu->arch.sie_block)); @@ -1166,6 +1250,7 @@ void kvm_arch_destroy_vm(struct kvm *kvm) gmap_free(kvm->arch.gmap); kvm_s390_destroy_adapters(kvm); kvm_s390_clear_float_irqs(kvm); + KVM_EVENT(3, "vm 0x%p destroyed", kvm); } /* Section: vcpu related */ @@ -1264,7 +1349,9 @@ static void kvm_s390_vcpu_initial_reset(struct kvm_vcpu *vcpu) void kvm_arch_vcpu_postcreate(struct kvm_vcpu *vcpu) { mutex_lock(&vcpu->kvm->lock); + preempt_disable(); vcpu->arch.sie_block->epoch = vcpu->kvm->arch.epoch; + preempt_enable(); mutex_unlock(&vcpu->kvm->lock); if (!kvm_is_ucontrol(vcpu->kvm)) vcpu->arch.gmap = vcpu->kvm->arch.gmap; @@ -1342,7 +1429,7 @@ int kvm_arch_vcpu_setup(struct kvm_vcpu *vcpu) } vcpu->arch.sie_block->ictl |= ICTL_ISKE | ICTL_SSKE | ICTL_RRBE; - if (kvm_s390_cmma_enabled(vcpu->kvm)) { + if (vcpu->kvm->arch.use_cmma) { rc = kvm_s390_vcpu_setup_cmma(vcpu); if (rc) return rc; @@ -1723,18 +1810,6 @@ int kvm_arch_vcpu_ioctl_set_mpstate(struct kvm_vcpu *vcpu, return rc; } -bool kvm_s390_cmma_enabled(struct kvm *kvm) -{ - if (!MACHINE_IS_LPAR) - return false; - /* only enable for z10 and later */ - if (!MACHINE_HAS_EDAT1) - return false; - if (!kvm->arch.use_cmma) - return false; - return true; -} - static bool ibs_enabled(struct kvm_vcpu *vcpu) { return atomic_read(&vcpu->arch.sie_block->cpuflags) & CPUSTAT_IBS; @@ -2340,6 +2415,7 @@ static int kvm_vcpu_ioctl_enable_cap(struct kvm_vcpu *vcpu, case KVM_CAP_S390_CSS_SUPPORT: if (!vcpu->kvm->arch.css_support) { vcpu->kvm->arch.css_support = 1; + VM_EVENT(vcpu->kvm, 3, "%s", "ENABLE: CSS support"); trace_kvm_s390_enable_css(vcpu->kvm); } r = 0; diff --git a/arch/s390/kvm/kvm-s390.h b/arch/s390/kvm/kvm-s390.h index c5704786e473..c446aabf60d3 100644 --- a/arch/s390/kvm/kvm-s390.h +++ b/arch/s390/kvm/kvm-s390.h @@ -27,6 +27,13 @@ typedef int (*intercept_handler_t)(struct kvm_vcpu *vcpu); #define TDB_FORMAT1 1 #define IS_ITDB_VALID(vcpu) ((*(char *)vcpu->arch.sie_block->itdba == TDB_FORMAT1)) +extern debug_info_t *kvm_s390_dbf; +#define KVM_EVENT(d_loglevel, d_string, d_args...)\ +do { \ + debug_sprintf_event(kvm_s390_dbf, d_loglevel, d_string "\n", \ + d_args); \ +} while (0) + #define VM_EVENT(d_kvm, d_loglevel, d_string, d_args...)\ do { \ debug_sprintf_event(d_kvm->arch.dbf, d_loglevel, d_string "\n", \ @@ -65,6 +72,8 @@ static inline u32 kvm_s390_get_prefix(struct kvm_vcpu *vcpu) static inline void kvm_s390_set_prefix(struct kvm_vcpu *vcpu, u32 prefix) { + VCPU_EVENT(vcpu, 3, "set prefix of cpu %03u to 0x%x", vcpu->vcpu_id, + prefix); vcpu->arch.sie_block->prefix = prefix >> GUEST_PREFIX_SHIFT; kvm_make_request(KVM_REQ_TLB_FLUSH, vcpu); kvm_make_request(KVM_REQ_MMU_RELOAD, vcpu); @@ -217,8 +226,6 @@ void exit_sie(struct kvm_vcpu *vcpu); void kvm_s390_sync_request(int req, struct kvm_vcpu *vcpu); int kvm_s390_vcpu_setup_cmma(struct kvm_vcpu *vcpu); void kvm_s390_vcpu_unsetup_cmma(struct kvm_vcpu *vcpu); -/* is cmma enabled */ -bool kvm_s390_cmma_enabled(struct kvm *kvm); unsigned long kvm_s390_fac_list_mask_size(void); extern unsigned long kvm_s390_fac_list_mask[]; diff --git a/arch/s390/kvm/priv.c b/arch/s390/kvm/priv.c index ad4242245771..4d21dc4d1a84 100644 --- a/arch/s390/kvm/priv.c +++ b/arch/s390/kvm/priv.c @@ -53,11 +53,14 @@ static int handle_set_clock(struct kvm_vcpu *vcpu) kvm_s390_set_psw_cc(vcpu, 3); return 0; } + VCPU_EVENT(vcpu, 3, "SCK: setting guest TOD to 0x%llx", val); val = (val - hostclk) & ~0x3fUL; mutex_lock(&vcpu->kvm->lock); + preempt_disable(); kvm_for_each_vcpu(i, cpup, vcpu->kvm) cpup->arch.sie_block->epoch = val; + preempt_enable(); mutex_unlock(&vcpu->kvm->lock); kvm_s390_set_psw_cc(vcpu, 0); @@ -98,8 +101,6 @@ static int handle_set_prefix(struct kvm_vcpu *vcpu) return kvm_s390_inject_program_int(vcpu, PGM_ADDRESSING); kvm_s390_set_prefix(vcpu, address); - - VCPU_EVENT(vcpu, 5, "setting prefix to %x", address); trace_kvm_s390_handle_prefix(vcpu, 1, address); return 0; } @@ -129,7 +130,7 @@ static int handle_store_prefix(struct kvm_vcpu *vcpu) if (rc) return kvm_s390_inject_prog_cond(vcpu, rc); - VCPU_EVENT(vcpu, 5, "storing prefix to %x", address); + VCPU_EVENT(vcpu, 3, "STPX: storing prefix 0x%x into 0x%llx", address, operand2); trace_kvm_s390_handle_prefix(vcpu, 0, address); return 0; } @@ -155,7 +156,7 @@ static int handle_store_cpu_address(struct kvm_vcpu *vcpu) if (rc) return kvm_s390_inject_prog_cond(vcpu, rc); - VCPU_EVENT(vcpu, 5, "storing cpu address to %llx", ga); + VCPU_EVENT(vcpu, 3, "STAP: storing cpu address (%u) to 0x%llx", vcpu_id, ga); trace_kvm_s390_handle_stap(vcpu, ga); return 0; } @@ -167,6 +168,7 @@ static int __skey_check_enable(struct kvm_vcpu *vcpu) return rc; rc = s390_enable_skey(); + VCPU_EVENT(vcpu, 3, "%s", "enabling storage keys for guest"); trace_kvm_s390_skey_related_inst(vcpu); vcpu->arch.sie_block->ictl &= ~(ICTL_ISKE | ICTL_SSKE | ICTL_RRBE); return rc; @@ -370,7 +372,7 @@ static int handle_stfl(struct kvm_vcpu *vcpu) &fac, sizeof(fac)); if (rc) return rc; - VCPU_EVENT(vcpu, 5, "store facility list value %x", fac); + VCPU_EVENT(vcpu, 3, "STFL: store facility list 0x%x", fac); trace_kvm_s390_handle_stfl(vcpu, fac); return 0; } @@ -468,7 +470,7 @@ static int handle_stidp(struct kvm_vcpu *vcpu) if (rc) return kvm_s390_inject_prog_cond(vcpu, rc); - VCPU_EVENT(vcpu, 5, "%s", "store cpu id"); + VCPU_EVENT(vcpu, 3, "STIDP: store cpu id 0x%llx", stidp_data); return 0; } @@ -521,7 +523,7 @@ static int handle_stsi(struct kvm_vcpu *vcpu) ar_t ar; vcpu->stat.instruction_stsi++; - VCPU_EVENT(vcpu, 4, "stsi: fc: %x sel1: %x sel2: %x", fc, sel1, sel2); + VCPU_EVENT(vcpu, 3, "STSI: fc: %u sel1: %u sel2: %u", fc, sel1, sel2); if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); @@ -758,10 +760,10 @@ static int handle_essa(struct kvm_vcpu *vcpu) struct gmap *gmap; int i; - VCPU_EVENT(vcpu, 5, "cmma release %d pages", entries); + VCPU_EVENT(vcpu, 4, "ESSA: release %d pages", entries); gmap = vcpu->arch.gmap; vcpu->stat.instruction_essa++; - if (!kvm_s390_cmma_enabled(vcpu->kvm)) + if (!vcpu->kvm->arch.use_cmma) return kvm_s390_inject_program_int(vcpu, PGM_OPERATION); if (vcpu->arch.sie_block->gpsw.mask & PSW_MASK_PSTATE) @@ -829,7 +831,7 @@ int kvm_s390_handle_lctl(struct kvm_vcpu *vcpu) if (ga & 3) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - VCPU_EVENT(vcpu, 5, "lctl r1:%x, r3:%x, addr:%llx", reg1, reg3, ga); + VCPU_EVENT(vcpu, 4, "LCTL: r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga); trace_kvm_s390_handle_lctl(vcpu, 0, reg1, reg3, ga); nr_regs = ((reg3 - reg1) & 0xf) + 1; @@ -868,7 +870,7 @@ int kvm_s390_handle_stctl(struct kvm_vcpu *vcpu) if (ga & 3) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - VCPU_EVENT(vcpu, 5, "stctl r1:%x, r3:%x, addr:%llx", reg1, reg3, ga); + VCPU_EVENT(vcpu, 4, "STCTL r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga); trace_kvm_s390_handle_stctl(vcpu, 0, reg1, reg3, ga); reg = reg1; @@ -902,7 +904,7 @@ static int handle_lctlg(struct kvm_vcpu *vcpu) if (ga & 7) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - VCPU_EVENT(vcpu, 5, "lctlg r1:%x, r3:%x, addr:%llx", reg1, reg3, ga); + VCPU_EVENT(vcpu, 4, "LCTLG: r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga); trace_kvm_s390_handle_lctl(vcpu, 1, reg1, reg3, ga); nr_regs = ((reg3 - reg1) & 0xf) + 1; @@ -940,7 +942,7 @@ static int handle_stctg(struct kvm_vcpu *vcpu) if (ga & 7) return kvm_s390_inject_program_int(vcpu, PGM_SPECIFICATION); - VCPU_EVENT(vcpu, 5, "stctg r1:%x, r3:%x, addr:%llx", reg1, reg3, ga); + VCPU_EVENT(vcpu, 4, "STCTG r1:%d, r3:%d, addr: 0x%llx", reg1, reg3, ga); trace_kvm_s390_handle_stctl(vcpu, 1, reg1, reg3, ga); reg = reg1; diff --git a/arch/s390/kvm/sigp.c b/arch/s390/kvm/sigp.c index 72e58bd2bee7..da690b69f9fe 100644 --- a/arch/s390/kvm/sigp.c +++ b/arch/s390/kvm/sigp.c @@ -205,9 +205,6 @@ static int __sigp_set_prefix(struct kvm_vcpu *vcpu, struct kvm_vcpu *dst_vcpu, *reg &= 0xffffffff00000000UL; *reg |= SIGP_STATUS_INCORRECT_STATE; return SIGP_CC_STATUS_STORED; - } else if (rc == 0) { - VCPU_EVENT(vcpu, 4, "set prefix of cpu %02x to %x", - dst_vcpu->vcpu_id, irq.u.prefix.address); } return rc; @@ -371,7 +368,8 @@ static int handle_sigp_dst(struct kvm_vcpu *vcpu, u8 order_code, return rc; } -static int handle_sigp_order_in_user_space(struct kvm_vcpu *vcpu, u8 order_code) +static int handle_sigp_order_in_user_space(struct kvm_vcpu *vcpu, u8 order_code, + u16 cpu_addr) { if (!vcpu->kvm->arch.user_sigp) return 0; @@ -414,9 +412,8 @@ static int handle_sigp_order_in_user_space(struct kvm_vcpu *vcpu, u8 order_code) default: vcpu->stat.instruction_sigp_unknown++; } - - VCPU_EVENT(vcpu, 4, "sigp order %u: completely handled in user space", - order_code); + VCPU_EVENT(vcpu, 3, "SIGP: order %u for CPU %d handled in userspace", + order_code, cpu_addr); return 1; } @@ -435,7 +432,7 @@ int kvm_s390_handle_sigp(struct kvm_vcpu *vcpu) return kvm_s390_inject_program_int(vcpu, PGM_PRIVILEGED_OP); order_code = kvm_s390_get_base_disp_rs(vcpu, NULL); - if (handle_sigp_order_in_user_space(vcpu, order_code)) + if (handle_sigp_order_in_user_space(vcpu, order_code, cpu_addr)) return -EOPNOTSUPP; if (r1 % 2) diff --git a/arch/s390/kvm/trace-s390.h b/arch/s390/kvm/trace-s390.h index 3208d33a48cb..cc1d6c68356f 100644 --- a/arch/s390/kvm/trace-s390.h +++ b/arch/s390/kvm/trace-s390.h @@ -105,11 +105,22 @@ TRACE_EVENT(kvm_s390_vcpu_start_stop, {KVM_S390_PROGRAM_INT, "program interrupt"}, \ {KVM_S390_SIGP_SET_PREFIX, "sigp set prefix"}, \ {KVM_S390_RESTART, "sigp restart"}, \ + {KVM_S390_INT_PFAULT_INIT, "pfault init"}, \ + {KVM_S390_INT_PFAULT_DONE, "pfault done"}, \ + {KVM_S390_MCHK, "machine check"}, \ + {KVM_S390_INT_CLOCK_COMP, "clock comparator"}, \ + {KVM_S390_INT_CPU_TIMER, "cpu timer"}, \ {KVM_S390_INT_VIRTIO, "virtio interrupt"}, \ {KVM_S390_INT_SERVICE, "sclp interrupt"}, \ {KVM_S390_INT_EMERGENCY, "sigp emergency"}, \ {KVM_S390_INT_EXTERNAL_CALL, "sigp ext call"} +#define get_irq_name(__type) \ + (__type > KVM_S390_INT_IO_MAX ? \ + __print_symbolic(__type, kvm_s390_int_type) : \ + (__type & KVM_S390_INT_IO_AI_MASK ? \ + "adapter I/O interrupt" : "subchannel I/O interrupt")) + TRACE_EVENT(kvm_s390_inject_vm, TP_PROTO(__u64 type, __u32 parm, __u64 parm64, int who), TP_ARGS(type, parm, parm64, who), @@ -131,22 +142,19 @@ TRACE_EVENT(kvm_s390_inject_vm, TP_printk("inject%s: type:%x (%s) parm:%x parm64:%llx", (__entry->who == 1) ? " (from kernel)" : (__entry->who == 2) ? " (from user)" : "", - __entry->inttype, - __print_symbolic(__entry->inttype, kvm_s390_int_type), + __entry->inttype, get_irq_name(__entry->inttype), __entry->parm, __entry->parm64) ); TRACE_EVENT(kvm_s390_inject_vcpu, - TP_PROTO(unsigned int id, __u64 type, __u32 parm, __u64 parm64, \ - int who), - TP_ARGS(id, type, parm, parm64, who), + TP_PROTO(unsigned int id, __u64 type, __u32 parm, __u64 parm64), + TP_ARGS(id, type, parm, parm64), TP_STRUCT__entry( __field(int, id) __field(__u32, inttype) __field(__u32, parm) __field(__u64, parm64) - __field(int, who) ), TP_fast_assign( @@ -154,15 +162,12 @@ TRACE_EVENT(kvm_s390_inject_vcpu, __entry->inttype = type & 0x00000000ffffffff; __entry->parm = parm; __entry->parm64 = parm64; - __entry->who = who; ), - TP_printk("inject%s (vcpu %d): type:%x (%s) parm:%x parm64:%llx", - (__entry->who == 1) ? " (from kernel)" : - (__entry->who == 2) ? " (from user)" : "", + TP_printk("inject (vcpu %d): type:%x (%s) parm:%x parm64:%llx", __entry->id, __entry->inttype, - __print_symbolic(__entry->inttype, kvm_s390_int_type), - __entry->parm, __entry->parm64) + get_irq_name(__entry->inttype), __entry->parm, + __entry->parm64) ); /* @@ -189,8 +194,8 @@ TRACE_EVENT(kvm_s390_deliver_interrupt, TP_printk("deliver interrupt (vcpu %d): type:%x (%s) " \ "data:%08llx %016llx", __entry->id, __entry->inttype, - __print_symbolic(__entry->inttype, kvm_s390_int_type), - __entry->data0, __entry->data1) + get_irq_name(__entry->inttype), __entry->data0, + __entry->data1) ); /* diff --git a/arch/sh/kernel/cpu/sh4/sq.c b/arch/sh/kernel/cpu/sh4/sq.c index 0a47bd3e7bee..4ca78ed71ad2 100644 --- a/arch/sh/kernel/cpu/sh4/sq.c +++ b/arch/sh/kernel/cpu/sh4/sq.c @@ -355,13 +355,12 @@ static int sq_dev_add(struct device *dev, struct subsys_interface *sif) return error; } -static int sq_dev_remove(struct device *dev, struct subsys_interface *sif) +static void sq_dev_remove(struct device *dev, struct subsys_interface *sif) { unsigned int cpu = dev->id; struct kobject *kobj = sq_kobject[cpu]; kobject_put(kobj); - return 0; } static struct subsys_interface sq_interface = { diff --git a/arch/tile/kernel/sysfs.c b/arch/tile/kernel/sysfs.c index a3ed12f8f83b..825867c53853 100644 --- a/arch/tile/kernel/sysfs.c +++ b/arch/tile/kernel/sysfs.c @@ -198,16 +198,13 @@ static int hv_stats_device_add(struct device *dev, struct subsys_interface *sif) return err; } -static int hv_stats_device_remove(struct device *dev, - struct subsys_interface *sif) +static void hv_stats_device_remove(struct device *dev, + struct subsys_interface *sif) { int cpu = dev->id; - if (!cpu_online(cpu)) - return 0; - - sysfs_remove_file(&dev->kobj, &dev_attr_hv_stats.attr); - return 0; + if (cpu_online(cpu)) + sysfs_remove_file(&dev->kobj, &dev_attr_hv_stats.attr); } diff --git a/arch/x86/entry/entry_64_compat.S b/arch/x86/entry/entry_64_compat.S index 5a1844765a7a..a7e257d9cb90 100644 --- a/arch/x86/entry/entry_64_compat.S +++ b/arch/x86/entry/entry_64_compat.S @@ -140,6 +140,7 @@ sysexit_from_sys_call: */ andl $~TS_COMPAT, ASM_THREAD_INFO(TI_status, %rsp, SIZEOF_PTREGS) movl RIP(%rsp), %ecx /* User %eip */ + movq RAX(%rsp), %rax RESTORE_RSI_RDI xorl %edx, %edx /* Do not leak kernel information */ xorq %r8, %r8 @@ -219,7 +220,6 @@ sysexit_from_sys_call: 1: setbe %al /* 1 if error, 0 if not */ movzbl %al, %edi /* zero-extend that into %edi */ call __audit_syscall_exit - movq RAX(%rsp), %rax /* reload syscall return value */ movl $(_TIF_ALLWORK_MASK & ~_TIF_SYSCALL_AUDIT), %edi DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF @@ -368,6 +368,7 @@ sysretl_from_sys_call: RESTORE_RSI_RDI_RDX movl RIP(%rsp), %ecx movl EFLAGS(%rsp), %r11d + movq RAX(%rsp), %rax xorq %r10, %r10 xorq %r9, %r9 xorq %r8, %r8 diff --git a/arch/x86/include/asm/kvm_host.h b/arch/x86/include/asm/kvm_host.h index 49ec9038ec14..c12e845f59e6 100644 --- a/arch/x86/include/asm/kvm_host.h +++ b/arch/x86/include/asm/kvm_host.h @@ -252,6 +252,11 @@ struct kvm_pio_request { int size; }; +struct rsvd_bits_validate { + u64 rsvd_bits_mask[2][4]; + u64 bad_mt_xwr; +}; + /* * x86 supports 3 paging modes (4-level 64-bit, 3-level 64-bit, and 2-level * 32-bit). The kvm_mmu structure abstracts the details of the current mmu @@ -289,8 +294,15 @@ struct kvm_mmu { u64 *pae_root; u64 *lm_root; - u64 rsvd_bits_mask[2][4]; - u64 bad_mt_xwr; + + /* + * check zero bits on shadow page table entries, these + * bits include not only hardware reserved bits but also + * the bits spte never used. + */ + struct rsvd_bits_validate shadow_zero_check; + + struct rsvd_bits_validate guest_rsvd_check; /* * Bitmap: bit set = last pte in walk @@ -358,6 +370,11 @@ struct kvm_mtrr { struct list_head head; }; +/* Hyper-V per vcpu emulation context */ +struct kvm_vcpu_hv { + u64 hv_vapic; +}; + struct kvm_vcpu_arch { /* * rip and regs accesses must go through @@ -514,8 +531,7 @@ struct kvm_vcpu_arch { /* used for guest single stepping over the given code position */ unsigned long singlestep_rip; - /* fields used by HYPER-V emulation */ - u64 hv_vapic; + struct kvm_vcpu_hv hyperv; cpumask_var_t wbinvd_dirty_mask; @@ -586,6 +602,17 @@ struct kvm_apic_map { struct kvm_lapic *logical_map[16][16]; }; +/* Hyper-V emulation context */ +struct kvm_hv { + u64 hv_guest_os_id; + u64 hv_hypercall; + u64 hv_tsc_page; + + /* Hyper-v based guest crash (NT kernel bugcheck) parameters */ + u64 hv_crash_param[HV_X64_MSR_CRASH_PARAMS]; + u64 hv_crash_ctl; +}; + struct kvm_arch { unsigned int n_used_mmu_pages; unsigned int n_requested_mmu_pages; @@ -645,16 +672,14 @@ struct kvm_arch { /* reads protected by irq_srcu, writes by irq_lock */ struct hlist_head mask_notifier_list; - /* fields used by HYPER-V emulation */ - u64 hv_guest_os_id; - u64 hv_hypercall; - u64 hv_tsc_page; + struct kvm_hv hyperv; #ifdef CONFIG_KVM_MMU_AUDIT int audit_point; #endif bool boot_vcpu_runs_old_kvmclock; + u32 bsp_vcpu_id; u64 disabled_quirks; }; @@ -1203,5 +1228,7 @@ int __x86_set_memory_region(struct kvm *kvm, const struct kvm_userspace_memory_region *mem); int x86_set_memory_region(struct kvm *kvm, const struct kvm_userspace_memory_region *mem); +bool kvm_vcpu_is_reset_bsp(struct kvm_vcpu *vcpu); +bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu); #endif /* _ASM_X86_KVM_HOST_H */ diff --git a/arch/x86/include/asm/mshyperv.h b/arch/x86/include/asm/mshyperv.h index c163215abb9a..aaf59b7da98a 100644 --- a/arch/x86/include/asm/mshyperv.h +++ b/arch/x86/include/asm/mshyperv.h @@ -7,6 +7,7 @@ struct ms_hyperv_info { u32 features; + u32 misc_features; u32 hints; }; @@ -20,4 +21,8 @@ void hyperv_vector_handler(struct pt_regs *regs); void hv_setup_vmbus_irq(void (*handler)(void)); void hv_remove_vmbus_irq(void); +void hv_setup_kexec_handler(void (*handler)(void)); +void hv_remove_kexec_handler(void); +void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)); +void hv_remove_crash_handler(void); #endif diff --git a/arch/x86/include/asm/sigcontext.h b/arch/x86/include/asm/sigcontext.h index 6fe6b182c998..9dfce4e0417d 100644 --- a/arch/x86/include/asm/sigcontext.h +++ b/arch/x86/include/asm/sigcontext.h @@ -57,9 +57,9 @@ struct sigcontext { unsigned long ip; unsigned long flags; unsigned short cs; - unsigned short __pad2; /* Was called gs, but was always zero. */ - unsigned short __pad1; /* Was called fs, but was always zero. */ - unsigned short ss; + unsigned short gs; + unsigned short fs; + unsigned short __pad0; unsigned long err; unsigned long trapno; unsigned long oldmask; diff --git a/arch/x86/include/asm/switch_to.h b/arch/x86/include/asm/switch_to.h index 751bf4b7bf11..d7f3b3b78ac3 100644 --- a/arch/x86/include/asm/switch_to.h +++ b/arch/x86/include/asm/switch_to.h @@ -79,12 +79,12 @@ do { \ #else /* CONFIG_X86_32 */ /* frame pointer must be last for get_wchan */ -#define SAVE_CONTEXT "pushq %%rbp ; movq %%rsi,%%rbp\n\t" -#define RESTORE_CONTEXT "movq %%rbp,%%rsi ; popq %%rbp\t" +#define SAVE_CONTEXT "pushf ; pushq %%rbp ; movq %%rsi,%%rbp\n\t" +#define RESTORE_CONTEXT "movq %%rbp,%%rsi ; popq %%rbp ; popf\t" #define __EXTRA_CLOBBER \ , "rcx", "rbx", "rdx", "r8", "r9", "r10", "r11", \ - "r12", "r13", "r14", "r15", "flags" + "r12", "r13", "r14", "r15" #ifdef CONFIG_CC_STACKPROTECTOR #define __switch_canary \ @@ -100,11 +100,7 @@ do { \ #define __switch_canary_iparam #endif /* CC_STACKPROTECTOR */ -/* - * There is no need to save or restore flags, because flags are always - * clean in kernel mode, with the possible exception of IOPL. Kernel IOPL - * has no effect. - */ +/* Save restore flags to clear handle leaking NT */ #define switch_to(prev, next, last) \ asm volatile(SAVE_CONTEXT \ "movq %%rsp,%P[threadrsp](%[prev])\n\t" /* save RSP */ \ diff --git a/arch/x86/include/asm/vmx.h b/arch/x86/include/asm/vmx.h index da772edd19ab..448b7ca61aee 100644 --- a/arch/x86/include/asm/vmx.h +++ b/arch/x86/include/asm/vmx.h @@ -47,6 +47,7 @@ #define CPU_BASED_MOV_DR_EXITING 0x00800000 #define CPU_BASED_UNCOND_IO_EXITING 0x01000000 #define CPU_BASED_USE_IO_BITMAPS 0x02000000 +#define CPU_BASED_MONITOR_TRAP_FLAG 0x08000000 #define CPU_BASED_USE_MSR_BITMAPS 0x10000000 #define CPU_BASED_MONITOR_EXITING 0x20000000 #define CPU_BASED_PAUSE_EXITING 0x40000000 @@ -367,29 +368,29 @@ enum vmcs_field { #define TYPE_PHYSICAL_APIC_EVENT (10 << 12) #define TYPE_PHYSICAL_APIC_INST (15 << 12) -/* segment AR */ -#define SEGMENT_AR_L_MASK (1 << 13) - -#define AR_TYPE_ACCESSES_MASK 1 -#define AR_TYPE_READABLE_MASK (1 << 1) -#define AR_TYPE_WRITEABLE_MASK (1 << 2) -#define AR_TYPE_CODE_MASK (1 << 3) -#define AR_TYPE_MASK 0x0f -#define AR_TYPE_BUSY_64_TSS 11 -#define AR_TYPE_BUSY_32_TSS 11 -#define AR_TYPE_BUSY_16_TSS 3 -#define AR_TYPE_LDT 2 - -#define AR_UNUSABLE_MASK (1 << 16) -#define AR_S_MASK (1 << 4) -#define AR_P_MASK (1 << 7) -#define AR_L_MASK (1 << 13) -#define AR_DB_MASK (1 << 14) -#define AR_G_MASK (1 << 15) -#define AR_DPL_SHIFT 5 -#define AR_DPL(ar) (((ar) >> AR_DPL_SHIFT) & 3) - -#define AR_RESERVD_MASK 0xfffe0f00 +/* segment AR in VMCS -- these are different from what LAR reports */ +#define VMX_SEGMENT_AR_L_MASK (1 << 13) + +#define VMX_AR_TYPE_ACCESSES_MASK 1 +#define VMX_AR_TYPE_READABLE_MASK (1 << 1) +#define VMX_AR_TYPE_WRITEABLE_MASK (1 << 2) +#define VMX_AR_TYPE_CODE_MASK (1 << 3) +#define VMX_AR_TYPE_MASK 0x0f +#define VMX_AR_TYPE_BUSY_64_TSS 11 +#define VMX_AR_TYPE_BUSY_32_TSS 11 +#define VMX_AR_TYPE_BUSY_16_TSS 3 +#define VMX_AR_TYPE_LDT 2 + +#define VMX_AR_UNUSABLE_MASK (1 << 16) +#define VMX_AR_S_MASK (1 << 4) +#define VMX_AR_P_MASK (1 << 7) +#define VMX_AR_L_MASK (1 << 13) +#define VMX_AR_DB_MASK (1 << 14) +#define VMX_AR_G_MASK (1 << 15) +#define VMX_AR_DPL_SHIFT 5 +#define VMX_AR_DPL(ar) (((ar) >> VMX_AR_DPL_SHIFT) & 3) + +#define VMX_AR_RESERVD_MASK 0xfffe0f00 #define TSS_PRIVATE_MEMSLOT (KVM_USER_MEM_SLOTS + 0) #define APIC_ACCESS_PAGE_PRIVATE_MEMSLOT (KVM_USER_MEM_SLOTS + 1) diff --git a/arch/x86/include/uapi/asm/hyperv.h b/arch/x86/include/uapi/asm/hyperv.h index f36d56bd7632..f0412c50c47b 100644 --- a/arch/x86/include/uapi/asm/hyperv.h +++ b/arch/x86/include/uapi/asm/hyperv.h @@ -27,6 +27,8 @@ #define HV_X64_MSR_VP_RUNTIME_AVAILABLE (1 << 0) /* Partition Reference Counter (HV_X64_MSR_TIME_REF_COUNT) available*/ #define HV_X64_MSR_TIME_REF_COUNT_AVAILABLE (1 << 1) +/* Partition reference TSC MSR is available */ +#define HV_X64_MSR_REFERENCE_TSC_AVAILABLE (1 << 9) /* A partition's reference time stamp counter (TSC) page */ #define HV_X64_MSR_REFERENCE_TSC 0x40000021 diff --git a/arch/x86/include/uapi/asm/sigcontext.h b/arch/x86/include/uapi/asm/sigcontext.h index 0e8a973de9ee..40836a9a7250 100644 --- a/arch/x86/include/uapi/asm/sigcontext.h +++ b/arch/x86/include/uapi/asm/sigcontext.h @@ -177,24 +177,9 @@ struct sigcontext { __u64 rip; __u64 eflags; /* RFLAGS */ __u16 cs; - - /* - * Prior to 2.5.64 ("[PATCH] x86-64 updates for 2.5.64-bk3"), - * Linux saved and restored fs and gs in these slots. This - * was counterproductive, as fsbase and gsbase were never - * saved, so arch_prctl was presumably unreliable. - * - * If these slots are ever needed for any other purpose, there - * is some risk that very old 64-bit binaries could get - * confused. I doubt that many such binaries still work, - * though, since the same patch in 2.5.64 also removed the - * 64-bit set_thread_area syscall, so it appears that there is - * no TLS API that works in both pre- and post-2.5.64 kernels. - */ - __u16 __pad2; /* Was gs. */ - __u16 __pad1; /* Was fs. */ - - __u16 ss; + __u16 gs; + __u16 fs; + __u16 __pad0; __u64 err; __u64 trapno; __u64 oldmask; diff --git a/arch/x86/include/uapi/asm/vmx.h b/arch/x86/include/uapi/asm/vmx.h index 1fe92181ee9e..37fee272618f 100644 --- a/arch/x86/include/uapi/asm/vmx.h +++ b/arch/x86/include/uapi/asm/vmx.h @@ -58,6 +58,7 @@ #define EXIT_REASON_INVALID_STATE 33 #define EXIT_REASON_MSR_LOAD_FAIL 34 #define EXIT_REASON_MWAIT_INSTRUCTION 36 +#define EXIT_REASON_MONITOR_TRAP_FLAG 37 #define EXIT_REASON_MONITOR_INSTRUCTION 39 #define EXIT_REASON_PAUSE_INSTRUCTION 40 #define EXIT_REASON_MCE_DURING_VMENTRY 41 @@ -106,6 +107,7 @@ { EXIT_REASON_MSR_READ, "MSR_READ" }, \ { EXIT_REASON_MSR_WRITE, "MSR_WRITE" }, \ { EXIT_REASON_MWAIT_INSTRUCTION, "MWAIT_INSTRUCTION" }, \ + { EXIT_REASON_MONITOR_TRAP_FLAG, "MONITOR_TRAP_FLAG" }, \ { EXIT_REASON_MONITOR_INSTRUCTION, "MONITOR_INSTRUCTION" }, \ { EXIT_REASON_PAUSE_INSTRUCTION, "PAUSE_INSTRUCTION" }, \ { EXIT_REASON_MCE_DURING_VMENTRY, "MCE_DURING_VMENTRY" }, \ diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c index dcb52850a28f..cde732c1b495 100644 --- a/arch/x86/kernel/apic/apic.c +++ b/arch/x86/kernel/apic/apic.c @@ -1424,7 +1424,7 @@ static inline void __x2apic_disable(void) { u64 msr; - if (cpu_has_apic) + if (!cpu_has_apic) return; rdmsrl(MSR_IA32_APICBASE, msr); @@ -1483,10 +1483,13 @@ void x2apic_setup(void) static __init void x2apic_disable(void) { - u32 x2apic_id; + u32 x2apic_id, state = x2apic_state; - if (x2apic_state != X2APIC_ON) - goto out; + x2apic_mode = 0; + x2apic_state = X2APIC_DISABLED; + + if (state != X2APIC_ON) + return; x2apic_id = read_apic_id(); if (x2apic_id >= 255) @@ -1494,9 +1497,6 @@ static __init void x2apic_disable(void) __x2apic_disable(); register_lapic_address(mp_lapic_addr); -out: - x2apic_state = X2APIC_DISABLED; - x2apic_mode = 0; } static __init void x2apic_enable(void) diff --git a/arch/x86/kernel/apic/vector.c b/arch/x86/kernel/apic/vector.c index f813261d9740..2683f36e4e0a 100644 --- a/arch/x86/kernel/apic/vector.c +++ b/arch/x86/kernel/apic/vector.c @@ -322,7 +322,7 @@ static int x86_vector_alloc_irqs(struct irq_domain *domain, unsigned int virq, irq_data->chip = &lapic_controller; irq_data->chip_data = data; irq_data->hwirq = virq + i; - err = assign_irq_vector_policy(virq, irq_data->node, data, + err = assign_irq_vector_policy(virq + i, irq_data->node, data, info); if (err) goto error; diff --git a/arch/x86/kernel/cpu/microcode/core.c b/arch/x86/kernel/cpu/microcode/core.c index 6236a54a63f4..3c986390058a 100644 --- a/arch/x86/kernel/cpu/microcode/core.c +++ b/arch/x86/kernel/cpu/microcode/core.c @@ -377,17 +377,16 @@ static int mc_device_add(struct device *dev, struct subsys_interface *sif) return err; } -static int mc_device_remove(struct device *dev, struct subsys_interface *sif) +static void mc_device_remove(struct device *dev, struct subsys_interface *sif) { int cpu = dev->id; if (!cpu_online(cpu)) - return 0; + return; pr_debug("CPU%d removed\n", cpu); microcode_fini_cpu(cpu); sysfs_remove_group(&dev->kobj, &mc_attr_group); - return 0; } static struct subsys_interface mc_cpu_interface = { diff --git a/arch/x86/kernel/cpu/mshyperv.c b/arch/x86/kernel/cpu/mshyperv.c index aad4bd84b475..f794bfa3c138 100644 --- a/arch/x86/kernel/cpu/mshyperv.c +++ b/arch/x86/kernel/cpu/mshyperv.c @@ -18,6 +18,7 @@ #include <linux/efi.h> #include <linux/interrupt.h> #include <linux/irq.h> +#include <linux/kexec.h> #include <asm/processor.h> #include <asm/hypervisor.h> #include <asm/hyperv.h> @@ -28,10 +29,14 @@ #include <asm/i8259.h> #include <asm/apic.h> #include <asm/timer.h> +#include <asm/reboot.h> struct ms_hyperv_info ms_hyperv; EXPORT_SYMBOL_GPL(ms_hyperv); +static void (*hv_kexec_handler)(void); +static void (*hv_crash_handler)(struct pt_regs *regs); + #if IS_ENABLED(CONFIG_HYPERV) static void (*vmbus_handler)(void); @@ -67,8 +72,47 @@ void hv_remove_vmbus_irq(void) } EXPORT_SYMBOL_GPL(hv_setup_vmbus_irq); EXPORT_SYMBOL_GPL(hv_remove_vmbus_irq); + +void hv_setup_kexec_handler(void (*handler)(void)) +{ + hv_kexec_handler = handler; +} +EXPORT_SYMBOL_GPL(hv_setup_kexec_handler); + +void hv_remove_kexec_handler(void) +{ + hv_kexec_handler = NULL; +} +EXPORT_SYMBOL_GPL(hv_remove_kexec_handler); + +void hv_setup_crash_handler(void (*handler)(struct pt_regs *regs)) +{ + hv_crash_handler = handler; +} +EXPORT_SYMBOL_GPL(hv_setup_crash_handler); + +void hv_remove_crash_handler(void) +{ + hv_crash_handler = NULL; +} +EXPORT_SYMBOL_GPL(hv_remove_crash_handler); #endif +static void hv_machine_shutdown(void) +{ + if (kexec_in_progress && hv_kexec_handler) + hv_kexec_handler(); + native_machine_shutdown(); +} + +static void hv_machine_crash_shutdown(struct pt_regs *regs) +{ + if (hv_crash_handler) + hv_crash_handler(regs); + native_machine_crash_shutdown(regs); +} + + static uint32_t __init ms_hyperv_platform(void) { u32 eax; @@ -114,6 +158,7 @@ static void __init ms_hyperv_init_platform(void) * Extract the features and hints */ ms_hyperv.features = cpuid_eax(HYPERV_CPUID_FEATURES); + ms_hyperv.misc_features = cpuid_edx(HYPERV_CPUID_FEATURES); ms_hyperv.hints = cpuid_eax(HYPERV_CPUID_ENLIGHTMENT_INFO); printk(KERN_INFO "HyperV: features 0x%x, hints 0x%x\n", @@ -141,6 +186,8 @@ static void __init ms_hyperv_init_platform(void) no_timer_check = 1; #endif + machine_ops.shutdown = hv_machine_shutdown; + machine_ops.crash_shutdown = hv_machine_crash_shutdown; } const __refconst struct hypervisor_x86 x86_hyper_ms_hyperv = { diff --git a/arch/x86/kernel/cpu/perf_event_intel.c b/arch/x86/kernel/cpu/perf_event_intel.c index b9826a981fb2..6326ae24e4d5 100644 --- a/arch/x86/kernel/cpu/perf_event_intel.c +++ b/arch/x86/kernel/cpu/perf_event_intel.c @@ -2534,7 +2534,7 @@ static int intel_pmu_cpu_prepare(int cpu) if (x86_pmu.extra_regs || x86_pmu.lbr_sel_map) { cpuc->shared_regs = allocate_shared_regs(cpu); if (!cpuc->shared_regs) - return NOTIFY_BAD; + goto err; } if (x86_pmu.flags & PMU_FL_EXCL_CNTRS) { @@ -2542,18 +2542,27 @@ static int intel_pmu_cpu_prepare(int cpu) cpuc->constraint_list = kzalloc(sz, GFP_KERNEL); if (!cpuc->constraint_list) - return NOTIFY_BAD; + goto err_shared_regs; cpuc->excl_cntrs = allocate_excl_cntrs(cpu); - if (!cpuc->excl_cntrs) { - kfree(cpuc->constraint_list); - kfree(cpuc->shared_regs); - return NOTIFY_BAD; - } + if (!cpuc->excl_cntrs) + goto err_constraint_list; + cpuc->excl_thread_id = 0; } return NOTIFY_OK; + +err_constraint_list: + kfree(cpuc->constraint_list); + cpuc->constraint_list = NULL; + +err_shared_regs: + kfree(cpuc->shared_regs); + cpuc->shared_regs = NULL; + +err: + return NOTIFY_BAD; } static void intel_pmu_cpu_starting(int cpu) diff --git a/arch/x86/kernel/cpu/perf_event_intel_cqm.c b/arch/x86/kernel/cpu/perf_event_intel_cqm.c index 63eb68b73589..377e8f8ed391 100644 --- a/arch/x86/kernel/cpu/perf_event_intel_cqm.c +++ b/arch/x86/kernel/cpu/perf_event_intel_cqm.c @@ -1255,7 +1255,7 @@ static inline void cqm_pick_event_reader(int cpu) cpumask_set_cpu(cpu, &cqm_cpumask); } -static void intel_cqm_cpu_prepare(unsigned int cpu) +static void intel_cqm_cpu_starting(unsigned int cpu) { struct intel_pqr_state *state = &per_cpu(pqr_state, cpu); struct cpuinfo_x86 *c = &cpu_data(cpu); @@ -1296,13 +1296,11 @@ static int intel_cqm_cpu_notifier(struct notifier_block *nb, unsigned int cpu = (unsigned long)hcpu; switch (action & ~CPU_TASKS_FROZEN) { - case CPU_UP_PREPARE: - intel_cqm_cpu_prepare(cpu); - break; case CPU_DOWN_PREPARE: intel_cqm_cpu_exit(cpu); break; case CPU_STARTING: + intel_cqm_cpu_starting(cpu); cqm_pick_event_reader(cpu); break; } @@ -1373,7 +1371,7 @@ static int __init intel_cqm_init(void) goto out; for_each_online_cpu(i) { - intel_cqm_cpu_prepare(i); + intel_cqm_cpu_starting(i); cqm_pick_event_reader(i); } diff --git a/arch/x86/kernel/fpu/core.c b/arch/x86/kernel/fpu/core.c index 79de954626fd..d25097c3fc1d 100644 --- a/arch/x86/kernel/fpu/core.c +++ b/arch/x86/kernel/fpu/core.c @@ -270,7 +270,7 @@ int fpu__copy(struct fpu *dst_fpu, struct fpu *src_fpu) dst_fpu->fpregs_active = 0; dst_fpu->last_cpu = -1; - if (src_fpu->fpstate_active) + if (src_fpu->fpstate_active && cpu_has_fpu) fpu_copy(dst_fpu, src_fpu); return 0; diff --git a/arch/x86/kernel/fpu/init.c b/arch/x86/kernel/fpu/init.c index 1e173f6285c7..d14e9ac3235a 100644 --- a/arch/x86/kernel/fpu/init.c +++ b/arch/x86/kernel/fpu/init.c @@ -40,7 +40,12 @@ static void fpu__init_cpu_generic(void) write_cr0(cr0); /* Flush out any pending x87 state: */ - asm volatile ("fninit"); +#ifdef CONFIG_MATH_EMULATION + if (!cpu_has_fpu) + fpstate_init_soft(¤t->thread.fpu.state.soft); + else +#endif + asm volatile ("fninit"); } /* diff --git a/arch/x86/kernel/process.c b/arch/x86/kernel/process.c index 397688beed4b..c27cad726765 100644 --- a/arch/x86/kernel/process.c +++ b/arch/x86/kernel/process.c @@ -408,6 +408,7 @@ static int prefer_mwait_c1_over_halt(const struct cpuinfo_x86 *c) static void mwait_idle(void) { if (!current_set_polling_and_test()) { + trace_cpu_idle_rcuidle(1, smp_processor_id()); if (this_cpu_has(X86_BUG_CLFLUSH_MONITOR)) { smp_mb(); /* quirk */ clflush((void *)¤t_thread_info()->flags); @@ -419,6 +420,7 @@ static void mwait_idle(void) __sti_mwait(0, 0); else local_irq_enable(); + trace_cpu_idle_rcuidle(PWR_EVENT_EXIT, smp_processor_id()); } else { local_irq_enable(); } diff --git a/arch/x86/kernel/signal.c b/arch/x86/kernel/signal.c index 206996c1669d..71820c42b6ce 100644 --- a/arch/x86/kernel/signal.c +++ b/arch/x86/kernel/signal.c @@ -93,8 +93,15 @@ int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc) COPY(r15); #endif /* CONFIG_X86_64 */ +#ifdef CONFIG_X86_32 COPY_SEG_CPL3(cs); COPY_SEG_CPL3(ss); +#else /* !CONFIG_X86_32 */ + /* Kernel saves and restores only the CS segment register on signals, + * which is the bare minimum needed to allow mixed 32/64-bit code. + * App's signal handler can save/restore other segments if needed. */ + COPY_SEG_CPL3(cs); +#endif /* CONFIG_X86_32 */ get_user_ex(tmpflags, &sc->flags); regs->flags = (regs->flags & ~FIX_EFLAGS) | (tmpflags & FIX_EFLAGS); @@ -154,9 +161,8 @@ int setup_sigcontext(struct sigcontext __user *sc, void __user *fpstate, #else /* !CONFIG_X86_32 */ put_user_ex(regs->flags, &sc->flags); put_user_ex(regs->cs, &sc->cs); - put_user_ex(0, &sc->__pad2); - put_user_ex(0, &sc->__pad1); - put_user_ex(regs->ss, &sc->ss); + put_user_ex(0, &sc->gs); + put_user_ex(0, &sc->fs); #endif /* CONFIG_X86_32 */ put_user_ex(fpstate, &sc->fpstate); @@ -451,19 +457,9 @@ static int __setup_rt_frame(int sig, struct ksignal *ksig, regs->sp = (unsigned long)frame; - /* - * Set up the CS and SS registers to run signal handlers in - * 64-bit mode, even if the handler happens to be interrupting - * 32-bit or 16-bit code. - * - * SS is subtle. In 64-bit mode, we don't need any particular - * SS descriptor, but we do need SS to be valid. It's possible - * that the old SS is entirely bogus -- this can happen if the - * signal we're trying to deliver is #GP or #SS caused by a bad - * SS value. - */ + /* Set up the CS register to run signal handlers in 64-bit mode, + even if the handler happens to be interrupting 32-bit code. */ regs->cs = __USER_CS; - regs->ss = __USER_DS; return 0; } diff --git a/arch/x86/kernel/step.c b/arch/x86/kernel/step.c index 6273324186ac..0ccb53a9fcd9 100644 --- a/arch/x86/kernel/step.c +++ b/arch/x86/kernel/step.c @@ -28,11 +28,11 @@ unsigned long convert_ip_to_linear(struct task_struct *child, struct pt_regs *re struct desc_struct *desc; unsigned long base; - seg &= ~7UL; + seg >>= 3; mutex_lock(&child->mm->context.lock); if (unlikely(!child->mm->context.ldt || - (seg >> 3) >= child->mm->context.ldt->size)) + seg >= child->mm->context.ldt->size)) addr = -1L; /* bogus selector, access would fault */ else { desc = &child->mm->context.ldt->entries[seg]; diff --git a/arch/x86/kvm/Makefile b/arch/x86/kvm/Makefile index 67d215cb8953..a1ff508bb423 100644 --- a/arch/x86/kvm/Makefile +++ b/arch/x86/kvm/Makefile @@ -12,7 +12,9 @@ kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o \ kvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o kvm-y += x86.o mmu.o emulate.o i8259.o irq.o lapic.o \ - i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o + i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \ + hyperv.o + kvm-$(CONFIG_KVM_DEVICE_ASSIGNMENT) += assigned-dev.o iommu.o kvm-intel-y += vmx.o pmu_intel.o kvm-amd-y += svm.o pmu_amd.o diff --git a/arch/x86/kvm/hyperv.c b/arch/x86/kvm/hyperv.c new file mode 100644 index 000000000000..a8160d2ae362 --- /dev/null +++ b/arch/x86/kvm/hyperv.c @@ -0,0 +1,377 @@ +/* + * KVM Microsoft Hyper-V emulation + * + * derived from arch/x86/kvm/x86.c + * + * Copyright (C) 2006 Qumranet, Inc. + * Copyright (C) 2008 Qumranet, Inc. + * Copyright IBM Corporation, 2008 + * Copyright 2010 Red Hat, Inc. and/or its affiliates. + * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com> + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * Amit Shah <amit.shah@qumranet.com> + * Ben-Ami Yassour <benami@il.ibm.com> + * Andrey Smetanin <asmetanin@virtuozzo.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "x86.h" +#include "lapic.h" +#include "hyperv.h" + +#include <linux/kvm_host.h> +#include <trace/events/kvm.h> + +#include "trace.h" + +static bool kvm_hv_msr_partition_wide(u32 msr) +{ + bool r = false; + + switch (msr) { + case HV_X64_MSR_GUEST_OS_ID: + case HV_X64_MSR_HYPERCALL: + case HV_X64_MSR_REFERENCE_TSC: + case HV_X64_MSR_TIME_REF_COUNT: + case HV_X64_MSR_CRASH_CTL: + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + r = true; + break; + } + + return r; +} + +static int kvm_hv_msr_get_crash_data(struct kvm_vcpu *vcpu, + u32 index, u64 *pdata) +{ + struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; + + if (WARN_ON_ONCE(index >= ARRAY_SIZE(hv->hv_crash_param))) + return -EINVAL; + + *pdata = hv->hv_crash_param[index]; + return 0; +} + +static int kvm_hv_msr_get_crash_ctl(struct kvm_vcpu *vcpu, u64 *pdata) +{ + struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; + + *pdata = hv->hv_crash_ctl; + return 0; +} + +static int kvm_hv_msr_set_crash_ctl(struct kvm_vcpu *vcpu, u64 data, bool host) +{ + struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; + + if (host) + hv->hv_crash_ctl = data & HV_X64_MSR_CRASH_CTL_NOTIFY; + + if (!host && (data & HV_X64_MSR_CRASH_CTL_NOTIFY)) { + + vcpu_debug(vcpu, "hv crash (0x%llx 0x%llx 0x%llx 0x%llx 0x%llx)\n", + hv->hv_crash_param[0], + hv->hv_crash_param[1], + hv->hv_crash_param[2], + hv->hv_crash_param[3], + hv->hv_crash_param[4]); + + /* Send notification about crash to user space */ + kvm_make_request(KVM_REQ_HV_CRASH, vcpu); + } + + return 0; +} + +static int kvm_hv_msr_set_crash_data(struct kvm_vcpu *vcpu, + u32 index, u64 data) +{ + struct kvm_hv *hv = &vcpu->kvm->arch.hyperv; + + if (WARN_ON_ONCE(index >= ARRAY_SIZE(hv->hv_crash_param))) + return -EINVAL; + + hv->hv_crash_param[index] = data; + return 0; +} + +static int kvm_hv_set_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data, + bool host) +{ + struct kvm *kvm = vcpu->kvm; + struct kvm_hv *hv = &kvm->arch.hyperv; + + switch (msr) { + case HV_X64_MSR_GUEST_OS_ID: + hv->hv_guest_os_id = data; + /* setting guest os id to zero disables hypercall page */ + if (!hv->hv_guest_os_id) + hv->hv_hypercall &= ~HV_X64_MSR_HYPERCALL_ENABLE; + break; + case HV_X64_MSR_HYPERCALL: { + u64 gfn; + unsigned long addr; + u8 instructions[4]; + + /* if guest os id is not set hypercall should remain disabled */ + if (!hv->hv_guest_os_id) + break; + if (!(data & HV_X64_MSR_HYPERCALL_ENABLE)) { + hv->hv_hypercall = data; + break; + } + gfn = data >> HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT; + addr = gfn_to_hva(kvm, gfn); + if (kvm_is_error_hva(addr)) + return 1; + kvm_x86_ops->patch_hypercall(vcpu, instructions); + ((unsigned char *)instructions)[3] = 0xc3; /* ret */ + if (__copy_to_user((void __user *)addr, instructions, 4)) + return 1; + hv->hv_hypercall = data; + mark_page_dirty(kvm, gfn); + break; + } + case HV_X64_MSR_REFERENCE_TSC: { + u64 gfn; + HV_REFERENCE_TSC_PAGE tsc_ref; + + memset(&tsc_ref, 0, sizeof(tsc_ref)); + hv->hv_tsc_page = data; + if (!(data & HV_X64_MSR_TSC_REFERENCE_ENABLE)) + break; + gfn = data >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT; + if (kvm_write_guest( + kvm, + gfn << HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT, + &tsc_ref, sizeof(tsc_ref))) + return 1; + mark_page_dirty(kvm, gfn); + break; + } + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + return kvm_hv_msr_set_crash_data(vcpu, + msr - HV_X64_MSR_CRASH_P0, + data); + case HV_X64_MSR_CRASH_CTL: + return kvm_hv_msr_set_crash_ctl(vcpu, data, host); + default: + vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n", + msr, data); + return 1; + } + return 0; +} + +static int kvm_hv_set_msr(struct kvm_vcpu *vcpu, u32 msr, u64 data) +{ + struct kvm_vcpu_hv *hv = &vcpu->arch.hyperv; + + switch (msr) { + case HV_X64_MSR_APIC_ASSIST_PAGE: { + u64 gfn; + unsigned long addr; + + if (!(data & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE)) { + hv->hv_vapic = data; + if (kvm_lapic_enable_pv_eoi(vcpu, 0)) + return 1; + break; + } + gfn = data >> HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT; + addr = kvm_vcpu_gfn_to_hva(vcpu, gfn); + if (kvm_is_error_hva(addr)) + return 1; + if (__clear_user((void __user *)addr, PAGE_SIZE)) + return 1; + hv->hv_vapic = data; + kvm_vcpu_mark_page_dirty(vcpu, gfn); + if (kvm_lapic_enable_pv_eoi(vcpu, + gfn_to_gpa(gfn) | KVM_MSR_ENABLED)) + return 1; + break; + } + case HV_X64_MSR_EOI: + return kvm_hv_vapic_msr_write(vcpu, APIC_EOI, data); + case HV_X64_MSR_ICR: + return kvm_hv_vapic_msr_write(vcpu, APIC_ICR, data); + case HV_X64_MSR_TPR: + return kvm_hv_vapic_msr_write(vcpu, APIC_TASKPRI, data); + default: + vcpu_unimpl(vcpu, "Hyper-V uhandled wrmsr: 0x%x data 0x%llx\n", + msr, data); + return 1; + } + + return 0; +} + +static int kvm_hv_get_msr_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) +{ + u64 data = 0; + struct kvm *kvm = vcpu->kvm; + struct kvm_hv *hv = &kvm->arch.hyperv; + + switch (msr) { + case HV_X64_MSR_GUEST_OS_ID: + data = hv->hv_guest_os_id; + break; + case HV_X64_MSR_HYPERCALL: + data = hv->hv_hypercall; + break; + case HV_X64_MSR_TIME_REF_COUNT: { + data = + div_u64(get_kernel_ns() + kvm->arch.kvmclock_offset, 100); + break; + } + case HV_X64_MSR_REFERENCE_TSC: + data = hv->hv_tsc_page; + break; + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + return kvm_hv_msr_get_crash_data(vcpu, + msr - HV_X64_MSR_CRASH_P0, + pdata); + case HV_X64_MSR_CRASH_CTL: + return kvm_hv_msr_get_crash_ctl(vcpu, pdata); + default: + vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); + return 1; + } + + *pdata = data; + return 0; +} + +static int kvm_hv_get_msr(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) +{ + u64 data = 0; + struct kvm_vcpu_hv *hv = &vcpu->arch.hyperv; + + switch (msr) { + case HV_X64_MSR_VP_INDEX: { + int r; + struct kvm_vcpu *v; + + kvm_for_each_vcpu(r, v, vcpu->kvm) { + if (v == vcpu) { + data = r; + break; + } + } + break; + } + case HV_X64_MSR_EOI: + return kvm_hv_vapic_msr_read(vcpu, APIC_EOI, pdata); + case HV_X64_MSR_ICR: + return kvm_hv_vapic_msr_read(vcpu, APIC_ICR, pdata); + case HV_X64_MSR_TPR: + return kvm_hv_vapic_msr_read(vcpu, APIC_TASKPRI, pdata); + case HV_X64_MSR_APIC_ASSIST_PAGE: + data = hv->hv_vapic; + break; + default: + vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); + return 1; + } + *pdata = data; + return 0; +} + +int kvm_hv_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host) +{ + if (kvm_hv_msr_partition_wide(msr)) { + int r; + + mutex_lock(&vcpu->kvm->lock); + r = kvm_hv_set_msr_pw(vcpu, msr, data, host); + mutex_unlock(&vcpu->kvm->lock); + return r; + } else + return kvm_hv_set_msr(vcpu, msr, data); +} + +int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) +{ + if (kvm_hv_msr_partition_wide(msr)) { + int r; + + mutex_lock(&vcpu->kvm->lock); + r = kvm_hv_get_msr_pw(vcpu, msr, pdata); + mutex_unlock(&vcpu->kvm->lock); + return r; + } else + return kvm_hv_get_msr(vcpu, msr, pdata); +} + +bool kvm_hv_hypercall_enabled(struct kvm *kvm) +{ + return kvm->arch.hyperv.hv_hypercall & HV_X64_MSR_HYPERCALL_ENABLE; +} + +int kvm_hv_hypercall(struct kvm_vcpu *vcpu) +{ + u64 param, ingpa, outgpa, ret; + uint16_t code, rep_idx, rep_cnt, res = HV_STATUS_SUCCESS, rep_done = 0; + bool fast, longmode; + + /* + * hypercall generates UD from non zero cpl and real mode + * per HYPER-V spec + */ + if (kvm_x86_ops->get_cpl(vcpu) != 0 || !is_protmode(vcpu)) { + kvm_queue_exception(vcpu, UD_VECTOR); + return 0; + } + + longmode = is_64_bit_mode(vcpu); + + if (!longmode) { + param = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDX) << 32) | + (kvm_register_read(vcpu, VCPU_REGS_RAX) & 0xffffffff); + ingpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RBX) << 32) | + (kvm_register_read(vcpu, VCPU_REGS_RCX) & 0xffffffff); + outgpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDI) << 32) | + (kvm_register_read(vcpu, VCPU_REGS_RSI) & 0xffffffff); + } +#ifdef CONFIG_X86_64 + else { + param = kvm_register_read(vcpu, VCPU_REGS_RCX); + ingpa = kvm_register_read(vcpu, VCPU_REGS_RDX); + outgpa = kvm_register_read(vcpu, VCPU_REGS_R8); + } +#endif + + code = param & 0xffff; + fast = (param >> 16) & 0x1; + rep_cnt = (param >> 32) & 0xfff; + rep_idx = (param >> 48) & 0xfff; + + trace_kvm_hv_hypercall(code, fast, rep_cnt, rep_idx, ingpa, outgpa); + + switch (code) { + case HV_X64_HV_NOTIFY_LONG_SPIN_WAIT: + kvm_vcpu_on_spin(vcpu); + break; + default: + res = HV_STATUS_INVALID_HYPERCALL_CODE; + break; + } + + ret = res | (((u64)rep_done & 0xfff) << 32); + if (longmode) { + kvm_register_write(vcpu, VCPU_REGS_RAX, ret); + } else { + kvm_register_write(vcpu, VCPU_REGS_RDX, ret >> 32); + kvm_register_write(vcpu, VCPU_REGS_RAX, ret & 0xffffffff); + } + + return 1; +} diff --git a/arch/x86/kvm/hyperv.h b/arch/x86/kvm/hyperv.h new file mode 100644 index 000000000000..c7bce559f67b --- /dev/null +++ b/arch/x86/kvm/hyperv.h @@ -0,0 +1,32 @@ +/* + * KVM Microsoft Hyper-V emulation + * + * derived from arch/x86/kvm/x86.c + * + * Copyright (C) 2006 Qumranet, Inc. + * Copyright (C) 2008 Qumranet, Inc. + * Copyright IBM Corporation, 2008 + * Copyright 2010 Red Hat, Inc. and/or its affiliates. + * Copyright (C) 2015 Andrey Smetanin <asmetanin@virtuozzo.com> + * + * Authors: + * Avi Kivity <avi@qumranet.com> + * Yaniv Kamay <yaniv@qumranet.com> + * Amit Shah <amit.shah@qumranet.com> + * Ben-Ami Yassour <benami@il.ibm.com> + * Andrey Smetanin <asmetanin@virtuozzo.com> + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef __ARCH_X86_KVM_HYPERV_H__ +#define __ARCH_X86_KVM_HYPERV_H__ + +int kvm_hv_set_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 data, bool host); +int kvm_hv_get_msr_common(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata); +bool kvm_hv_hypercall_enabled(struct kvm *kvm); +int kvm_hv_hypercall(struct kvm_vcpu *vcpu); + +#endif diff --git a/arch/x86/kvm/i8259.c b/arch/x86/kvm/i8259.c index fef922ff2635..7cc2360f1848 100644 --- a/arch/x86/kvm/i8259.c +++ b/arch/x86/kvm/i8259.c @@ -651,15 +651,10 @@ fail_unlock: return NULL; } -void kvm_destroy_pic(struct kvm *kvm) +void kvm_destroy_pic(struct kvm_pic *vpic) { - struct kvm_pic *vpic = kvm->arch.vpic; - - if (vpic) { - kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev_master); - kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev_slave); - kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, &vpic->dev_eclr); - kvm->arch.vpic = NULL; - kfree(vpic); - } + kvm_io_bus_unregister_dev(vpic->kvm, KVM_PIO_BUS, &vpic->dev_master); + kvm_io_bus_unregister_dev(vpic->kvm, KVM_PIO_BUS, &vpic->dev_slave); + kvm_io_bus_unregister_dev(vpic->kvm, KVM_PIO_BUS, &vpic->dev_eclr); + kfree(vpic); } diff --git a/arch/x86/kvm/irq.h b/arch/x86/kvm/irq.h index ad68c73008c5..3d782a2c336a 100644 --- a/arch/x86/kvm/irq.h +++ b/arch/x86/kvm/irq.h @@ -74,7 +74,7 @@ struct kvm_pic { }; struct kvm_pic *kvm_create_pic(struct kvm *kvm); -void kvm_destroy_pic(struct kvm *kvm); +void kvm_destroy_pic(struct kvm_pic *vpic); int kvm_pic_read_irq(struct kvm *kvm); void kvm_pic_update_irq(struct kvm_pic *s); @@ -85,11 +85,11 @@ static inline struct kvm_pic *pic_irqchip(struct kvm *kvm) static inline int irqchip_in_kernel(struct kvm *kvm) { - int ret; + struct kvm_pic *vpic = pic_irqchip(kvm); - ret = (pic_irqchip(kvm) != NULL); + /* Read vpic before kvm->irq_routing. */ smp_rmb(); - return ret; + return vpic != NULL; } void kvm_pic_reset(struct kvm_kpic_state *s); diff --git a/arch/x86/kvm/lapic.c b/arch/x86/kvm/lapic.c index 2a5ca97c263b..9a3e342e3cda 100644 --- a/arch/x86/kvm/lapic.c +++ b/arch/x86/kvm/lapic.c @@ -1900,8 +1900,9 @@ void kvm_lapic_sync_from_vapic(struct kvm_vcpu *vcpu) if (!test_bit(KVM_APIC_CHECK_VAPIC, &vcpu->arch.apic_attention)) return; - kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data, - sizeof(u32)); + if (kvm_read_guest_cached(vcpu->kvm, &vcpu->arch.apic->vapic_cache, &data, + sizeof(u32))) + return; apic_set_tpr(vcpu->arch.apic, data & 0xff); } diff --git a/arch/x86/kvm/lapic.h b/arch/x86/kvm/lapic.h index 71952748222a..764037991d26 100644 --- a/arch/x86/kvm/lapic.h +++ b/arch/x86/kvm/lapic.h @@ -91,7 +91,7 @@ int kvm_hv_vapic_msr_read(struct kvm_vcpu *vcpu, u32 msr, u64 *data); static inline bool kvm_hv_vapic_assist_page_enabled(struct kvm_vcpu *vcpu) { - return vcpu->arch.hv_vapic & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE; + return vcpu->arch.hyperv.hv_vapic & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE; } int kvm_lapic_enable_pv_eoi(struct kvm_vcpu *vcpu, u64 data); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 44171462bd2a..fb16a8ea3dee 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -357,12 +357,6 @@ static u64 __get_spte_lockless(u64 *sptep) { return ACCESS_ONCE(*sptep); } - -static bool __check_direct_spte_mmio_pf(u64 spte) -{ - /* It is valid if the spte is zapped. */ - return spte == 0ull; -} #else union split_spte { struct { @@ -478,23 +472,6 @@ retry: return spte.spte; } - -static bool __check_direct_spte_mmio_pf(u64 spte) -{ - union split_spte sspte = (union split_spte)spte; - u32 high_mmio_mask = shadow_mmio_mask >> 32; - - /* It is valid if the spte is zapped. */ - if (spte == 0ull) - return true; - - /* It is valid if the spte is being zapped. */ - if (sspte.spte_low == 0ull && - (sspte.spte_high & high_mmio_mask) == high_mmio_mask) - return true; - - return false; -} #endif static bool spte_is_locklessly_modifiable(u64 spte) @@ -3291,54 +3268,89 @@ static gpa_t nonpaging_gva_to_gpa_nested(struct kvm_vcpu *vcpu, gva_t vaddr, return vcpu->arch.nested_mmu.translate_gpa(vcpu, vaddr, access, exception); } -static bool quickly_check_mmio_pf(struct kvm_vcpu *vcpu, u64 addr, bool direct) +static bool +__is_rsvd_bits_set(struct rsvd_bits_validate *rsvd_check, u64 pte, int level) { - if (direct) - return vcpu_match_mmio_gpa(vcpu, addr); + int bit7 = (pte >> 7) & 1, low6 = pte & 0x3f; - return vcpu_match_mmio_gva(vcpu, addr); + return (pte & rsvd_check->rsvd_bits_mask[bit7][level-1]) | + ((rsvd_check->bad_mt_xwr & (1ull << low6)) != 0); } +static bool is_rsvd_bits_set(struct kvm_mmu *mmu, u64 gpte, int level) +{ + return __is_rsvd_bits_set(&mmu->guest_rsvd_check, gpte, level); +} -/* - * On direct hosts, the last spte is only allows two states - * for mmio page fault: - * - It is the mmio spte - * - It is zapped or it is being zapped. - * - * This function completely checks the spte when the last spte - * is not the mmio spte. - */ -static bool check_direct_spte_mmio_pf(u64 spte) +static bool is_shadow_zero_bits_set(struct kvm_mmu *mmu, u64 spte, int level) { - return __check_direct_spte_mmio_pf(spte); + return __is_rsvd_bits_set(&mmu->shadow_zero_check, spte, level); } -static u64 walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr) +static bool quickly_check_mmio_pf(struct kvm_vcpu *vcpu, u64 addr, bool direct) +{ + if (direct) + return vcpu_match_mmio_gpa(vcpu, addr); + + return vcpu_match_mmio_gva(vcpu, addr); +} + +/* return true if reserved bit is detected on spte. */ +static bool +walk_shadow_page_get_mmio_spte(struct kvm_vcpu *vcpu, u64 addr, u64 *sptep) { struct kvm_shadow_walk_iterator iterator; - u64 spte = 0ull; + u64 sptes[PT64_ROOT_LEVEL], spte = 0ull; + int root, leaf; + bool reserved = false; if (!VALID_PAGE(vcpu->arch.mmu.root_hpa)) - return spte; + goto exit; walk_shadow_page_lockless_begin(vcpu); - for_each_shadow_entry_lockless(vcpu, addr, iterator, spte) + + for (shadow_walk_init(&iterator, vcpu, addr), root = iterator.level; + shadow_walk_okay(&iterator); + __shadow_walk_next(&iterator, spte)) { + leaf = iterator.level; + spte = mmu_spte_get_lockless(iterator.sptep); + + sptes[leaf - 1] = spte; + if (!is_shadow_present_pte(spte)) break; + + reserved |= is_shadow_zero_bits_set(&vcpu->arch.mmu, spte, + leaf); + } + walk_shadow_page_lockless_end(vcpu); - return spte; + if (reserved) { + pr_err("%s: detect reserved bits on spte, addr 0x%llx, dump hierarchy:\n", + __func__, addr); + while (root >= leaf) { + pr_err("------ spte 0x%llx level %d.\n", + sptes[root - 1], root); + root--; + } + } +exit: + *sptep = spte; + return reserved; } int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct) { u64 spte; + bool reserved; if (quickly_check_mmio_pf(vcpu, addr, direct)) return RET_MMIO_PF_EMULATE; - spte = walk_shadow_page_get_mmio_spte(vcpu, addr); + reserved = walk_shadow_page_get_mmio_spte(vcpu, addr, &spte); + if (unlikely(reserved)) + return RET_MMIO_PF_BUG; if (is_mmio_spte(spte)) { gfn_t gfn = get_mmio_spte_gfn(spte); @@ -3356,13 +3368,6 @@ int handle_mmio_page_fault_common(struct kvm_vcpu *vcpu, u64 addr, bool direct) } /* - * It's ok if the gva is remapped by other cpus on shadow guest, - * it's a BUG if the gfn is not a mmio page. - */ - if (direct && !check_direct_spte_mmio_pf(spte)) - return RET_MMIO_PF_BUG; - - /* * If the page table is zapped by other cpus, let CPU fault again on * the address. */ @@ -3604,19 +3609,21 @@ static inline bool is_last_gpte(struct kvm_mmu *mmu, unsigned level, unsigned gp #include "paging_tmpl.h" #undef PTTYPE -static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu, - struct kvm_mmu *context) +static void +__reset_rsvds_bits_mask(struct kvm_vcpu *vcpu, + struct rsvd_bits_validate *rsvd_check, + int maxphyaddr, int level, bool nx, bool gbpages, + bool pse) { - int maxphyaddr = cpuid_maxphyaddr(vcpu); u64 exb_bit_rsvd = 0; u64 gbpages_bit_rsvd = 0; u64 nonleaf_bit8_rsvd = 0; - context->bad_mt_xwr = 0; + rsvd_check->bad_mt_xwr = 0; - if (!context->nx) + if (!nx) exb_bit_rsvd = rsvd_bits(63, 63); - if (!guest_cpuid_has_gbpages(vcpu)) + if (!gbpages) gbpages_bit_rsvd = rsvd_bits(7, 7); /* @@ -3626,80 +3633,95 @@ static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu, if (guest_cpuid_is_amd(vcpu)) nonleaf_bit8_rsvd = rsvd_bits(8, 8); - switch (context->root_level) { + switch (level) { case PT32_ROOT_LEVEL: /* no rsvd bits for 2 level 4K page table entries */ - context->rsvd_bits_mask[0][1] = 0; - context->rsvd_bits_mask[0][0] = 0; - context->rsvd_bits_mask[1][0] = context->rsvd_bits_mask[0][0]; + rsvd_check->rsvd_bits_mask[0][1] = 0; + rsvd_check->rsvd_bits_mask[0][0] = 0; + rsvd_check->rsvd_bits_mask[1][0] = + rsvd_check->rsvd_bits_mask[0][0]; - if (!is_pse(vcpu)) { - context->rsvd_bits_mask[1][1] = 0; + if (!pse) { + rsvd_check->rsvd_bits_mask[1][1] = 0; break; } if (is_cpuid_PSE36()) /* 36bits PSE 4MB page */ - context->rsvd_bits_mask[1][1] = rsvd_bits(17, 21); + rsvd_check->rsvd_bits_mask[1][1] = rsvd_bits(17, 21); else /* 32 bits PSE 4MB page */ - context->rsvd_bits_mask[1][1] = rsvd_bits(13, 21); + rsvd_check->rsvd_bits_mask[1][1] = rsvd_bits(13, 21); break; case PT32E_ROOT_LEVEL: - context->rsvd_bits_mask[0][2] = + rsvd_check->rsvd_bits_mask[0][2] = rsvd_bits(maxphyaddr, 63) | rsvd_bits(5, 8) | rsvd_bits(1, 2); /* PDPTE */ - context->rsvd_bits_mask[0][1] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[0][1] = exb_bit_rsvd | rsvd_bits(maxphyaddr, 62); /* PDE */ - context->rsvd_bits_mask[0][0] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[0][0] = exb_bit_rsvd | rsvd_bits(maxphyaddr, 62); /* PTE */ - context->rsvd_bits_mask[1][1] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[1][1] = exb_bit_rsvd | rsvd_bits(maxphyaddr, 62) | rsvd_bits(13, 20); /* large page */ - context->rsvd_bits_mask[1][0] = context->rsvd_bits_mask[0][0]; + rsvd_check->rsvd_bits_mask[1][0] = + rsvd_check->rsvd_bits_mask[0][0]; break; case PT64_ROOT_LEVEL: - context->rsvd_bits_mask[0][3] = exb_bit_rsvd | - nonleaf_bit8_rsvd | rsvd_bits(7, 7) | rsvd_bits(maxphyaddr, 51); - context->rsvd_bits_mask[0][2] = exb_bit_rsvd | - nonleaf_bit8_rsvd | gbpages_bit_rsvd | rsvd_bits(maxphyaddr, 51); - context->rsvd_bits_mask[0][1] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[0][3] = exb_bit_rsvd | + nonleaf_bit8_rsvd | rsvd_bits(7, 7) | + rsvd_bits(maxphyaddr, 51); + rsvd_check->rsvd_bits_mask[0][2] = exb_bit_rsvd | + nonleaf_bit8_rsvd | gbpages_bit_rsvd | rsvd_bits(maxphyaddr, 51); - context->rsvd_bits_mask[0][0] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[0][1] = exb_bit_rsvd | rsvd_bits(maxphyaddr, 51); - context->rsvd_bits_mask[1][3] = context->rsvd_bits_mask[0][3]; - context->rsvd_bits_mask[1][2] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[0][0] = exb_bit_rsvd | + rsvd_bits(maxphyaddr, 51); + rsvd_check->rsvd_bits_mask[1][3] = + rsvd_check->rsvd_bits_mask[0][3]; + rsvd_check->rsvd_bits_mask[1][2] = exb_bit_rsvd | gbpages_bit_rsvd | rsvd_bits(maxphyaddr, 51) | rsvd_bits(13, 29); - context->rsvd_bits_mask[1][1] = exb_bit_rsvd | + rsvd_check->rsvd_bits_mask[1][1] = exb_bit_rsvd | rsvd_bits(maxphyaddr, 51) | rsvd_bits(13, 20); /* large page */ - context->rsvd_bits_mask[1][0] = context->rsvd_bits_mask[0][0]; + rsvd_check->rsvd_bits_mask[1][0] = + rsvd_check->rsvd_bits_mask[0][0]; break; } } -static void reset_rsvds_bits_mask_ept(struct kvm_vcpu *vcpu, - struct kvm_mmu *context, bool execonly) +static void reset_rsvds_bits_mask(struct kvm_vcpu *vcpu, + struct kvm_mmu *context) +{ + __reset_rsvds_bits_mask(vcpu, &context->guest_rsvd_check, + cpuid_maxphyaddr(vcpu), context->root_level, + context->nx, guest_cpuid_has_gbpages(vcpu), + is_pse(vcpu)); +} + +static void +__reset_rsvds_bits_mask_ept(struct rsvd_bits_validate *rsvd_check, + int maxphyaddr, bool execonly) { - int maxphyaddr = cpuid_maxphyaddr(vcpu); int pte; - context->rsvd_bits_mask[0][3] = + rsvd_check->rsvd_bits_mask[0][3] = rsvd_bits(maxphyaddr, 51) | rsvd_bits(3, 7); - context->rsvd_bits_mask[0][2] = + rsvd_check->rsvd_bits_mask[0][2] = rsvd_bits(maxphyaddr, 51) | rsvd_bits(3, 6); - context->rsvd_bits_mask[0][1] = + rsvd_check->rsvd_bits_mask[0][1] = rsvd_bits(maxphyaddr, 51) | rsvd_bits(3, 6); - context->rsvd_bits_mask[0][0] = rsvd_bits(maxphyaddr, 51); + rsvd_check->rsvd_bits_mask[0][0] = rsvd_bits(maxphyaddr, 51); /* large page */ - context->rsvd_bits_mask[1][3] = context->rsvd_bits_mask[0][3]; - context->rsvd_bits_mask[1][2] = + rsvd_check->rsvd_bits_mask[1][3] = rsvd_check->rsvd_bits_mask[0][3]; + rsvd_check->rsvd_bits_mask[1][2] = rsvd_bits(maxphyaddr, 51) | rsvd_bits(12, 29); - context->rsvd_bits_mask[1][1] = + rsvd_check->rsvd_bits_mask[1][1] = rsvd_bits(maxphyaddr, 51) | rsvd_bits(12, 20); - context->rsvd_bits_mask[1][0] = context->rsvd_bits_mask[0][0]; + rsvd_check->rsvd_bits_mask[1][0] = rsvd_check->rsvd_bits_mask[0][0]; for (pte = 0; pte < 64; pte++) { int rwx_bits = pte & 7; @@ -3707,10 +3729,64 @@ static void reset_rsvds_bits_mask_ept(struct kvm_vcpu *vcpu, if (mt == 0x2 || mt == 0x3 || mt == 0x7 || rwx_bits == 0x2 || rwx_bits == 0x6 || (rwx_bits == 0x4 && !execonly)) - context->bad_mt_xwr |= (1ull << pte); + rsvd_check->bad_mt_xwr |= (1ull << pte); } } +static void reset_rsvds_bits_mask_ept(struct kvm_vcpu *vcpu, + struct kvm_mmu *context, bool execonly) +{ + __reset_rsvds_bits_mask_ept(&context->guest_rsvd_check, + cpuid_maxphyaddr(vcpu), execonly); +} + +/* + * the page table on host is the shadow page table for the page + * table in guest or amd nested guest, its mmu features completely + * follow the features in guest. + */ +void +reset_shadow_zero_bits_mask(struct kvm_vcpu *vcpu, struct kvm_mmu *context) +{ + __reset_rsvds_bits_mask(vcpu, &context->shadow_zero_check, + boot_cpu_data.x86_phys_bits, + context->shadow_root_level, context->nx, + guest_cpuid_has_gbpages(vcpu), is_pse(vcpu)); +} +EXPORT_SYMBOL_GPL(reset_shadow_zero_bits_mask); + +/* + * the direct page table on host, use as much mmu features as + * possible, however, kvm currently does not do execution-protection. + */ +static void +reset_tdp_shadow_zero_bits_mask(struct kvm_vcpu *vcpu, + struct kvm_mmu *context) +{ + if (guest_cpuid_is_amd(vcpu)) + __reset_rsvds_bits_mask(vcpu, &context->shadow_zero_check, + boot_cpu_data.x86_phys_bits, + context->shadow_root_level, false, + cpu_has_gbpages, true); + else + __reset_rsvds_bits_mask_ept(&context->shadow_zero_check, + boot_cpu_data.x86_phys_bits, + false); + +} + +/* + * as the comments in reset_shadow_zero_bits_mask() except it + * is the shadow page table for intel nested guest. + */ +static void +reset_ept_shadow_zero_bits_mask(struct kvm_vcpu *vcpu, + struct kvm_mmu *context, bool execonly) +{ + __reset_rsvds_bits_mask_ept(&context->shadow_zero_check, + boot_cpu_data.x86_phys_bits, execonly); +} + static void update_permission_bitmask(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, bool ept) { @@ -3889,6 +3965,7 @@ static void init_kvm_tdp_mmu(struct kvm_vcpu *vcpu) update_permission_bitmask(vcpu, context, false); update_last_pte_bitmap(vcpu, context); + reset_tdp_shadow_zero_bits_mask(vcpu, context); } void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu) @@ -3916,6 +3993,7 @@ void kvm_init_shadow_mmu(struct kvm_vcpu *vcpu) context->base_role.smap_andnot_wp = smap && !is_write_protection(vcpu); context->base_role.smm = is_smm(vcpu); + reset_shadow_zero_bits_mask(vcpu, context); } EXPORT_SYMBOL_GPL(kvm_init_shadow_mmu); @@ -3939,6 +4017,7 @@ void kvm_init_shadow_ept_mmu(struct kvm_vcpu *vcpu, bool execonly) update_permission_bitmask(vcpu, context, true); reset_rsvds_bits_mask_ept(vcpu, context, execonly); + reset_ept_shadow_zero_bits_mask(vcpu, context, execonly); } EXPORT_SYMBOL_GPL(kvm_init_shadow_ept_mmu); @@ -4860,28 +4939,6 @@ unsigned int kvm_mmu_calculate_mmu_pages(struct kvm *kvm) return nr_mmu_pages; } -int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]) -{ - struct kvm_shadow_walk_iterator iterator; - u64 spte; - int nr_sptes = 0; - - if (!VALID_PAGE(vcpu->arch.mmu.root_hpa)) - return nr_sptes; - - walk_shadow_page_lockless_begin(vcpu); - for_each_shadow_entry_lockless(vcpu, addr, iterator, spte) { - sptes[iterator.level-1] = spte; - nr_sptes++; - if (!is_shadow_present_pte(spte)) - break; - } - walk_shadow_page_lockless_end(vcpu); - - return nr_sptes; -} -EXPORT_SYMBOL_GPL(kvm_mmu_get_spte_hierarchy); - void kvm_mmu_destroy(struct kvm_vcpu *vcpu) { kvm_mmu_unload(vcpu); diff --git a/arch/x86/kvm/mmu.h b/arch/x86/kvm/mmu.h index 398d21c0f6dd..e4202e41d535 100644 --- a/arch/x86/kvm/mmu.h +++ b/arch/x86/kvm/mmu.h @@ -50,9 +50,11 @@ static inline u64 rsvd_bits(int s, int e) return ((1ULL << (e - s + 1)) - 1) << s; } -int kvm_mmu_get_spte_hierarchy(struct kvm_vcpu *vcpu, u64 addr, u64 sptes[4]); void kvm_mmu_set_mmio_spte_mask(u64 mmio_mask); +void +reset_shadow_zero_bits_mask(struct kvm_vcpu *vcpu, struct kvm_mmu *context); + /* * Return values of handle_mmio_page_fault_common: * RET_MMIO_PF_EMULATE: it is a real mmio page fault, emulate the instruction diff --git a/arch/x86/kvm/paging_tmpl.h b/arch/x86/kvm/paging_tmpl.h index 0f67d7e24800..736e6ab8784d 100644 --- a/arch/x86/kvm/paging_tmpl.h +++ b/arch/x86/kvm/paging_tmpl.h @@ -128,14 +128,6 @@ static inline void FNAME(protect_clean_gpte)(unsigned *access, unsigned gpte) *access &= mask; } -static bool FNAME(is_rsvd_bits_set)(struct kvm_mmu *mmu, u64 gpte, int level) -{ - int bit7 = (gpte >> 7) & 1, low6 = gpte & 0x3f; - - return (gpte & mmu->rsvd_bits_mask[bit7][level-1]) | - ((mmu->bad_mt_xwr & (1ull << low6)) != 0); -} - static inline int FNAME(is_present_gpte)(unsigned long pte) { #if PTTYPE != PTTYPE_EPT @@ -172,7 +164,7 @@ static bool FNAME(prefetch_invalid_gpte)(struct kvm_vcpu *vcpu, struct kvm_mmu_page *sp, u64 *spte, u64 gpte) { - if (FNAME(is_rsvd_bits_set)(&vcpu->arch.mmu, gpte, PT_PAGE_TABLE_LEVEL)) + if (is_rsvd_bits_set(&vcpu->arch.mmu, gpte, PT_PAGE_TABLE_LEVEL)) goto no_present; if (!FNAME(is_present_gpte)(gpte)) @@ -353,8 +345,7 @@ retry_walk: if (unlikely(!FNAME(is_present_gpte)(pte))) goto error; - if (unlikely(FNAME(is_rsvd_bits_set)(mmu, pte, - walker->level))) { + if (unlikely(is_rsvd_bits_set(mmu, pte, walker->level))) { errcode |= PFERR_RSVD_MASK | PFERR_PRESENT_MASK; goto error; } diff --git a/arch/x86/kvm/pmu_amd.c b/arch/x86/kvm/pmu_amd.c index 886aa25a7131..39b91127ef07 100644 --- a/arch/x86/kvm/pmu_amd.c +++ b/arch/x86/kvm/pmu_amd.c @@ -133,8 +133,6 @@ static int amd_pmu_set_msr(struct kvm_vcpu *vcpu, struct msr_data *msr_info) /* MSR_K7_PERFCTRn */ pmc = get_gp_pmc(pmu, msr, MSR_K7_PERFCTR0); if (pmc) { - if (!msr_info->host_initiated) - data = (s64)data; pmc->counter += data - pmc_read_counter(pmc); return 0; } diff --git a/arch/x86/kvm/svm.c b/arch/x86/kvm/svm.c index 8e0c0844c6b9..74d825716f4f 100644 --- a/arch/x86/kvm/svm.c +++ b/arch/x86/kvm/svm.c @@ -1173,6 +1173,10 @@ static u64 svm_get_mt_mask(struct kvm_vcpu *vcpu, gfn_t gfn, bool is_mmio) if (!is_mmio && !kvm_arch_has_assigned_device(vcpu->kvm)) return 0; + if (!kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_CD_NW_CLEARED) && + kvm_read_cr0(vcpu) & X86_CR0_CD) + return _PAGE_NOCACHE; + mtrr = kvm_mtrr_get_guest_memory_type(vcpu, gfn); return mtrr2protval[mtrr]; } @@ -1667,13 +1671,10 @@ static void svm_set_cr0(struct kvm_vcpu *vcpu, unsigned long cr0) if (!vcpu->fpu_active) cr0 |= X86_CR0_TS; - /* - * re-enable caching here because the QEMU bios - * does not do it - this results in some delay at - * reboot - */ - if (kvm_check_has_quirk(vcpu->kvm, KVM_X86_QUIRK_CD_NW_CLEARED)) - cr0 &= ~(X86_CR0_CD | X86_CR0_NW); + + /* These are emulated via page tables. */ + cr0 &= ~(X86_CR0_CD | X86_CR0_NW); + svm->vmcb->save.cr0 = cr0; mark_dirty(svm->vmcb, VMCB_CR); update_cr0_intercept(svm); @@ -2106,6 +2107,7 @@ static void nested_svm_init_mmu_context(struct kvm_vcpu *vcpu) vcpu->arch.mmu.get_pdptr = nested_svm_get_tdp_pdptr; vcpu->arch.mmu.inject_page_fault = nested_svm_inject_npf_exit; vcpu->arch.mmu.shadow_root_level = get_npt_level(); + reset_shadow_zero_bits_mask(vcpu, &vcpu->arch.mmu); vcpu->arch.walk_mmu = &vcpu->arch.nested_mmu; } diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 83b7b5cd75d5..da1590ea43fc 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -2443,10 +2443,10 @@ static void nested_vmx_setup_ctls_msrs(struct vcpu_vmx *vmx) CPU_BASED_CR8_LOAD_EXITING | CPU_BASED_CR8_STORE_EXITING | #endif CPU_BASED_MOV_DR_EXITING | CPU_BASED_UNCOND_IO_EXITING | - CPU_BASED_USE_IO_BITMAPS | CPU_BASED_MONITOR_EXITING | - CPU_BASED_RDPMC_EXITING | CPU_BASED_RDTSC_EXITING | - CPU_BASED_PAUSE_EXITING | CPU_BASED_TPR_SHADOW | - CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; + CPU_BASED_USE_IO_BITMAPS | CPU_BASED_MONITOR_TRAP_FLAG | + CPU_BASED_MONITOR_EXITING | CPU_BASED_RDPMC_EXITING | + CPU_BASED_RDTSC_EXITING | CPU_BASED_PAUSE_EXITING | + CPU_BASED_TPR_SHADOW | CPU_BASED_ACTIVATE_SECONDARY_CONTROLS; /* * We can allow some features even when not supported by the * hardware. For example, L1 can specify an MSR bitmap - and we @@ -3423,12 +3423,12 @@ static void enter_lmode(struct kvm_vcpu *vcpu) vmx_segment_cache_clear(to_vmx(vcpu)); guest_tr_ar = vmcs_read32(GUEST_TR_AR_BYTES); - if ((guest_tr_ar & AR_TYPE_MASK) != AR_TYPE_BUSY_64_TSS) { + if ((guest_tr_ar & VMX_AR_TYPE_MASK) != VMX_AR_TYPE_BUSY_64_TSS) { pr_debug_ratelimited("%s: tss fixup for long mode. \n", __func__); vmcs_write32(GUEST_TR_AR_BYTES, - (guest_tr_ar & ~AR_TYPE_MASK) - | AR_TYPE_BUSY_64_TSS); + (guest_tr_ar & ~VMX_AR_TYPE_MASK) + | VMX_AR_TYPE_BUSY_64_TSS); } vmx_set_efer(vcpu, vcpu->arch.efer | EFER_LMA); } @@ -3719,7 +3719,7 @@ static int vmx_get_cpl(struct kvm_vcpu *vcpu) return 0; else { int ar = vmx_read_guest_seg_ar(vmx, VCPU_SREG_SS); - return AR_DPL(ar); + return VMX_AR_DPL(ar); } } @@ -3847,11 +3847,11 @@ static bool code_segment_valid(struct kvm_vcpu *vcpu) if (cs.unusable) return false; - if (~cs.type & (AR_TYPE_CODE_MASK|AR_TYPE_ACCESSES_MASK)) + if (~cs.type & (VMX_AR_TYPE_CODE_MASK|VMX_AR_TYPE_ACCESSES_MASK)) return false; if (!cs.s) return false; - if (cs.type & AR_TYPE_WRITEABLE_MASK) { + if (cs.type & VMX_AR_TYPE_WRITEABLE_MASK) { if (cs.dpl > cs_rpl) return false; } else { @@ -3901,7 +3901,7 @@ static bool data_segment_valid(struct kvm_vcpu *vcpu, int seg) return false; if (!var.present) return false; - if (~var.type & (AR_TYPE_CODE_MASK|AR_TYPE_WRITEABLE_MASK)) { + if (~var.type & (VMX_AR_TYPE_CODE_MASK|VMX_AR_TYPE_WRITEABLE_MASK)) { if (var.dpl < rpl) /* DPL < RPL */ return false; } @@ -5759,73 +5759,9 @@ static int handle_ept_violation(struct kvm_vcpu *vcpu) return kvm_mmu_page_fault(vcpu, gpa, error_code, NULL, 0); } -static u64 ept_rsvd_mask(u64 spte, int level) -{ - int i; - u64 mask = 0; - - for (i = 51; i > boot_cpu_data.x86_phys_bits; i--) - mask |= (1ULL << i); - - if (level == 4) - /* bits 7:3 reserved */ - mask |= 0xf8; - else if (spte & (1ULL << 7)) - /* - * 1GB/2MB page, bits 29:12 or 20:12 reserved respectively, - * level == 1 if the hypervisor is using the ignored bit 7. - */ - mask |= (PAGE_SIZE << ((level - 1) * 9)) - PAGE_SIZE; - else if (level > 1) - /* bits 6:3 reserved */ - mask |= 0x78; - - return mask; -} - -static void ept_misconfig_inspect_spte(struct kvm_vcpu *vcpu, u64 spte, - int level) -{ - printk(KERN_ERR "%s: spte 0x%llx level %d\n", __func__, spte, level); - - /* 010b (write-only) */ - WARN_ON((spte & 0x7) == 0x2); - - /* 110b (write/execute) */ - WARN_ON((spte & 0x7) == 0x6); - - /* 100b (execute-only) and value not supported by logical processor */ - if (!cpu_has_vmx_ept_execute_only()) - WARN_ON((spte & 0x7) == 0x4); - - /* not 000b */ - if ((spte & 0x7)) { - u64 rsvd_bits = spte & ept_rsvd_mask(spte, level); - - if (rsvd_bits != 0) { - printk(KERN_ERR "%s: rsvd_bits = 0x%llx\n", - __func__, rsvd_bits); - WARN_ON(1); - } - - /* bits 5:3 are _not_ reserved for large page or leaf page */ - if ((rsvd_bits & 0x38) == 0) { - u64 ept_mem_type = (spte & 0x38) >> 3; - - if (ept_mem_type == 2 || ept_mem_type == 3 || - ept_mem_type == 7) { - printk(KERN_ERR "%s: ept_mem_type=0x%llx\n", - __func__, ept_mem_type); - WARN_ON(1); - } - } - } -} - static int handle_ept_misconfig(struct kvm_vcpu *vcpu) { - u64 sptes[4]; - int nr_sptes, i, ret; + int ret; gpa_t gpa; gpa = vmcs_read64(GUEST_PHYSICAL_ADDRESS); @@ -5846,13 +5782,7 @@ static int handle_ept_misconfig(struct kvm_vcpu *vcpu) return 1; /* It is the real ept misconfig */ - printk(KERN_ERR "EPT: Misconfiguration.\n"); - printk(KERN_ERR "EPT: GPA: 0x%llx\n", gpa); - - nr_sptes = kvm_mmu_get_spte_hierarchy(vcpu, gpa, sptes); - - for (i = PT64_ROOT_LEVEL; i > PT64_ROOT_LEVEL - nr_sptes; --i) - ept_misconfig_inspect_spte(vcpu, sptes[i-1], i); + WARN_ON(1); vcpu->run->exit_reason = KVM_EXIT_UNKNOWN; vcpu->run->hw.hardware_exit_reason = EXIT_REASON_EPT_MISCONFIG; @@ -6246,6 +6176,11 @@ static int handle_mwait(struct kvm_vcpu *vcpu) return handle_nop(vcpu); } +static int handle_monitor_trap(struct kvm_vcpu *vcpu) +{ + return 1; +} + static int handle_monitor(struct kvm_vcpu *vcpu) { printk_once(KERN_WARNING "kvm: MONITOR instruction emulated as NOP!\n"); @@ -6408,8 +6343,12 @@ static enum hrtimer_restart vmx_preemption_timer_fn(struct hrtimer *timer) */ static int get_vmx_mem_address(struct kvm_vcpu *vcpu, unsigned long exit_qualification, - u32 vmx_instruction_info, gva_t *ret) + u32 vmx_instruction_info, bool wr, gva_t *ret) { + gva_t off; + bool exn; + struct kvm_segment s; + /* * According to Vol. 3B, "Information for VM Exits Due to Instruction * Execution", on an exit, vmx_instruction_info holds most of the @@ -6434,22 +6373,63 @@ static int get_vmx_mem_address(struct kvm_vcpu *vcpu, /* Addr = segment_base + offset */ /* offset = base + [index * scale] + displacement */ - *ret = vmx_get_segment_base(vcpu, seg_reg); + off = exit_qualification; /* holds the displacement */ if (base_is_valid) - *ret += kvm_register_read(vcpu, base_reg); + off += kvm_register_read(vcpu, base_reg); if (index_is_valid) - *ret += kvm_register_read(vcpu, index_reg)<<scaling; - *ret += exit_qualification; /* holds the displacement */ + off += kvm_register_read(vcpu, index_reg)<<scaling; + vmx_get_segment(vcpu, &s, seg_reg); + *ret = s.base + off; if (addr_size == 1) /* 32 bit */ *ret &= 0xffffffff; - /* - * TODO: throw #GP (and return 1) in various cases that the VM* - * instructions require it - e.g., offset beyond segment limit, - * unusable or unreadable/unwritable segment, non-canonical 64-bit - * address, and so on. Currently these are not checked. - */ + /* Checks for #GP/#SS exceptions. */ + exn = false; + if (is_protmode(vcpu)) { + /* Protected mode: apply checks for segment validity in the + * following order: + * - segment type check (#GP(0) may be thrown) + * - usability check (#GP(0)/#SS(0)) + * - limit check (#GP(0)/#SS(0)) + */ + if (wr) + /* #GP(0) if the destination operand is located in a + * read-only data segment or any code segment. + */ + exn = ((s.type & 0xa) == 0 || (s.type & 8)); + else + /* #GP(0) if the source operand is located in an + * execute-only code segment + */ + exn = ((s.type & 0xa) == 8); + } + if (exn) { + kvm_queue_exception_e(vcpu, GP_VECTOR, 0); + return 1; + } + if (is_long_mode(vcpu)) { + /* Long mode: #GP(0)/#SS(0) if the memory address is in a + * non-canonical form. This is an only check for long mode. + */ + exn = is_noncanonical_address(*ret); + } else if (is_protmode(vcpu)) { + /* Protected mode: #GP(0)/#SS(0) if the segment is unusable. + */ + exn = (s.unusable != 0); + /* Protected mode: #GP(0)/#SS(0) if the memory + * operand is outside the segment limit. + */ + exn = exn || (off + sizeof(u64) > s.limit); + } + if (exn) { + kvm_queue_exception_e(vcpu, + seg_reg == VCPU_SREG_SS ? + SS_VECTOR : GP_VECTOR, + 0); + return 1; + } + return 0; } @@ -6471,7 +6451,7 @@ static int nested_vmx_check_vmptr(struct kvm_vcpu *vcpu, int exit_reason, int maxphyaddr = cpuid_maxphyaddr(vcpu); if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION), - vmcs_read32(VMX_INSTRUCTION_INFO), &gva)) + vmcs_read32(VMX_INSTRUCTION_INFO), false, &gva)) return 1; if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &vmptr, @@ -6999,7 +6979,7 @@ static int handle_vmread(struct kvm_vcpu *vcpu) field_value); } else { if (get_vmx_mem_address(vcpu, exit_qualification, - vmx_instruction_info, &gva)) + vmx_instruction_info, true, &gva)) return 1; /* _system ok, as nested_vmx_check_permission verified cpl=0 */ kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, gva, @@ -7036,7 +7016,7 @@ static int handle_vmwrite(struct kvm_vcpu *vcpu) (((vmx_instruction_info) >> 3) & 0xf)); else { if (get_vmx_mem_address(vcpu, exit_qualification, - vmx_instruction_info, &gva)) + vmx_instruction_info, false, &gva)) return 1; if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &field_value, (is_64_bit_mode(vcpu) ? 8 : 4), &e)) { @@ -7128,7 +7108,7 @@ static int handle_vmptrst(struct kvm_vcpu *vcpu) return 1; if (get_vmx_mem_address(vcpu, exit_qualification, - vmx_instruction_info, &vmcs_gva)) + vmx_instruction_info, true, &vmcs_gva)) return 1; /* ok to use *_system, as nested_vmx_check_permission verified cpl=0 */ if (kvm_write_guest_virt_system(&vcpu->arch.emulate_ctxt, vmcs_gva, @@ -7184,7 +7164,7 @@ static int handle_invept(struct kvm_vcpu *vcpu) * operand is read even if it isn't needed (e.g., for type==global) */ if (get_vmx_mem_address(vcpu, vmcs_readl(EXIT_QUALIFICATION), - vmx_instruction_info, &gva)) + vmx_instruction_info, false, &gva)) return 1; if (kvm_read_guest_virt(&vcpu->arch.emulate_ctxt, gva, &operand, sizeof(operand), &e)) { @@ -7282,6 +7262,7 @@ static int (*const kvm_vmx_exit_handlers[])(struct kvm_vcpu *vcpu) = { [EXIT_REASON_EPT_MISCONFIG] = handle_ept_misconfig, [EXIT_REASON_PAUSE_INSTRUCTION] = handle_pause, [EXIT_REASON_MWAIT_INSTRUCTION] = handle_mwait, + [EXIT_REASON_MONITOR_TRAP_FLAG] = handle_monitor_trap, [EXIT_REASON_MONITOR_INSTRUCTION] = handle_monitor, [EXIT_REASON_INVEPT] = handle_invept, [EXIT_REASON_INVVPID] = handle_invvpid, @@ -7542,6 +7523,8 @@ static bool nested_vmx_exit_handled(struct kvm_vcpu *vcpu) return true; case EXIT_REASON_MWAIT_INSTRUCTION: return nested_cpu_has(vmcs12, CPU_BASED_MWAIT_EXITING); + case EXIT_REASON_MONITOR_TRAP_FLAG: + return nested_cpu_has(vmcs12, CPU_BASED_MONITOR_TRAP_FLAG); case EXIT_REASON_MONITOR_INSTRUCTION: return nested_cpu_has(vmcs12, CPU_BASED_MONITOR_EXITING); case EXIT_REASON_PAUSE_INSTRUCTION: diff --git a/arch/x86/kvm/x86.c b/arch/x86/kvm/x86.c index 5ef2560075bf..4bbc2a1676c9 100644 --- a/arch/x86/kvm/x86.c +++ b/arch/x86/kvm/x86.c @@ -29,6 +29,7 @@ #include "cpuid.h" #include "assigned-dev.h" #include "pmu.h" +#include "hyperv.h" #include <linux/clocksource.h> #include <linux/interrupt.h> @@ -221,11 +222,9 @@ static void shared_msr_update(unsigned slot, u32 msr) void kvm_define_shared_msr(unsigned slot, u32 msr) { BUG_ON(slot >= KVM_NR_SHARED_MSRS); + shared_msrs_global.msrs[slot] = msr; if (slot >= shared_msrs_global.nr) shared_msrs_global.nr = slot + 1; - shared_msrs_global.msrs[slot] = msr; - /* we need ensured the shared_msr_global have been updated */ - smp_wmb(); } EXPORT_SYMBOL_GPL(kvm_define_shared_msr); @@ -526,7 +525,8 @@ int load_pdptrs(struct kvm_vcpu *vcpu, struct kvm_mmu *mmu, unsigned long cr3) } for (i = 0; i < ARRAY_SIZE(pdpte); ++i) { if (is_present_gpte(pdpte[i]) && - (pdpte[i] & vcpu->arch.mmu.rsvd_bits_mask[0][2])) { + (pdpte[i] & + vcpu->arch.mmu.guest_rsvd_check.rsvd_bits_mask[0][2])) { ret = 0; goto out; } @@ -949,6 +949,8 @@ static u32 emulated_msrs[] = { MSR_KVM_SYSTEM_TIME_NEW, MSR_KVM_WALL_CLOCK_NEW, HV_X64_MSR_GUEST_OS_ID, HV_X64_MSR_HYPERCALL, HV_X64_MSR_TIME_REF_COUNT, HV_X64_MSR_REFERENCE_TSC, + HV_X64_MSR_CRASH_P0, HV_X64_MSR_CRASH_P1, HV_X64_MSR_CRASH_P2, + HV_X64_MSR_CRASH_P3, HV_X64_MSR_CRASH_P4, HV_X64_MSR_CRASH_CTL, HV_X64_MSR_APIC_ASSIST_PAGE, MSR_KVM_ASYNC_PF_EN, MSR_KVM_STEAL_TIME, MSR_KVM_PV_EOI_EN, @@ -1217,11 +1219,6 @@ static void kvm_get_time_scale(uint32_t scaled_khz, uint32_t base_khz, __func__, base_khz, scaled_khz, shift, *pmultiplier); } -static inline u64 get_kernel_ns(void) -{ - return ktime_get_boot_ns(); -} - #ifdef CONFIG_X86_64 static atomic_t kvm_guest_has_master_clock = ATOMIC_INIT(0); #endif @@ -1869,123 +1866,6 @@ out: return r; } -static bool kvm_hv_hypercall_enabled(struct kvm *kvm) -{ - return kvm->arch.hv_hypercall & HV_X64_MSR_HYPERCALL_ENABLE; -} - -static bool kvm_hv_msr_partition_wide(u32 msr) -{ - bool r = false; - switch (msr) { - case HV_X64_MSR_GUEST_OS_ID: - case HV_X64_MSR_HYPERCALL: - case HV_X64_MSR_REFERENCE_TSC: - case HV_X64_MSR_TIME_REF_COUNT: - r = true; - break; - } - - return r; -} - -static int set_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 data) -{ - struct kvm *kvm = vcpu->kvm; - - switch (msr) { - case HV_X64_MSR_GUEST_OS_ID: - kvm->arch.hv_guest_os_id = data; - /* setting guest os id to zero disables hypercall page */ - if (!kvm->arch.hv_guest_os_id) - kvm->arch.hv_hypercall &= ~HV_X64_MSR_HYPERCALL_ENABLE; - break; - case HV_X64_MSR_HYPERCALL: { - u64 gfn; - unsigned long addr; - u8 instructions[4]; - - /* if guest os id is not set hypercall should remain disabled */ - if (!kvm->arch.hv_guest_os_id) - break; - if (!(data & HV_X64_MSR_HYPERCALL_ENABLE)) { - kvm->arch.hv_hypercall = data; - break; - } - gfn = data >> HV_X64_MSR_HYPERCALL_PAGE_ADDRESS_SHIFT; - addr = gfn_to_hva(kvm, gfn); - if (kvm_is_error_hva(addr)) - return 1; - kvm_x86_ops->patch_hypercall(vcpu, instructions); - ((unsigned char *)instructions)[3] = 0xc3; /* ret */ - if (__copy_to_user((void __user *)addr, instructions, 4)) - return 1; - kvm->arch.hv_hypercall = data; - mark_page_dirty(kvm, gfn); - break; - } - case HV_X64_MSR_REFERENCE_TSC: { - u64 gfn; - HV_REFERENCE_TSC_PAGE tsc_ref; - memset(&tsc_ref, 0, sizeof(tsc_ref)); - kvm->arch.hv_tsc_page = data; - if (!(data & HV_X64_MSR_TSC_REFERENCE_ENABLE)) - break; - gfn = data >> HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT; - if (kvm_write_guest(kvm, gfn << HV_X64_MSR_TSC_REFERENCE_ADDRESS_SHIFT, - &tsc_ref, sizeof(tsc_ref))) - return 1; - mark_page_dirty(kvm, gfn); - break; - } - default: - vcpu_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " - "data 0x%llx\n", msr, data); - return 1; - } - return 0; -} - -static int set_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 data) -{ - switch (msr) { - case HV_X64_MSR_APIC_ASSIST_PAGE: { - u64 gfn; - unsigned long addr; - - if (!(data & HV_X64_MSR_APIC_ASSIST_PAGE_ENABLE)) { - vcpu->arch.hv_vapic = data; - if (kvm_lapic_enable_pv_eoi(vcpu, 0)) - return 1; - break; - } - gfn = data >> HV_X64_MSR_APIC_ASSIST_PAGE_ADDRESS_SHIFT; - addr = kvm_vcpu_gfn_to_hva(vcpu, gfn); - if (kvm_is_error_hva(addr)) - return 1; - if (__clear_user((void __user *)addr, PAGE_SIZE)) - return 1; - vcpu->arch.hv_vapic = data; - kvm_vcpu_mark_page_dirty(vcpu, gfn); - if (kvm_lapic_enable_pv_eoi(vcpu, gfn_to_gpa(gfn) | KVM_MSR_ENABLED)) - return 1; - break; - } - case HV_X64_MSR_EOI: - return kvm_hv_vapic_msr_write(vcpu, APIC_EOI, data); - case HV_X64_MSR_ICR: - return kvm_hv_vapic_msr_write(vcpu, APIC_ICR, data); - case HV_X64_MSR_TPR: - return kvm_hv_vapic_msr_write(vcpu, APIC_TASKPRI, data); - default: - vcpu_unimpl(vcpu, "HYPER-V unimplemented wrmsr: 0x%x " - "data 0x%llx\n", msr, data); - return 1; - } - - return 0; -} - static int kvm_pv_enable_async_pf(struct kvm_vcpu *vcpu, u64 data) { gpa_t gpa = data & ~0x3f; @@ -2105,7 +1985,7 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) if (guest_cpuid_has_tsc_adjust(vcpu)) { if (!msr_info->host_initiated) { s64 adj = data - vcpu->arch.ia32_tsc_adjust_msr; - kvm_x86_ops->adjust_tsc_offset(vcpu, adj, true); + adjust_tsc_offset_guest(vcpu, adj); } vcpu->arch.ia32_tsc_adjust_msr = data; } @@ -2224,15 +2104,10 @@ int kvm_set_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) */ break; case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15: - if (kvm_hv_msr_partition_wide(msr)) { - int r; - mutex_lock(&vcpu->kvm->lock); - r = set_msr_hyperv_pw(vcpu, msr, data); - mutex_unlock(&vcpu->kvm->lock); - return r; - } else - return set_msr_hyperv(vcpu, msr, data); - break; + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + case HV_X64_MSR_CRASH_CTL: + return kvm_hv_set_msr_common(vcpu, msr, data, + msr_info->host_initiated); case MSR_IA32_BBL_CR_CTL3: /* Drop writes to this legacy MSR -- see rdmsr * counterpart for further detail. @@ -2315,68 +2190,6 @@ static int get_msr_mce(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) return 0; } -static int get_msr_hyperv_pw(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) -{ - u64 data = 0; - struct kvm *kvm = vcpu->kvm; - - switch (msr) { - case HV_X64_MSR_GUEST_OS_ID: - data = kvm->arch.hv_guest_os_id; - break; - case HV_X64_MSR_HYPERCALL: - data = kvm->arch.hv_hypercall; - break; - case HV_X64_MSR_TIME_REF_COUNT: { - data = - div_u64(get_kernel_ns() + kvm->arch.kvmclock_offset, 100); - break; - } - case HV_X64_MSR_REFERENCE_TSC: - data = kvm->arch.hv_tsc_page; - break; - default: - vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); - return 1; - } - - *pdata = data; - return 0; -} - -static int get_msr_hyperv(struct kvm_vcpu *vcpu, u32 msr, u64 *pdata) -{ - u64 data = 0; - - switch (msr) { - case HV_X64_MSR_VP_INDEX: { - int r; - struct kvm_vcpu *v; - kvm_for_each_vcpu(r, v, vcpu->kvm) { - if (v == vcpu) { - data = r; - break; - } - } - break; - } - case HV_X64_MSR_EOI: - return kvm_hv_vapic_msr_read(vcpu, APIC_EOI, pdata); - case HV_X64_MSR_ICR: - return kvm_hv_vapic_msr_read(vcpu, APIC_ICR, pdata); - case HV_X64_MSR_TPR: - return kvm_hv_vapic_msr_read(vcpu, APIC_TASKPRI, pdata); - case HV_X64_MSR_APIC_ASSIST_PAGE: - data = vcpu->arch.hv_vapic; - break; - default: - vcpu_unimpl(vcpu, "Hyper-V unhandled rdmsr: 0x%x\n", msr); - return 1; - } - *pdata = data; - return 0; -} - int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) { switch (msr_info->index) { @@ -2493,14 +2306,10 @@ int kvm_get_msr_common(struct kvm_vcpu *vcpu, struct msr_data *msr_info) msr_info->data = 0x20000000; break; case HV_X64_MSR_GUEST_OS_ID ... HV_X64_MSR_SINT15: - if (kvm_hv_msr_partition_wide(msr_info->index)) { - int r; - mutex_lock(&vcpu->kvm->lock); - r = get_msr_hyperv_pw(vcpu, msr_info->index, &msr_info->data); - mutex_unlock(&vcpu->kvm->lock); - return r; - } else - return get_msr_hyperv(vcpu, msr_info->index, &msr_info->data); + case HV_X64_MSR_CRASH_P0 ... HV_X64_MSR_CRASH_P4: + case HV_X64_MSR_CRASH_CTL: + return kvm_hv_get_msr_common(vcpu, + msr_info->index, &msr_info->data); break; case MSR_IA32_BBL_CR_CTL3: /* This legacy MSR exists but isn't fully documented in current @@ -2651,6 +2460,7 @@ int kvm_vm_ioctl_check_extension(struct kvm *kvm, long ext) case KVM_CAP_TSC_DEADLINE_TIMER: case KVM_CAP_ENABLE_CAP_VM: case KVM_CAP_DISABLE_QUIRKS: + case KVM_CAP_SET_BOOT_CPU_ID: #ifdef CONFIG_KVM_DEVICE_ASSIGNMENT case KVM_CAP_ASSIGN_DEV_IRQ: case KVM_CAP_PCI_2_3: @@ -3817,30 +3627,25 @@ long kvm_arch_vm_ioctl(struct file *filp, r = kvm_ioapic_init(kvm); if (r) { mutex_lock(&kvm->slots_lock); - kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, - &vpic->dev_master); - kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, - &vpic->dev_slave); - kvm_io_bus_unregister_dev(kvm, KVM_PIO_BUS, - &vpic->dev_eclr); + kvm_destroy_pic(vpic); mutex_unlock(&kvm->slots_lock); - kfree(vpic); goto create_irqchip_unlock; } } else goto create_irqchip_unlock; - smp_wmb(); - kvm->arch.vpic = vpic; - smp_wmb(); r = kvm_setup_default_irq_routing(kvm); if (r) { mutex_lock(&kvm->slots_lock); mutex_lock(&kvm->irq_lock); kvm_ioapic_destroy(kvm); - kvm_destroy_pic(kvm); + kvm_destroy_pic(vpic); mutex_unlock(&kvm->irq_lock); mutex_unlock(&kvm->slots_lock); + goto create_irqchip_unlock; } + /* Write kvm->irq_routing before kvm->arch.vpic. */ + smp_wmb(); + kvm->arch.vpic = vpic; create_irqchip_unlock: mutex_unlock(&kvm->lock); break; @@ -3967,6 +3772,15 @@ long kvm_arch_vm_ioctl(struct file *filp, r = kvm_vm_ioctl_reinject(kvm, &control); break; } + case KVM_SET_BOOT_CPU_ID: + r = 0; + mutex_lock(&kvm->lock); + if (atomic_read(&kvm->online_vcpus) != 0) + r = -EBUSY; + else + kvm->arch.bsp_vcpu_id = arg; + mutex_unlock(&kvm->lock); + break; case KVM_XEN_HVM_CONFIG: { r = -EFAULT; if (copy_from_user(&kvm->arch.xen_hvm_config, argp, @@ -5882,66 +5696,6 @@ int kvm_emulate_halt(struct kvm_vcpu *vcpu) } EXPORT_SYMBOL_GPL(kvm_emulate_halt); -int kvm_hv_hypercall(struct kvm_vcpu *vcpu) -{ - u64 param, ingpa, outgpa, ret; - uint16_t code, rep_idx, rep_cnt, res = HV_STATUS_SUCCESS, rep_done = 0; - bool fast, longmode; - - /* - * hypercall generates UD from non zero cpl and real mode - * per HYPER-V spec - */ - if (kvm_x86_ops->get_cpl(vcpu) != 0 || !is_protmode(vcpu)) { - kvm_queue_exception(vcpu, UD_VECTOR); - return 0; - } - - longmode = is_64_bit_mode(vcpu); - - if (!longmode) { - param = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDX) << 32) | - (kvm_register_read(vcpu, VCPU_REGS_RAX) & 0xffffffff); - ingpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RBX) << 32) | - (kvm_register_read(vcpu, VCPU_REGS_RCX) & 0xffffffff); - outgpa = ((u64)kvm_register_read(vcpu, VCPU_REGS_RDI) << 32) | - (kvm_register_read(vcpu, VCPU_REGS_RSI) & 0xffffffff); - } -#ifdef CONFIG_X86_64 - else { - param = kvm_register_read(vcpu, VCPU_REGS_RCX); - ingpa = kvm_register_read(vcpu, VCPU_REGS_RDX); - outgpa = kvm_register_read(vcpu, VCPU_REGS_R8); - } -#endif - - code = param & 0xffff; - fast = (param >> 16) & 0x1; - rep_cnt = (param >> 32) & 0xfff; - rep_idx = (param >> 48) & 0xfff; - - trace_kvm_hv_hypercall(code, fast, rep_cnt, rep_idx, ingpa, outgpa); - - switch (code) { - case HV_X64_HV_NOTIFY_LONG_SPIN_WAIT: - kvm_vcpu_on_spin(vcpu); - break; - default: - res = HV_STATUS_INVALID_HYPERCALL_CODE; - break; - } - - ret = res | (((u64)rep_done & 0xfff) << 32); - if (longmode) { - kvm_register_write(vcpu, VCPU_REGS_RAX, ret); - } else { - kvm_register_write(vcpu, VCPU_REGS_RDX, ret >> 32); - kvm_register_write(vcpu, VCPU_REGS_RAX, ret & 0xffffffff); - } - - return 1; -} - /* * kvm_pv_kick_cpu_op: Kick a vcpu. * @@ -6327,6 +6081,7 @@ static void process_smi_save_state_64(struct kvm_vcpu *vcpu, char *buf) static void process_smi(struct kvm_vcpu *vcpu) { struct kvm_segment cs, ds; + struct desc_ptr dt; char buf[512]; u32 cr0; @@ -6359,6 +6114,10 @@ static void process_smi(struct kvm_vcpu *vcpu) kvm_x86_ops->set_cr4(vcpu, 0); + /* Undocumented: IDT limit is set to zero on entry to SMM. */ + dt.address = dt.size = 0; + kvm_x86_ops->set_idt(vcpu, &dt); + __kvm_set_dr(vcpu, 7, DR7_FIXED_1); cs.selector = (vcpu->arch.smbase >> 4) & 0xffff; @@ -6513,6 +6272,12 @@ static int vcpu_enter_guest(struct kvm_vcpu *vcpu) vcpu_scan_ioapic(vcpu); if (kvm_check_request(KVM_REQ_APIC_PAGE_RELOAD, vcpu)) kvm_vcpu_reload_apic_access_page(vcpu); + if (kvm_check_request(KVM_REQ_HV_CRASH, vcpu)) { + vcpu->run->exit_reason = KVM_EXIT_SYSTEM_EVENT; + vcpu->run->system_event.type = KVM_SYSTEM_EVENT_CRASH; + r = 0; + goto out; + } } if (kvm_check_request(KVM_REQ_EVENT, vcpu) || req_int_win) { @@ -7535,6 +7300,17 @@ void kvm_arch_check_processor_compat(void *rtn) kvm_x86_ops->check_processor_compatibility(rtn); } +bool kvm_vcpu_is_reset_bsp(struct kvm_vcpu *vcpu) +{ + return vcpu->kvm->arch.bsp_vcpu_id == vcpu->vcpu_id; +} +EXPORT_SYMBOL_GPL(kvm_vcpu_is_reset_bsp); + +bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu) +{ + return (vcpu->arch.apic_base & MSR_IA32_APICBASE_BSP) != 0; +} + bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { return irqchip_in_kernel(vcpu->kvm) == (vcpu->arch.apic != NULL); diff --git a/arch/x86/kvm/x86.h b/arch/x86/kvm/x86.h index 0ca2f3e4803c..2f822cd886c2 100644 --- a/arch/x86/kvm/x86.h +++ b/arch/x86/kvm/x86.h @@ -147,6 +147,11 @@ static inline void kvm_register_writel(struct kvm_vcpu *vcpu, return kvm_register_write(vcpu, reg, val); } +static inline u64 get_kernel_ns(void) +{ + return ktime_get_boot_ns(); +} + static inline bool kvm_check_has_quirk(struct kvm *kvm, u64 quirk) { return !(kvm->arch.disabled_quirks & quirk); diff --git a/arch/x86/math-emu/fpu_entry.c b/arch/x86/math-emu/fpu_entry.c index f37e84ab49f3..3d8f2e421466 100644 --- a/arch/x86/math-emu/fpu_entry.c +++ b/arch/x86/math-emu/fpu_entry.c @@ -29,7 +29,6 @@ #include <asm/uaccess.h> #include <asm/traps.h> -#include <asm/desc.h> #include <asm/user.h> #include <asm/fpu/internal.h> @@ -181,7 +180,7 @@ void math_emulate(struct math_emu_info *info) math_abort(FPU_info, SIGILL); } - code_descriptor = LDT_DESCRIPTOR(FPU_CS); + code_descriptor = FPU_get_ldt_descriptor(FPU_CS); if (SEG_D_SIZE(code_descriptor)) { /* The above test may be wrong, the book is not clear */ /* Segmented 32 bit protected mode */ diff --git a/arch/x86/math-emu/fpu_system.h b/arch/x86/math-emu/fpu_system.h index 9ccecb61a4fa..5e044d506b7a 100644 --- a/arch/x86/math-emu/fpu_system.h +++ b/arch/x86/math-emu/fpu_system.h @@ -16,9 +16,24 @@ #include <linux/kernel.h> #include <linux/mm.h> -/* s is always from a cpu register, and the cpu does bounds checking - * during register load --> no further bounds checks needed */ -#define LDT_DESCRIPTOR(s) (((struct desc_struct *)current->mm->context.ldt)[(s) >> 3]) +#include <asm/desc.h> +#include <asm/mmu_context.h> + +static inline struct desc_struct FPU_get_ldt_descriptor(unsigned seg) +{ + static struct desc_struct zero_desc; + struct desc_struct ret = zero_desc; + +#ifdef CONFIG_MODIFY_LDT_SYSCALL + seg >>= 3; + mutex_lock(¤t->mm->context.lock); + if (current->mm->context.ldt && seg < current->mm->context.ldt->size) + ret = current->mm->context.ldt->entries[seg]; + mutex_unlock(¤t->mm->context.lock); +#endif + return ret; +} + #define SEG_D_SIZE(x) ((x).b & (3 << 21)) #define SEG_G_BIT(x) ((x).b & (1 << 23)) #define SEG_GRANULARITY(x) (((x).b & (1 << 23)) ? 4096 : 1) diff --git a/arch/x86/math-emu/get_address.c b/arch/x86/math-emu/get_address.c index 6ef5e99380f9..8300db71c2a6 100644 --- a/arch/x86/math-emu/get_address.c +++ b/arch/x86/math-emu/get_address.c @@ -20,7 +20,6 @@ #include <linux/stddef.h> #include <asm/uaccess.h> -#include <asm/desc.h> #include "fpu_system.h" #include "exception.h" @@ -158,7 +157,7 @@ static long pm_address(u_char FPU_modrm, u_char segment, addr->selector = PM_REG_(segment); } - descriptor = LDT_DESCRIPTOR(PM_REG_(segment)); + descriptor = FPU_get_ldt_descriptor(addr->selector); base_address = SEG_BASE_ADDR(descriptor); address = base_address + offset; limit = base_address diff --git a/arch/x86/xen/Kconfig b/arch/x86/xen/Kconfig index e88fda867a33..484145368a24 100644 --- a/arch/x86/xen/Kconfig +++ b/arch/x86/xen/Kconfig @@ -8,7 +8,7 @@ config XEN select PARAVIRT_CLOCK select XEN_HAVE_PVMMU depends on X86_64 || (X86_32 && X86_PAE) - depends on X86_TSC + depends on X86_LOCAL_APIC && X86_TSC help This is the Linux Xen port. Enabling this will allow the kernel to boot in a paravirtualized environment under the @@ -17,7 +17,7 @@ config XEN config XEN_DOM0 def_bool y depends on XEN && PCI_XEN && SWIOTLB_XEN - depends on X86_LOCAL_APIC && X86_IO_APIC && ACPI && PCI + depends on X86_IO_APIC && ACPI && PCI config XEN_PVHVM def_bool y diff --git a/arch/x86/xen/Makefile b/arch/x86/xen/Makefile index 7322755f337a..4b6e29ac0968 100644 --- a/arch/x86/xen/Makefile +++ b/arch/x86/xen/Makefile @@ -13,13 +13,13 @@ CFLAGS_mmu.o := $(nostackp) obj-y := enlighten.o setup.o multicalls.o mmu.o irq.o \ time.o xen-asm.o xen-asm_$(BITS).o \ grant-table.o suspend.o platform-pci-unplug.o \ - p2m.o + p2m.o apic.o obj-$(CONFIG_EVENT_TRACING) += trace.o obj-$(CONFIG_SMP) += smp.o obj-$(CONFIG_PARAVIRT_SPINLOCKS)+= spinlock.o obj-$(CONFIG_XEN_DEBUG_FS) += debugfs.o -obj-$(CONFIG_XEN_DOM0) += apic.o vga.o +obj-$(CONFIG_XEN_DOM0) += vga.o obj-$(CONFIG_SWIOTLB_XEN) += pci-swiotlb-xen.o obj-$(CONFIG_XEN_EFI) += efi.o diff --git a/arch/x86/xen/xen-ops.h b/arch/x86/xen/xen-ops.h index c20fe29e65f4..2292721b1d10 100644 --- a/arch/x86/xen/xen-ops.h +++ b/arch/x86/xen/xen-ops.h @@ -101,17 +101,15 @@ struct dom0_vga_console_info; #ifdef CONFIG_XEN_DOM0 void __init xen_init_vga(const struct dom0_vga_console_info *, size_t size); -void __init xen_init_apic(void); #else static inline void __init xen_init_vga(const struct dom0_vga_console_info *info, size_t size) { } -static inline void __init xen_init_apic(void) -{ -} #endif +void __init xen_init_apic(void); + #ifdef CONFIG_XEN_EFI extern void xen_efi_init(void); #else diff --git a/block/blk-settings.c b/block/blk-settings.c index 12600bfffca9..e0057d035200 100644 --- a/block/blk-settings.c +++ b/block/blk-settings.c @@ -241,8 +241,8 @@ EXPORT_SYMBOL(blk_queue_bounce_limit); * Description: * Enables a low level driver to set a hard upper limit, * max_hw_sectors, on the size of requests. max_hw_sectors is set by - * the device driver based upon the combined capabilities of I/O - * controller and storage device. + * the device driver based upon the capabilities of the I/O + * controller. * * max_sectors is a soft limit imposed by the block layer for * filesystem type requests. This value can be overridden on a diff --git a/crypto/authencesn.c b/crypto/authencesn.c index a3da6770bc9e..b8efe36ce114 100644 --- a/crypto/authencesn.c +++ b/crypto/authencesn.c @@ -393,8 +393,6 @@ static int crypto_authenc_esn_genicv(struct aead_request *req, u8 *iv, struct scatterlist *cipher = areq_ctx->cipher; struct scatterlist *hsg = areq_ctx->hsg; struct scatterlist *tsg = areq_ctx->tsg; - struct scatterlist *assoc1; - struct scatterlist *assoc2; unsigned int ivsize = crypto_aead_ivsize(authenc_esn); unsigned int cryptlen = req->cryptlen; struct page *dstp; @@ -412,27 +410,19 @@ static int crypto_authenc_esn_genicv(struct aead_request *req, u8 *iv, cryptlen += ivsize; } - if (sg_is_last(assoc)) - return -EINVAL; - - assoc1 = assoc + 1; - if (sg_is_last(assoc1)) - return -EINVAL; - - assoc2 = assoc + 2; - if (!sg_is_last(assoc2)) + if (assoc->length < 12) return -EINVAL; sg_init_table(hsg, 2); - sg_set_page(hsg, sg_page(assoc), assoc->length, assoc->offset); - sg_set_page(hsg + 1, sg_page(assoc2), assoc2->length, assoc2->offset); + sg_set_page(hsg, sg_page(assoc), 4, assoc->offset); + sg_set_page(hsg + 1, sg_page(assoc), 4, assoc->offset + 8); sg_init_table(tsg, 1); - sg_set_page(tsg, sg_page(assoc1), assoc1->length, assoc1->offset); + sg_set_page(tsg, sg_page(assoc), 4, assoc->offset + 4); areq_ctx->cryptlen = cryptlen; - areq_ctx->headlen = assoc->length + assoc2->length; - areq_ctx->trailen = assoc1->length; + areq_ctx->headlen = 8; + areq_ctx->trailen = 4; areq_ctx->sg = dst; areq_ctx->complete = authenc_esn_geniv_ahash_done; @@ -563,8 +553,6 @@ static int crypto_authenc_esn_iverify(struct aead_request *req, u8 *iv, struct scatterlist *cipher = areq_ctx->cipher; struct scatterlist *hsg = areq_ctx->hsg; struct scatterlist *tsg = areq_ctx->tsg; - struct scatterlist *assoc1; - struct scatterlist *assoc2; unsigned int ivsize = crypto_aead_ivsize(authenc_esn); struct page *srcp; u8 *vsrc; @@ -580,27 +568,19 @@ static int crypto_authenc_esn_iverify(struct aead_request *req, u8 *iv, cryptlen += ivsize; } - if (sg_is_last(assoc)) - return -EINVAL; - - assoc1 = assoc + 1; - if (sg_is_last(assoc1)) - return -EINVAL; - - assoc2 = assoc + 2; - if (!sg_is_last(assoc2)) + if (assoc->length < 12) return -EINVAL; sg_init_table(hsg, 2); - sg_set_page(hsg, sg_page(assoc), assoc->length, assoc->offset); - sg_set_page(hsg + 1, sg_page(assoc2), assoc2->length, assoc2->offset); + sg_set_page(hsg, sg_page(assoc), 4, assoc->offset); + sg_set_page(hsg + 1, sg_page(assoc), 4, assoc->offset + 8); sg_init_table(tsg, 1); - sg_set_page(tsg, sg_page(assoc1), assoc1->length, assoc1->offset); + sg_set_page(tsg, sg_page(assoc), 4, assoc->offset + 4); areq_ctx->cryptlen = cryptlen; - areq_ctx->headlen = assoc->length + assoc2->length; - areq_ctx->trailen = assoc1->length; + areq_ctx->headlen = 8; + areq_ctx->trailen = 4; areq_ctx->sg = src; areq_ctx->complete = authenc_esn_verify_ahash_done; diff --git a/drivers/Kconfig b/drivers/Kconfig index 6e973b8e3a3b..4e2e6aaf0b88 100644 --- a/drivers/Kconfig +++ b/drivers/Kconfig @@ -184,4 +184,6 @@ source "drivers/android/Kconfig" source "drivers/nvdimm/Kconfig" +source "drivers/nvmem/Kconfig" + endmenu diff --git a/drivers/Makefile b/drivers/Makefile index b64b49f6e01b..4c270f5414f0 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -165,3 +165,4 @@ obj-$(CONFIG_RAS) += ras/ obj-$(CONFIG_THUNDERBOLT) += thunderbolt/ obj-$(CONFIG_CORESIGHT) += hwtracing/coresight/ obj-$(CONFIG_ANDROID) += android/ +obj-$(CONFIG_NVMEM) += nvmem/ diff --git a/drivers/acpi/nfit.c b/drivers/acpi/nfit.c index 628a42c41ab1..cf0fd96a7602 100644 --- a/drivers/acpi/nfit.c +++ b/drivers/acpi/nfit.c @@ -702,11 +702,11 @@ static ssize_t flags_show(struct device *dev, u16 flags = to_nfit_memdev(dev)->flags; return sprintf(buf, "%s%s%s%s%s\n", - flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save " : "", - flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore " : "", - flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush " : "", - flags & ACPI_NFIT_MEM_ARMED ? "arm " : "", - flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart " : ""); + flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save_fail " : "", + flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore_fail " : "", + flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush_fail " : "", + flags & ACPI_NFIT_MEM_ARMED ? "not_armed " : "", + flags & ACPI_NFIT_MEM_HEALTH_OBSERVED ? "smart_event " : ""); } static DEVICE_ATTR_RO(flags); @@ -849,12 +849,12 @@ static int acpi_nfit_register_dimms(struct acpi_nfit_desc *acpi_desc) if ((mem_flags & ACPI_NFIT_MEM_FAILED_MASK) == 0) continue; - dev_info(acpi_desc->dev, "%s: failed: %s%s%s%s\n", + dev_info(acpi_desc->dev, "%s flags:%s%s%s%s\n", nvdimm_name(nvdimm), - mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? "save " : "", - mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? "restore " : "", - mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? "flush " : "", - mem_flags & ACPI_NFIT_MEM_ARMED ? "arm " : ""); + mem_flags & ACPI_NFIT_MEM_SAVE_FAILED ? " save_fail" : "", + mem_flags & ACPI_NFIT_MEM_RESTORE_FAILED ? " restore_fail":"", + mem_flags & ACPI_NFIT_MEM_FLUSH_FAILED ? " flush_fail" : "", + mem_flags & ACPI_NFIT_MEM_ARMED ? " not_armed" : ""); } @@ -1024,7 +1024,7 @@ static void wmb_blk(struct nfit_blk *nfit_blk) wmb_pmem(); } -static u64 read_blk_stat(struct nfit_blk *nfit_blk, unsigned int bw) +static u32 read_blk_stat(struct nfit_blk *nfit_blk, unsigned int bw) { struct nfit_blk_mmio *mmio = &nfit_blk->mmio[DCR]; u64 offset = nfit_blk->stat_offset + mmio->size * bw; @@ -1032,7 +1032,7 @@ static u64 read_blk_stat(struct nfit_blk *nfit_blk, unsigned int bw) if (mmio->num_lines) offset = to_interleave_offset(offset, mmio); - return readq(mmio->base + offset); + return readl(mmio->base + offset); } static void write_blk_ctl(struct nfit_blk *nfit_blk, unsigned int bw, diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c index 815f75ef2411..2922f1f252d5 100644 --- a/drivers/acpi/video_detect.c +++ b/drivers/acpi/video_detect.c @@ -32,6 +32,7 @@ #include <linux/module.h> #include <linux/pci.h> #include <linux/types.h> +#include <linux/workqueue.h> #include <acpi/video.h> ACPI_MODULE_NAME("video"); @@ -41,6 +42,7 @@ void acpi_video_unregister_backlight(void); static bool backlight_notifier_registered; static struct notifier_block backlight_nb; +static struct work_struct backlight_notify_work; static enum acpi_backlight_type acpi_backlight_cmdline = acpi_backlight_undef; static enum acpi_backlight_type acpi_backlight_dmi = acpi_backlight_undef; @@ -262,6 +264,13 @@ static const struct dmi_system_id video_detect_dmi_table[] = { { }, }; +/* This uses a workqueue to avoid various locking ordering issues */ +static void acpi_video_backlight_notify_work(struct work_struct *work) +{ + if (acpi_video_get_backlight_type() != acpi_backlight_video) + acpi_video_unregister_backlight(); +} + static int acpi_video_backlight_notify(struct notifier_block *nb, unsigned long val, void *bd) { @@ -269,9 +278,8 @@ static int acpi_video_backlight_notify(struct notifier_block *nb, /* A raw bl registering may change video -> native */ if (backlight->props.type == BACKLIGHT_RAW && - val == BACKLIGHT_REGISTERED && - acpi_video_get_backlight_type() != acpi_backlight_video) - acpi_video_unregister_backlight(); + val == BACKLIGHT_REGISTERED) + schedule_work(&backlight_notify_work); return NOTIFY_OK; } @@ -304,6 +312,8 @@ enum acpi_backlight_type acpi_video_get_backlight_type(void) acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, ACPI_UINT32_MAX, find_video, NULL, &video_caps, NULL); + INIT_WORK(&backlight_notify_work, + acpi_video_backlight_notify_work); backlight_nb.notifier_call = acpi_video_backlight_notify; backlight_nb.priority = 0; if (backlight_register_notifier(&backlight_nb) == 0) diff --git a/drivers/ata/ahci_brcmstb.c b/drivers/ata/ahci_brcmstb.c index ce1e3a885981..14b7305d2ba0 100644 --- a/drivers/ata/ahci_brcmstb.c +++ b/drivers/ata/ahci_brcmstb.c @@ -92,7 +92,7 @@ static inline u32 brcm_sata_readreg(void __iomem *addr) * Other architectures (e.g., ARM) either do not support big endian, or * else leave I/O in little endian mode. */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) return __raw_readl(addr); else return readl_relaxed(addr); @@ -101,7 +101,7 @@ static inline u32 brcm_sata_readreg(void __iomem *addr) static inline void brcm_sata_writereg(u32 val, void __iomem *addr) { /* See brcm_sata_readreg() comments */ - if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(__BIG_ENDIAN)) + if (IS_ENABLED(CONFIG_MIPS) && IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) __raw_writel(val, addr); else writel_relaxed(val, addr); @@ -209,6 +209,7 @@ static void brcm_sata_init(struct brcm_ahci_priv *priv) priv->top_ctrl + SATA_TOP_CTRL_BUS_CTRL); } +#ifdef CONFIG_PM_SLEEP static int brcm_ahci_suspend(struct device *dev) { struct ata_host *host = dev_get_drvdata(dev); @@ -231,6 +232,7 @@ static int brcm_ahci_resume(struct device *dev) brcm_sata_phys_enable(priv); return ahci_platform_resume(dev); } +#endif static struct scsi_host_template ahci_platform_sht = { AHCI_SHT(DRV_NAME), diff --git a/drivers/ata/libata-core.c b/drivers/ata/libata-core.c index db5d9f79a247..790e0deb278e 100644 --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -694,11 +694,11 @@ static int ata_rwcmd_protocol(struct ata_taskfile *tf, struct ata_device *dev) * RETURNS: * Block address read from @tf. */ -u64 ata_tf_read_block(const struct ata_taskfile *tf, struct ata_device *dev) +u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev) { u64 block = 0; - if (!dev || tf->flags & ATA_TFLAG_LBA) { + if (tf->flags & ATA_TFLAG_LBA) { if (tf->flags & ATA_TFLAG_LBA48) { block |= (u64)tf->hob_lbah << 40; block |= (u64)tf->hob_lbam << 32; @@ -2147,24 +2147,6 @@ static int ata_dev_config_ncq(struct ata_device *dev, return 0; } -static void ata_dev_config_sense_reporting(struct ata_device *dev) -{ - unsigned int err_mask; - - if (!ata_id_has_sense_reporting(dev->id)) - return; - - if (ata_id_sense_reporting_enabled(dev->id)) - return; - - err_mask = ata_dev_set_feature(dev, SETFEATURE_SENSE_DATA, 0x1); - if (err_mask) { - ata_dev_dbg(dev, - "failed to enable Sense Data Reporting, Emask 0x%x\n", - err_mask); - } -} - /** * ata_dev_configure - Configure the specified ATA/ATAPI device * @dev: Target device to configure @@ -2387,7 +2369,7 @@ int ata_dev_configure(struct ata_device *dev) dev->devslp_timing[i] = sata_setting[j]; } } - ata_dev_config_sense_reporting(dev); + dev->cdb_len = 16; } @@ -4248,6 +4230,8 @@ static const struct ata_blacklist_entry ata_device_blacklist [] = { ATA_HORKAGE_ZERO_AFTER_TRIM, }, { "Samsung SSD 8*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | ATA_HORKAGE_ZERO_AFTER_TRIM, }, + { "FCCT*M500*", NULL, ATA_HORKAGE_NO_NCQ_TRIM | + ATA_HORKAGE_ZERO_AFTER_TRIM, }, /* devices that don't properly handle TRIM commands */ { "SuperSSpeed S238*", NULL, ATA_HORKAGE_NOTRIM, }, diff --git a/drivers/ata/libata-eh.c b/drivers/ata/libata-eh.c index 7465031a893c..cb0508af1459 100644 --- a/drivers/ata/libata-eh.c +++ b/drivers/ata/libata-eh.c @@ -1592,8 +1592,6 @@ static int ata_eh_read_log_10h(struct ata_device *dev, tf->hob_lbah = buf[10]; tf->nsect = buf[12]; tf->hob_nsect = buf[13]; - if (ata_id_has_ncq_autosense(dev->id)) - tf->auxiliary = buf[14] << 16 | buf[15] << 8 | buf[16]; return 0; } @@ -1630,70 +1628,6 @@ unsigned int atapi_eh_tur(struct ata_device *dev, u8 *r_sense_key) } /** - * ata_eh_request_sense - perform REQUEST_SENSE_DATA_EXT - * @dev: device to perform REQUEST_SENSE_SENSE_DATA_EXT to - * @sense_buf: result sense data buffer (SCSI_SENSE_BUFFERSIZE bytes long) - * @dfl_sense_key: default sense key to use - * - * Perform REQUEST_SENSE_DATA_EXT after the device reported CHECK - * SENSE. This function is EH helper. - * - * LOCKING: - * Kernel thread context (may sleep). - * - * RETURNS: - * encoded sense data on success, 0 on failure or if sense data - * is not available. - */ -static u32 ata_eh_request_sense(struct ata_queued_cmd *qc, - struct scsi_cmnd *cmd) -{ - struct ata_device *dev = qc->dev; - struct ata_taskfile tf; - unsigned int err_mask; - - if (!cmd) - return 0; - - DPRINTK("ATA request sense\n"); - ata_dev_warn(dev, "request sense\n"); - if (!ata_id_sense_reporting_enabled(dev->id)) { - ata_dev_warn(qc->dev, "sense data reporting disabled\n"); - return 0; - } - ata_tf_init(dev, &tf); - - tf.flags |= ATA_TFLAG_ISADDR | ATA_TFLAG_DEVICE; - tf.flags |= ATA_TFLAG_LBA | ATA_TFLAG_LBA48; - tf.command = ATA_CMD_REQ_SENSE_DATA; - tf.protocol = ATA_PROT_NODATA; - - err_mask = ata_exec_internal(dev, &tf, NULL, DMA_NONE, NULL, 0, 0); - /* - * ACS-4 states: - * The device may set the SENSE DATA AVAILABLE bit to one in the - * STATUS field and clear the ERROR bit to zero in the STATUS field - * to indicate that the command returned completion without an error - * and the sense data described in table 306 is available. - * - * IOW the 'ATA_SENSE' bit might not be set even though valid - * sense data is available. - * So check for both. - */ - if ((tf.command & ATA_SENSE) || - tf.lbah != 0 || tf.lbam != 0 || tf.lbal != 0) { - ata_scsi_set_sense(cmd, tf.lbah, tf.lbam, tf.lbal); - qc->flags |= ATA_QCFLAG_SENSE_VALID; - ata_dev_warn(dev, "sense data %02x/%02x/%02x\n", - tf.lbah, tf.lbam, tf.lbal); - } else { - ata_dev_warn(dev, "request sense failed stat %02x emask %x\n", - tf.command, err_mask); - } - return err_mask; -} - -/** * atapi_eh_request_sense - perform ATAPI REQUEST_SENSE * @dev: device to perform REQUEST_SENSE to * @sense_buf: result sense data buffer (SCSI_SENSE_BUFFERSIZE bytes long) @@ -1855,19 +1789,6 @@ void ata_eh_analyze_ncq_error(struct ata_link *link) memcpy(&qc->result_tf, &tf, sizeof(tf)); qc->result_tf.flags = ATA_TFLAG_ISADDR | ATA_TFLAG_LBA | ATA_TFLAG_LBA48; qc->err_mask |= AC_ERR_DEV | AC_ERR_NCQ; - if (qc->result_tf.auxiliary) { - char sense_key, asc, ascq; - - sense_key = (qc->result_tf.auxiliary >> 16) & 0xff; - asc = (qc->result_tf.auxiliary >> 8) & 0xff; - ascq = qc->result_tf.auxiliary & 0xff; - ata_dev_dbg(dev, "NCQ Autosense %02x/%02x/%02x\n", - sense_key, asc, ascq); - ata_scsi_set_sense(qc->scsicmd, sense_key, asc, ascq); - ata_scsi_set_sense_information(qc->scsicmd, &qc->result_tf); - qc->flags |= ATA_QCFLAG_SENSE_VALID; - } - ehc->i.err_mask &= ~AC_ERR_DEV; } @@ -1897,27 +1818,6 @@ static unsigned int ata_eh_analyze_tf(struct ata_queued_cmd *qc, return ATA_EH_RESET; } - /* - * Sense data reporting does not work if the - * device fault bit is set. - */ - if ((stat & ATA_SENSE) && !(stat & ATA_DF) && - !(qc->flags & ATA_QCFLAG_SENSE_VALID)) { - if (!(qc->ap->pflags & ATA_PFLAG_FROZEN)) { - tmp = ata_eh_request_sense(qc, qc->scsicmd); - if (tmp) - qc->err_mask |= tmp; - else - ata_scsi_set_sense_information(qc->scsicmd, tf); - } else { - ata_dev_warn(qc->dev, "sense data available but port frozen\n"); - } - } - - /* Set by NCQ autosense or request sense above */ - if (qc->flags & ATA_QCFLAG_SENSE_VALID) - return 0; - if (stat & (ATA_ERR | ATA_DF)) qc->err_mask |= AC_ERR_DEV; else @@ -2661,15 +2561,14 @@ static void ata_eh_link_report(struct ata_link *link) #ifdef CONFIG_ATA_VERBOSE_ERROR if (res->command & (ATA_BUSY | ATA_DRDY | ATA_DF | ATA_DRQ | - ATA_SENSE | ATA_ERR)) { + ATA_ERR)) { if (res->command & ATA_BUSY) ata_dev_err(qc->dev, "status: { Busy }\n"); else - ata_dev_err(qc->dev, "status: { %s%s%s%s%s}\n", + ata_dev_err(qc->dev, "status: { %s%s%s%s}\n", res->command & ATA_DRDY ? "DRDY " : "", res->command & ATA_DF ? "DF " : "", res->command & ATA_DRQ ? "DRQ " : "", - res->command & ATA_SENSE ? "SENSE " : "", res->command & ATA_ERR ? "ERR " : ""); } diff --git a/drivers/ata/libata-scsi.c b/drivers/ata/libata-scsi.c index 641a61a59e89..0d7f0da3a269 100644 --- a/drivers/ata/libata-scsi.c +++ b/drivers/ata/libata-scsi.c @@ -270,28 +270,13 @@ DEVICE_ATTR(unload_heads, S_IRUGO | S_IWUSR, ata_scsi_park_show, ata_scsi_park_store); EXPORT_SYMBOL_GPL(dev_attr_unload_heads); -void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) +static void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq) { - if (!cmd) - return; - cmd->result = (DRIVER_SENSE << 24) | SAM_STAT_CHECK_CONDITION; scsi_build_sense_buffer(0, cmd->sense_buffer, sk, asc, ascq); } -void ata_scsi_set_sense_information(struct scsi_cmnd *cmd, - const struct ata_taskfile *tf) -{ - u64 information; - - if (!cmd) - return; - - information = ata_tf_read_block(tf, NULL); - scsi_set_sense_information(cmd->sense_buffer, information); -} - static ssize_t ata_scsi_em_message_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1792,9 +1777,7 @@ static void ata_scsi_qc_complete(struct ata_queued_cmd *qc) ((cdb[2] & 0x20) || need_sense)) { ata_gen_passthru_sense(qc); } else { - if (qc->flags & ATA_QCFLAG_SENSE_VALID) { - cmd->result = SAM_STAT_CHECK_CONDITION; - } else if (!need_sense) { + if (!need_sense) { cmd->result = SAM_STAT_GOOD; } else { /* TODO: decide which descriptor format to use diff --git a/drivers/ata/libata.h b/drivers/ata/libata.h index a998a175f9f1..f840ca18a7c0 100644 --- a/drivers/ata/libata.h +++ b/drivers/ata/libata.h @@ -67,8 +67,7 @@ extern struct ata_queued_cmd *ata_qc_new_init(struct ata_device *dev, int tag); extern int ata_build_rw_tf(struct ata_taskfile *tf, struct ata_device *dev, u64 block, u32 n_block, unsigned int tf_flags, unsigned int tag); -extern u64 ata_tf_read_block(const struct ata_taskfile *tf, - struct ata_device *dev); +extern u64 ata_tf_read_block(struct ata_taskfile *tf, struct ata_device *dev); extern unsigned ata_exec_internal(struct ata_device *dev, struct ata_taskfile *tf, const u8 *cdb, int dma_dir, void *buf, unsigned int buflen, @@ -138,9 +137,6 @@ extern int ata_scsi_add_hosts(struct ata_host *host, struct scsi_host_template *sht); extern void ata_scsi_scan_host(struct ata_port *ap, int sync); extern int ata_scsi_offline_dev(struct ata_device *dev); -extern void ata_scsi_set_sense(struct scsi_cmnd *cmd, u8 sk, u8 asc, u8 ascq); -extern void ata_scsi_set_sense_information(struct scsi_cmnd *cmd, - const struct ata_taskfile *tf); extern void ata_scsi_media_change_notify(struct ata_device *dev); extern void ata_scsi_hotplug(struct work_struct *work); extern void ata_schedule_scsi_eh(struct Scsi_Host *shost); diff --git a/drivers/ata/sata_sx4.c b/drivers/ata/sata_sx4.c index 3a18a8a719b4..fab504fd9cfd 100644 --- a/drivers/ata/sata_sx4.c +++ b/drivers/ata/sata_sx4.c @@ -1238,8 +1238,12 @@ static unsigned int pdc20621_prog_dimm_global(struct ata_host *host) readl(mmio + PDC_SDRAM_CONTROL); /* Turn on for ECC */ - pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, - PDC_DIMM_SPD_TYPE, &spd0); + if (!pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, + PDC_DIMM_SPD_TYPE, &spd0)) { + pr_err("Failed in i2c read: device=%#x, subaddr=%#x\n", + PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE); + return 1; + } if (spd0 == 0x02) { data |= (0x01 << 16); writel(data, mmio + PDC_SDRAM_CONTROL); @@ -1380,8 +1384,12 @@ static unsigned int pdc20621_dimm_init(struct ata_host *host) /* ECC initiliazation. */ - pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, - PDC_DIMM_SPD_TYPE, &spd0); + if (!pdc20621_i2c_read(host, PDC_DIMM0_SPD_DEV_ADDRESS, + PDC_DIMM_SPD_TYPE, &spd0)) { + pr_err("Failed in i2c read: device=%#x, subaddr=%#x\n", + PDC_DIMM0_SPD_DEV_ADDRESS, PDC_DIMM_SPD_TYPE); + return 1; + } if (spd0 == 0x02) { void *buf; VPRINTK("Start ECC initialization\n"); diff --git a/drivers/auxdisplay/ks0108.c b/drivers/auxdisplay/ks0108.c index 5b93852392b8..816de9eaac26 100644 --- a/drivers/auxdisplay/ks0108.c +++ b/drivers/auxdisplay/ks0108.c @@ -23,6 +23,8 @@ * */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + #include <linux/init.h> #include <linux/module.h> #include <linux/kernel.h> @@ -90,17 +92,19 @@ void ks0108_displaystate(unsigned char state) void ks0108_startline(unsigned char startline) { - ks0108_writedata(min(startline,(unsigned char)63) | bit(6) | bit(7)); + ks0108_writedata(min_t(unsigned char, startline, 63) | bit(6) | + bit(7)); } void ks0108_address(unsigned char address) { - ks0108_writedata(min(address,(unsigned char)63) | bit(6)); + ks0108_writedata(min_t(unsigned char, address, 63) | bit(6)); } void ks0108_page(unsigned char page) { - ks0108_writedata(min(page,(unsigned char)7) | bit(3) | bit(4) | bit(5) | bit(7)); + ks0108_writedata(min_t(unsigned char, page, 7) | bit(3) | bit(4) | + bit(5) | bit(7)); } EXPORT_SYMBOL_GPL(ks0108_writedata); @@ -121,52 +125,71 @@ unsigned char ks0108_isinited(void) } EXPORT_SYMBOL_GPL(ks0108_isinited); -/* - * Module Init & Exit - */ - -static int __init ks0108_init(void) +static void ks0108_parport_attach(struct parport *port) { - int result; - int ret = -EINVAL; - - ks0108_parport = parport_find_base(ks0108_port); - if (ks0108_parport == NULL) { - printk(KERN_ERR KS0108_NAME ": ERROR: " - "parport didn't find %i port\n", ks0108_port); - goto none; - } - - ks0108_pardevice = parport_register_device(ks0108_parport, KS0108_NAME, - NULL, NULL, NULL, PARPORT_DEV_EXCL, NULL); - if (ks0108_pardevice == NULL) { - printk(KERN_ERR KS0108_NAME ": ERROR: " - "parport didn't register new device\n"); - goto none; + struct pardev_cb ks0108_cb; + + if (port->base != ks0108_port) + return; + + memset(&ks0108_cb, 0, sizeof(ks0108_cb)); + ks0108_cb.flags = PARPORT_DEV_EXCL; + ks0108_pardevice = parport_register_dev_model(port, KS0108_NAME, + &ks0108_cb, 0); + if (!ks0108_pardevice) { + pr_err("ERROR: parport didn't register new device\n"); + return; } - - result = parport_claim(ks0108_pardevice); - if (result != 0) { - printk(KERN_ERR KS0108_NAME ": ERROR: " - "can't claim %i parport, maybe in use\n", ks0108_port); - ret = result; - goto registered; + if (parport_claim(ks0108_pardevice)) { + pr_err("could not claim access to parport %i. Aborting.\n", + ks0108_port); + goto err_unreg_device; } + ks0108_parport = port; ks0108_inited = 1; - return 0; + return; -registered: +err_unreg_device: parport_unregister_device(ks0108_pardevice); - -none: - return ret; + ks0108_pardevice = NULL; } -static void __exit ks0108_exit(void) +static void ks0108_parport_detach(struct parport *port) { + if (port->base != ks0108_port) + return; + + if (!ks0108_pardevice) { + pr_err("%s: already unregistered.\n", KS0108_NAME); + return; + } + parport_release(ks0108_pardevice); parport_unregister_device(ks0108_pardevice); + ks0108_pardevice = NULL; + ks0108_parport = NULL; +} + +/* + * Module Init & Exit + */ + +static struct parport_driver ks0108_parport_driver = { + .name = "ks0108", + .match_port = ks0108_parport_attach, + .detach = ks0108_parport_detach, + .devmodel = true, +}; + +static int __init ks0108_init(void) +{ + return parport_register_driver(&ks0108_parport_driver); +} + +static void __exit ks0108_exit(void) +{ + parport_unregister_driver(&ks0108_parport_driver); } module_init(ks0108_init); diff --git a/drivers/base/base.h b/drivers/base/base.h index fd3347d9f153..1782f3aa386e 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -63,7 +63,7 @@ struct driver_private { * binding of drivers which were unable to get all the resources needed by * the device; typically because it depends on another driver getting * probed first. - * @device - pointer back to the struct class that this structure is + * @device - pointer back to the struct device that this structure is * associated with. * * Nothing outside of the driver core should ever touch these fields. @@ -134,6 +134,7 @@ extern int devres_release_all(struct device *dev); /* /sys/devices directory */ extern struct kset *devices_kset; +extern void devices_kset_move_last(struct device *dev); #if defined(CONFIG_MODULES) && defined(CONFIG_SYSFS) extern void module_add_driver(struct module *mod, struct device_driver *drv); diff --git a/drivers/base/core.c b/drivers/base/core.c index dafae6d2f7ac..fc5a558f62f9 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -534,6 +534,52 @@ static DEVICE_ATTR_RO(dev); struct kset *devices_kset; /** + * devices_kset_move_before - Move device in the devices_kset's list. + * @deva: Device to move. + * @devb: Device @deva should come before. + */ +static void devices_kset_move_before(struct device *deva, struct device *devb) +{ + if (!devices_kset) + return; + pr_debug("devices_kset: Moving %s before %s\n", + dev_name(deva), dev_name(devb)); + spin_lock(&devices_kset->list_lock); + list_move_tail(&deva->kobj.entry, &devb->kobj.entry); + spin_unlock(&devices_kset->list_lock); +} + +/** + * devices_kset_move_after - Move device in the devices_kset's list. + * @deva: Device to move + * @devb: Device @deva should come after. + */ +static void devices_kset_move_after(struct device *deva, struct device *devb) +{ + if (!devices_kset) + return; + pr_debug("devices_kset: Moving %s after %s\n", + dev_name(deva), dev_name(devb)); + spin_lock(&devices_kset->list_lock); + list_move(&deva->kobj.entry, &devb->kobj.entry); + spin_unlock(&devices_kset->list_lock); +} + +/** + * devices_kset_move_last - move the device to the end of devices_kset's list. + * @dev: device to move + */ +void devices_kset_move_last(struct device *dev) +{ + if (!devices_kset) + return; + pr_debug("devices_kset: Moving %s to end of list\n", dev_name(dev)); + spin_lock(&devices_kset->list_lock); + list_move_tail(&dev->kobj.entry, &devices_kset->list); + spin_unlock(&devices_kset->list_lock); +} + +/** * device_create_file - create sysfs attribute file for device. * @dev: device. * @attr: device attribute descriptor. @@ -1923,12 +1969,15 @@ int device_move(struct device *dev, struct device *new_parent, break; case DPM_ORDER_DEV_AFTER_PARENT: device_pm_move_after(dev, new_parent); + devices_kset_move_after(dev, new_parent); break; case DPM_ORDER_PARENT_BEFORE_DEV: device_pm_move_before(new_parent, dev); + devices_kset_move_before(new_parent, dev); break; case DPM_ORDER_DEV_LAST: device_pm_move_last(dev); + devices_kset_move_last(dev); break; } diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index 78720e706176..91bbb1959d8d 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -41,7 +41,7 @@ static void change_cpu_under_node(struct cpu *cpu, cpu->node_id = to_nid; } -static int __ref cpu_subsys_online(struct device *dev) +static int cpu_subsys_online(struct device *dev) { struct cpu *cpu = container_of(dev, struct cpu, dev); int cpuid = dev->id; diff --git a/drivers/base/dd.c b/drivers/base/dd.c index a638bbb1a27a..cc2b1d4801fd 100644 --- a/drivers/base/dd.c +++ b/drivers/base/dd.c @@ -304,6 +304,14 @@ static int really_probe(struct device *dev, struct device_driver *drv) goto probe_failed; } + /* + * Ensure devices are listed in devices_kset in correct order + * It's important to move Dev to the end of devices_kset before + * calling .probe, because it could be recursive and parent Dev + * should always go first + */ + devices_kset_move_last(dev); + if (dev->bus->probe) { ret = dev->bus->probe(dev); if (ret) diff --git a/drivers/base/devres.c b/drivers/base/devres.c index c8a53d1e019f..875464690117 100644 --- a/drivers/base/devres.c +++ b/drivers/base/devres.c @@ -297,10 +297,10 @@ void * devres_get(struct device *dev, void *new_res, if (!dr) { add_dr(dev, &new_dr->node); dr = new_dr; - new_dr = NULL; + new_res = NULL; } spin_unlock_irqrestore(&dev->devres_lock, flags); - devres_free(new_dr); + devres_free(new_res); return dr->data; } diff --git a/drivers/base/firmware_class.c b/drivers/base/firmware_class.c index 894bda114224..8524450e75bd 100644 --- a/drivers/base/firmware_class.c +++ b/drivers/base/firmware_class.c @@ -443,7 +443,7 @@ static int fw_add_devm_name(struct device *dev, const char *name) return -ENOMEM; fwn->name = kstrdup_const(name, GFP_KERNEL); if (!fwn->name) { - kfree(fwn); + devres_free(fwn); return -ENOMEM; } diff --git a/drivers/base/regmap/regcache-rbtree.c b/drivers/base/regmap/regcache-rbtree.c index 81751a49d8bf..56486d92c4e7 100644 --- a/drivers/base/regmap/regcache-rbtree.c +++ b/drivers/base/regmap/regcache-rbtree.c @@ -296,11 +296,20 @@ static int regcache_rbtree_insert_to_block(struct regmap *map, if (!blk) return -ENOMEM; - present = krealloc(rbnode->cache_present, - BITS_TO_LONGS(blklen) * sizeof(*present), GFP_KERNEL); - if (!present) { - kfree(blk); - return -ENOMEM; + if (BITS_TO_LONGS(blklen) > BITS_TO_LONGS(rbnode->blklen)) { + present = krealloc(rbnode->cache_present, + BITS_TO_LONGS(blklen) * sizeof(*present), + GFP_KERNEL); + if (!present) { + kfree(blk); + return -ENOMEM; + } + + memset(present + BITS_TO_LONGS(rbnode->blklen), 0, + (BITS_TO_LONGS(blklen) - BITS_TO_LONGS(rbnode->blklen)) + * sizeof(*present)); + } else { + present = rbnode->cache_present; } /* insert the register value in the correct place in the rbnode block */ diff --git a/drivers/block/mtip32xx/mtip32xx.c b/drivers/block/mtip32xx/mtip32xx.c index 4a2ef09e6704..f504232c1ee7 100644 --- a/drivers/block/mtip32xx/mtip32xx.c +++ b/drivers/block/mtip32xx/mtip32xx.c @@ -3756,6 +3756,14 @@ static int mtip_init_cmd(void *data, struct request *rq, unsigned int hctx_idx, struct mtip_cmd *cmd = blk_mq_rq_to_pdu(rq); u32 host_cap_64 = readl(dd->mmio + HOST_CAP) & HOST_CAP_64; + /* + * For flush requests, request_idx starts at the end of the + * tag space. Since we don't support FLUSH/FUA, simply return + * 0 as there's nothing to be done. + */ + if (request_idx >= MTIP_MAX_COMMAND_SLOTS) + return 0; + cmd->command = dmam_alloc_coherent(&dd->pdev->dev, CMD_DMA_ALLOC_SZ, &cmd->command_dma, GFP_KERNEL); if (!cmd->command) diff --git a/drivers/block/xen-blkback/blkback.c b/drivers/block/xen-blkback/blkback.c index ced96777b677..954c0029fb3b 100644 --- a/drivers/block/xen-blkback/blkback.c +++ b/drivers/block/xen-blkback/blkback.c @@ -369,8 +369,8 @@ static void purge_persistent_gnt(struct xen_blkif *blkif) return; } - if (work_pending(&blkif->persistent_purge_work)) { - pr_alert_ratelimited("Scheduled work from previous purge is still pending, cannot purge list\n"); + if (work_busy(&blkif->persistent_purge_work)) { + pr_alert_ratelimited("Scheduled work from previous purge is still busy, cannot purge list\n"); return; } diff --git a/drivers/block/xen-blkfront.c b/drivers/block/xen-blkfront.c index 6d89ed35d80c..7a8a73f1fc04 100644 --- a/drivers/block/xen-blkfront.c +++ b/drivers/block/xen-blkfront.c @@ -179,6 +179,7 @@ static DEFINE_SPINLOCK(minor_lock); ((_segs + SEGS_PER_INDIRECT_FRAME - 1)/SEGS_PER_INDIRECT_FRAME) static int blkfront_setup_indirect(struct blkfront_info *info); +static int blkfront_gather_backend_features(struct blkfront_info *info); static int get_id_from_freelist(struct blkfront_info *info) { @@ -1128,8 +1129,10 @@ static void blkif_completion(struct blk_shadow *s, struct blkfront_info *info, * Add the used indirect page back to the list of * available pages for indirect grefs. */ - indirect_page = pfn_to_page(s->indirect_grants[i]->pfn); - list_add(&indirect_page->lru, &info->indirect_pages); + if (!info->feature_persistent) { + indirect_page = pfn_to_page(s->indirect_grants[i]->pfn); + list_add(&indirect_page->lru, &info->indirect_pages); + } s->indirect_grants[i]->gref = GRANT_INVALID_REF; list_add_tail(&s->indirect_grants[i]->node, &info->grants); } @@ -1519,7 +1522,7 @@ static int blkif_recover(struct blkfront_info *info) info->shadow_free = info->ring.req_prod_pvt; info->shadow[BLK_RING_SIZE(info)-1].req.u.rw.id = 0x0fffffff; - rc = blkfront_setup_indirect(info); + rc = blkfront_gather_backend_features(info); if (rc) { kfree(copy); return rc; @@ -1720,20 +1723,13 @@ static void blkfront_setup_discard(struct blkfront_info *info) static int blkfront_setup_indirect(struct blkfront_info *info) { - unsigned int indirect_segments, segs; + unsigned int segs; int err, i; - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, - "feature-max-indirect-segments", "%u", &indirect_segments, - NULL); - if (err) { - info->max_indirect_segments = 0; + if (info->max_indirect_segments == 0) segs = BLKIF_MAX_SEGMENTS_PER_REQUEST; - } else { - info->max_indirect_segments = min(indirect_segments, - xen_blkif_max_segments); + else segs = info->max_indirect_segments; - } err = fill_grant_buffer(info, (segs + INDIRECT_GREFS(segs)) * BLK_RING_SIZE(info)); if (err) @@ -1797,6 +1793,68 @@ out_of_memory: } /* + * Gather all backend feature-* + */ +static int blkfront_gather_backend_features(struct blkfront_info *info) +{ + int err; + int barrier, flush, discard, persistent; + unsigned int indirect_segments; + + info->feature_flush = 0; + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-barrier", "%d", &barrier, + NULL); + + /* + * If there's no "feature-barrier" defined, then it means + * we're dealing with a very old backend which writes + * synchronously; nothing to do. + * + * If there are barriers, then we use flush. + */ + if (!err && barrier) + info->feature_flush = REQ_FLUSH | REQ_FUA; + /* + * And if there is "feature-flush-cache" use that above + * barriers. + */ + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-flush-cache", "%d", &flush, + NULL); + + if (!err && flush) + info->feature_flush = REQ_FLUSH; + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-discard", "%d", &discard, + NULL); + + if (!err && discard) + blkfront_setup_discard(info); + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-persistent", "%u", &persistent, + NULL); + if (err) + info->feature_persistent = 0; + else + info->feature_persistent = persistent; + + err = xenbus_gather(XBT_NIL, info->xbdev->otherend, + "feature-max-indirect-segments", "%u", &indirect_segments, + NULL); + if (err) + info->max_indirect_segments = 0; + else + info->max_indirect_segments = min(indirect_segments, + xen_blkif_max_segments); + + return blkfront_setup_indirect(info); +} + +/* * Invoked when the backend is finally 'ready' (and has told produced * the details about the physical device - #sectors, size, etc). */ @@ -1807,7 +1865,6 @@ static void blkfront_connect(struct blkfront_info *info) unsigned int physical_sector_size; unsigned int binfo; int err; - int barrier, flush, discard, persistent; switch (info->connected) { case BLKIF_STATE_CONNECTED: @@ -1864,48 +1921,7 @@ static void blkfront_connect(struct blkfront_info *info) if (err != 1) physical_sector_size = sector_size; - info->feature_flush = 0; - - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, - "feature-barrier", "%d", &barrier, - NULL); - - /* - * If there's no "feature-barrier" defined, then it means - * we're dealing with a very old backend which writes - * synchronously; nothing to do. - * - * If there are barriers, then we use flush. - */ - if (!err && barrier) - info->feature_flush = REQ_FLUSH | REQ_FUA; - /* - * And if there is "feature-flush-cache" use that above - * barriers. - */ - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, - "feature-flush-cache", "%d", &flush, - NULL); - - if (!err && flush) - info->feature_flush = REQ_FLUSH; - - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, - "feature-discard", "%d", &discard, - NULL); - - if (!err && discard) - blkfront_setup_discard(info); - - err = xenbus_gather(XBT_NIL, info->xbdev->otherend, - "feature-persistent", "%u", &persistent, - NULL); - if (err) - info->feature_persistent = 0; - else - info->feature_persistent = persistent; - - err = blkfront_setup_indirect(info); + err = blkfront_gather_backend_features(info); if (err) { xenbus_dev_fatal(info->xbdev, err, "setup_indirect at %s", info->xbdev->otherend); diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index fb655e8d1e3b..763301c7828c 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -496,10 +496,9 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize) kfree(meta); } -static struct zram_meta *zram_meta_alloc(int device_id, u64 disksize) +static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize) { size_t num_pages; - char pool_name[8]; struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); if (!meta) @@ -512,7 +511,6 @@ static struct zram_meta *zram_meta_alloc(int device_id, u64 disksize) goto out_error; } - snprintf(pool_name, sizeof(pool_name), "zram%d", device_id); meta->mem_pool = zs_create_pool(pool_name, GFP_NOIO | __GFP_HIGHMEM); if (!meta->mem_pool) { pr_err("Error creating memory pool\n"); @@ -1031,7 +1029,7 @@ static ssize_t disksize_store(struct device *dev, return -EINVAL; disksize = PAGE_ALIGN(disksize); - meta = zram_meta_alloc(zram->disk->first_minor, disksize); + meta = zram_meta_alloc(zram->disk->disk_name, disksize); if (!meta) return -ENOMEM; diff --git a/drivers/char/misc.c b/drivers/char/misc.c index fdb0f9b3fe45..8069b361b8dd 100644 --- a/drivers/char/misc.c +++ b/drivers/char/misc.c @@ -243,17 +243,15 @@ int misc_register(struct miscdevice * misc) * @misc: device to unregister * * Unregister a miscellaneous device that was previously - * successfully registered with misc_register(). Success - * is indicated by a zero return, a negative errno code - * indicates an error. + * successfully registered with misc_register(). */ -int misc_deregister(struct miscdevice *misc) +void misc_deregister(struct miscdevice *misc) { int i = DYNAMIC_MINORS - misc->minor - 1; if (WARN_ON(list_empty(&misc->list))) - return -EINVAL; + return; mutex_lock(&misc_mtx); list_del(&misc->list); @@ -261,7 +259,6 @@ int misc_deregister(struct miscdevice *misc) if (i < DYNAMIC_MINORS && i >= 0) clear_bit(i, misc_minors); mutex_unlock(&misc_mtx); - return 0; } EXPORT_SYMBOL(misc_register); @@ -281,10 +278,9 @@ static char *misc_devnode(struct device *dev, umode_t *mode) static int __init misc_init(void) { int err; + struct proc_dir_entry *ret; -#ifdef CONFIG_PROC_FS - proc_create("misc", 0, NULL, &misc_proc_fops); -#endif + ret = proc_create("misc", 0, NULL, &misc_proc_fops); misc_class = class_create(THIS_MODULE, "misc"); err = PTR_ERR(misc_class); if (IS_ERR(misc_class)) @@ -300,7 +296,8 @@ fail_printk: printk("unable to get major %d for misc devices\n", MISC_MAJOR); class_destroy(misc_class); fail_remove: - remove_proc_entry("misc", NULL); + if (ret) + remove_proc_entry("misc", NULL); return err; } subsys_initcall(misc_init); diff --git a/drivers/char/nvram.c b/drivers/char/nvram.c index 9df78e2cc45d..97c2d8d433d6 100644 --- a/drivers/char/nvram.c +++ b/drivers/char/nvram.c @@ -702,7 +702,7 @@ static void atari_proc_infos(unsigned char *nvram, struct seq_file *seq, seq_printf(seq, "%ds%s\n", nvram[10], nvram[10] < 8 ? ", no memory test" : ""); - vmode = (nvram[14] << 8) || nvram[15]; + vmode = (nvram[14] << 8) | nvram[15]; seq_printf(seq, "Video mode : %s colors, %d columns, %s %s monitor\n", colors[vmode & 7], diff --git a/drivers/char/toshiba.c b/drivers/char/toshiba.c index 014c9d90d297..f5a45d887a37 100644 --- a/drivers/char/toshiba.c +++ b/drivers/char/toshiba.c @@ -430,7 +430,7 @@ static int tosh_probe(void) int i,major,minor,day,year,month,flag; unsigned char signature[7] = { 0x54,0x4f,0x53,0x48,0x49,0x42,0x41 }; SMMRegisters regs; - void __iomem *bios = ioremap_cache(0xf0000, 0x10000); + void __iomem *bios = ioremap(0xf0000, 0x10000); if (!bios) return -ENOMEM; diff --git a/drivers/char/xillybus/xillybus_pcie.c b/drivers/char/xillybus/xillybus_pcie.c index d8266bc2ae35..9418300214e9 100644 --- a/drivers/char/xillybus/xillybus_pcie.c +++ b/drivers/char/xillybus/xillybus_pcie.c @@ -193,14 +193,16 @@ static int xilly_probe(struct pci_dev *pdev, } /* - * In theory, an attempt to set the DMA mask to 64 and dma_using_dac=1 - * is the right thing. But some unclever PCIe drivers report it's OK - * when the hardware drops those 64-bit PCIe packets. So trust - * nobody and use 32 bits DMA addressing in any case. + * Some (old and buggy?) hardware drops 64-bit addressed PCIe packets, + * even when the PCIe driver claims that a 64-bit mask is OK. On the + * other hand, on some architectures, 64-bit addressing is mandatory. + * So go for the 64-bit mask only when failing is the other option. */ if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) { endpoint->dma_using_dac = 0; + } else if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { + endpoint->dma_using_dac = 1; } else { dev_err(endpoint->dev, "Failed to set DMA mask. Aborting.\n"); return -ENODEV; diff --git a/drivers/clk/pxa/clk-pxa3xx.c b/drivers/clk/pxa/clk-pxa3xx.c index 4b93a1efb36d..ac03ba49e9d1 100644 --- a/drivers/clk/pxa/clk-pxa3xx.c +++ b/drivers/clk/pxa/clk-pxa3xx.c @@ -126,7 +126,7 @@ PARENTS(pxa3xx_ac97_bus) = { "ring_osc_60mhz", "ac97" }; PARENTS(pxa3xx_sbus) = { "ring_osc_60mhz", "system_bus" }; PARENTS(pxa3xx_smemcbus) = { "ring_osc_60mhz", "smemc" }; -#define CKEN_AB(bit) ((CKEN_ ## bit > 31) ? &CKENA : &CKENB) +#define CKEN_AB(bit) ((CKEN_ ## bit > 31) ? &CKENB : &CKENA) #define PXA3XX_CKEN(dev_id, con_id, parents, mult_lp, div_lp, mult_hp, \ div_hp, bit, is_lp, flags) \ PXA_CKEN(dev_id, con_id, bit, parents, mult_lp, div_lp, \ diff --git a/drivers/clocksource/sh_cmt.c b/drivers/clocksource/sh_cmt.c index b8ff3c64cc45..c96de14036a0 100644 --- a/drivers/clocksource/sh_cmt.c +++ b/drivers/clocksource/sh_cmt.c @@ -661,6 +661,9 @@ static void sh_cmt_clocksource_suspend(struct clocksource *cs) { struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); + if (!ch->cs_enabled) + return; + sh_cmt_stop(ch, FLAG_CLOCKSOURCE); pm_genpd_syscore_poweroff(&ch->cmt->pdev->dev); } @@ -669,6 +672,9 @@ static void sh_cmt_clocksource_resume(struct clocksource *cs) { struct sh_cmt_channel *ch = cs_to_sh_cmt(cs); + if (!ch->cs_enabled) + return; + pm_genpd_syscore_poweron(&ch->cmt->pdev->dev); sh_cmt_start(ch, FLAG_CLOCKSOURCE); } diff --git a/drivers/clocksource/timer-imx-gpt.c b/drivers/clocksource/timer-imx-gpt.c index 2d59038dec43..86c7eb66bdfb 100644 --- a/drivers/clocksource/timer-imx-gpt.c +++ b/drivers/clocksource/timer-imx-gpt.c @@ -462,6 +462,7 @@ void __init mxc_timer_init(unsigned long pbase, int irq, enum imx_gpt_type type) BUG_ON(!imxtm->base); imxtm->type = type; + imxtm->irq = irq; _mxc_timer_init(imxtm); } diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 7a3c30c4336f..9bb09ce98d04 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1521,13 +1521,13 @@ static int __cpufreq_remove_dev_finish(struct device *dev) * * Removes the cpufreq interface for a CPU device. */ -static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) +static void cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) { unsigned int cpu = dev->id; struct cpufreq_policy *policy = per_cpu(cpufreq_cpu_data, cpu); if (!policy) - return 0; + return; if (cpu_online(cpu)) { __cpufreq_remove_dev_prepare(dev); @@ -1538,7 +1538,7 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) if (cpumask_empty(policy->real_cpus)) { cpufreq_policy_free(policy, true); - return 0; + return; } if (cpu != policy->kobj_cpu) { @@ -1557,8 +1557,6 @@ static int cpufreq_remove_dev(struct device *dev, struct subsys_interface *sif) policy->kobj_cpu = new_cpu; WARN_ON(kobject_move(&policy->kobj, &new_dev->kobj)); } - - return 0; } static void handle_update(struct work_struct *work) diff --git a/drivers/cpufreq/exynos-cpufreq.c b/drivers/cpufreq/exynos-cpufreq.c index ae5b2bd3a978..fa3dd840a837 100644 --- a/drivers/cpufreq/exynos-cpufreq.c +++ b/drivers/cpufreq/exynos-cpufreq.c @@ -180,7 +180,7 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) ret = exynos5250_cpufreq_init(exynos_info); } else { pr_err("%s: Unknown SoC type\n", __func__); - return -ENODEV; + ret = -ENODEV; } if (ret) @@ -188,12 +188,14 @@ static int exynos_cpufreq_probe(struct platform_device *pdev) if (exynos_info->set_freq == NULL) { dev_err(&pdev->dev, "No set_freq function (ERR)\n"); + ret = -EINVAL; goto err_vdd_arm; } arm_regulator = regulator_get(NULL, "vdd_arm"); if (IS_ERR(arm_regulator)) { dev_err(&pdev->dev, "failed to get resource vdd_arm\n"); + ret = -EINVAL; goto err_vdd_arm; } @@ -225,7 +227,7 @@ err_cpufreq_reg: regulator_put(arm_regulator); err_vdd_arm: kfree(exynos_info); - return -EINVAL; + return ret; } static struct platform_driver exynos_cpufreq_platdrv = { diff --git a/drivers/crypto/caam/caamhash.c b/drivers/crypto/caam/caamhash.c index dae1e8099969..f9c78751989e 100644 --- a/drivers/crypto/caam/caamhash.c +++ b/drivers/crypto/caam/caamhash.c @@ -909,13 +909,14 @@ static int ahash_final_ctx(struct ahash_request *req) state->buflen_1; u32 *sh_desc = ctx->sh_desc_fin, *desc; dma_addr_t ptr = ctx->sh_desc_fin_dma; - int sec4_sg_bytes; + int sec4_sg_bytes, sec4_sg_src_index; int digestsize = crypto_ahash_digestsize(ahash); struct ahash_edesc *edesc; int ret = 0; int sh_len; - sec4_sg_bytes = (1 + (buflen ? 1 : 0)) * sizeof(struct sec4_sg_entry); + sec4_sg_src_index = 1 + (buflen ? 1 : 0); + sec4_sg_bytes = sec4_sg_src_index * sizeof(struct sec4_sg_entry); /* allocate space for base edesc and hw desc commands, link tables */ edesc = kmalloc(sizeof(struct ahash_edesc) + DESC_JOB_IO_LEN + @@ -942,7 +943,7 @@ static int ahash_final_ctx(struct ahash_request *req) state->buf_dma = try_buf_map_to_sec4_sg(jrdev, edesc->sec4_sg + 1, buf, state->buf_dma, buflen, last_buflen); - (edesc->sec4_sg + sec4_sg_bytes - 1)->len |= SEC4_SG_LEN_FIN; + (edesc->sec4_sg + sec4_sg_src_index - 1)->len |= SEC4_SG_LEN_FIN; edesc->sec4_sg_dma = dma_map_single(jrdev, edesc->sec4_sg, sec4_sg_bytes, DMA_TO_DEVICE); diff --git a/drivers/crypto/nx/nx-sha256.c b/drivers/crypto/nx/nx-sha256.c index 08f8d5cd6334..becb738c897b 100644 --- a/drivers/crypto/nx/nx-sha256.c +++ b/drivers/crypto/nx/nx-sha256.c @@ -71,7 +71,6 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data, struct sha256_state *sctx = shash_desc_ctx(desc); struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; - struct nx_sg *in_sg; struct nx_sg *out_sg; u64 to_process = 0, leftover, total; unsigned long irq_flags; @@ -97,7 +96,6 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data, NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; - in_sg = nx_ctx->in_sg; max_sg_len = min_t(u64, nx_ctx->ap->sglen, nx_driver.of.max_sg_len/sizeof(struct nx_sg)); max_sg_len = min_t(u64, max_sg_len, @@ -114,17 +112,12 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data, } do { - /* - * to_process: the SHA256_BLOCK_SIZE data chunk to process in - * this update. This value is also restricted by the sg list - * limits. - */ - to_process = total - to_process; - to_process = to_process & ~(SHA256_BLOCK_SIZE - 1); + int used_sgs = 0; + struct nx_sg *in_sg = nx_ctx->in_sg; if (buf_len) { data_len = buf_len; - in_sg = nx_build_sg_list(nx_ctx->in_sg, + in_sg = nx_build_sg_list(in_sg, (u8 *) sctx->buf, &data_len, max_sg_len); @@ -133,15 +126,27 @@ static int nx_sha256_update(struct shash_desc *desc, const u8 *data, rc = -EINVAL; goto out; } + used_sgs = in_sg - nx_ctx->in_sg; } + /* to_process: SHA256_BLOCK_SIZE aligned chunk to be + * processed in this iteration. This value is restricted + * by sg list limits and number of sgs we already used + * for leftover data. (see above) + * In ideal case, we could allow NX_PAGE_SIZE * max_sg_len, + * but because data may not be aligned, we need to account + * for that too. */ + to_process = min_t(u64, total, + (max_sg_len - 1 - used_sgs) * NX_PAGE_SIZE); + to_process = to_process & ~(SHA256_BLOCK_SIZE - 1); + data_len = to_process - buf_len; in_sg = nx_build_sg_list(in_sg, (u8 *) data, &data_len, max_sg_len); nx_ctx->op.inlen = (nx_ctx->in_sg - in_sg) * sizeof(struct nx_sg); - to_process = (data_len + buf_len); + to_process = data_len + buf_len; leftover = total - to_process; /* diff --git a/drivers/crypto/nx/nx-sha512.c b/drivers/crypto/nx/nx-sha512.c index aff0fe58eac0..b6e183d58d73 100644 --- a/drivers/crypto/nx/nx-sha512.c +++ b/drivers/crypto/nx/nx-sha512.c @@ -71,7 +71,6 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data, struct sha512_state *sctx = shash_desc_ctx(desc); struct nx_crypto_ctx *nx_ctx = crypto_tfm_ctx(&desc->tfm->base); struct nx_csbcpb *csbcpb = (struct nx_csbcpb *)nx_ctx->csbcpb; - struct nx_sg *in_sg; struct nx_sg *out_sg; u64 to_process, leftover = 0, total; unsigned long irq_flags; @@ -97,7 +96,6 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data, NX_CPB_FDM(csbcpb) |= NX_FDM_INTERMEDIATE; NX_CPB_FDM(csbcpb) |= NX_FDM_CONTINUATION; - in_sg = nx_ctx->in_sg; max_sg_len = min_t(u64, nx_ctx->ap->sglen, nx_driver.of.max_sg_len/sizeof(struct nx_sg)); max_sg_len = min_t(u64, max_sg_len, @@ -114,18 +112,12 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data, } do { - /* - * to_process: the SHA512_BLOCK_SIZE data chunk to process in - * this update. This value is also restricted by the sg list - * limits. - */ - to_process = total - leftover; - to_process = to_process & ~(SHA512_BLOCK_SIZE - 1); - leftover = total - to_process; + int used_sgs = 0; + struct nx_sg *in_sg = nx_ctx->in_sg; if (buf_len) { data_len = buf_len; - in_sg = nx_build_sg_list(nx_ctx->in_sg, + in_sg = nx_build_sg_list(in_sg, (u8 *) sctx->buf, &data_len, max_sg_len); @@ -133,8 +125,20 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data, rc = -EINVAL; goto out; } + used_sgs = in_sg - nx_ctx->in_sg; } + /* to_process: SHA512_BLOCK_SIZE aligned chunk to be + * processed in this iteration. This value is restricted + * by sg list limits and number of sgs we already used + * for leftover data. (see above) + * In ideal case, we could allow NX_PAGE_SIZE * max_sg_len, + * but because data may not be aligned, we need to account + * for that too. */ + to_process = min_t(u64, total, + (max_sg_len - 1 - used_sgs) * NX_PAGE_SIZE); + to_process = to_process & ~(SHA512_BLOCK_SIZE - 1); + data_len = to_process - buf_len; in_sg = nx_build_sg_list(in_sg, (u8 *) data, &data_len, max_sg_len); @@ -146,7 +150,7 @@ static int nx_sha512_update(struct shash_desc *desc, const u8 *data, goto out; } - to_process = (data_len + buf_len); + to_process = data_len + buf_len; leftover = total - to_process; /* diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index 4a4cce15f25d..3ff284c8e3d5 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -689,6 +689,10 @@ struct dma_chan *dma_request_slave_channel(struct device *dev, struct dma_chan *ch = dma_request_slave_channel_reason(dev, name); if (IS_ERR(ch)) return NULL; + + dma_cap_set(DMA_PRIVATE, ch->device->cap_mask); + ch->device->privatecnt++; + return ch; } EXPORT_SYMBOL_GPL(dma_request_slave_channel); diff --git a/drivers/edac/ppc4xx_edac.c b/drivers/edac/ppc4xx_edac.c index 3515b381c131..711d8ad74f11 100644 --- a/drivers/edac/ppc4xx_edac.c +++ b/drivers/edac/ppc4xx_edac.c @@ -920,7 +920,7 @@ static int ppc4xx_edac_init_csrows(struct mem_ctl_info *mci, u32 mcopt1) */ for (row = 0; row < mci->nr_csrows; row++) { - struct csrow_info *csi = &mci->csrows[row]; + struct csrow_info *csi = mci->csrows[row]; /* * Get the configuration settings for this diff --git a/drivers/extcon/extcon-arizona.c b/drivers/extcon/extcon-arizona.c index ad87f263056f..4b9f09cc38d8 100644 --- a/drivers/extcon/extcon-arizona.c +++ b/drivers/extcon/extcon-arizona.c @@ -20,10 +20,12 @@ #include <linux/slab.h> #include <linux/interrupt.h> #include <linux/err.h> +#include <linux/gpio/consumer.h> #include <linux/gpio.h> #include <linux/input.h> #include <linux/platform_device.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/extcon.h> @@ -46,6 +48,9 @@ #define HPDET_DEBOUNCE 500 #define DEFAULT_MICD_TIMEOUT 2000 +#define MICD_DBTIME_TWO_READINGS 2 +#define MICD_DBTIME_FOUR_READINGS 4 + #define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \ ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \ ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \ @@ -94,6 +99,8 @@ struct arizona_extcon_info { int hpdet_ip_version; struct extcon_dev *edev; + + struct gpio_desc *micd_pol_gpio; }; static const struct arizona_micd_config micd_default_modes[] = { @@ -204,6 +211,10 @@ static void arizona_extcon_set_mode(struct arizona_extcon_info *info, int mode) if (arizona->pdata.micd_pol_gpio > 0) gpio_set_value_cansleep(arizona->pdata.micd_pol_gpio, info->micd_modes[mode].gpio); + else + gpiod_set_value_cansleep(info->micd_pol_gpio, + info->micd_modes[mode].gpio); + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_BIAS_SRC_MASK, info->micd_modes[mode].bias << @@ -757,10 +768,11 @@ static void arizona_micd_timeout_work(struct work_struct *work) mutex_lock(&info->lock); dev_dbg(info->arizona->dev, "MICD timed out, reporting HP\n"); - arizona_identify_headphone(info); info->detecting = false; + arizona_identify_headphone(info); + arizona_stop_mic(info); mutex_unlock(&info->lock); @@ -820,12 +832,18 @@ static void arizona_micd_detect(struct work_struct *work) /* Due to jack detect this should never happen */ if (!(val & ARIZONA_MICD_STS)) { dev_warn(arizona->dev, "Detected open circuit\n"); + info->mic = false; + arizona_stop_mic(info); info->detecting = false; + arizona_identify_headphone(info); goto handled; } /* If we got a high impedence we should have a headset, report it. */ if (info->detecting && (val & ARIZONA_MICD_LVL_8)) { + info->mic = true; + info->detecting = false; + arizona_identify_headphone(info); ret = extcon_set_cable_state_(info->edev, @@ -841,8 +859,6 @@ static void arizona_micd_detect(struct work_struct *work) ret); } - info->mic = true; - info->detecting = false; goto handled; } @@ -855,10 +871,11 @@ static void arizona_micd_detect(struct work_struct *work) if (info->detecting && (val & MICD_LVL_1_TO_7)) { if (info->jack_flips >= info->micd_num_modes * 10) { dev_dbg(arizona->dev, "Detected HP/line\n"); - arizona_identify_headphone(info); info->detecting = false; + arizona_identify_headphone(info); + arizona_stop_mic(info); } else { info->micd_mode++; @@ -1110,12 +1127,12 @@ static void arizona_micd_set_level(struct arizona *arizona, int index, regmap_update_bits(arizona->regmap, reg, mask, level); } -static int arizona_extcon_of_get_pdata(struct arizona *arizona) +static int arizona_extcon_device_get_pdata(struct arizona *arizona) { struct arizona_pdata *pdata = &arizona->pdata; unsigned int val = ARIZONA_ACCDET_MODE_HPL; - of_property_read_u32(arizona->dev->of_node, "wlf,hpdet-channel", &val); + device_property_read_u32(arizona->dev, "wlf,hpdet-channel", &val); switch (val) { case ARIZONA_ACCDET_MODE_HPL: case ARIZONA_ACCDET_MODE_HPR: @@ -1127,6 +1144,24 @@ static int arizona_extcon_of_get_pdata(struct arizona *arizona) pdata->hpdet_channel = ARIZONA_ACCDET_MODE_HPL; } + device_property_read_u32(arizona->dev, "wlf,micd-detect-debounce", + &pdata->micd_detect_debounce); + + device_property_read_u32(arizona->dev, "wlf,micd-bias-start-time", + &pdata->micd_bias_start_time); + + device_property_read_u32(arizona->dev, "wlf,micd-rate", + &pdata->micd_rate); + + device_property_read_u32(arizona->dev, "wlf,micd-dbtime", + &pdata->micd_dbtime); + + device_property_read_u32(arizona->dev, "wlf,micd-timeout", + &pdata->micd_timeout); + + pdata->micd_force_micbias = device_property_read_bool(arizona->dev, + "wlf,micd-force-micbias"); + return 0; } @@ -1147,10 +1182,8 @@ static int arizona_extcon_probe(struct platform_device *pdev) if (!info) return -ENOMEM; - if (IS_ENABLED(CONFIG_OF)) { - if (!dev_get_platdata(arizona->dev)) - arizona_extcon_of_get_pdata(arizona); - } + if (!dev_get_platdata(arizona->dev)) + arizona_extcon_device_get_pdata(arizona); info->micvdd = devm_regulator_get(&pdev->dev, "MICVDD"); if (IS_ERR(info->micvdd)) { @@ -1241,6 +1274,27 @@ static int arizona_extcon_probe(struct platform_device *pdev) arizona->pdata.micd_pol_gpio, ret); goto err_register; } + } else { + if (info->micd_modes[0].gpio) + mode = GPIOD_OUT_HIGH; + else + mode = GPIOD_OUT_LOW; + + /* We can't use devm here because we need to do the get + * against the MFD device, as that is where the of_node + * will reside, but if we devm against that the GPIO + * will not be freed if the extcon driver is unloaded. + */ + info->micd_pol_gpio = gpiod_get_optional(arizona->dev, + "wlf,micd-pol", + GPIOD_OUT_LOW); + if (IS_ERR(info->micd_pol_gpio)) { + ret = PTR_ERR(info->micd_pol_gpio); + dev_err(arizona->dev, + "Failed to get microphone polarity GPIO: %d\n", + ret); + goto err_register; + } } if (arizona->pdata.hpdet_id_gpio > 0) { @@ -1251,7 +1305,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) if (ret != 0) { dev_err(arizona->dev, "Failed to request GPIO%d: %d\n", arizona->pdata.hpdet_id_gpio, ret); - goto err_register; + goto err_gpio; } } @@ -1267,11 +1321,19 @@ static int arizona_extcon_probe(struct platform_device *pdev) arizona->pdata.micd_rate << ARIZONA_MICD_RATE_SHIFT); - if (arizona->pdata.micd_dbtime) + switch (arizona->pdata.micd_dbtime) { + case MICD_DBTIME_FOUR_READINGS: regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, ARIZONA_MICD_DBTIME_MASK, - arizona->pdata.micd_dbtime - << ARIZONA_MICD_DBTIME_SHIFT); + ARIZONA_MICD_DBTIME); + break; + case MICD_DBTIME_TWO_READINGS: + regmap_update_bits(arizona->regmap, ARIZONA_MIC_DETECT_1, + ARIZONA_MICD_DBTIME_MASK, 0); + break; + default: + break; + } BUILD_BUG_ON(ARRAY_SIZE(arizona_micd_levels) != 0x40); @@ -1295,7 +1357,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) dev_err(arizona->dev, "MICD ranges must be sorted\n"); ret = -EINVAL; - goto err_input; + goto err_gpio; } } } @@ -1314,7 +1376,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) dev_err(arizona->dev, "Unsupported MICD level %d\n", info->micd_ranges[i].max); ret = -EINVAL; - goto err_input; + goto err_gpio; } dev_dbg(arizona->dev, "%d ohms for MICD threshold %d\n", @@ -1387,7 +1449,7 @@ static int arizona_extcon_probe(struct platform_device *pdev) if (ret != 0) { dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n", ret); - goto err_input; + goto err_gpio; } ret = arizona_set_irq_wake(arizona, jack_irq_rise, 1); @@ -1458,7 +1520,8 @@ err_rise_wake: arizona_set_irq_wake(arizona, jack_irq_rise, 0); err_rise: arizona_free_irq(arizona, jack_irq_rise, info); -err_input: +err_gpio: + gpiod_put(info->micd_pol_gpio); err_register: pm_runtime_disable(&pdev->dev); return ret; @@ -1470,6 +1533,8 @@ static int arizona_extcon_remove(struct platform_device *pdev) struct arizona *arizona = info->arizona; int jack_irq_rise, jack_irq_fall; + gpiod_put(info->micd_pol_gpio); + pm_runtime_disable(&pdev->dev); regmap_update_bits(arizona->regmap, diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index 355459a54e8b..57c24fa52edb 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -65,22 +65,6 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id) return IRQ_HANDLED; } -static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf) -{ - struct device *dev = edev->dev.parent; - struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev); - const char *state; - - if (extcon_get_state(edev)) - state = extcon_data->state_on; - else - state = extcon_data->state_off; - - if (state) - return sprintf(buf, "%s\n", state); - return -EINVAL; -} - static int gpio_extcon_probe(struct platform_device *pdev) { struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev); @@ -110,8 +94,6 @@ static int gpio_extcon_probe(struct platform_device *pdev) extcon_data->state_on = pdata->state_on; extcon_data->state_off = pdata->state_off; extcon_data->check_on_resume = pdata->check_on_resume; - if (pdata->state_on && pdata->state_off) - extcon_data->edev->print_state = extcon_gpio_print_state; ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, pdev->name); diff --git a/drivers/extcon/extcon-max77843.c b/drivers/extcon/extcon-max77843.c index fac2f1417a79..cc5e7bca38c8 100644 --- a/drivers/extcon/extcon-max77843.c +++ b/drivers/extcon/extcon-max77843.c @@ -781,6 +781,15 @@ static int max77843_muic_probe(struct platform_device *pdev) /* Support virtual irq domain for max77843 MUIC device */ INIT_WORK(&info->irq_work, max77843_muic_irq_work); + /* Clear IRQ bits before request IRQs */ + ret = regmap_bulk_read(max77843->regmap_muic, + MAX77843_MUIC_REG_INT1, info->status, + MAX77843_MUIC_IRQ_NUM); + if (ret) { + dev_err(&pdev->dev, "Failed to Clear IRQ bits\n"); + goto err_muic_irq; + } + for (i = 0; i < ARRAY_SIZE(max77843_muic_irqs); i++) { struct max77843_muic_irq *muic_irq = &max77843_muic_irqs[i]; unsigned int virq = 0; diff --git a/drivers/extcon/extcon-palmas.c b/drivers/extcon/extcon-palmas.c index eebdf2a33bfe..93c30a885740 100644 --- a/drivers/extcon/extcon-palmas.c +++ b/drivers/extcon/extcon-palmas.c @@ -28,6 +28,11 @@ #include <linux/mfd/palmas.h> #include <linux/of.h> #include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/gpio/consumer.h> +#include <linux/workqueue.h> + +#define USB_GPIO_DEBOUNCE_MS 20 /* ms */ static const unsigned int palmas_extcon_cable[] = { EXTCON_USB, @@ -35,8 +40,6 @@ static const unsigned int palmas_extcon_cable[] = { EXTCON_NONE, }; -static const int mutually_exclusive[] = {0x3, 0x0}; - static void palmas_usb_wakeup(struct palmas *palmas, int enable) { if (enable) @@ -120,19 +123,54 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb) return IRQ_HANDLED; } +static void palmas_gpio_id_detect(struct work_struct *work) +{ + int id; + struct palmas_usb *palmas_usb = container_of(to_delayed_work(work), + struct palmas_usb, + wq_detectid); + struct extcon_dev *edev = palmas_usb->edev; + + if (!palmas_usb->id_gpiod) + return; + + id = gpiod_get_value_cansleep(palmas_usb->id_gpiod); + + if (id) { + extcon_set_cable_state_(edev, EXTCON_USB_HOST, false); + dev_info(palmas_usb->dev, "USB-HOST cable is detached\n"); + } else { + extcon_set_cable_state_(edev, EXTCON_USB_HOST, true); + dev_info(palmas_usb->dev, "USB-HOST cable is attached\n"); + } +} + +static irqreturn_t palmas_gpio_id_irq_handler(int irq, void *_palmas_usb) +{ + struct palmas_usb *palmas_usb = _palmas_usb; + + queue_delayed_work(system_power_efficient_wq, &palmas_usb->wq_detectid, + palmas_usb->sw_debounce_jiffies); + + return IRQ_HANDLED; +} + static void palmas_enable_irq(struct palmas_usb *palmas_usb) { palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, PALMAS_USB_VBUS_CTRL_SET, PALMAS_USB_VBUS_CTRL_SET_VBUS_ACT_COMP); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_CTRL_SET, PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); + if (palmas_usb->enable_id_detection) { + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_CTRL_SET, + PALMAS_USB_ID_CTRL_SET_ID_ACT_COMP); - palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, - PALMAS_USB_ID_INT_EN_HI_SET, - PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | - PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); + palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE, + PALMAS_USB_ID_INT_EN_HI_SET, + PALMAS_USB_ID_INT_EN_HI_SET_ID_GND | + PALMAS_USB_ID_INT_EN_HI_SET_ID_FLOAT); + } if (palmas_usb->enable_vbus_detection) palmas_vbus_irq_handler(palmas_usb->vbus_irq, palmas_usb); @@ -171,20 +209,37 @@ static int palmas_usb_probe(struct platform_device *pdev) palmas_usb->wakeup = pdata->wakeup; } + palmas_usb->id_gpiod = devm_gpiod_get_optional(&pdev->dev, "id", + GPIOD_IN); + if (IS_ERR(palmas_usb->id_gpiod)) { + dev_err(&pdev->dev, "failed to get id gpio\n"); + return PTR_ERR(palmas_usb->id_gpiod); + } + + if (palmas_usb->enable_id_detection && palmas_usb->id_gpiod) { + palmas_usb->enable_id_detection = false; + palmas_usb->enable_gpio_id_detection = true; + } + + if (palmas_usb->enable_gpio_id_detection) { + u32 debounce; + + if (of_property_read_u32(node, "debounce-delay-ms", &debounce)) + debounce = USB_GPIO_DEBOUNCE_MS; + + status = gpiod_set_debounce(palmas_usb->id_gpiod, + debounce * 1000); + if (status < 0) + palmas_usb->sw_debounce_jiffies = msecs_to_jiffies(debounce); + } + + INIT_DELAYED_WORK(&palmas_usb->wq_detectid, palmas_gpio_id_detect); + palmas->usb = palmas_usb; palmas_usb->palmas = palmas; palmas_usb->dev = &pdev->dev; - palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data, - PALMAS_ID_OTG_IRQ); - palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data, - PALMAS_ID_IRQ); - palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data, - PALMAS_VBUS_OTG_IRQ); - palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, - PALMAS_VBUS_IRQ); - palmas_usb_wakeup(palmas, palmas_usb->wakeup); platform_set_drvdata(pdev, palmas_usb); @@ -195,7 +250,6 @@ static int palmas_usb_probe(struct platform_device *pdev) dev_err(&pdev->dev, "failed to allocate extcon device\n"); return -ENOMEM; } - palmas_usb->edev->mutually_exclusive = mutually_exclusive; status = devm_extcon_dev_register(&pdev->dev, palmas_usb->edev); if (status) { @@ -204,6 +258,10 @@ static int palmas_usb_probe(struct platform_device *pdev) } if (palmas_usb->enable_id_detection) { + palmas_usb->id_otg_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_ID_OTG_IRQ); + palmas_usb->id_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_ID_IRQ); status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->id_irq, NULL, palmas_id_irq_handler, @@ -215,9 +273,33 @@ static int palmas_usb_probe(struct platform_device *pdev) palmas_usb->id_irq, status); return status; } + } else if (palmas_usb->enable_gpio_id_detection) { + palmas_usb->gpio_id_irq = gpiod_to_irq(palmas_usb->id_gpiod); + if (palmas_usb->gpio_id_irq < 0) { + dev_err(&pdev->dev, "failed to get id irq\n"); + return palmas_usb->gpio_id_irq; + } + status = devm_request_threaded_irq(&pdev->dev, + palmas_usb->gpio_id_irq, + NULL, + palmas_gpio_id_irq_handler, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "palmas_usb_id", + palmas_usb); + if (status < 0) { + dev_err(&pdev->dev, + "failed to request handler for id irq\n"); + return status; + } } if (palmas_usb->enable_vbus_detection) { + palmas_usb->vbus_otg_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_VBUS_OTG_IRQ); + palmas_usb->vbus_irq = regmap_irq_get_virq(palmas->irq_data, + PALMAS_VBUS_IRQ); status = devm_request_threaded_irq(palmas_usb->dev, palmas_usb->vbus_irq, NULL, palmas_vbus_irq_handler, @@ -232,10 +314,21 @@ static int palmas_usb_probe(struct platform_device *pdev) } palmas_enable_irq(palmas_usb); + /* perform initial detection */ + palmas_gpio_id_detect(&palmas_usb->wq_detectid.work); device_set_wakeup_capable(&pdev->dev, true); return 0; } +static int palmas_usb_remove(struct platform_device *pdev) +{ + struct palmas_usb *palmas_usb = platform_get_drvdata(pdev); + + cancel_delayed_work_sync(&palmas_usb->wq_detectid); + + return 0; +} + #ifdef CONFIG_PM_SLEEP static int palmas_usb_suspend(struct device *dev) { @@ -246,6 +339,8 @@ static int palmas_usb_suspend(struct device *dev) enable_irq_wake(palmas_usb->vbus_irq); if (palmas_usb->enable_id_detection) enable_irq_wake(palmas_usb->id_irq); + if (palmas_usb->enable_gpio_id_detection) + enable_irq_wake(palmas_usb->gpio_id_irq); } return 0; } @@ -259,6 +354,8 @@ static int palmas_usb_resume(struct device *dev) disable_irq_wake(palmas_usb->vbus_irq); if (palmas_usb->enable_id_detection) disable_irq_wake(palmas_usb->id_irq); + if (palmas_usb->enable_gpio_id_detection) + disable_irq_wake(palmas_usb->gpio_id_irq); } return 0; }; @@ -276,6 +373,7 @@ static const struct of_device_id of_palmas_match_tbl[] = { static struct platform_driver palmas_usb_driver = { .probe = palmas_usb_probe, + .remove = palmas_usb_remove, .driver = { .name = "palmas-usb", .of_match_table = of_palmas_match_tbl, diff --git a/drivers/extcon/extcon-rt8973a.c b/drivers/extcon/extcon-rt8973a.c index 92c939221a41..11592e980bc1 100644 --- a/drivers/extcon/extcon-rt8973a.c +++ b/drivers/extcon/extcon-rt8973a.c @@ -693,7 +693,6 @@ MODULE_DEVICE_TABLE(i2c, rt8973a_i2c_id); static struct i2c_driver rt8973a_muic_i2c_driver = { .driver = { .name = "rt8973a", - .owner = THIS_MODULE, .pm = &rt8973a_muic_pm_ops, .of_match_table = rt8973a_dt_match, }, diff --git a/drivers/extcon/extcon-sm5502.c b/drivers/extcon/extcon-sm5502.c index 817dece23b4c..0ffefefa2e26 100644 --- a/drivers/extcon/extcon-sm5502.c +++ b/drivers/extcon/extcon-sm5502.c @@ -685,7 +685,6 @@ MODULE_DEVICE_TABLE(i2c, sm5502_i2c_id); static struct i2c_driver sm5502_muic_i2c_driver = { .driver = { .name = "sm5502", - .owner = THIS_MODULE, .pm = &sm5502_muic_pm_ops, .of_match_table = sm5502_dt_match, }, diff --git a/drivers/extcon/extcon-usb-gpio.c b/drivers/extcon/extcon-usb-gpio.c index a2a44536a608..2b2fecffb1ad 100644 --- a/drivers/extcon/extcon-usb-gpio.c +++ b/drivers/extcon/extcon-usb-gpio.c @@ -15,6 +15,7 @@ */ #include <linux/extcon.h> +#include <linux/gpio.h> #include <linux/gpio/consumer.h> #include <linux/init.h> #include <linux/interrupt.h> diff --git a/drivers/extcon/extcon.c b/drivers/extcon/extcon.c index 43b57b02d050..a07addde297b 100644 --- a/drivers/extcon/extcon.c +++ b/drivers/extcon/extcon.c @@ -126,7 +126,7 @@ static int find_cable_index_by_id(struct extcon_dev *edev, const unsigned int id static int find_cable_id_by_name(struct extcon_dev *edev, const char *name) { - unsigned int id = -EINVAL; + int id = -EINVAL; int i = 0; /* Find the id of extcon cable */ @@ -143,7 +143,7 @@ static int find_cable_id_by_name(struct extcon_dev *edev, const char *name) static int find_cable_index_by_name(struct extcon_dev *edev, const char *name) { - unsigned int id; + int id; if (edev->max_supported == 0) return -EINVAL; @@ -172,14 +172,6 @@ static ssize_t state_show(struct device *dev, struct device_attribute *attr, int i, count = 0; struct extcon_dev *edev = dev_get_drvdata(dev); - if (edev->print_state) { - int ret = edev->print_state(edev, buf); - - if (ret >= 0) - return ret; - /* Use default if failed */ - } - if (edev->max_supported == 0) return sprintf(buf, "%u\n", edev->state); @@ -272,6 +264,9 @@ int extcon_update_state(struct extcon_dev *edev, u32 mask, u32 state) unsigned long flags; bool attached; + if (!edev) + return -EINVAL; + spin_lock_irqsave(&edev->lock, flags); if (edev->state != ((edev->state & ~mask) | (state & mask))) { @@ -345,6 +340,9 @@ EXPORT_SYMBOL_GPL(extcon_update_state); */ int extcon_set_state(struct extcon_dev *edev, u32 state) { + if (!edev) + return -EINVAL; + return extcon_update_state(edev, 0xffffffff, state); } EXPORT_SYMBOL_GPL(extcon_set_state); @@ -358,6 +356,9 @@ int extcon_get_cable_state_(struct extcon_dev *edev, const unsigned int id) { int index; + if (!edev) + return -EINVAL; + index = find_cable_index_by_id(edev, id); if (index < 0) return index; @@ -378,7 +379,7 @@ EXPORT_SYMBOL_GPL(extcon_get_cable_state_); */ int extcon_get_cable_state(struct extcon_dev *edev, const char *cable_name) { - unsigned int id; + int id; id = find_cable_id_by_name(edev, cable_name); if (id < 0) @@ -402,6 +403,9 @@ int extcon_set_cable_state_(struct extcon_dev *edev, unsigned int id, u32 state; int index; + if (!edev) + return -EINVAL; + index = find_cable_index_by_id(edev, id); if (index < 0) return index; @@ -426,7 +430,7 @@ EXPORT_SYMBOL_GPL(extcon_set_cable_state_); int extcon_set_cable_state(struct extcon_dev *edev, const char *cable_name, bool cable_state) { - unsigned int id; + int id; id = find_cable_id_by_name(edev, cable_name); if (id < 0) @@ -444,6 +448,9 @@ struct extcon_dev *extcon_get_extcon_dev(const char *extcon_name) { struct extcon_dev *sd; + if (!extcon_name) + return ERR_PTR(-EINVAL); + mutex_lock(&extcon_dev_list_lock); list_for_each_entry(sd, &extcon_dev_list, entry) { if (!strcmp(sd->name, extcon_name)) @@ -572,6 +579,9 @@ int extcon_register_notifier(struct extcon_dev *edev, unsigned int id, unsigned long flags; int ret, idx; + if (!edev || !nb) + return -EINVAL; + idx = find_cable_index_by_id(edev, id); spin_lock_irqsave(&edev->lock, flags); @@ -594,6 +604,9 @@ int extcon_unregister_notifier(struct extcon_dev *edev, unsigned int id, unsigned long flags; int ret, idx; + if (!edev || !nb) + return -EINVAL; + idx = find_cable_index_by_id(edev, id); spin_lock_irqsave(&edev->lock, flags); @@ -654,6 +667,9 @@ struct extcon_dev *extcon_dev_allocate(const unsigned int *supported_cable) { struct extcon_dev *edev; + if (!supported_cable) + return ERR_PTR(-EINVAL); + edev = kzalloc(sizeof(*edev), GFP_KERNEL); if (!edev) return ERR_PTR(-ENOMEM); @@ -754,7 +770,7 @@ int extcon_dev_register(struct extcon_dev *edev) return ret; } - if (!edev->supported_cable) + if (!edev || !edev->supported_cable) return -EINVAL; for (; edev->supported_cable[index] != EXTCON_NONE; index++); @@ -960,6 +976,9 @@ void extcon_dev_unregister(struct extcon_dev *edev) { int index; + if (!edev) + return; + mutex_lock(&extcon_dev_list_lock); list_del(&edev->entry); mutex_unlock(&extcon_dev_list_lock); @@ -1066,6 +1085,9 @@ struct extcon_dev *extcon_get_edev_by_phandle(struct device *dev, int index) struct device_node *node; struct extcon_dev *edev; + if (!dev) + return ERR_PTR(-EINVAL); + if (!dev->of_node) { dev_err(dev, "device does not have a device node entry\n"); return ERR_PTR(-EINVAL); diff --git a/drivers/firmware/broadcom/bcm47xx_nvram.c b/drivers/firmware/broadcom/bcm47xx_nvram.c index 87add3fdce52..e41594510b97 100644 --- a/drivers/firmware/broadcom/bcm47xx_nvram.c +++ b/drivers/firmware/broadcom/bcm47xx_nvram.c @@ -245,4 +245,4 @@ char *bcm47xx_nvram_get_contents(size_t *nvram_size) } EXPORT_SYMBOL(bcm47xx_nvram_get_contents); -MODULE_LICENSE("GPLv2"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c index 2f7a5efa21c2..f5c22556ec2c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_uvd.c @@ -374,7 +374,7 @@ static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) unsigned height_in_mb = ALIGN(height / 16, 2); unsigned fs_in_mb = width_in_mb * height_in_mb; - unsigned image_size, tmp, min_dpb_size, num_dpb_buffer; + unsigned image_size, tmp, min_dpb_size, num_dpb_buffer, min_ctx_size; image_size = width * height; image_size += image_size / 2; @@ -466,6 +466,8 @@ static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) num_dpb_buffer = (le32_to_cpu(msg[59]) & 0xff) + 2; min_dpb_size = image_size * num_dpb_buffer; + min_ctx_size = ((width + 255) / 16) * ((height + 255) / 16) + * 16 * num_dpb_buffer + 52 * 1024; break; default: @@ -486,6 +488,7 @@ static int amdgpu_uvd_cs_msg_decode(uint32_t *msg, unsigned buf_sizes[]) buf_sizes[0x1] = dpb_size; buf_sizes[0x2] = image_size; + buf_sizes[0x4] = min_ctx_size; return 0; } @@ -628,6 +631,13 @@ static int amdgpu_uvd_cs_pass2(struct amdgpu_uvd_cs_ctx *ctx) return -EINVAL; } + } else if (cmd == 0x206) { + if ((end - start) < ctx->buf_sizes[4]) { + DRM_ERROR("buffer (%d) to small (%d / %d)!\n", cmd, + (unsigned)(end - start), + ctx->buf_sizes[4]); + return -EINVAL; + } } else if ((cmd != 0x100) && (cmd != 0x204)) { DRM_ERROR("invalid UVD command %X!\n", cmd); return -EINVAL; @@ -755,9 +765,10 @@ int amdgpu_uvd_ring_parse_cs(struct amdgpu_cs_parser *parser, uint32_t ib_idx) struct amdgpu_uvd_cs_ctx ctx = {}; unsigned buf_sizes[] = { [0x00000000] = 2048, - [0x00000001] = 32 * 1024 * 1024, - [0x00000002] = 2048 * 1152 * 3, + [0x00000001] = 0xFFFFFFFF, + [0x00000002] = 0xFFFFFFFF, [0x00000003] = 2048, + [0x00000004] = 0xFFFFFFFF, }; struct amdgpu_ib *ib = &parser->ibs[ib_idx]; int r; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c index f5a42ab1f65c..20e2cfd521d5 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v8_0.c @@ -3135,7 +3135,7 @@ static int gfx_v8_0_cp_compute_resume(struct amdgpu_device *adev) WREG32(mmCP_MEC_DOORBELL_RANGE_LOWER, AMDGPU_DOORBELL_KIQ << 2); WREG32(mmCP_MEC_DOORBELL_RANGE_UPPER, - 0x7FFFF << 2); + AMDGPU_DOORBELL_MEC_RING7 << 2); } tmp = RREG32(mmCP_HQD_PQ_DOORBELL_CONTROL); tmp = REG_SET_FIELD(tmp, CP_HQD_PQ_DOORBELL_CONTROL, diff --git a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c index 6fad1f9648f3..ef6182bc8e5e 100644 --- a/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c +++ b/drivers/gpu/drm/atmel-hlcdc/atmel_hlcdc_dc.c @@ -559,7 +559,7 @@ static int atmel_hlcdc_dc_drm_remove(struct platform_device *pdev) return 0; } -#ifdef CONFIG_PM +#ifdef CONFIG_PM_SLEEP static int atmel_hlcdc_dc_drm_suspend(struct device *dev) { struct drm_device *drm_dev = dev_get_drvdata(dev); diff --git a/drivers/gpu/drm/drm_dp_mst_topology.c b/drivers/gpu/drm/drm_dp_mst_topology.c index b0487c9f018c..eb603f1defc2 100644 --- a/drivers/gpu/drm/drm_dp_mst_topology.c +++ b/drivers/gpu/drm/drm_dp_mst_topology.c @@ -873,9 +873,10 @@ static void drm_dp_destroy_port(struct kref *kref) from an EDID retrieval */ if (port->connector) { mutex_lock(&mgr->destroy_connector_lock); - list_add(&port->connector->destroy_list, &mgr->destroy_connector_list); + list_add(&port->next, &mgr->destroy_connector_list); mutex_unlock(&mgr->destroy_connector_lock); schedule_work(&mgr->destroy_connector_work); + return; } drm_dp_port_teardown_pdt(port, port->pdt); @@ -2659,7 +2660,7 @@ static void drm_dp_tx_work(struct work_struct *work) static void drm_dp_destroy_connector_work(struct work_struct *work) { struct drm_dp_mst_topology_mgr *mgr = container_of(work, struct drm_dp_mst_topology_mgr, destroy_connector_work); - struct drm_connector *connector; + struct drm_dp_mst_port *port; /* * Not a regular list traverse as we have to drop the destroy @@ -2668,15 +2669,21 @@ static void drm_dp_destroy_connector_work(struct work_struct *work) */ for (;;) { mutex_lock(&mgr->destroy_connector_lock); - connector = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_connector, destroy_list); - if (!connector) { + port = list_first_entry_or_null(&mgr->destroy_connector_list, struct drm_dp_mst_port, next); + if (!port) { mutex_unlock(&mgr->destroy_connector_lock); break; } - list_del(&connector->destroy_list); + list_del(&port->next); mutex_unlock(&mgr->destroy_connector_lock); - mgr->cbs->destroy_connector(mgr, connector); + mgr->cbs->destroy_connector(mgr, port->connector); + + drm_dp_port_teardown_pdt(port, port->pdt); + + if (!port->input && port->vcpi.vcpi > 0) + drm_dp_mst_put_payload_id(mgr, port->vcpi.vcpi); + kfree(port); } } diff --git a/drivers/gpu/drm/exynos/exynos_drm_fimc.c b/drivers/gpu/drm/exynos/exynos_drm_fimc.c index 842d6b8dc3c4..2a652359af64 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_fimc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_fimc.c @@ -1745,7 +1745,6 @@ static int fimc_probe(struct platform_device *pdev) spin_lock_init(&ctx->lock); platform_set_drvdata(pdev, ctx); - pm_runtime_set_active(dev); pm_runtime_enable(dev); ret = exynos_drm_ippdrv_register(ippdrv); diff --git a/drivers/gpu/drm/exynos/exynos_drm_gsc.c b/drivers/gpu/drm/exynos/exynos_drm_gsc.c index 8040ed2a831f..f1c6b76c127f 100644 --- a/drivers/gpu/drm/exynos/exynos_drm_gsc.c +++ b/drivers/gpu/drm/exynos/exynos_drm_gsc.c @@ -593,8 +593,7 @@ static int gsc_src_set_transf(struct device *dev, gsc_write(cfg, GSC_IN_CON); - ctx->rotation = cfg & - (GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0; + ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; *swap = ctx->rotation; return 0; @@ -857,8 +856,7 @@ static int gsc_dst_set_transf(struct device *dev, gsc_write(cfg, GSC_IN_CON); - ctx->rotation = cfg & - (GSC_IN_ROT_90 | GSC_IN_ROT_270) ? 1 : 0; + ctx->rotation = (cfg & GSC_IN_ROT_90) ? 1 : 0; *swap = ctx->rotation; return 0; diff --git a/drivers/gpu/drm/exynos/exynos_hdmi.c b/drivers/gpu/drm/exynos/exynos_hdmi.c index 99e286489031..4a00990e4ae4 100644 --- a/drivers/gpu/drm/exynos/exynos_hdmi.c +++ b/drivers/gpu/drm/exynos/exynos_hdmi.c @@ -1064,6 +1064,7 @@ static int hdmi_get_modes(struct drm_connector *connector) { struct hdmi_context *hdata = ctx_from_connector(connector); struct edid *edid; + int ret; if (!hdata->ddc_adpt) return -ENODEV; @@ -1079,7 +1080,11 @@ static int hdmi_get_modes(struct drm_connector *connector) drm_mode_connector_update_edid_property(connector, edid); - return drm_add_edid_modes(connector, edid); + ret = drm_add_edid_modes(connector, edid); + + kfree(edid); + + return ret; } static int hdmi_find_phy_conf(struct hdmi_context *hdata, u32 pixel_clock) diff --git a/drivers/gpu/drm/exynos/exynos_mixer.c b/drivers/gpu/drm/exynos/exynos_mixer.c index cae98db33062..4706b56902b4 100644 --- a/drivers/gpu/drm/exynos/exynos_mixer.c +++ b/drivers/gpu/drm/exynos/exynos_mixer.c @@ -718,6 +718,10 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) /* handling VSYNC */ if (val & MXR_INT_STATUS_VSYNC) { + /* vsync interrupt use different bit for read and clear */ + val |= MXR_INT_CLEAR_VSYNC; + val &= ~MXR_INT_STATUS_VSYNC; + /* interlace scan need to check shadow register */ if (ctx->interlace) { base = mixer_reg_read(res, MXR_GRAPHIC_BASE(0)); @@ -743,11 +747,6 @@ static irqreturn_t mixer_irq_handler(int irq, void *arg) out: /* clear interrupts */ - if (~val & MXR_INT_EN_VSYNC) { - /* vsync interrupt use different bit for read and clear */ - val &= ~MXR_INT_EN_VSYNC; - val |= MXR_INT_CLEAR_VSYNC; - } mixer_reg_write(res, MXR_INT_STATUS, val); spin_unlock(&res->reg_slock); @@ -907,8 +906,8 @@ static int mixer_enable_vblank(struct exynos_drm_crtc *crtc) } /* enable vsync interrupt */ - mixer_reg_writemask(res, MXR_INT_EN, MXR_INT_EN_VSYNC, - MXR_INT_EN_VSYNC); + mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); + mixer_reg_writemask(res, MXR_INT_EN, ~0, MXR_INT_EN_VSYNC); return 0; } @@ -918,7 +917,13 @@ static void mixer_disable_vblank(struct exynos_drm_crtc *crtc) struct mixer_context *mixer_ctx = crtc->ctx; struct mixer_resources *res = &mixer_ctx->mixer_res; + if (!mixer_ctx->powered) { + mixer_ctx->int_en &= MXR_INT_EN_VSYNC; + return; + } + /* disable vsync interrupt */ + mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); mixer_reg_writemask(res, MXR_INT_EN, 0, MXR_INT_EN_VSYNC); } @@ -1047,6 +1052,8 @@ static void mixer_enable(struct exynos_drm_crtc *crtc) mixer_reg_writemask(res, MXR_STATUS, ~0, MXR_STATUS_SOFT_RESET); + if (ctx->int_en & MXR_INT_EN_VSYNC) + mixer_reg_writemask(res, MXR_INT_STATUS, ~0, MXR_INT_CLEAR_VSYNC); mixer_reg_write(res, MXR_INT_EN, ctx->int_en); mixer_win_reset(ctx); } diff --git a/drivers/gpu/drm/i915/intel_atomic.c b/drivers/gpu/drm/i915/intel_atomic.c index 7ed8033aae60..8e35e0d013df 100644 --- a/drivers/gpu/drm/i915/intel_atomic.c +++ b/drivers/gpu/drm/i915/intel_atomic.c @@ -129,8 +129,9 @@ int intel_atomic_commit(struct drm_device *dev, struct drm_atomic_state *state, bool async) { - int ret; - int i; + struct drm_crtc_state *crtc_state; + struct drm_crtc *crtc; + int ret, i; if (async) { DRM_DEBUG_KMS("i915 does not yet support async commit\n"); @@ -142,48 +143,18 @@ int intel_atomic_commit(struct drm_device *dev, return ret; /* Point of no return */ - - /* - * FIXME: The proper sequence here will eventually be: - * - * drm_atomic_helper_swap_state(dev, state) - * drm_atomic_helper_commit_modeset_disables(dev, state); - * drm_atomic_helper_commit_planes(dev, state); - * drm_atomic_helper_commit_modeset_enables(dev, state); - * drm_atomic_helper_wait_for_vblanks(dev, state); - * drm_atomic_helper_cleanup_planes(dev, state); - * drm_atomic_state_free(state); - * - * once we have full atomic modeset. For now, just manually update - * plane states to avoid clobbering good states with dummy states - * while nuclear pageflipping. - */ - for (i = 0; i < dev->mode_config.num_total_plane; i++) { - struct drm_plane *plane = state->planes[i]; - - if (!plane) - continue; - - plane->state->state = state; - swap(state->plane_states[i], plane->state); - plane->state->state = NULL; - } + drm_atomic_helper_swap_state(dev, state); /* swap crtc_scaler_state */ - for (i = 0; i < dev->mode_config.num_crtc; i++) { - struct drm_crtc *crtc = state->crtcs[i]; - if (!crtc) { - continue; - } - - to_intel_crtc(crtc)->config->scaler_state = - to_intel_crtc_state(state->crtc_states[i])->scaler_state; + for_each_crtc_in_state(state, crtc, crtc_state, i) { + to_intel_crtc(crtc)->config = to_intel_crtc_state(crtc->state); if (INTEL_INFO(dev)->gen >= 9) skl_detach_scalers(to_intel_crtc(crtc)); + + drm_atomic_helper_commit_planes_on_crtc(crtc_state); } - drm_atomic_helper_commit_planes(dev, state); drm_atomic_helper_wait_for_vblanks(dev, state); drm_atomic_helper_cleanup_planes(dev, state); drm_atomic_state_free(state); diff --git a/drivers/gpu/drm/i915/intel_bios.c b/drivers/gpu/drm/i915/intel_bios.c index 3dcd59e694db..198fc3c3291b 100644 --- a/drivers/gpu/drm/i915/intel_bios.c +++ b/drivers/gpu/drm/i915/intel_bios.c @@ -1075,34 +1075,15 @@ parse_device_mapping(struct drm_i915_private *dev_priv, const union child_device_config *p_child; union child_device_config *child_dev_ptr; int i, child_device_num, count; - u8 expected_size; - u16 block_size; + u16 block_size; p_defs = find_section(bdb, BDB_GENERAL_DEFINITIONS); if (!p_defs) { DRM_DEBUG_KMS("No general definition block is found, no devices defined.\n"); return; } - if (bdb->version < 195) { - expected_size = 33; - } else if (bdb->version == 195) { - expected_size = 37; - } else if (bdb->version <= 197) { - expected_size = 38; - } else { - expected_size = 38; - DRM_DEBUG_DRIVER("Expected child_device_config size for BDB version %u not known; assuming %u\n", - expected_size, bdb->version); - } - - if (expected_size > sizeof(*p_child)) { - DRM_ERROR("child_device_config cannot fit in p_child\n"); - return; - } - - if (p_defs->child_dev_size != expected_size) { - DRM_ERROR("Size mismatch; child_device_config size=%u (expected %u); bdb->version: %u\n", - p_defs->child_dev_size, expected_size, bdb->version); + if (p_defs->child_dev_size < sizeof(*p_child)) { + DRM_ERROR("General definiton block child device size is too small.\n"); return; } /* get the block size of general definitions */ @@ -1149,7 +1130,7 @@ parse_device_mapping(struct drm_i915_private *dev_priv, child_dev_ptr = dev_priv->vbt.child_dev + count; count++; - memcpy(child_dev_ptr, p_child, p_defs->child_dev_size); + memcpy(child_dev_ptr, p_child, sizeof(*p_child)); } return; } diff --git a/drivers/gpu/drm/i915/intel_display.c b/drivers/gpu/drm/i915/intel_display.c index 30e0f54ba19d..87476ff181dd 100644 --- a/drivers/gpu/drm/i915/intel_display.c +++ b/drivers/gpu/drm/i915/intel_display.c @@ -11826,7 +11826,9 @@ encoder_retry: goto encoder_retry; } - pipe_config->dither = pipe_config->pipe_bpp != base_bpp; + /* Dithering seems to not pass-through bits correctly when it should, so + * only enable it on 6bpc panels. */ + pipe_config->dither = pipe_config->pipe_bpp == 6*3; DRM_DEBUG_KMS("plane bpp: %i, pipe bpp: %i, dithering: %i\n", base_bpp, pipe_config->pipe_bpp, pipe_config->dither); @@ -12624,17 +12626,17 @@ static int __intel_set_mode(struct drm_crtc *modeset_crtc, modeset_update_crtc_power_domains(state); - drm_atomic_helper_commit_planes(dev, state); - /* Now enable the clocks, plane, pipe, and connectors that we set up. */ for_each_crtc_in_state(state, crtc, crtc_state, i) { - if (!needs_modeset(crtc->state) || !crtc->state->enable) + if (!needs_modeset(crtc->state) || !crtc->state->enable) { + drm_atomic_helper_commit_planes_on_crtc(crtc_state); continue; + } update_scanline_offset(to_intel_crtc(crtc)); dev_priv->display.crtc_enable(crtc); - intel_crtc_enable_planes(crtc); + drm_atomic_helper_commit_planes_on_crtc(crtc_state); } /* FIXME: add subpixel order */ @@ -12891,20 +12893,11 @@ intel_modeset_stage_output_state(struct drm_device *dev, return 0; } -static bool primary_plane_visible(struct drm_crtc *crtc) -{ - struct intel_plane_state *plane_state = - to_intel_plane_state(crtc->primary->state); - - return plane_state->visible; -} - static int intel_crtc_set_config(struct drm_mode_set *set) { struct drm_device *dev; struct drm_atomic_state *state = NULL; struct intel_crtc_state *pipe_config; - bool primary_plane_was_visible; int ret; BUG_ON(!set); @@ -12943,38 +12936,8 @@ static int intel_crtc_set_config(struct drm_mode_set *set) intel_update_pipe_size(to_intel_crtc(set->crtc)); - primary_plane_was_visible = primary_plane_visible(set->crtc); - ret = intel_set_mode_with_config(set->crtc, pipe_config, true); - if (ret == 0 && - pipe_config->base.enable && - pipe_config->base.planes_changed && - !needs_modeset(&pipe_config->base)) { - struct intel_crtc *intel_crtc = to_intel_crtc(set->crtc); - - /* - * We need to make sure the primary plane is re-enabled if it - * has previously been turned off. - */ - if (ret == 0 && !primary_plane_was_visible && - primary_plane_visible(set->crtc)) { - WARN_ON(!intel_crtc->active); - intel_post_enable_primary(set->crtc); - } - - /* - * In the fastboot case this may be our only check of the - * state after boot. It would be better to only do it on - * the first update, but we don't have a nice way of doing that - * (and really, set_config isn't used much for high freq page - * flipping, so increasing its cost here shouldn't be a big - * deal). - */ - if (i915.fastboot && ret == 0) - intel_modeset_check_state(set->crtc->dev); - } - if (ret) { DRM_DEBUG_KMS("failed to set mode on [CRTC:%d], err = %d\n", set->crtc->base.id, ret); @@ -13305,6 +13268,9 @@ intel_check_primary_plane(struct drm_plane *plane, */ if (IS_BROADWELL(dev)) intel_crtc->atomic.wait_vblank = true; + + if (crtc_state) + intel_crtc->atomic.post_enable_primary = true; } /* @@ -13317,6 +13283,10 @@ intel_check_primary_plane(struct drm_plane *plane, if (!state->visible || !fb) intel_crtc->atomic.disable_ips = true; + if (!state->visible && old_state->visible && + crtc_state && !needs_modeset(&crtc_state->base)) + intel_crtc->atomic.pre_disable_primary = true; + intel_crtc->atomic.fb_bits |= INTEL_FRONTBUFFER_PRIMARY(intel_crtc->pipe); @@ -15034,6 +15004,7 @@ static void intel_modeset_readout_hw_state(struct drm_device *dev) struct intel_plane_state *plane_state; memset(crtc->config, 0, sizeof(*crtc->config)); + crtc->config->base.crtc = &crtc->base; crtc->config->quirks |= PIPE_CONFIG_QUIRK_INHERITED_MODE; diff --git a/drivers/gpu/drm/i915/intel_dp.c b/drivers/gpu/drm/i915/intel_dp.c index 6e8faa253792..1df0e1fe235f 100644 --- a/drivers/gpu/drm/i915/intel_dp.c +++ b/drivers/gpu/drm/i915/intel_dp.c @@ -93,9 +93,6 @@ static const struct dp_link_dpll chv_dpll[] = { static const int skl_rates[] = { 162000, 216000, 270000, 324000, 432000, 540000 }; -static const int chv_rates[] = { 162000, 202500, 210000, 216000, - 243000, 270000, 324000, 405000, - 420000, 432000, 540000 }; static const int default_rates[] = { 162000, 270000, 540000 }; /** @@ -1169,24 +1166,31 @@ intel_dp_sink_rates(struct intel_dp *intel_dp, const int **sink_rates) return (intel_dp_max_link_bw(intel_dp) >> 3) + 1; } +static bool intel_dp_source_supports_hbr2(struct drm_device *dev) +{ + /* WaDisableHBR2:skl */ + if (IS_SKYLAKE(dev) && INTEL_REVID(dev) <= SKL_REVID_B0) + return false; + + if ((IS_HASWELL(dev) && !IS_HSW_ULX(dev)) || IS_BROADWELL(dev) || + (INTEL_INFO(dev)->gen >= 9)) + return true; + else + return false; +} + static int intel_dp_source_rates(struct drm_device *dev, const int **source_rates) { if (IS_SKYLAKE(dev)) { *source_rates = skl_rates; return ARRAY_SIZE(skl_rates); - } else if (IS_CHERRYVIEW(dev)) { - *source_rates = chv_rates; - return ARRAY_SIZE(chv_rates); } *source_rates = default_rates; - if (IS_SKYLAKE(dev) && INTEL_REVID(dev) <= SKL_REVID_B0) - /* WaDisableHBR2:skl */ - return (DP_LINK_BW_2_7 >> 3) + 1; - else if (INTEL_INFO(dev)->gen >= 8 || - (IS_HASWELL(dev) && !IS_HSW_ULX(dev))) + /* This depends on the fact that 5.4 is last value in the array */ + if (intel_dp_source_supports_hbr2(dev)) return (DP_LINK_BW_5_4 >> 3) + 1; else return (DP_LINK_BW_2_7 >> 3) + 1; @@ -3941,10 +3945,15 @@ intel_dp_get_dpcd(struct intel_dp *intel_dp) } } - /* Training Pattern 3 support, both source and sink */ + /* Training Pattern 3 support, Intel platforms that support HBR2 alone + * have support for TP3 hence that check is used along with dpcd check + * to ensure TP3 can be enabled. + * SKL < B0: due it's WaDisableHBR2 is the only exception where TP3 is + * supported but still not enabled. + */ if (intel_dp->dpcd[DP_DPCD_REV] >= 0x12 && intel_dp->dpcd[DP_MAX_LANE_COUNT] & DP_TPS3_SUPPORTED && - (IS_HASWELL(dev_priv) || INTEL_INFO(dev_priv)->gen >= 8)) { + intel_dp_source_supports_hbr2(dev)) { intel_dp->use_tps3 = true; DRM_DEBUG_KMS("Displayport TPS3 supported\n"); } else diff --git a/drivers/gpu/drm/i915/intel_lrc.c b/drivers/gpu/drm/i915/intel_lrc.c index 9b74ffae5f5a..7f2161a1ff5d 100644 --- a/drivers/gpu/drm/i915/intel_lrc.c +++ b/drivers/gpu/drm/i915/intel_lrc.c @@ -1012,6 +1012,8 @@ static int intel_lr_context_pin(struct intel_engine_cs *ring, ret = intel_pin_and_map_ringbuffer_obj(ring->dev, ringbuf); if (ret) goto unpin_ctx_obj; + + ctx_obj->dirty = true; } return ret; diff --git a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c index 52c22b026005..e10f9644140f 100644 --- a/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c +++ b/drivers/gpu/drm/nouveau/nvkm/engine/fifo/gk104.c @@ -166,30 +166,14 @@ gk104_fifo_context_attach(struct nvkm_object *parent, } static int -gk104_fifo_chan_kick(struct gk104_fifo_chan *chan) -{ - struct nvkm_object *obj = (void *)chan; - struct gk104_fifo_priv *priv = (void *)obj->engine; - - nv_wr32(priv, 0x002634, chan->base.chid); - if (!nv_wait(priv, 0x002634, 0x100000, 0x000000)) { - nv_error(priv, "channel %d [%s] kick timeout\n", - chan->base.chid, nvkm_client_name(chan)); - return -EBUSY; - } - - return 0; -} - -static int gk104_fifo_context_detach(struct nvkm_object *parent, bool suspend, struct nvkm_object *object) { struct nvkm_bar *bar = nvkm_bar(parent); + struct gk104_fifo_priv *priv = (void *)parent->engine; struct gk104_fifo_base *base = (void *)parent->parent; struct gk104_fifo_chan *chan = (void *)parent; u32 addr; - int ret; switch (nv_engidx(object->engine)) { case NVDEV_ENGINE_SW : return 0; @@ -204,9 +188,13 @@ gk104_fifo_context_detach(struct nvkm_object *parent, bool suspend, return -EINVAL; } - ret = gk104_fifo_chan_kick(chan); - if (ret && suspend) - return ret; + nv_wr32(priv, 0x002634, chan->base.chid); + if (!nv_wait(priv, 0x002634, 0xffffffff, chan->base.chid)) { + nv_error(priv, "channel %d [%s] kick timeout\n", + chan->base.chid, nvkm_client_name(chan)); + if (suspend) + return -EBUSY; + } if (addr) { nv_wo32(base, addr + 0x00, 0x00000000); @@ -331,7 +319,6 @@ gk104_fifo_chan_fini(struct nvkm_object *object, bool suspend) gk104_fifo_runlist_update(priv, chan->engine); } - gk104_fifo_chan_kick(chan); nv_wr32(priv, 0x800000 + (chid * 8), 0x00000000); return nvkm_fifo_channel_fini(&chan->base, suspend); } diff --git a/drivers/gpu/drm/radeon/radeon_irq_kms.c b/drivers/gpu/drm/radeon/radeon_irq_kms.c index 1162bfa464f3..171d3e43c30c 100644 --- a/drivers/gpu/drm/radeon/radeon_irq_kms.c +++ b/drivers/gpu/drm/radeon/radeon_irq_kms.c @@ -79,6 +79,11 @@ static void radeon_hotplug_work_func(struct work_struct *work) struct drm_mode_config *mode_config = &dev->mode_config; struct drm_connector *connector; + /* we can race here at startup, some boards seem to trigger + * hotplug irqs when they shouldn't. */ + if (!rdev->mode_info.mode_config_initialized) + return; + mutex_lock(&mode_config->mutex); if (mode_config->num_connector) { list_for_each_entry(connector, &mode_config->connector_list, head) diff --git a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c index 654c8daeb5ab..97ad3bcb99a7 100644 --- a/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c +++ b/drivers/gpu/drm/vmwgfx/vmwgfx_execbuf.c @@ -2492,7 +2492,7 @@ int vmw_execbuf_process(struct drm_file *file_priv, ret = ttm_eu_reserve_buffers(&ticket, &sw_context->validate_nodes, true, NULL); if (unlikely(ret != 0)) - goto out_err; + goto out_err_nores; ret = vmw_validate_buffers(dev_priv, sw_context); if (unlikely(ret != 0)) @@ -2536,6 +2536,7 @@ int vmw_execbuf_process(struct drm_file *file_priv, vmw_resource_relocations_free(&sw_context->res_relocations); vmw_fifo_commit(dev_priv, command_size); + mutex_unlock(&dev_priv->binding_mutex); vmw_query_bo_switch_commit(dev_priv, sw_context); ret = vmw_execbuf_fence_commands(file_priv, dev_priv, @@ -2551,7 +2552,6 @@ int vmw_execbuf_process(struct drm_file *file_priv, DRM_ERROR("Fence submission error. Syncing.\n"); vmw_resource_list_unreserve(&sw_context->resource_list, false); - mutex_unlock(&dev_priv->binding_mutex); ttm_eu_fence_buffer_objects(&ticket, &sw_context->validate_nodes, (void *) fence); diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index 3511bbaba505..e3c63640df73 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -462,12 +462,15 @@ out: static void hidinput_cleanup_battery(struct hid_device *dev) { + const struct power_supply_desc *psy_desc; + if (!dev->battery) return; + psy_desc = dev->battery->desc; power_supply_unregister(dev->battery); - kfree(dev->battery->desc->name); - kfree(dev->battery->desc); + kfree(psy_desc->name); + kfree(psy_desc); dev->battery = NULL; } #else /* !CONFIG_HID_BATTERY_STRENGTH */ diff --git a/drivers/hid/hid-uclogic.c b/drivers/hid/hid-uclogic.c index 94167310e15a..b905d501e752 100644 --- a/drivers/hid/hid-uclogic.c +++ b/drivers/hid/hid-uclogic.c @@ -858,7 +858,7 @@ static int uclogic_tablet_enable(struct hid_device *hdev) for (p = drvdata->rdesc; p <= drvdata->rdesc + drvdata->rsize - 4;) { if (p[0] == 0xFE && p[1] == 0xED && p[2] == 0x1D && - p[3] < sizeof(params)) { + p[3] < ARRAY_SIZE(params)) { v = params[p[3]]; put_unaligned(cpu_to_le32(v), (s32 *)p); p += 4; diff --git a/drivers/hid/wacom_sys.c b/drivers/hid/wacom_sys.c index 44958d79d598..01b937e63cf3 100644 --- a/drivers/hid/wacom_sys.c +++ b/drivers/hid/wacom_sys.c @@ -1284,6 +1284,39 @@ fail_register_pen_input: return error; } +/* + * Not all devices report physical dimensions from HID. + * Compute the default from hardcoded logical dimension + * and resolution before driver overwrites them. + */ +static void wacom_set_default_phy(struct wacom_features *features) +{ + if (features->x_resolution) { + features->x_phy = (features->x_max * 100) / + features->x_resolution; + features->y_phy = (features->y_max * 100) / + features->y_resolution; + } +} + +static void wacom_calculate_res(struct wacom_features *features) +{ + /* set unit to "100th of a mm" for devices not reported by HID */ + if (!features->unit) { + features->unit = 0x11; + features->unitExpo = -3; + } + + features->x_resolution = wacom_calc_hid_res(features->x_max, + features->x_phy, + features->unit, + features->unitExpo); + features->y_resolution = wacom_calc_hid_res(features->y_max, + features->y_phy, + features->unit, + features->unitExpo); +} + static void wacom_wireless_work(struct work_struct *work) { struct wacom *wacom = container_of(work, struct wacom, work); @@ -1341,6 +1374,8 @@ static void wacom_wireless_work(struct work_struct *work) if (wacom_wac1->features.type != INTUOSHT && wacom_wac1->features.type != BAMBOO_PT) wacom_wac1->features.device_type |= WACOM_DEVICETYPE_PAD; + wacom_set_default_phy(&wacom_wac1->features); + wacom_calculate_res(&wacom_wac1->features); snprintf(wacom_wac1->pen_name, WACOM_NAME_MAX, "%s (WL) Pen", wacom_wac1->features.name); snprintf(wacom_wac1->pad_name, WACOM_NAME_MAX, "%s (WL) Pad", @@ -1359,7 +1394,9 @@ static void wacom_wireless_work(struct work_struct *work) wacom_wac2->features = *((struct wacom_features *)id->driver_data); wacom_wac2->features.pktlen = WACOM_PKGLEN_BBTOUCH3; + wacom_set_default_phy(&wacom_wac2->features); wacom_wac2->features.x_max = wacom_wac2->features.y_max = 4096; + wacom_calculate_res(&wacom_wac2->features); snprintf(wacom_wac2->touch_name, WACOM_NAME_MAX, "%s (WL) Finger",wacom_wac2->features.name); snprintf(wacom_wac2->pad_name, WACOM_NAME_MAX, @@ -1407,39 +1444,6 @@ void wacom_battery_work(struct work_struct *work) } } -/* - * Not all devices report physical dimensions from HID. - * Compute the default from hardcoded logical dimension - * and resolution before driver overwrites them. - */ -static void wacom_set_default_phy(struct wacom_features *features) -{ - if (features->x_resolution) { - features->x_phy = (features->x_max * 100) / - features->x_resolution; - features->y_phy = (features->y_max * 100) / - features->y_resolution; - } -} - -static void wacom_calculate_res(struct wacom_features *features) -{ - /* set unit to "100th of a mm" for devices not reported by HID */ - if (!features->unit) { - features->unit = 0x11; - features->unitExpo = -3; - } - - features->x_resolution = wacom_calc_hid_res(features->x_max, - features->x_phy, - features->unit, - features->unitExpo); - features->y_resolution = wacom_calc_hid_res(features->y_max, - features->y_phy, - features->unit, - features->unitExpo); -} - static size_t wacom_compute_pktlen(struct hid_device *hdev) { struct hid_report_enum *report_enum; diff --git a/drivers/hv/channel.c b/drivers/hv/channel.c index 603ce97e9027..c4dcab048cb8 100644 --- a/drivers/hv/channel.c +++ b/drivers/hv/channel.c @@ -601,6 +601,7 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, u64 aligned_data = 0; int ret; bool signal = false; + int num_vecs = ((bufferlen != 0) ? 3 : 1); /* Setup the descriptor */ @@ -618,7 +619,8 @@ int vmbus_sendpacket_ctl(struct vmbus_channel *channel, void *buffer, bufferlist[2].iov_base = &aligned_data; bufferlist[2].iov_len = (packetlen_aligned - packetlen); - ret = hv_ringbuffer_write(&channel->outbound, bufferlist, 3, &signal); + ret = hv_ringbuffer_write(&channel->outbound, bufferlist, num_vecs, + &signal); /* * Signalling the host is conditional on many factors: diff --git a/drivers/hv/channel_mgmt.c b/drivers/hv/channel_mgmt.c index 4506a6623618..2f9aead4ecfc 100644 --- a/drivers/hv/channel_mgmt.c +++ b/drivers/hv/channel_mgmt.c @@ -347,6 +347,7 @@ enum { IDE = 0, SCSI, NIC, + ND_NIC, MAX_PERF_CHN, }; @@ -391,6 +392,7 @@ static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_gui struct vmbus_channel *primary = channel->primary_channel; int next_node; struct cpumask available_mask; + struct cpumask *alloced_mask; for (i = IDE; i < MAX_PERF_CHN; i++) { if (!memcmp(type_guid->b, hp_devs[i].guid, @@ -408,7 +410,6 @@ static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_gui * channel, bind it to cpu 0. */ channel->numa_node = 0; - cpumask_set_cpu(0, &channel->alloced_cpus_in_node); channel->target_cpu = 0; channel->target_vp = hv_context.vp_index[0]; return; @@ -433,21 +434,38 @@ static void init_vp_index(struct vmbus_channel *channel, const uuid_le *type_gui channel->numa_node = next_node; primary = channel; } + alloced_mask = &hv_context.hv_numa_map[primary->numa_node]; - if (cpumask_weight(&primary->alloced_cpus_in_node) == + if (cpumask_weight(alloced_mask) == cpumask_weight(cpumask_of_node(primary->numa_node))) { /* * We have cycled through all the CPUs in the node; * reset the alloced map. */ - cpumask_clear(&primary->alloced_cpus_in_node); + cpumask_clear(alloced_mask); } - cpumask_xor(&available_mask, &primary->alloced_cpus_in_node, + cpumask_xor(&available_mask, alloced_mask, cpumask_of_node(primary->numa_node)); - cur_cpu = cpumask_next(-1, &available_mask); - cpumask_set_cpu(cur_cpu, &primary->alloced_cpus_in_node); + cur_cpu = -1; + while (true) { + cur_cpu = cpumask_next(cur_cpu, &available_mask); + if (cur_cpu >= nr_cpu_ids) { + cur_cpu = -1; + cpumask_copy(&available_mask, + cpumask_of_node(primary->numa_node)); + continue; + } + + if (!cpumask_test_cpu(cur_cpu, + &primary->alloced_cpus_in_node)) { + cpumask_set_cpu(cur_cpu, + &primary->alloced_cpus_in_node); + cpumask_set_cpu(cur_cpu, alloced_mask); + break; + } + } channel->target_cpu = cur_cpu; channel->target_vp = hv_context.vp_index[cur_cpu]; @@ -469,6 +487,10 @@ void vmbus_initiate_unload(void) { struct vmbus_channel_message_header hdr; + /* Pre-Win2012R2 hosts don't support reconnect */ + if (vmbus_proto_version < VERSION_WIN8_1) + return; + init_completion(&vmbus_connection.unload_event); memset(&hdr, 0, sizeof(struct vmbus_channel_message_header)); hdr.msgtype = CHANNELMSG_UNLOAD; diff --git a/drivers/hv/hv.c b/drivers/hv/hv.c index d3943bceecc3..6341be8739ae 100644 --- a/drivers/hv/hv.c +++ b/drivers/hv/hv.c @@ -93,11 +93,14 @@ static int query_hypervisor_info(void) */ static u64 do_hypercall(u64 control, void *input, void *output) { -#ifdef CONFIG_X86_64 - u64 hv_status = 0; u64 input_address = (input) ? virt_to_phys(input) : 0; u64 output_address = (output) ? virt_to_phys(output) : 0; void *hypercall_page = hv_context.hypercall_page; +#ifdef CONFIG_X86_64 + u64 hv_status = 0; + + if (!hypercall_page) + return (u64)ULLONG_MAX; __asm__ __volatile__("mov %0, %%r8" : : "r" (output_address) : "r8"); __asm__ __volatile__("call *%3" : "=a" (hv_status) : @@ -112,13 +115,13 @@ static u64 do_hypercall(u64 control, void *input, void *output) u32 control_lo = control & 0xFFFFFFFF; u32 hv_status_hi = 1; u32 hv_status_lo = 1; - u64 input_address = (input) ? virt_to_phys(input) : 0; u32 input_address_hi = input_address >> 32; u32 input_address_lo = input_address & 0xFFFFFFFF; - u64 output_address = (output) ? virt_to_phys(output) : 0; u32 output_address_hi = output_address >> 32; u32 output_address_lo = output_address & 0xFFFFFFFF; - void *hypercall_page = hv_context.hypercall_page; + + if (!hypercall_page) + return (u64)ULLONG_MAX; __asm__ __volatile__ ("call *%8" : "=d"(hv_status_hi), "=a"(hv_status_lo) : "d" (control_hi), @@ -130,6 +133,56 @@ static u64 do_hypercall(u64 control, void *input, void *output) #endif /* !x86_64 */ } +#ifdef CONFIG_X86_64 +static cycle_t read_hv_clock_tsc(struct clocksource *arg) +{ + cycle_t current_tick; + struct ms_hyperv_tsc_page *tsc_pg = hv_context.tsc_page; + + if (tsc_pg->tsc_sequence != -1) { + /* + * Use the tsc page to compute the value. + */ + + while (1) { + cycle_t tmp; + u32 sequence = tsc_pg->tsc_sequence; + u64 cur_tsc; + u64 scale = tsc_pg->tsc_scale; + s64 offset = tsc_pg->tsc_offset; + + rdtscll(cur_tsc); + /* current_tick = ((cur_tsc *scale) >> 64) + offset */ + asm("mulq %3" + : "=d" (current_tick), "=a" (tmp) + : "a" (cur_tsc), "r" (scale)); + + current_tick += offset; + if (tsc_pg->tsc_sequence == sequence) + return current_tick; + + if (tsc_pg->tsc_sequence != -1) + continue; + /* + * Fallback using MSR method. + */ + break; + } + } + rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); + return current_tick; +} + +static struct clocksource hyperv_cs_tsc = { + .name = "hyperv_clocksource_tsc_page", + .rating = 425, + .read = read_hv_clock_tsc, + .mask = CLOCKSOURCE_MASK(64), + .flags = CLOCK_SOURCE_IS_CONTINUOUS, +}; +#endif + + /* * hv_init - Main initialization routine. * @@ -139,7 +192,9 @@ int hv_init(void) { int max_leaf; union hv_x64_msr_hypercall_contents hypercall_msr; + union hv_x64_msr_hypercall_contents tsc_msr; void *virtaddr = NULL; + void *va_tsc = NULL; memset(hv_context.synic_event_page, 0, sizeof(void *) * NR_CPUS); memset(hv_context.synic_message_page, 0, @@ -183,6 +238,22 @@ int hv_init(void) hv_context.hypercall_page = virtaddr; +#ifdef CONFIG_X86_64 + if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { + va_tsc = __vmalloc(PAGE_SIZE, GFP_KERNEL, PAGE_KERNEL); + if (!va_tsc) + goto cleanup; + hv_context.tsc_page = va_tsc; + + rdmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); + + tsc_msr.enable = 1; + tsc_msr.guest_physical_address = vmalloc_to_pfn(va_tsc); + + wrmsrl(HV_X64_MSR_REFERENCE_TSC, tsc_msr.as_uint64); + clocksource_register_hz(&hyperv_cs_tsc, NSEC_PER_SEC/100); + } +#endif return 0; cleanup: @@ -216,6 +287,21 @@ void hv_cleanup(void) vfree(hv_context.hypercall_page); hv_context.hypercall_page = NULL; } + +#ifdef CONFIG_X86_64 + /* + * Cleanup the TSC page based CS. + */ + if (ms_hyperv.features & HV_X64_MSR_REFERENCE_TSC_AVAILABLE) { + clocksource_change_rating(&hyperv_cs_tsc, 10); + clocksource_unregister(&hyperv_cs_tsc); + + hypercall_msr.as_uint64 = 0; + wrmsrl(HV_X64_MSR_REFERENCE_TSC, hypercall_msr.as_uint64); + vfree(hv_context.tsc_page); + hv_context.tsc_page = NULL; + } +#endif } /* @@ -271,7 +357,7 @@ static int hv_ce_set_next_event(unsigned long delta, { cycle_t current_tick; - WARN_ON(evt->mode != CLOCK_EVT_MODE_ONESHOT); + WARN_ON(!clockevent_state_oneshot(evt)); rdmsrl(HV_X64_MSR_TIME_REF_COUNT, current_tick); current_tick += delta; @@ -279,31 +365,24 @@ static int hv_ce_set_next_event(unsigned long delta, return 0; } -static void hv_ce_setmode(enum clock_event_mode mode, - struct clock_event_device *evt) +static int hv_ce_shutdown(struct clock_event_device *evt) +{ + wrmsrl(HV_X64_MSR_STIMER0_COUNT, 0); + wrmsrl(HV_X64_MSR_STIMER0_CONFIG, 0); + + return 0; +} + +static int hv_ce_set_oneshot(struct clock_event_device *evt) { union hv_timer_config timer_cfg; - switch (mode) { - case CLOCK_EVT_MODE_PERIODIC: - /* unsupported */ - break; - - case CLOCK_EVT_MODE_ONESHOT: - timer_cfg.enable = 1; - timer_cfg.auto_enable = 1; - timer_cfg.sintx = VMBUS_MESSAGE_SINT; - wrmsrl(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); - break; - - case CLOCK_EVT_MODE_UNUSED: - case CLOCK_EVT_MODE_SHUTDOWN: - wrmsrl(HV_X64_MSR_STIMER0_COUNT, 0); - wrmsrl(HV_X64_MSR_STIMER0_CONFIG, 0); - break; - case CLOCK_EVT_MODE_RESUME: - break; - } + timer_cfg.enable = 1; + timer_cfg.auto_enable = 1; + timer_cfg.sintx = VMBUS_MESSAGE_SINT; + wrmsrl(HV_X64_MSR_STIMER0_CONFIG, timer_cfg.as_uint64); + + return 0; } static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu) @@ -318,7 +397,8 @@ static void hv_init_clockevent_device(struct clock_event_device *dev, int cpu) * references to the hv_vmbus module making it impossible to unload. */ - dev->set_mode = hv_ce_setmode; + dev->set_state_shutdown = hv_ce_shutdown; + dev->set_state_oneshot = hv_ce_set_oneshot; dev->set_next_event = hv_ce_set_next_event; } @@ -329,6 +409,13 @@ int hv_synic_alloc(void) size_t ced_size = sizeof(struct clock_event_device); int cpu; + hv_context.hv_numa_map = kzalloc(sizeof(struct cpumask) * nr_node_ids, + GFP_ATOMIC); + if (hv_context.hv_numa_map == NULL) { + pr_err("Unable to allocate NUMA map\n"); + goto err; + } + for_each_online_cpu(cpu) { hv_context.event_dpc[cpu] = kmalloc(size, GFP_ATOMIC); if (hv_context.event_dpc[cpu] == NULL) { @@ -342,6 +429,7 @@ int hv_synic_alloc(void) pr_err("Unable to allocate clock event device\n"); goto err; } + hv_init_clockevent_device(hv_context.clk_evt[cpu], cpu); hv_context.synic_message_page[cpu] = @@ -390,6 +478,7 @@ void hv_synic_free(void) { int cpu; + kfree(hv_context.hv_numa_map); for_each_online_cpu(cpu) hv_synic_free_cpu(cpu); } @@ -503,8 +592,7 @@ void hv_synic_cleanup(void *arg) /* Turn off clockevent device */ if (ms_hyperv.features & HV_X64_MSR_SYNTIMER_AVAILABLE) - hv_ce_setmode(CLOCK_EVT_MODE_SHUTDOWN, - hv_context.clk_evt[cpu]); + hv_ce_shutdown(hv_context.clk_evt[cpu]); rdmsrl(HV_X64_MSR_SINT0 + VMBUS_MESSAGE_SINT, shared_sint.as_uint64); @@ -530,6 +618,4 @@ void hv_synic_cleanup(void *arg) rdmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); sctrl.enable = 0; wrmsrl(HV_X64_MSR_SCONTROL, sctrl.as_uint64); - - hv_synic_free_cpu(cpu); } diff --git a/drivers/hv/hv_balloon.c b/drivers/hv/hv_balloon.c index 8a725cd69ad7..b853b4b083bd 100644 --- a/drivers/hv/hv_balloon.c +++ b/drivers/hv/hv_balloon.c @@ -62,11 +62,13 @@ enum { DYNMEM_PROTOCOL_VERSION_1 = DYNMEM_MAKE_VERSION(0, 3), DYNMEM_PROTOCOL_VERSION_2 = DYNMEM_MAKE_VERSION(1, 0), + DYNMEM_PROTOCOL_VERSION_3 = DYNMEM_MAKE_VERSION(2, 0), DYNMEM_PROTOCOL_VERSION_WIN7 = DYNMEM_PROTOCOL_VERSION_1, DYNMEM_PROTOCOL_VERSION_WIN8 = DYNMEM_PROTOCOL_VERSION_2, + DYNMEM_PROTOCOL_VERSION_WIN10 = DYNMEM_PROTOCOL_VERSION_3, - DYNMEM_PROTOCOL_VERSION_CURRENT = DYNMEM_PROTOCOL_VERSION_WIN8 + DYNMEM_PROTOCOL_VERSION_CURRENT = DYNMEM_PROTOCOL_VERSION_WIN10 }; @@ -1296,13 +1298,25 @@ static void version_resp(struct hv_dynmem_device *dm, if (dm->next_version == 0) goto version_error; - dm->next_version = 0; memset(&version_req, 0, sizeof(struct dm_version_request)); version_req.hdr.type = DM_VERSION_REQUEST; version_req.hdr.size = sizeof(struct dm_version_request); version_req.hdr.trans_id = atomic_inc_return(&trans_id); - version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN7; - version_req.is_last_attempt = 1; + version_req.version.version = dm->next_version; + + /* + * Set the next version to try in case current version fails. + * Win7 protocol ought to be the last one to try. + */ + switch (version_req.version.version) { + case DYNMEM_PROTOCOL_VERSION_WIN8: + dm->next_version = DYNMEM_PROTOCOL_VERSION_WIN7; + version_req.is_last_attempt = 0; + break; + default: + dm->next_version = 0; + version_req.is_last_attempt = 1; + } ret = vmbus_sendpacket(dm->dev->channel, &version_req, sizeof(struct dm_version_request), @@ -1442,7 +1456,7 @@ static int balloon_probe(struct hv_device *dev, dm_device.dev = dev; dm_device.state = DM_INITIALIZING; - dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN7; + dm_device.next_version = DYNMEM_PROTOCOL_VERSION_WIN8; init_completion(&dm_device.host_event); init_completion(&dm_device.config_event); INIT_LIST_HEAD(&dm_device.ha_region_list); @@ -1474,7 +1488,7 @@ static int balloon_probe(struct hv_device *dev, version_req.hdr.type = DM_VERSION_REQUEST; version_req.hdr.size = sizeof(struct dm_version_request); version_req.hdr.trans_id = atomic_inc_return(&trans_id); - version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN8; + version_req.version.version = DYNMEM_PROTOCOL_VERSION_WIN10; version_req.is_last_attempt = 0; ret = vmbus_sendpacket(dev->channel, &version_req, diff --git a/drivers/hv/hv_fcopy.c b/drivers/hv/hv_fcopy.c index b50dd330cf31..db4b887b889d 100644 --- a/drivers/hv/hv_fcopy.c +++ b/drivers/hv/hv_fcopy.c @@ -116,7 +116,7 @@ static int fcopy_handle_handshake(u32 version) static void fcopy_send_data(struct work_struct *dummy) { - struct hv_start_fcopy smsg_out; + struct hv_start_fcopy *smsg_out = NULL; int operation = fcopy_transaction.fcopy_msg->operation; struct hv_start_fcopy *smsg_in; void *out_src; @@ -136,21 +136,24 @@ static void fcopy_send_data(struct work_struct *dummy) switch (operation) { case START_FILE_COPY: out_len = sizeof(struct hv_start_fcopy); - memset(&smsg_out, 0, out_len); - smsg_out.hdr.operation = operation; + smsg_out = kzalloc(sizeof(*smsg_out), GFP_KERNEL); + if (!smsg_out) + return; + + smsg_out->hdr.operation = operation; smsg_in = (struct hv_start_fcopy *)fcopy_transaction.fcopy_msg; utf16s_to_utf8s((wchar_t *)smsg_in->file_name, W_MAX_PATH, UTF16_LITTLE_ENDIAN, - (__u8 *)&smsg_out.file_name, W_MAX_PATH - 1); + (__u8 *)&smsg_out->file_name, W_MAX_PATH - 1); utf16s_to_utf8s((wchar_t *)smsg_in->path_name, W_MAX_PATH, UTF16_LITTLE_ENDIAN, - (__u8 *)&smsg_out.path_name, W_MAX_PATH - 1); + (__u8 *)&smsg_out->path_name, W_MAX_PATH - 1); - smsg_out.copy_flags = smsg_in->copy_flags; - smsg_out.file_size = smsg_in->file_size; - out_src = &smsg_out; + smsg_out->copy_flags = smsg_in->copy_flags; + smsg_out->file_size = smsg_in->file_size; + out_src = smsg_out; break; default: @@ -168,6 +171,8 @@ static void fcopy_send_data(struct work_struct *dummy) fcopy_transaction.state = HVUTIL_READY; } } + kfree(smsg_out); + return; } diff --git a/drivers/hv/hv_kvp.c b/drivers/hv/hv_kvp.c index d85798d5992c..74c38a9f34a6 100644 --- a/drivers/hv/hv_kvp.c +++ b/drivers/hv/hv_kvp.c @@ -353,6 +353,9 @@ kvp_send_key(struct work_struct *dummy) return; message = kzalloc(sizeof(*message), GFP_KERNEL); + if (!message) + return; + message->kvp_hdr.operation = operation; message->kvp_hdr.pool = pool; in_msg = kvp_transaction.kvp_msg; diff --git a/drivers/hv/hv_utils_transport.c b/drivers/hv/hv_utils_transport.c index ea7ba5ef16a9..6a9d80a5332d 100644 --- a/drivers/hv/hv_utils_transport.c +++ b/drivers/hv/hv_utils_transport.c @@ -186,7 +186,7 @@ int hvutil_transport_send(struct hvutil_transport *hvt, void *msg, int len) return -EINVAL; } else if (hvt->mode == HVUTIL_TRANSPORT_NETLINK) { cn_msg = kzalloc(sizeof(*cn_msg) + len, GFP_ATOMIC); - if (!msg) + if (!cn_msg) return -ENOMEM; cn_msg->id.idx = hvt->cn_id.idx; cn_msg->id.val = hvt->cn_id.val; diff --git a/drivers/hv/hyperv_vmbus.h b/drivers/hv/hyperv_vmbus.h index cddc0c9f6bf9..3d70e36c918e 100644 --- a/drivers/hv/hyperv_vmbus.h +++ b/drivers/hv/hyperv_vmbus.h @@ -141,7 +141,7 @@ struct hv_port_info { struct { u32 target_sint; u32 target_vp; - u16 base_flag_bumber; + u16 base_flag_number; u16 flag_count; u32 rsvdz; } event_port_info; @@ -517,6 +517,7 @@ struct hv_context { u64 guestid; void *hypercall_page; + void *tsc_page; bool synic_initialized; @@ -551,10 +552,23 @@ struct hv_context { * Support PV clockevent device. */ struct clock_event_device *clk_evt[NR_CPUS]; + /* + * To manage allocations in a NUMA node. + * Array indexed by numa node ID. + */ + struct cpumask *hv_numa_map; }; extern struct hv_context hv_context; +struct ms_hyperv_tsc_page { + volatile u32 tsc_sequence; + u32 reserved1; + volatile u64 tsc_scale; + volatile s64 tsc_offset; + u64 reserved2[509]; +}; + struct hv_ring_buffer_debug_info { u32 current_interrupt_mask; u32 current_read_index; diff --git a/drivers/hv/ring_buffer.c b/drivers/hv/ring_buffer.c index 6361d124f67d..70a1a9a22f87 100644 --- a/drivers/hv/ring_buffer.c +++ b/drivers/hv/ring_buffer.c @@ -103,10 +103,9 @@ static bool hv_need_to_signal(u32 old_write, struct hv_ring_buffer_info *rbi) * there is room for the producer to send the pending packet. */ -static bool hv_need_to_signal_on_read(u32 old_rd, - struct hv_ring_buffer_info *rbi) +static bool hv_need_to_signal_on_read(u32 prev_write_sz, + struct hv_ring_buffer_info *rbi) { - u32 prev_write_sz; u32 cur_write_sz; u32 r_size; u32 write_loc = rbi->ring_buffer->write_index; @@ -123,10 +122,6 @@ static bool hv_need_to_signal_on_read(u32 old_rd, cur_write_sz = write_loc >= read_loc ? r_size - (write_loc - read_loc) : read_loc - write_loc; - prev_write_sz = write_loc >= old_rd ? r_size - (write_loc - old_rd) : - old_rd - write_loc; - - if ((prev_write_sz < pending_sz) && (cur_write_sz >= pending_sz)) return true; @@ -517,7 +512,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, u32 next_read_location = 0; u64 prev_indices = 0; unsigned long flags; - u32 old_read; if (buflen <= 0) return -EINVAL; @@ -528,8 +522,6 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, &bytes_avail_toread, &bytes_avail_towrite); - old_read = bytes_avail_toread; - /* Make sure there is something to read */ if (bytes_avail_toread < buflen) { spin_unlock_irqrestore(&inring_info->ring_lock, flags); @@ -560,7 +552,7 @@ int hv_ringbuffer_read(struct hv_ring_buffer_info *inring_info, void *buffer, spin_unlock_irqrestore(&inring_info->ring_lock, flags); - *signal = hv_need_to_signal_on_read(old_read, inring_info); + *signal = hv_need_to_signal_on_read(bytes_avail_towrite, inring_info); return 0; } diff --git a/drivers/hv/vmbus_drv.c b/drivers/hv/vmbus_drv.c index cf204005ee78..f19b6f7a467a 100644 --- a/drivers/hv/vmbus_drv.c +++ b/drivers/hv/vmbus_drv.c @@ -39,6 +39,8 @@ #include <asm/mshyperv.h> #include <linux/notifier.h> #include <linux/ptrace.h> +#include <linux/screen_info.h> +#include <linux/kdebug.h> #include "hyperv_vmbus.h" static struct acpi_device *hv_acpi_dev; @@ -48,12 +50,18 @@ static struct completion probe_event; static int irq; -static int hyperv_panic_event(struct notifier_block *nb, - unsigned long event, void *ptr) +static void hyperv_report_panic(struct pt_regs *regs) { - struct pt_regs *regs; + static bool panic_reported; - regs = current_pt_regs(); + /* + * We prefer to report panic on 'die' chain as we have proper + * registers to report, but if we miss it (e.g. on BUG()) we need + * to report it on 'panic'. + */ + if (panic_reported) + return; + panic_reported = true; wrmsrl(HV_X64_MSR_CRASH_P0, regs->ip); wrmsrl(HV_X64_MSR_CRASH_P1, regs->ax); @@ -65,18 +73,37 @@ static int hyperv_panic_event(struct notifier_block *nb, * Let Hyper-V know there is crash data available */ wrmsrl(HV_X64_MSR_CRASH_CTL, HV_CRASH_CTL_CRASH_NOTIFY); +} + +static int hyperv_panic_event(struct notifier_block *nb, unsigned long val, + void *args) +{ + struct pt_regs *regs; + + regs = current_pt_regs(); + + hyperv_report_panic(regs); return NOTIFY_DONE; } +static int hyperv_die_event(struct notifier_block *nb, unsigned long val, + void *args) +{ + struct die_args *die = (struct die_args *)args; + struct pt_regs *regs = die->regs; + + hyperv_report_panic(regs); + return NOTIFY_DONE; +} + +static struct notifier_block hyperv_die_block = { + .notifier_call = hyperv_die_event, +}; static struct notifier_block hyperv_panic_block = { .notifier_call = hyperv_panic_event, }; -struct resource hyperv_mmio = { - .name = "hyperv mmio", - .flags = IORESOURCE_MEM, -}; -EXPORT_SYMBOL_GPL(hyperv_mmio); +struct resource *hyperv_mmio; static int vmbus_exists(void) { @@ -414,6 +441,43 @@ static ssize_t in_write_bytes_avail_show(struct device *dev, } static DEVICE_ATTR_RO(in_write_bytes_avail); +static ssize_t channel_vp_mapping_show(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct hv_device *hv_dev = device_to_hv_device(dev); + struct vmbus_channel *channel = hv_dev->channel, *cur_sc; + unsigned long flags; + int buf_size = PAGE_SIZE, n_written, tot_written; + struct list_head *cur; + + if (!channel) + return -ENODEV; + + tot_written = snprintf(buf, buf_size, "%u:%u\n", + channel->offermsg.child_relid, channel->target_cpu); + + spin_lock_irqsave(&channel->lock, flags); + + list_for_each(cur, &channel->sc_list) { + if (tot_written >= buf_size - 1) + break; + + cur_sc = list_entry(cur, struct vmbus_channel, sc_list); + n_written = scnprintf(buf + tot_written, + buf_size - tot_written, + "%u:%u\n", + cur_sc->offermsg.child_relid, + cur_sc->target_cpu); + tot_written += n_written; + } + + spin_unlock_irqrestore(&channel->lock, flags); + + return tot_written; +} +static DEVICE_ATTR_RO(channel_vp_mapping); + /* Set up per device attributes in /sys/bus/vmbus/devices/<bus device> */ static struct attribute *vmbus_attrs[] = { &dev_attr_id.attr, @@ -438,6 +502,7 @@ static struct attribute *vmbus_attrs[] = { &dev_attr_in_write_index.attr, &dev_attr_in_read_bytes_avail.attr, &dev_attr_in_write_bytes_avail.attr, + &dev_attr_channel_vp_mapping.attr, NULL, }; ATTRIBUTE_GROUPS(vmbus); @@ -763,38 +828,6 @@ static void vmbus_isr(void) } } -#ifdef CONFIG_HOTPLUG_CPU -static int hyperv_cpu_disable(void) -{ - return -ENOSYS; -} - -static void hv_cpu_hotplug_quirk(bool vmbus_loaded) -{ - static void *previous_cpu_disable; - - /* - * Offlining a CPU when running on newer hypervisors (WS2012R2, Win8, - * ...) is not supported at this moment as channel interrupts are - * distributed across all of them. - */ - - if ((vmbus_proto_version == VERSION_WS2008) || - (vmbus_proto_version == VERSION_WIN7)) - return; - - if (vmbus_loaded) { - previous_cpu_disable = smp_ops.cpu_disable; - smp_ops.cpu_disable = hyperv_cpu_disable; - pr_notice("CPU offlining is not supported by hypervisor\n"); - } else if (previous_cpu_disable) - smp_ops.cpu_disable = previous_cpu_disable; -} -#else -static void hv_cpu_hotplug_quirk(bool vmbus_loaded) -{ -} -#endif /* * vmbus_bus_init -Main vmbus driver initialization routine. @@ -836,12 +869,14 @@ static int vmbus_bus_init(int irq) if (ret) goto err_alloc; - hv_cpu_hotplug_quirk(true); + if (vmbus_proto_version > VERSION_WIN7) + cpu_hotplug_disable(); /* * Only register if the crash MSRs are available */ - if (ms_hyperv.features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { + if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { + register_die_notifier(&hyperv_die_block); atomic_notifier_chain_register(&panic_notifier_list, &hyperv_panic_block); } @@ -863,8 +898,8 @@ err_cleanup: } /** - * __vmbus_child_driver_register - Register a vmbus's driver - * @drv: Pointer to driver structure you want to register + * __vmbus_child_driver_register() - Register a vmbus's driver + * @hv_driver: Pointer to driver structure you want to register * @owner: owner module of the drv * @mod_name: module name string * @@ -896,7 +931,8 @@ EXPORT_SYMBOL_GPL(__vmbus_driver_register); /** * vmbus_driver_unregister() - Unregister a vmbus's driver - * @drv: Pointer to driver structure you want to un-register + * @hv_driver: Pointer to driver structure you want to + * un-register * * Un-register the given driver that was previous registered with a call to * vmbus_driver_register() @@ -982,30 +1018,184 @@ void vmbus_device_unregister(struct hv_device *device_obj) /* - * VMBUS is an acpi enumerated device. Get the the information we + * VMBUS is an acpi enumerated device. Get the information we * need from DSDT. */ - +#define VTPM_BASE_ADDRESS 0xfed40000 static acpi_status vmbus_walk_resources(struct acpi_resource *res, void *ctx) { + resource_size_t start = 0; + resource_size_t end = 0; + struct resource *new_res; + struct resource **old_res = &hyperv_mmio; + struct resource **prev_res = NULL; + switch (res->type) { case ACPI_RESOURCE_TYPE_IRQ: irq = res->data.irq.interrupts[0]; + return AE_OK; + + /* + * "Address" descriptors are for bus windows. Ignore + * "memory" descriptors, which are for registers on + * devices. + */ + case ACPI_RESOURCE_TYPE_ADDRESS32: + start = res->data.address32.address.minimum; + end = res->data.address32.address.maximum; break; case ACPI_RESOURCE_TYPE_ADDRESS64: - hyperv_mmio.start = res->data.address64.address.minimum; - hyperv_mmio.end = res->data.address64.address.maximum; + start = res->data.address64.address.minimum; + end = res->data.address64.address.maximum; break; + + default: + /* Unused resource type */ + return AE_OK; + } + /* + * Ignore ranges that are below 1MB, as they're not + * necessary or useful here. + */ + if (end < 0x100000) + return AE_OK; + + new_res = kzalloc(sizeof(*new_res), GFP_ATOMIC); + if (!new_res) + return AE_NO_MEMORY; + + /* If this range overlaps the virtual TPM, truncate it. */ + if (end > VTPM_BASE_ADDRESS && start < VTPM_BASE_ADDRESS) + end = VTPM_BASE_ADDRESS; + + new_res->name = "hyperv mmio"; + new_res->flags = IORESOURCE_MEM; + new_res->start = start; + new_res->end = end; + + do { + if (!*old_res) { + *old_res = new_res; + break; + } + + if ((*old_res)->end < new_res->start) { + new_res->sibling = *old_res; + if (prev_res) + (*prev_res)->sibling = new_res; + *old_res = new_res; + break; + } + + prev_res = old_res; + old_res = &(*old_res)->sibling; + + } while (1); return AE_OK; } +static int vmbus_acpi_remove(struct acpi_device *device) +{ + struct resource *cur_res; + struct resource *next_res; + + if (hyperv_mmio) { + for (cur_res = hyperv_mmio; cur_res; cur_res = next_res) { + next_res = cur_res->sibling; + kfree(cur_res); + } + } + + return 0; +} + +/** + * vmbus_allocate_mmio() - Pick a memory-mapped I/O range. + * @new: If successful, supplied a pointer to the + * allocated MMIO space. + * @device_obj: Identifies the caller + * @min: Minimum guest physical address of the + * allocation + * @max: Maximum guest physical address + * @size: Size of the range to be allocated + * @align: Alignment of the range to be allocated + * @fb_overlap_ok: Whether this allocation can be allowed + * to overlap the video frame buffer. + * + * This function walks the resources granted to VMBus by the + * _CRS object in the ACPI namespace underneath the parent + * "bridge" whether that's a root PCI bus in the Generation 1 + * case or a Module Device in the Generation 2 case. It then + * attempts to allocate from the global MMIO pool in a way that + * matches the constraints supplied in these parameters and by + * that _CRS. + * + * Return: 0 on success, -errno on failure + */ +int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, + resource_size_t min, resource_size_t max, + resource_size_t size, resource_size_t align, + bool fb_overlap_ok) +{ + struct resource *iter; + resource_size_t range_min, range_max, start, local_min, local_max; + const char *dev_n = dev_name(&device_obj->device); + u32 fb_end = screen_info.lfb_base + (screen_info.lfb_size << 1); + int i; + + for (iter = hyperv_mmio; iter; iter = iter->sibling) { + if ((iter->start >= max) || (iter->end <= min)) + continue; + + range_min = iter->start; + range_max = iter->end; + + /* If this range overlaps the frame buffer, split it into + two tries. */ + for (i = 0; i < 2; i++) { + local_min = range_min; + local_max = range_max; + if (fb_overlap_ok || (range_min >= fb_end) || + (range_max <= screen_info.lfb_base)) { + i++; + } else { + if ((range_min <= screen_info.lfb_base) && + (range_max >= screen_info.lfb_base)) { + /* + * The frame buffer is in this window, + * so trim this into the part that + * preceeds the frame buffer. + */ + local_max = screen_info.lfb_base - 1; + range_min = fb_end; + } else { + range_min = fb_end; + continue; + } + } + + start = (local_min + align - 1) & ~(align - 1); + for (; start + size - 1 <= local_max; start += align) { + *new = request_mem_region_exclusive(start, size, + dev_n); + if (*new) + return 0; + } + } + } + + return -ENXIO; +} +EXPORT_SYMBOL_GPL(vmbus_allocate_mmio); + static int vmbus_acpi_add(struct acpi_device *device) { acpi_status result; int ret_val = -ENODEV; + struct acpi_device *ancestor; hv_acpi_dev = device; @@ -1015,35 +1205,27 @@ static int vmbus_acpi_add(struct acpi_device *device) if (ACPI_FAILURE(result)) goto acpi_walk_err; /* - * The parent of the vmbus acpi device (Gen2 firmware) is the VMOD that - * has the mmio ranges. Get that. + * Some ancestor of the vmbus acpi device (Gen1 or Gen2 + * firmware) is the VMOD that has the mmio ranges. Get that. */ - if (device->parent) { - result = acpi_walk_resources(device->parent->handle, - METHOD_NAME__CRS, - vmbus_walk_resources, NULL); + for (ancestor = device->parent; ancestor; ancestor = ancestor->parent) { + result = acpi_walk_resources(ancestor->handle, METHOD_NAME__CRS, + vmbus_walk_resources, NULL); if (ACPI_FAILURE(result)) - goto acpi_walk_err; - if (hyperv_mmio.start && hyperv_mmio.end) - request_resource(&iomem_resource, &hyperv_mmio); + continue; + if (hyperv_mmio) + break; } ret_val = 0; acpi_walk_err: complete(&probe_event); + if (ret_val) + vmbus_acpi_remove(device); return ret_val; } -static int vmbus_acpi_remove(struct acpi_device *device) -{ - int ret = 0; - - if (hyperv_mmio.start && hyperv_mmio.end) - ret = release_resource(&hyperv_mmio); - return ret; -} - static const struct acpi_device_id vmbus_acpi_device_ids[] = { {"VMBUS", 0}, {"VMBus", 0}, @@ -1060,6 +1242,29 @@ static struct acpi_driver vmbus_acpi_driver = { }, }; +static void hv_kexec_handler(void) +{ + int cpu; + + hv_synic_clockevents_cleanup(); + vmbus_initiate_unload(); + for_each_online_cpu(cpu) + smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); + hv_cleanup(); +}; + +static void hv_crash_handler(struct pt_regs *regs) +{ + vmbus_initiate_unload(); + /* + * In crash handler we can't schedule synic cleanup for all CPUs, + * doing the cleanup for current CPU only. This should be sufficient + * for kdump. + */ + hv_synic_cleanup(NULL); + hv_cleanup(); +}; + static int __init hv_acpi_init(void) { int ret, t; @@ -1092,6 +1297,9 @@ static int __init hv_acpi_init(void) if (ret) goto cleanup; + hv_setup_kexec_handler(hv_kexec_handler); + hv_setup_crash_handler(hv_crash_handler); + return 0; cleanup: @@ -1104,13 +1312,16 @@ static void __exit vmbus_exit(void) { int cpu; + hv_remove_kexec_handler(); + hv_remove_crash_handler(); vmbus_connection.conn_state = DISCONNECTED; hv_synic_clockevents_cleanup(); vmbus_disconnect(); hv_remove_vmbus_irq(); tasklet_kill(&msg_dpc); vmbus_free_channels(); - if (ms_hyperv.features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { + if (ms_hyperv.misc_features & HV_FEATURE_GUEST_CRASH_MSR_AVAILABLE) { + unregister_die_notifier(&hyperv_die_block); atomic_notifier_chain_unregister(&panic_notifier_list, &hyperv_panic_block); } @@ -1120,8 +1331,10 @@ static void __exit vmbus_exit(void) tasklet_kill(hv_context.event_dpc[cpu]); smp_call_function_single(cpu, hv_synic_cleanup, NULL, 1); } + hv_synic_free(); acpi_bus_unregister_driver(&vmbus_acpi_driver); - hv_cpu_hotplug_quirk(false); + if (vmbus_proto_version > VERSION_WIN7) + cpu_hotplug_enable(); } diff --git a/drivers/hwtracing/coresight/coresight-etm.h b/drivers/hwtracing/coresight/coresight-etm.h index 098ffbec0a44..b4481eb29304 100644 --- a/drivers/hwtracing/coresight/coresight-etm.h +++ b/drivers/hwtracing/coresight/coresight-etm.h @@ -183,7 +183,9 @@ * @seq_13_event: event causing the transition from 1 to 3. * @seq_curr_state: current value of the sequencer register. * @ctxid_idx: index for the context ID registers. - * @ctxid_val: value for the context ID to trigger on. + * @ctxid_pid: value for the context ID to trigger on. + * @ctxid_vpid: Virtual PID seen by users if PID namespace is enabled, otherwise + * the same value of ctxid_pid. * @ctxid_mask: mask applicable to all the context IDs. * @sync_freq: Synchronisation frequency. * @timestamp_event: Defines an event that requests the insertion @@ -235,7 +237,8 @@ struct etm_drvdata { u32 seq_13_event; u32 seq_curr_state; u8 ctxid_idx; - u32 ctxid_val[ETM_MAX_CTXID_CMP]; + u32 ctxid_pid[ETM_MAX_CTXID_CMP]; + u32 ctxid_vpid[ETM_MAX_CTXID_CMP]; u32 ctxid_mask; u32 sync_freq; u32 timestamp_event; diff --git a/drivers/hwtracing/coresight/coresight-etm3x.c b/drivers/hwtracing/coresight/coresight-etm3x.c index 018a00fda611..bf2476ed9356 100644 --- a/drivers/hwtracing/coresight/coresight-etm3x.c +++ b/drivers/hwtracing/coresight/coresight-etm3x.c @@ -237,8 +237,11 @@ static void etm_set_default(struct etm_drvdata *drvdata) drvdata->seq_curr_state = 0x0; drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->nr_ctxid_cmp; i++) - drvdata->ctxid_val[i] = 0x0; + for (i = 0; i < drvdata->nr_ctxid_cmp; i++) { + drvdata->ctxid_pid[i] = 0x0; + drvdata->ctxid_vpid[i] = 0x0; + } + drvdata->ctxid_mask = 0x0; } @@ -289,7 +292,7 @@ static void etm_enable_hw(void *info) for (i = 0; i < drvdata->nr_ext_out; i++) etm_writel(drvdata, ETM_DEFAULT_EVENT_VAL, ETMEXTOUTEVRn(i)); for (i = 0; i < drvdata->nr_ctxid_cmp; i++) - etm_writel(drvdata, drvdata->ctxid_val[i], ETMCIDCVRn(i)); + etm_writel(drvdata, drvdata->ctxid_pid[i], ETMCIDCVRn(i)); etm_writel(drvdata, drvdata->ctxid_mask, ETMCIDCMR); etm_writel(drvdata, drvdata->sync_freq, ETMSYNCFR); /* No external input selected */ @@ -1386,38 +1389,41 @@ static ssize_t ctxid_idx_store(struct device *dev, } static DEVICE_ATTR_RW(ctxid_idx); -static ssize_t ctxid_val_show(struct device *dev, +static ssize_t ctxid_pid_show(struct device *dev, struct device_attribute *attr, char *buf) { unsigned long val; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); spin_lock(&drvdata->spinlock); - val = drvdata->ctxid_val[drvdata->ctxid_idx]; + val = drvdata->ctxid_vpid[drvdata->ctxid_idx]; spin_unlock(&drvdata->spinlock); return sprintf(buf, "%#lx\n", val); } -static ssize_t ctxid_val_store(struct device *dev, +static ssize_t ctxid_pid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { int ret; - unsigned long val; + unsigned long vpid, pid; struct etm_drvdata *drvdata = dev_get_drvdata(dev->parent); - ret = kstrtoul(buf, 16, &val); + ret = kstrtoul(buf, 16, &vpid); if (ret) return ret; + pid = coresight_vpid_to_pid(vpid); + spin_lock(&drvdata->spinlock); - drvdata->ctxid_val[drvdata->ctxid_idx] = val; + drvdata->ctxid_pid[drvdata->ctxid_idx] = pid; + drvdata->ctxid_vpid[drvdata->ctxid_idx] = vpid; spin_unlock(&drvdata->spinlock); return size; } -static DEVICE_ATTR_RW(ctxid_val); +static DEVICE_ATTR_RW(ctxid_pid); static ssize_t ctxid_mask_show(struct device *dev, struct device_attribute *attr, char *buf) @@ -1609,7 +1615,7 @@ static struct attribute *coresight_etm_attrs[] = { &dev_attr_seq_13_event.attr, &dev_attr_seq_curr_state.attr, &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_val.attr, + &dev_attr_ctxid_pid.attr, &dev_attr_ctxid_mask.attr, &dev_attr_sync_freq.attr, &dev_attr_timestamp_event.attr, @@ -1912,6 +1918,11 @@ static struct amba_id etm_ids[] = { .mask = 0x0003ffff, .data = "PTM 1.1", }, + { /* PTM 1.1 Qualcomm */ + .id = 0x0003006f, + .mask = 0x0003ffff, + .data = "PTM 1.1", + }, { 0, 0}, }; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x.c index 1312e993c501..254a81a4e6f4 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.c +++ b/drivers/hwtracing/coresight/coresight-etm4x.c @@ -155,7 +155,7 @@ static void etm4_enable_hw(void *info) drvdata->base + TRCACATRn(i)); } for (i = 0; i < drvdata->numcidc; i++) - writeq_relaxed(drvdata->ctxid_val[i], + writeq_relaxed(drvdata->ctxid_pid[i], drvdata->base + TRCCIDCVRn(i)); writel_relaxed(drvdata->ctxid_mask0, drvdata->base + TRCCIDCCTLR0); writel_relaxed(drvdata->ctxid_mask1, drvdata->base + TRCCIDCCTLR1); @@ -506,8 +506,11 @@ static ssize_t reset_store(struct device *dev, } drvdata->ctxid_idx = 0x0; - for (i = 0; i < drvdata->numcidc; i++) - drvdata->ctxid_val[i] = 0x0; + for (i = 0; i < drvdata->numcidc; i++) { + drvdata->ctxid_pid[i] = 0x0; + drvdata->ctxid_vpid[i] = 0x0; + } + drvdata->ctxid_mask0 = 0x0; drvdata->ctxid_mask1 = 0x0; @@ -1815,7 +1818,7 @@ static ssize_t ctxid_idx_store(struct device *dev, } static DEVICE_ATTR_RW(ctxid_idx); -static ssize_t ctxid_val_show(struct device *dev, +static ssize_t ctxid_pid_show(struct device *dev, struct device_attribute *attr, char *buf) { @@ -1825,17 +1828,17 @@ static ssize_t ctxid_val_show(struct device *dev, spin_lock(&drvdata->spinlock); idx = drvdata->ctxid_idx; - val = (unsigned long)drvdata->ctxid_val[idx]; + val = (unsigned long)drvdata->ctxid_vpid[idx]; spin_unlock(&drvdata->spinlock); return scnprintf(buf, PAGE_SIZE, "%#lx\n", val); } -static ssize_t ctxid_val_store(struct device *dev, +static ssize_t ctxid_pid_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) { u8 idx; - unsigned long val; + unsigned long vpid, pid; struct etmv4_drvdata *drvdata = dev_get_drvdata(dev->parent); /* @@ -1845,16 +1848,19 @@ static ssize_t ctxid_val_store(struct device *dev, */ if (!drvdata->ctxid_size || !drvdata->numcidc) return -EINVAL; - if (kstrtoul(buf, 16, &val)) + if (kstrtoul(buf, 16, &vpid)) return -EINVAL; + pid = coresight_vpid_to_pid(vpid); + spin_lock(&drvdata->spinlock); idx = drvdata->ctxid_idx; - drvdata->ctxid_val[idx] = (u64)val; + drvdata->ctxid_pid[idx] = (u64)pid; + drvdata->ctxid_vpid[idx] = (u64)vpid; spin_unlock(&drvdata->spinlock); return size; } -static DEVICE_ATTR_RW(ctxid_val); +static DEVICE_ATTR_RW(ctxid_pid); static ssize_t ctxid_masks_show(struct device *dev, struct device_attribute *attr, @@ -1949,7 +1955,7 @@ static ssize_t ctxid_masks_store(struct device *dev, */ for (j = 0; j < 8; j++) { if (maskbyte & 1) - drvdata->ctxid_val[i] &= ~(0xFF << (j * 8)); + drvdata->ctxid_pid[i] &= ~(0xFF << (j * 8)); maskbyte >>= 1; } /* Select the next ctxid comparator mask value */ @@ -2193,7 +2199,7 @@ static struct attribute *coresight_etmv4_attrs[] = { &dev_attr_res_idx.attr, &dev_attr_res_ctrl.attr, &dev_attr_ctxid_idx.attr, - &dev_attr_ctxid_val.attr, + &dev_attr_ctxid_pid.attr, &dev_attr_ctxid_masks.attr, &dev_attr_vmid_idx.attr, &dev_attr_vmid_val.attr, @@ -2513,8 +2519,11 @@ static void etm4_init_default_data(struct etmv4_drvdata *drvdata) drvdata->addr_type[1] = ETM_ADDR_TYPE_RANGE; } - for (i = 0; i < drvdata->numcidc; i++) - drvdata->ctxid_val[i] = 0x0; + for (i = 0; i < drvdata->numcidc; i++) { + drvdata->ctxid_pid[i] = 0x0; + drvdata->ctxid_vpid[i] = 0x0; + } + drvdata->ctxid_mask0 = 0x0; drvdata->ctxid_mask1 = 0x0; diff --git a/drivers/hwtracing/coresight/coresight-etm4x.h b/drivers/hwtracing/coresight/coresight-etm4x.h index e08e983dd2d9..c34100205ca9 100644 --- a/drivers/hwtracing/coresight/coresight-etm4x.h +++ b/drivers/hwtracing/coresight/coresight-etm4x.h @@ -265,7 +265,9 @@ * @addr_type: Current status of the comparator register. * @ctxid_idx: Context ID index selector. * @ctxid_size: Size of the context ID field to consider. - * @ctxid_val: Value of the context ID comparator. + * @ctxid_pid: Value of the context ID comparator. + * @ctxid_vpid: Virtual PID seen by users if PID namespace is enabled, otherwise + * the same value of ctxid_pid. * @ctxid_mask0:Context ID comparator mask for comparator 0-3. * @ctxid_mask1:Context ID comparator mask for comparator 4-7. * @vmid_idx: VM ID index selector. @@ -352,7 +354,8 @@ struct etmv4_drvdata { u8 addr_type[ETM_MAX_SINGLE_ADDR_CMP]; u8 ctxid_idx; u8 ctxid_size; - u64 ctxid_val[ETMv4_MAX_CTXID_CMP]; + u64 ctxid_pid[ETMv4_MAX_CTXID_CMP]; + u64 ctxid_vpid[ETMv4_MAX_CTXID_CMP]; u32 ctxid_mask0; u32 ctxid_mask1; u8 vmid_idx; diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 7974b7c3da6b..963ac197c253 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -12,7 +12,6 @@ #include <linux/kernel.h> #include <linux/module.h> -#include <linux/init.h> #include <linux/device.h> #include <linux/platform_device.h> #include <linux/io.h> @@ -184,17 +183,7 @@ static struct platform_driver replicator_driver = { }, }; -static int __init replicator_init(void) -{ - return platform_driver_register(&replicator_driver); -} -module_init(replicator_init); - -static void __exit replicator_exit(void) -{ - platform_driver_unregister(&replicator_driver); -} -module_exit(replicator_exit); +builtin_platform_driver(replicator_driver); MODULE_LICENSE("GPL v2"); MODULE_DESCRIPTION("CoreSight Replicator driver"); diff --git a/drivers/infiniband/hw/cxgb4/cq.c b/drivers/infiniband/hw/cxgb4/cq.c index c7aab48f07cd..92d518382a9f 100644 --- a/drivers/infiniband/hw/cxgb4/cq.c +++ b/drivers/infiniband/hw/cxgb4/cq.c @@ -814,7 +814,7 @@ static int c4iw_poll_cq_one(struct c4iw_cq *chp, struct ib_wc *wc) printk(KERN_ERR MOD "Unexpected cqe_status 0x%x for QPID=0x%0x\n", CQE_STATUS(&cqe), CQE_QPID(&cqe)); - ret = -EINVAL; + wc->status = IB_WC_FATAL_ERR; } } out: diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c index 097d7216d98e..c6dc644aa580 100644 --- a/drivers/input/keyboard/gpio_keys_polled.c +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -246,7 +246,7 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) * convert it to descriptor. */ if (!button->gpiod && gpio_is_valid(button->gpio)) { - unsigned flags = 0; + unsigned flags = GPIOF_IN; if (button->active_low) flags |= GPIOF_ACTIVE_LOW; diff --git a/drivers/irqchip/irq-crossbar.c b/drivers/irqchip/irq-crossbar.c index 692fe2bc8197..c12bb93334ff 100644 --- a/drivers/irqchip/irq-crossbar.c +++ b/drivers/irqchip/irq-crossbar.c @@ -68,7 +68,9 @@ static struct irq_chip crossbar_chip = { .irq_mask = irq_chip_mask_parent, .irq_unmask = irq_chip_unmask_parent, .irq_retrigger = irq_chip_retrigger_hierarchy, - .irq_set_wake = irq_chip_set_wake_parent, + .irq_set_type = irq_chip_set_type_parent, + .flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SKIP_SET_WAKE, #ifdef CONFIG_SMP .irq_set_affinity = irq_chip_set_affinity_parent, #endif diff --git a/drivers/md/dm-cache-policy-mq.c b/drivers/md/dm-cache-policy-mq.c index 32814371b8d3..aa1b41ca40f7 100644 --- a/drivers/md/dm-cache-policy-mq.c +++ b/drivers/md/dm-cache-policy-mq.c @@ -1471,5 +1471,3 @@ module_exit(mq_exit); MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("mq cache policy"); - -MODULE_ALIAS("dm-cache-default"); diff --git a/drivers/md/dm-cache-policy-smq.c b/drivers/md/dm-cache-policy-smq.c index 48a4a826ae07..200366c62231 100644 --- a/drivers/md/dm-cache-policy-smq.c +++ b/drivers/md/dm-cache-policy-smq.c @@ -1789,3 +1789,5 @@ module_exit(smq_exit); MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>"); MODULE_LICENSE("GPL"); MODULE_DESCRIPTION("smq cache policy"); + +MODULE_ALIAS("dm-cache-default"); diff --git a/drivers/md/dm-ioctl.c b/drivers/md/dm-ioctl.c index 720ceeb7fa9b..80a439543259 100644 --- a/drivers/md/dm-ioctl.c +++ b/drivers/md/dm-ioctl.c @@ -1919,9 +1919,7 @@ int __init dm_interface_init(void) void dm_interface_exit(void) { - if (misc_deregister(&_dm_misc) < 0) - DMERR("misc_deregister failed for control device"); - + misc_deregister(&_dm_misc); dm_hash_exit(); } diff --git a/drivers/md/dm-thin-metadata.c b/drivers/md/dm-thin-metadata.c index 48dfe3c4d6aa..6ba47cfb1443 100644 --- a/drivers/md/dm-thin-metadata.c +++ b/drivers/md/dm-thin-metadata.c @@ -1293,8 +1293,8 @@ static int __release_metadata_snap(struct dm_pool_metadata *pmd) return r; disk_super = dm_block_data(copy); - dm_sm_dec_block(pmd->metadata_sm, le64_to_cpu(disk_super->data_mapping_root)); - dm_sm_dec_block(pmd->metadata_sm, le64_to_cpu(disk_super->device_details_root)); + dm_btree_del(&pmd->info, le64_to_cpu(disk_super->data_mapping_root)); + dm_btree_del(&pmd->details_info, le64_to_cpu(disk_super->device_details_root)); dm_sm_dec_block(pmd->metadata_sm, held_root); return dm_tm_unlock(pmd->tm, copy); diff --git a/drivers/md/persistent-data/dm-btree-internal.h b/drivers/md/persistent-data/dm-btree-internal.h index bf2b80d5c470..8731b6ea026b 100644 --- a/drivers/md/persistent-data/dm-btree-internal.h +++ b/drivers/md/persistent-data/dm-btree-internal.h @@ -138,4 +138,10 @@ int lower_bound(struct btree_node *n, uint64_t key); extern struct dm_block_validator btree_node_validator; +/* + * Value type for upper levels of multi-level btrees. + */ +extern void init_le64_type(struct dm_transaction_manager *tm, + struct dm_btree_value_type *vt); + #endif /* DM_BTREE_INTERNAL_H */ diff --git a/drivers/md/persistent-data/dm-btree-remove.c b/drivers/md/persistent-data/dm-btree-remove.c index 9ca9eccd512f..4222f774cf36 100644 --- a/drivers/md/persistent-data/dm-btree-remove.c +++ b/drivers/md/persistent-data/dm-btree-remove.c @@ -544,14 +544,6 @@ static int remove_raw(struct shadow_spine *s, struct dm_btree_info *info, return r; } -static struct dm_btree_value_type le64_type = { - .context = NULL, - .size = sizeof(__le64), - .inc = NULL, - .dec = NULL, - .equal = NULL -}; - int dm_btree_remove(struct dm_btree_info *info, dm_block_t root, uint64_t *keys, dm_block_t *new_root) { @@ -559,12 +551,14 @@ int dm_btree_remove(struct dm_btree_info *info, dm_block_t root, int index = 0, r = 0; struct shadow_spine spine; struct btree_node *n; + struct dm_btree_value_type le64_vt; + init_le64_type(info->tm, &le64_vt); init_shadow_spine(&spine, info); for (level = 0; level < info->levels; level++) { r = remove_raw(&spine, info, (level == last_level ? - &info->value_type : &le64_type), + &info->value_type : &le64_vt), root, keys[level], (unsigned *)&index); if (r < 0) break; @@ -654,11 +648,13 @@ static int remove_one(struct dm_btree_info *info, dm_block_t root, int index = 0, r = 0; struct shadow_spine spine; struct btree_node *n; + struct dm_btree_value_type le64_vt; uint64_t k; + init_le64_type(info->tm, &le64_vt); init_shadow_spine(&spine, info); for (level = 0; level < last_level; level++) { - r = remove_raw(&spine, info, &le64_type, + r = remove_raw(&spine, info, &le64_vt, root, keys[level], (unsigned *) &index); if (r < 0) goto out; diff --git a/drivers/md/persistent-data/dm-btree-spine.c b/drivers/md/persistent-data/dm-btree-spine.c index 1b5e13ec7f96..0dee514ba4c5 100644 --- a/drivers/md/persistent-data/dm-btree-spine.c +++ b/drivers/md/persistent-data/dm-btree-spine.c @@ -249,3 +249,40 @@ int shadow_root(struct shadow_spine *s) { return s->root; } + +static void le64_inc(void *context, const void *value_le) +{ + struct dm_transaction_manager *tm = context; + __le64 v_le; + + memcpy(&v_le, value_le, sizeof(v_le)); + dm_tm_inc(tm, le64_to_cpu(v_le)); +} + +static void le64_dec(void *context, const void *value_le) +{ + struct dm_transaction_manager *tm = context; + __le64 v_le; + + memcpy(&v_le, value_le, sizeof(v_le)); + dm_tm_dec(tm, le64_to_cpu(v_le)); +} + +static int le64_equal(void *context, const void *value1_le, const void *value2_le) +{ + __le64 v1_le, v2_le; + + memcpy(&v1_le, value1_le, sizeof(v1_le)); + memcpy(&v2_le, value2_le, sizeof(v2_le)); + return v1_le == v2_le; +} + +void init_le64_type(struct dm_transaction_manager *tm, + struct dm_btree_value_type *vt) +{ + vt->context = tm; + vt->size = sizeof(__le64); + vt->inc = le64_inc; + vt->dec = le64_dec; + vt->equal = le64_equal; +} diff --git a/drivers/md/persistent-data/dm-btree.c b/drivers/md/persistent-data/dm-btree.c index fdd3793e22f9..c7726cebc495 100644 --- a/drivers/md/persistent-data/dm-btree.c +++ b/drivers/md/persistent-data/dm-btree.c @@ -667,12 +667,7 @@ static int insert(struct dm_btree_info *info, dm_block_t root, struct btree_node *n; struct dm_btree_value_type le64_type; - le64_type.context = NULL; - le64_type.size = sizeof(__le64); - le64_type.inc = NULL; - le64_type.dec = NULL; - le64_type.equal = NULL; - + init_le64_type(info->tm, &le64_type); init_shadow_spine(&spine, info); for (level = 0; level < (info->levels - 1); level++) { diff --git a/drivers/media/dvb-frontends/Kconfig b/drivers/media/dvb-frontends/Kconfig index 0d35f5850ff1..5ab90f36a6a6 100644 --- a/drivers/media/dvb-frontends/Kconfig +++ b/drivers/media/dvb-frontends/Kconfig @@ -240,7 +240,7 @@ config DVB_SI21XX config DVB_TS2020 tristate "Montage Tehnology TS2020 based tuners" - depends on DVB_CORE + depends on DVB_CORE && I2C select REGMAP_I2C default m if !MEDIA_SUBDRV_AUTOSELECT help diff --git a/drivers/media/pci/cobalt/Kconfig b/drivers/media/pci/cobalt/Kconfig index 3be1b2c3c386..6a1c0089bb62 100644 --- a/drivers/media/pci/cobalt/Kconfig +++ b/drivers/media/pci/cobalt/Kconfig @@ -2,6 +2,7 @@ config VIDEO_COBALT tristate "Cisco Cobalt support" depends on VIDEO_V4L2 && I2C && MEDIA_CONTROLLER depends on PCI_MSI && MTD_COMPLEX_MAPPINGS && GPIOLIB + depends on SND select I2C_ALGOBIT select VIDEO_ADV7604 select VIDEO_ADV7511 diff --git a/drivers/media/pci/cobalt/cobalt-irq.c b/drivers/media/pci/cobalt/cobalt-irq.c index dd4bff9cf339..d1f5898d11ba 100644 --- a/drivers/media/pci/cobalt/cobalt-irq.c +++ b/drivers/media/pci/cobalt/cobalt-irq.c @@ -139,7 +139,7 @@ done: also know about dropped frames. */ cb->vb.v4l2_buf.sequence = s->sequence++; vb2_buffer_done(&cb->vb, (skip || s->unstable_frame) ? - VB2_BUF_STATE_QUEUED : VB2_BUF_STATE_DONE); + VB2_BUF_STATE_REQUEUEING : VB2_BUF_STATE_DONE); } irqreturn_t cobalt_irq_handler(int irq, void *dev_id) diff --git a/drivers/media/pci/mantis/mantis_dma.c b/drivers/media/pci/mantis/mantis_dma.c index 1d59c7e039f7..87990ece5848 100644 --- a/drivers/media/pci/mantis/mantis_dma.c +++ b/drivers/media/pci/mantis/mantis_dma.c @@ -130,10 +130,11 @@ err: int mantis_dma_init(struct mantis_pci *mantis) { - int err = 0; + int err; dprintk(MANTIS_DEBUG, 1, "Mantis DMA init"); - if (mantis_alloc_buffers(mantis) < 0) { + err = mantis_alloc_buffers(mantis); + if (err < 0) { dprintk(MANTIS_ERROR, 1, "Error allocating DMA buffer"); /* Stop RISC Engine */ diff --git a/drivers/media/rc/ir-rc5-decoder.c b/drivers/media/rc/ir-rc5-decoder.c index 8939ebd74391..84fa6e9b59a1 100644 --- a/drivers/media/rc/ir-rc5-decoder.c +++ b/drivers/media/rc/ir-rc5-decoder.c @@ -184,125 +184,9 @@ out: return -EINVAL; } -static struct ir_raw_timings_manchester ir_rc5_timings = { - .leader = RC5_UNIT, - .pulse_space_start = 0, - .clock = RC5_UNIT, - .trailer_space = RC5_UNIT * 10, -}; - -static struct ir_raw_timings_manchester ir_rc5x_timings[2] = { - { - .leader = RC5_UNIT, - .pulse_space_start = 0, - .clock = RC5_UNIT, - .trailer_space = RC5X_SPACE, - }, - { - .clock = RC5_UNIT, - .trailer_space = RC5_UNIT * 10, - }, -}; - -static struct ir_raw_timings_manchester ir_rc5_sz_timings = { - .leader = RC5_UNIT, - .pulse_space_start = 0, - .clock = RC5_UNIT, - .trailer_space = RC5_UNIT * 10, -}; - -static int ir_rc5_validate_filter(const struct rc_scancode_filter *scancode, - unsigned int important_bits) -{ - /* all important bits of scancode should be set in mask */ - if (~scancode->mask & important_bits) - return -EINVAL; - /* extra bits in mask should be zero in data */ - if (scancode->mask & scancode->data & ~important_bits) - return -EINVAL; - return 0; -} - -/** - * ir_rc5_encode() - Encode a scancode as a stream of raw events - * - * @protocols: allowed protocols - * @scancode: scancode filter describing scancode (helps distinguish between - * protocol subtypes when scancode is ambiguous) - * @events: array of raw ir events to write into - * @max: maximum size of @events - * - * Returns: The number of events written. - * -ENOBUFS if there isn't enough space in the array to fit the - * encoding. In this case all @max events will have been written. - * -EINVAL if the scancode is ambiguous or invalid. - */ -static int ir_rc5_encode(u64 protocols, - const struct rc_scancode_filter *scancode, - struct ir_raw_event *events, unsigned int max) -{ - int ret; - struct ir_raw_event *e = events; - unsigned int data, xdata, command, commandx, system; - - /* Detect protocol and convert scancode to raw data */ - if (protocols & RC_BIT_RC5 && - !ir_rc5_validate_filter(scancode, 0x1f7f)) { - /* decode scancode */ - command = (scancode->data & 0x003f) >> 0; - commandx = (scancode->data & 0x0040) >> 6; - system = (scancode->data & 0x1f00) >> 8; - /* encode data */ - data = !commandx << 12 | system << 6 | command; - - /* Modulate the data */ - ret = ir_raw_gen_manchester(&e, max, &ir_rc5_timings, RC5_NBITS, - data); - if (ret < 0) - return ret; - } else if (protocols & RC_BIT_RC5X && - !ir_rc5_validate_filter(scancode, 0x1f7f3f)) { - /* decode scancode */ - xdata = (scancode->data & 0x00003f) >> 0; - command = (scancode->data & 0x003f00) >> 8; - commandx = (scancode->data & 0x004000) >> 14; - system = (scancode->data & 0x1f0000) >> 16; - /* commandx and system overlap, bits must match when encoded */ - if (commandx == (system & 0x1)) - return -EINVAL; - /* encode data */ - data = 1 << 18 | system << 12 | command << 6 | xdata; - - /* Modulate the data */ - ret = ir_raw_gen_manchester(&e, max, &ir_rc5x_timings[0], - CHECK_RC5X_NBITS, - data >> (RC5X_NBITS-CHECK_RC5X_NBITS)); - if (ret < 0) - return ret; - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc5x_timings[1], - RC5X_NBITS - CHECK_RC5X_NBITS, - data); - if (ret < 0) - return ret; - } else if (protocols & RC_BIT_RC5_SZ && - !ir_rc5_validate_filter(scancode, 0x2fff)) { - /* RC5-SZ scancode is raw enough for Manchester as it is */ - ret = ir_raw_gen_manchester(&e, max, &ir_rc5_sz_timings, - RC5_SZ_NBITS, scancode->data & 0x2fff); - if (ret < 0) - return ret; - } else { - return -EINVAL; - } - - return e - events; -} - static struct ir_raw_handler rc5_handler = { .protocols = RC_BIT_RC5 | RC_BIT_RC5X | RC_BIT_RC5_SZ, .decode = ir_rc5_decode, - .encode = ir_rc5_encode, }; static int __init ir_rc5_decode_init(void) diff --git a/drivers/media/rc/ir-rc6-decoder.c b/drivers/media/rc/ir-rc6-decoder.c index f9c70baf6e0c..d16bc67af732 100644 --- a/drivers/media/rc/ir-rc6-decoder.c +++ b/drivers/media/rc/ir-rc6-decoder.c @@ -291,133 +291,11 @@ out: return -EINVAL; } -static struct ir_raw_timings_manchester ir_rc6_timings[4] = { - { - .leader = RC6_PREFIX_PULSE, - .pulse_space_start = 0, - .clock = RC6_UNIT, - .invert = 1, - .trailer_space = RC6_PREFIX_SPACE, - }, - { - .clock = RC6_UNIT, - .invert = 1, - }, - { - .clock = RC6_UNIT * 2, - .invert = 1, - }, - { - .clock = RC6_UNIT, - .invert = 1, - .trailer_space = RC6_SUFFIX_SPACE, - }, -}; - -static int ir_rc6_validate_filter(const struct rc_scancode_filter *scancode, - unsigned int important_bits) -{ - /* all important bits of scancode should be set in mask */ - if (~scancode->mask & important_bits) - return -EINVAL; - /* extra bits in mask should be zero in data */ - if (scancode->mask & scancode->data & ~important_bits) - return -EINVAL; - return 0; -} - -/** - * ir_rc6_encode() - Encode a scancode as a stream of raw events - * - * @protocols: allowed protocols - * @scancode: scancode filter describing scancode (helps distinguish between - * protocol subtypes when scancode is ambiguous) - * @events: array of raw ir events to write into - * @max: maximum size of @events - * - * Returns: The number of events written. - * -ENOBUFS if there isn't enough space in the array to fit the - * encoding. In this case all @max events will have been written. - * -EINVAL if the scancode is ambiguous or invalid. - */ -static int ir_rc6_encode(u64 protocols, - const struct rc_scancode_filter *scancode, - struct ir_raw_event *events, unsigned int max) -{ - int ret; - struct ir_raw_event *e = events; - - if (protocols & RC_BIT_RC6_0 && - !ir_rc6_validate_filter(scancode, 0xffff)) { - - /* Modulate the preamble */ - ret = ir_raw_gen_manchester(&e, max, &ir_rc6_timings[0], 0, 0); - if (ret < 0) - return ret; - - /* Modulate the header (Start Bit & Mode-0) */ - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc6_timings[1], - RC6_HEADER_NBITS, (1 << 3)); - if (ret < 0) - return ret; - - /* Modulate Trailer Bit */ - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc6_timings[2], 1, 0); - if (ret < 0) - return ret; - - /* Modulate rest of the data */ - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc6_timings[3], RC6_0_NBITS, - scancode->data); - if (ret < 0) - return ret; - - } else if (protocols & (RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | - RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE) && - !ir_rc6_validate_filter(scancode, 0x8fffffff)) { - - /* Modulate the preamble */ - ret = ir_raw_gen_manchester(&e, max, &ir_rc6_timings[0], 0, 0); - if (ret < 0) - return ret; - - /* Modulate the header (Start Bit & Header-version 6 */ - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc6_timings[1], - RC6_HEADER_NBITS, (1 << 3 | 6)); - if (ret < 0) - return ret; - - /* Modulate Trailer Bit */ - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc6_timings[2], 1, 0); - if (ret < 0) - return ret; - - /* Modulate rest of the data */ - ret = ir_raw_gen_manchester(&e, max - (e - events), - &ir_rc6_timings[3], - fls(scancode->mask), - scancode->data); - if (ret < 0) - return ret; - - } else { - return -EINVAL; - } - - return e - events; -} - static struct ir_raw_handler rc6_handler = { .protocols = RC_BIT_RC6_0 | RC_BIT_RC6_6A_20 | RC_BIT_RC6_6A_24 | RC_BIT_RC6_6A_32 | RC_BIT_RC6_MCE, .decode = ir_rc6_decode, - .encode = ir_rc6_encode, }; static int __init ir_rc6_decode_init(void) diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index baeb5971fd52..85af7a869167 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -526,130 +526,6 @@ static int nvt_set_tx_carrier(struct rc_dev *dev, u32 carrier) return 0; } -static int nvt_write_wakeup_codes(struct rc_dev *dev, - const u8 *wakeup_sample_buf, int count) -{ - int i = 0; - u8 reg, reg_learn_mode; - unsigned long flags; - struct nvt_dev *nvt = dev->priv; - - nvt_dbg_wake("writing wakeup samples"); - - reg = nvt_cir_wake_reg_read(nvt, CIR_WAKE_IRCON); - reg_learn_mode = reg & ~CIR_WAKE_IRCON_MODE0; - reg_learn_mode |= CIR_WAKE_IRCON_MODE1; - - /* Lock the learn area to prevent racing with wake-isr */ - spin_lock_irqsave(&nvt->nvt_lock, flags); - - /* Enable fifo writes */ - nvt_cir_wake_reg_write(nvt, reg_learn_mode, CIR_WAKE_IRCON); - - /* Clear cir wake rx fifo */ - nvt_clear_cir_wake_fifo(nvt); - - if (count > WAKE_FIFO_LEN) { - nvt_dbg_wake("HW FIFO too small for all wake samples"); - count = WAKE_FIFO_LEN; - } - - if (count) - pr_info("Wake samples (%d) =", count); - else - pr_info("Wake sample fifo cleared"); - - /* Write wake samples to fifo */ - for (i = 0; i < count; i++) { - pr_cont(" %02x", wakeup_sample_buf[i]); - nvt_cir_wake_reg_write(nvt, wakeup_sample_buf[i], - CIR_WAKE_WR_FIFO_DATA); - } - pr_cont("\n"); - - /* Switch cir to wakeup mode and disable fifo writing */ - nvt_cir_wake_reg_write(nvt, reg, CIR_WAKE_IRCON); - - /* Set number of bytes needed for wake */ - nvt_cir_wake_reg_write(nvt, count ? count : - CIR_WAKE_FIFO_CMP_BYTES, - CIR_WAKE_FIFO_CMP_DEEP); - - spin_unlock_irqrestore(&nvt->nvt_lock, flags); - - return 0; -} - -static int nvt_ir_raw_set_wakeup_filter(struct rc_dev *dev, - struct rc_scancode_filter *sc_filter) -{ - u8 *reg_buf; - u8 buf_val; - int i, ret, count; - unsigned int val; - struct ir_raw_event *raw; - bool complete; - - /* Require both mask and data to be set before actually committing */ - if (!sc_filter->mask || !sc_filter->data) - return 0; - - raw = kmalloc_array(WAKE_FIFO_LEN, sizeof(*raw), GFP_KERNEL); - if (!raw) - return -ENOMEM; - - ret = ir_raw_encode_scancode(dev->enabled_wakeup_protocols, sc_filter, - raw, WAKE_FIFO_LEN); - complete = (ret != -ENOBUFS); - if (!complete) - ret = WAKE_FIFO_LEN; - else if (ret < 0) - goto out_raw; - - reg_buf = kmalloc_array(WAKE_FIFO_LEN, sizeof(*reg_buf), GFP_KERNEL); - if (!reg_buf) { - ret = -ENOMEM; - goto out_raw; - } - - /* Inspect the ir samples */ - for (i = 0, count = 0; i < ret && count < WAKE_FIFO_LEN; ++i) { - val = NS_TO_US((raw[i]).duration) / SAMPLE_PERIOD; - - /* Split too large values into several smaller ones */ - while (val > 0 && count < WAKE_FIFO_LEN) { - - /* Skip last value for better comparison tolerance */ - if (complete && i == ret - 1 && val < BUF_LEN_MASK) - break; - - /* Clamp values to BUF_LEN_MASK at most */ - buf_val = (val > BUF_LEN_MASK) ? BUF_LEN_MASK : val; - - reg_buf[count] = buf_val; - val -= buf_val; - if ((raw[i]).pulse) - reg_buf[count] |= BUF_PULSE_BIT; - count++; - } - } - - ret = nvt_write_wakeup_codes(dev, reg_buf, count); - - kfree(reg_buf); -out_raw: - kfree(raw); - - return ret; -} - -/* Dummy implementation. nuvoton is agnostic to the protocol used */ -static int nvt_ir_raw_change_wakeup_protocol(struct rc_dev *dev, - u64 *rc_type) -{ - return 0; -} - /* * nvt_tx_ir * @@ -1167,14 +1043,11 @@ static int nvt_probe(struct pnp_dev *pdev, const struct pnp_device_id *dev_id) /* Set up the rc device */ rdev->priv = nvt; rdev->driver_type = RC_DRIVER_IR_RAW; - rdev->encode_wakeup = true; rdev->allowed_protocols = RC_BIT_ALL; rdev->open = nvt_open; rdev->close = nvt_close; rdev->tx_ir = nvt_tx_ir; rdev->s_tx_carrier = nvt_set_tx_carrier; - rdev->s_wakeup_filter = nvt_ir_raw_set_wakeup_filter; - rdev->change_wakeup_protocol = nvt_ir_raw_change_wakeup_protocol; rdev->input_name = "Nuvoton w836x7hg Infrared Remote Transceiver"; rdev->input_phys = "nuvoton/cir0"; rdev->input_id.bustype = BUS_HOST; diff --git a/drivers/media/rc/nuvoton-cir.h b/drivers/media/rc/nuvoton-cir.h index 9d0e161c2a88..e1cf23c3875b 100644 --- a/drivers/media/rc/nuvoton-cir.h +++ b/drivers/media/rc/nuvoton-cir.h @@ -63,7 +63,6 @@ static int debug; */ #define TX_BUF_LEN 256 #define RX_BUF_LEN 32 -#define WAKE_FIFO_LEN 67 struct nvt_dev { struct pnp_dev *pdev; diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 4b994aa2f2a7..b68d4f762734 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -25,8 +25,6 @@ struct ir_raw_handler { u64 protocols; /* which are handled by this handler */ int (*decode)(struct rc_dev *dev, struct ir_raw_event event); - int (*encode)(u64 protocols, const struct rc_scancode_filter *scancode, - struct ir_raw_event *events, unsigned int max); /* These two should only be used by the lirc decoder */ int (*raw_register)(struct rc_dev *dev); @@ -152,44 +150,10 @@ static inline bool is_timing_event(struct ir_raw_event ev) #define TO_US(duration) DIV_ROUND_CLOSEST((duration), 1000) #define TO_STR(is_pulse) ((is_pulse) ? "pulse" : "space") -/* functions for IR encoders */ - -static inline void init_ir_raw_event_duration(struct ir_raw_event *ev, - unsigned int pulse, - u32 duration) -{ - init_ir_raw_event(ev); - ev->duration = duration; - ev->pulse = pulse; -} - -/** - * struct ir_raw_timings_manchester - Manchester coding timings - * @leader: duration of leader pulse (if any) 0 if continuing - * existing signal (see @pulse_space_start) - * @pulse_space_start: 1 for starting with pulse (0 for starting with space) - * @clock: duration of each pulse/space in ns - * @invert: if set clock logic is inverted - * (0 = space + pulse, 1 = pulse + space) - * @trailer_space: duration of trailer space in ns - */ -struct ir_raw_timings_manchester { - unsigned int leader; - unsigned int pulse_space_start:1; - unsigned int clock; - unsigned int invert:1; - unsigned int trailer_space; -}; - -int ir_raw_gen_manchester(struct ir_raw_event **ev, unsigned int max, - const struct ir_raw_timings_manchester *timings, - unsigned int n, unsigned int data); - /* * Routines from rc-raw.c to be used internally and by decoders */ u64 ir_raw_get_allowed_protocols(void); -u64 ir_raw_get_encode_protocols(void); int ir_raw_event_register(struct rc_dev *dev); void ir_raw_event_unregister(struct rc_dev *dev); int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler); diff --git a/drivers/media/rc/rc-ir-raw.c b/drivers/media/rc/rc-ir-raw.c index b9e4645c731c..b732ac6a26d8 100644 --- a/drivers/media/rc/rc-ir-raw.c +++ b/drivers/media/rc/rc-ir-raw.c @@ -30,7 +30,6 @@ static LIST_HEAD(ir_raw_client_list); static DEFINE_MUTEX(ir_raw_handler_lock); static LIST_HEAD(ir_raw_handler_list); static u64 available_protocols; -static u64 encode_protocols; static int ir_raw_event_thread(void *data) { @@ -241,146 +240,12 @@ ir_raw_get_allowed_protocols(void) return protocols; } -/* used internally by the sysfs interface */ -u64 -ir_raw_get_encode_protocols(void) -{ - u64 protocols; - - mutex_lock(&ir_raw_handler_lock); - protocols = encode_protocols; - mutex_unlock(&ir_raw_handler_lock); - return protocols; -} - static int change_protocol(struct rc_dev *dev, u64 *rc_type) { /* the caller will update dev->enabled_protocols */ return 0; } -/** - * ir_raw_gen_manchester() - Encode data with Manchester (bi-phase) modulation. - * @ev: Pointer to pointer to next free event. *@ev is incremented for - * each raw event filled. - * @max: Maximum number of raw events to fill. - * @timings: Manchester modulation timings. - * @n: Number of bits of data. - * @data: Data bits to encode. - * - * Encodes the @n least significant bits of @data using Manchester (bi-phase) - * modulation with the timing characteristics described by @timings, writing up - * to @max raw IR events using the *@ev pointer. - * - * Returns: 0 on success. - * -ENOBUFS if there isn't enough space in the array to fit the - * full encoded data. In this case all @max events will have been - * written. - */ -int ir_raw_gen_manchester(struct ir_raw_event **ev, unsigned int max, - const struct ir_raw_timings_manchester *timings, - unsigned int n, unsigned int data) -{ - bool need_pulse; - unsigned int i; - int ret = -ENOBUFS; - - i = 1 << (n - 1); - - if (timings->leader) { - if (!max--) - return ret; - if (timings->pulse_space_start) { - init_ir_raw_event_duration((*ev)++, 1, timings->leader); - - if (!max--) - return ret; - init_ir_raw_event_duration((*ev), 0, timings->leader); - } else { - init_ir_raw_event_duration((*ev), 1, timings->leader); - } - i >>= 1; - } else { - /* continue existing signal */ - --(*ev); - } - /* from here on *ev will point to the last event rather than the next */ - - while (n && i > 0) { - need_pulse = !(data & i); - if (timings->invert) - need_pulse = !need_pulse; - if (need_pulse == !!(*ev)->pulse) { - (*ev)->duration += timings->clock; - } else { - if (!max--) - goto nobufs; - init_ir_raw_event_duration(++(*ev), need_pulse, - timings->clock); - } - - if (!max--) - goto nobufs; - init_ir_raw_event_duration(++(*ev), !need_pulse, - timings->clock); - i >>= 1; - } - - if (timings->trailer_space) { - if (!(*ev)->pulse) - (*ev)->duration += timings->trailer_space; - else if (!max--) - goto nobufs; - else - init_ir_raw_event_duration(++(*ev), 0, - timings->trailer_space); - } - - ret = 0; -nobufs: - /* point to the next event rather than last event before returning */ - ++(*ev); - return ret; -} -EXPORT_SYMBOL(ir_raw_gen_manchester); - -/** - * ir_raw_encode_scancode() - Encode a scancode as raw events - * - * @protocols: permitted protocols - * @scancode: scancode filter describing a single scancode - * @events: array of raw events to write into - * @max: max number of raw events - * - * Attempts to encode the scancode as raw events. - * - * Returns: The number of events written. - * -ENOBUFS if there isn't enough space in the array to fit the - * encoding. In this case all @max events will have been written. - * -EINVAL if the scancode is ambiguous or invalid, or if no - * compatible encoder was found. - */ -int ir_raw_encode_scancode(u64 protocols, - const struct rc_scancode_filter *scancode, - struct ir_raw_event *events, unsigned int max) -{ - struct ir_raw_handler *handler; - int ret = -EINVAL; - - mutex_lock(&ir_raw_handler_lock); - list_for_each_entry(handler, &ir_raw_handler_list, list) { - if (handler->protocols & protocols && handler->encode) { - ret = handler->encode(protocols, scancode, events, max); - if (ret >= 0 || ret == -ENOBUFS) - break; - } - } - mutex_unlock(&ir_raw_handler_lock); - - return ret; -} -EXPORT_SYMBOL(ir_raw_encode_scancode); - /* * Used to (un)register raw event clients */ @@ -463,8 +328,6 @@ int ir_raw_handler_register(struct ir_raw_handler *ir_raw_handler) list_for_each_entry(raw, &ir_raw_client_list, list) ir_raw_handler->raw_register(raw->dev); available_protocols |= ir_raw_handler->protocols; - if (ir_raw_handler->encode) - encode_protocols |= ir_raw_handler->protocols; mutex_unlock(&ir_raw_handler_lock); return 0; @@ -481,8 +344,6 @@ void ir_raw_handler_unregister(struct ir_raw_handler *ir_raw_handler) list_for_each_entry(raw, &ir_raw_client_list, list) ir_raw_handler->raw_unregister(raw->dev); available_protocols &= ~ir_raw_handler->protocols; - if (ir_raw_handler->encode) - encode_protocols &= ~ir_raw_handler->protocols; mutex_unlock(&ir_raw_handler_lock); } EXPORT_SYMBOL(ir_raw_handler_unregister); diff --git a/drivers/media/rc/rc-loopback.c b/drivers/media/rc/rc-loopback.c index d8bdf63ce985..63dace8198b0 100644 --- a/drivers/media/rc/rc-loopback.c +++ b/drivers/media/rc/rc-loopback.c @@ -26,7 +26,6 @@ #include <linux/device.h> #include <linux/module.h> #include <linux/sched.h> -#include <linux/slab.h> #include <media/rc-core.h> #define DRIVER_NAME "rc-loopback" @@ -177,39 +176,6 @@ static int loop_set_carrier_report(struct rc_dev *dev, int enable) return 0; } -static int loop_set_wakeup_filter(struct rc_dev *dev, - struct rc_scancode_filter *sc_filter) -{ - static const unsigned int max = 512; - struct ir_raw_event *raw; - int ret; - int i; - - /* fine to disable filter */ - if (!sc_filter->mask) - return 0; - - /* encode the specified filter and loop it back */ - raw = kmalloc_array(max, sizeof(*raw), GFP_KERNEL); - ret = ir_raw_encode_scancode(dev->enabled_wakeup_protocols, sc_filter, - raw, max); - /* still loop back the partial raw IR even if it's incomplete */ - if (ret == -ENOBUFS) - ret = max; - if (ret >= 0) { - /* do the loopback */ - for (i = 0; i < ret; ++i) - ir_raw_event_store(dev, &raw[i]); - ir_raw_event_handle(dev); - - ret = 0; - } - - kfree(raw); - - return ret; -} - static int __init loop_init(void) { struct rc_dev *rc; @@ -229,7 +195,6 @@ static int __init loop_init(void) rc->map_name = RC_MAP_EMPTY; rc->priv = &loopdev; rc->driver_type = RC_DRIVER_IR_RAW; - rc->encode_wakeup = true; rc->allowed_protocols = RC_BIT_ALL; rc->timeout = 100 * 1000 * 1000; /* 100 ms */ rc->min_timeout = 1; @@ -244,7 +209,6 @@ static int __init loop_init(void) rc->s_idle = loop_set_idle; rc->s_learning_mode = loop_set_learning_mode; rc->s_carrier_report = loop_set_carrier_report; - rc->s_wakeup_filter = loop_set_wakeup_filter; loopdev.txmask = RXMASK_REGULAR; loopdev.txcarrier = 36000; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 9d015db65280..0ff388a16168 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -865,8 +865,6 @@ static ssize_t show_protocols(struct device *device, } else { enabled = dev->enabled_wakeup_protocols; allowed = dev->allowed_wakeup_protocols; - if (dev->encode_wakeup && !allowed) - allowed = ir_raw_get_encode_protocols(); } mutex_unlock(&dev->lock); @@ -1408,16 +1406,13 @@ int rc_register_device(struct rc_dev *dev) path ? path : "N/A"); kfree(path); - if (dev->driver_type == RC_DRIVER_IR_RAW || dev->encode_wakeup) { + if (dev->driver_type == RC_DRIVER_IR_RAW) { /* Load raw decoders, if they aren't already */ if (!raw_init) { IR_dprintk(1, "Loading raw decoders\n"); ir_raw_init(); raw_init = true; } - } - - if (dev->driver_type == RC_DRIVER_IR_RAW) { /* calls ir_register_device so unlock mutex here*/ mutex_unlock(&dev->lock); rc = ir_raw_event_register(dev); diff --git a/drivers/media/v4l2-core/videobuf2-core.c b/drivers/media/v4l2-core/videobuf2-core.c index 93b315459098..a14c428f70e9 100644 --- a/drivers/media/v4l2-core/videobuf2-core.c +++ b/drivers/media/v4l2-core/videobuf2-core.c @@ -715,6 +715,7 @@ static void __fill_v4l2_buffer(struct vb2_buffer *vb, struct v4l2_buffer *b) break; case VB2_BUF_STATE_PREPARING: case VB2_BUF_STATE_DEQUEUED: + case VB2_BUF_STATE_REQUEUEING: /* nothing */ break; } @@ -1182,7 +1183,8 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) if (WARN_ON(state != VB2_BUF_STATE_DONE && state != VB2_BUF_STATE_ERROR && - state != VB2_BUF_STATE_QUEUED)) + state != VB2_BUF_STATE_QUEUED && + state != VB2_BUF_STATE_REQUEUEING)) state = VB2_BUF_STATE_ERROR; #ifdef CONFIG_VIDEO_ADV_DEBUG @@ -1199,22 +1201,30 @@ void vb2_buffer_done(struct vb2_buffer *vb, enum vb2_buffer_state state) for (plane = 0; plane < vb->num_planes; ++plane) call_void_memop(vb, finish, vb->planes[plane].mem_priv); - /* Add the buffer to the done buffers list */ spin_lock_irqsave(&q->done_lock, flags); - vb->state = state; - if (state != VB2_BUF_STATE_QUEUED) + if (state == VB2_BUF_STATE_QUEUED || + state == VB2_BUF_STATE_REQUEUEING) { + vb->state = VB2_BUF_STATE_QUEUED; + } else { + /* Add the buffer to the done buffers list */ list_add_tail(&vb->done_entry, &q->done_list); + vb->state = state; + } atomic_dec(&q->owned_by_drv_count); spin_unlock_irqrestore(&q->done_lock, flags); - if (state == VB2_BUF_STATE_QUEUED) { + switch (state) { + case VB2_BUF_STATE_QUEUED: + return; + case VB2_BUF_STATE_REQUEUEING: if (q->start_streaming_called) __enqueue_in_driver(vb); return; + default: + /* Inform any processes that may be waiting for buffers */ + wake_up(&q->done_wq); + break; } - - /* Inform any processes that may be waiting for buffers */ - wake_up(&q->done_wq); } EXPORT_SYMBOL_GPL(vb2_buffer_done); @@ -1244,19 +1254,19 @@ EXPORT_SYMBOL_GPL(vb2_discard_done); static void vb2_warn_zero_bytesused(struct vb2_buffer *vb) { - static bool __check_once __read_mostly; + static bool check_once; - if (__check_once) + if (check_once) return; - __check_once = true; - __WARN(); + check_once = true; + WARN_ON(1); - pr_warn_once("use of bytesused == 0 is deprecated and will be removed in the future,\n"); + pr_warn("use of bytesused == 0 is deprecated and will be removed in the future,\n"); if (vb->vb2_queue->allow_zero_bytesused) - pr_warn_once("use VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) instead.\n"); + pr_warn("use VIDIOC_DECODER_CMD(V4L2_DEC_CMD_STOP) instead.\n"); else - pr_warn_once("use the actual size instead.\n"); + pr_warn("use the actual size instead.\n"); } /** diff --git a/drivers/memory/omap-gpmc.c b/drivers/memory/omap-gpmc.c index 3a27a84ad3ec..9426276dbe14 100644 --- a/drivers/memory/omap-gpmc.c +++ b/drivers/memory/omap-gpmc.c @@ -2245,6 +2245,9 @@ void omap3_gpmc_save_context(void) { int i; + if (!gpmc_base) + return; + gpmc_context.sysconfig = gpmc_read_reg(GPMC_SYSCONFIG); gpmc_context.irqenable = gpmc_read_reg(GPMC_IRQENABLE); gpmc_context.timeout_ctrl = gpmc_read_reg(GPMC_TIMEOUT_CONTROL); @@ -2277,6 +2280,9 @@ void omap3_gpmc_restore_context(void) { int i; + if (!gpmc_base) + return; + gpmc_write_reg(GPMC_SYSCONFIG, gpmc_context.sysconfig); gpmc_write_reg(GPMC_IRQENABLE, gpmc_context.irqenable); gpmc_write_reg(GPMC_TIMEOUT_CONTROL, gpmc_context.timeout_ctrl); diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index 653815950aa2..3f68dd251ce8 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -115,7 +115,7 @@ config MFD_CROS_EC_I2C config MFD_CROS_EC_SPI tristate "ChromeOS Embedded Controller (SPI)" - depends on MFD_CROS_EC && CROS_EC_PROTO && SPI && OF + depends on MFD_CROS_EC && CROS_EC_PROTO && SPI ---help--- If you say Y here, you get support for talking to the ChromeOS EC diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index bebf58a06a6b..a72ddb295078 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -651,7 +651,7 @@ static int arizona_runtime_suspend(struct device *dev) arizona->has_fully_powered_off = true; - disable_irq(arizona->irq); + disable_irq_nosync(arizona->irq); arizona_enable_reset(arizona); regulator_bulk_disable(arizona->num_core_supplies, arizona->core_supplies); @@ -1141,10 +1141,6 @@ int arizona_dev_init(struct arizona *arizona) arizona->pdata.gpio_defaults[i]); } - pm_runtime_set_autosuspend_delay(arizona->dev, 100); - pm_runtime_use_autosuspend(arizona->dev); - pm_runtime_enable(arizona->dev); - /* Chip default */ if (!arizona->pdata.clk32k_src) arizona->pdata.clk32k_src = ARIZONA_32KZ_MCLK2; @@ -1245,11 +1241,17 @@ int arizona_dev_init(struct arizona *arizona) arizona->pdata.spk_fmt[i]); } + pm_runtime_set_active(arizona->dev); + pm_runtime_enable(arizona->dev); + /* Set up for interrupts */ ret = arizona_irq_init(arizona); if (ret != 0) goto err_reset; + pm_runtime_set_autosuspend_delay(arizona->dev, 100); + pm_runtime_use_autosuspend(arizona->dev); + arizona_request_irq(arizona, ARIZONA_IRQ_CLKGEN_ERR, "CLKGEN error", arizona_clkgen_err, arizona); arizona_request_irq(arizona, ARIZONA_IRQ_OVERCLOCKED, "Overclocked", @@ -1278,10 +1280,6 @@ int arizona_dev_init(struct arizona *arizona) goto err_irq; } -#ifdef CONFIG_PM - regulator_disable(arizona->dcvdd); -#endif - return 0; err_irq: diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 42c38525904b..ccccc2943f2f 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -271,6 +271,16 @@ config HP_ILO To compile this driver as a module, choose M here: the module will be called hpilo. +config QCOM_COINCELL + tristate "Qualcomm coincell charger support" + depends on MFD_SPMI_PMIC || COMPILE_TEST + help + This driver supports the coincell block found inside of + Qualcomm PMICs. The coincell charger provides a means to + charge a coincell battery or backup capacitor which is used + to maintain PMIC register and RTC state in the absence of + external power. + config SGI_GRU tristate "SGI GRU driver" depends on X86_UV && SMP diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index d056fb7186fe..537d7f3b78da 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -18,6 +18,7 @@ obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o obj-$(CONFIG_TIFM_7XX1) += tifm_7xx1.o obj-$(CONFIG_PHANTOM) += phantom.o +obj-$(CONFIG_QCOM_COINCELL) += qcom-coincell.o obj-$(CONFIG_SENSORS_BH1780) += bh1780gli.o obj-$(CONFIG_SENSORS_BH1770) += bh1770glc.o obj-$(CONFIG_SENSORS_APDS990X) += apds990x.o diff --git a/drivers/misc/ad525x_dpot-i2c.c b/drivers/misc/ad525x_dpot-i2c.c index 705b881e186d..d11187d36ddd 100644 --- a/drivers/misc/ad525x_dpot-i2c.c +++ b/drivers/misc/ad525x_dpot-i2c.c @@ -106,7 +106,6 @@ MODULE_DEVICE_TABLE(i2c, ad_dpot_id); static struct i2c_driver ad_dpot_i2c_driver = { .driver = { .name = "ad_dpot", - .owner = THIS_MODULE, }, .probe = ad_dpot_i2c_probe, .remove = ad_dpot_i2c_remove, diff --git a/drivers/misc/apds990x.c b/drivers/misc/apds990x.c index 3739ffa9cdf1..a3e789b85cc8 100644 --- a/drivers/misc/apds990x.c +++ b/drivers/misc/apds990x.c @@ -1275,7 +1275,6 @@ static const struct dev_pm_ops apds990x_pm_ops = { static struct i2c_driver apds990x_driver = { .driver = { .name = "apds990x", - .owner = THIS_MODULE, .pm = &apds990x_pm_ops, }, .probe = apds990x_probe, diff --git a/drivers/misc/bh1770glc.c b/drivers/misc/bh1770glc.c index b756381b8250..753d7ecdadaa 100644 --- a/drivers/misc/bh1770glc.c +++ b/drivers/misc/bh1770glc.c @@ -1396,7 +1396,6 @@ static const struct dev_pm_ops bh1770_pm_ops = { static struct i2c_driver bh1770_driver = { .driver = { .name = "bh1770glc", - .owner = THIS_MODULE, .pm = &bh1770_pm_ops, }, .probe = bh1770_probe, diff --git a/drivers/misc/bmp085-i2c.c b/drivers/misc/bmp085-i2c.c index a7c16295b816..f35c218aaa1a 100644 --- a/drivers/misc/bmp085-i2c.c +++ b/drivers/misc/bmp085-i2c.c @@ -66,7 +66,6 @@ MODULE_DEVICE_TABLE(i2c, bmp085_id); static struct i2c_driver bmp085_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = BMP085_NAME, }, .id_table = bmp085_id, diff --git a/drivers/misc/cxl/sysfs.c b/drivers/misc/cxl/sysfs.c index 31f38bc71a3d..87cd747bb511 100644 --- a/drivers/misc/cxl/sysfs.c +++ b/drivers/misc/cxl/sysfs.c @@ -443,12 +443,7 @@ static ssize_t afu_read_config(struct file *filp, struct kobject *kobj, struct afu_config_record *cr = to_cr(kobj); struct cxl_afu *afu = to_cxl_afu(container_of(kobj->parent, struct device, kobj)); - u64 i, j, val, size = afu->crs_len; - - if (off > size) - return 0; - if (off + count > size) - count = size - off; + u64 i, j, val; for (i = 0; i < count;) { val = cxl_afu_cr_read64(afu, cr->cr, off & ~0x7); diff --git a/drivers/misc/ds1682.c b/drivers/misc/ds1682.c index b909fb30232a..c7112276a039 100644 --- a/drivers/misc/ds1682.c +++ b/drivers/misc/ds1682.c @@ -148,12 +148,6 @@ static ssize_t ds1682_eeprom_read(struct file *filp, struct kobject *kobj, dev_dbg(&client->dev, "ds1682_eeprom_read(p=%p, off=%lli, c=%zi)\n", buf, off, count); - if (off >= DS1682_EEPROM_SIZE) - return 0; - - if (off + count > DS1682_EEPROM_SIZE) - count = DS1682_EEPROM_SIZE - off; - rc = i2c_smbus_read_i2c_block_data(client, DS1682_REG_EEPROM + off, count, buf); if (rc < 0) @@ -171,12 +165,6 @@ static ssize_t ds1682_eeprom_write(struct file *filp, struct kobject *kobj, dev_dbg(&client->dev, "ds1682_eeprom_write(p=%p, off=%lli, c=%zi)\n", buf, off, count); - if (off >= DS1682_EEPROM_SIZE) - return -ENOSPC; - - if (off + count > DS1682_EEPROM_SIZE) - count = DS1682_EEPROM_SIZE - off; - /* Write out to the device */ if (i2c_smbus_write_i2c_block_data(client, DS1682_REG_EEPROM + off, count, buf) < 0) diff --git a/drivers/misc/eeprom/Kconfig b/drivers/misc/eeprom/Kconfig index 9536852fd4c6..04f2e1fa9dd1 100644 --- a/drivers/misc/eeprom/Kconfig +++ b/drivers/misc/eeprom/Kconfig @@ -96,17 +96,4 @@ config EEPROM_DIGSY_MTC_CFG If unsure, say N. -config EEPROM_SUNXI_SID - tristate "Allwinner sunxi security ID support" - depends on ARCH_SUNXI && SYSFS - help - This is a driver for the 'security ID' available on various Allwinner - devices. - - Due to the potential risks involved with changing e-fuses, - this driver is read-only. - - This driver can also be built as a module. If so, the module - will be called sunxi_sid. - endmenu diff --git a/drivers/misc/eeprom/Makefile b/drivers/misc/eeprom/Makefile index 9507aec95e94..fc1e81d29267 100644 --- a/drivers/misc/eeprom/Makefile +++ b/drivers/misc/eeprom/Makefile @@ -4,5 +4,4 @@ obj-$(CONFIG_EEPROM_LEGACY) += eeprom.o obj-$(CONFIG_EEPROM_MAX6875) += max6875.o obj-$(CONFIG_EEPROM_93CX6) += eeprom_93cx6.o obj-$(CONFIG_EEPROM_93XX46) += eeprom_93xx46.o -obj-$(CONFIG_EEPROM_SUNXI_SID) += sunxi_sid.o obj-$(CONFIG_EEPROM_DIGSY_MTC_CFG) += digsy_mtc_eeprom.o diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c index 6ded3dc36644..2b254f3a1154 100644 --- a/drivers/misc/eeprom/at24.c +++ b/drivers/misc/eeprom/at24.c @@ -686,7 +686,6 @@ static int at24_remove(struct i2c_client *client) static struct i2c_driver at24_driver = { .driver = { .name = "at24", - .owner = THIS_MODULE, }, .probe = at24_probe, .remove = at24_remove, diff --git a/drivers/misc/eeprom/eeprom.c b/drivers/misc/eeprom/eeprom.c index b432873def96..7342fd637031 100644 --- a/drivers/misc/eeprom/eeprom.c +++ b/drivers/misc/eeprom/eeprom.c @@ -88,11 +88,6 @@ static ssize_t eeprom_read(struct file *filp, struct kobject *kobj, struct eeprom_data *data = i2c_get_clientdata(client); u8 slice; - if (off > EEPROM_SIZE) - return 0; - if (off + count > EEPROM_SIZE) - count = EEPROM_SIZE - off; - /* Only refresh slices which contain requested bytes */ for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++) eeprom_update_client(client, slice); diff --git a/drivers/misc/eeprom/eeprom_93xx46.c b/drivers/misc/eeprom/eeprom_93xx46.c index 9ebeacdb8ec4..a6bd9e3fe9d3 100644 --- a/drivers/misc/eeprom/eeprom_93xx46.c +++ b/drivers/misc/eeprom/eeprom_93xx46.c @@ -48,13 +48,6 @@ eeprom_93xx46_bin_read(struct file *filp, struct kobject *kobj, dev = container_of(kobj, struct device, kobj); edev = dev_get_drvdata(dev); - if (unlikely(off >= edev->bin.size)) - return 0; - if ((off + count) > edev->bin.size) - count = edev->bin.size - off; - if (unlikely(!count)) - return count; - cmd_addr = OP_READ << edev->addrlen; if (edev->addrlen == 7) { @@ -200,13 +193,6 @@ eeprom_93xx46_bin_write(struct file *filp, struct kobject *kobj, dev = container_of(kobj, struct device, kobj); edev = dev_get_drvdata(dev); - if (unlikely(off >= edev->bin.size)) - return -EFBIG; - if ((off + count) > edev->bin.size) - count = edev->bin.size - off; - if (unlikely(!count)) - return count; - /* only write even number of bytes on 16-bit devices */ if (edev->addrlen == 6) { step = 2; diff --git a/drivers/misc/eeprom/max6875.c b/drivers/misc/eeprom/max6875.c index 580ff9df5529..9aa4332a6b04 100644 --- a/drivers/misc/eeprom/max6875.c +++ b/drivers/misc/eeprom/max6875.c @@ -114,12 +114,6 @@ static ssize_t max6875_read(struct file *filp, struct kobject *kobj, struct max6875_data *data = i2c_get_clientdata(client); int slice, max_slice; - if (off > USER_EEPROM_SIZE) - return 0; - - if (off + count > USER_EEPROM_SIZE) - count = USER_EEPROM_SIZE - off; - /* refresh slices which contain requested bytes */ max_slice = (off + count - 1) >> SLICE_BITS; for (slice = (off >> SLICE_BITS); slice <= max_slice; slice++) diff --git a/drivers/misc/eeprom/sunxi_sid.c b/drivers/misc/eeprom/sunxi_sid.c deleted file mode 100644 index 8385177ff32b..000000000000 --- a/drivers/misc/eeprom/sunxi_sid.c +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> - * http://www.linux-sunxi.org - * - * 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. - * - * This driver exposes the Allwinner security ID, efuses exported in byte- - * sized chunks. - */ - -#include <linux/compiler.h> -#include <linux/device.h> -#include <linux/err.h> -#include <linux/export.h> -#include <linux/fs.h> -#include <linux/io.h> -#include <linux/kernel.h> -#include <linux/kobject.h> -#include <linux/module.h> -#include <linux/of_device.h> -#include <linux/platform_device.h> -#include <linux/random.h> -#include <linux/slab.h> -#include <linux/stat.h> -#include <linux/sysfs.h> -#include <linux/types.h> - -#define DRV_NAME "sunxi-sid" - -struct sunxi_sid_data { - void __iomem *reg_base; - unsigned int keysize; -}; - -/* We read the entire key, due to a 32 bit read alignment requirement. Since we - * want to return the requested byte, this results in somewhat slower code and - * uses 4 times more reads as needed but keeps code simpler. Since the SID is - * only very rarely probed, this is not really an issue. - */ -static u8 sunxi_sid_read_byte(const struct sunxi_sid_data *sid_data, - const unsigned int offset) -{ - u32 sid_key; - - if (offset >= sid_data->keysize) - return 0; - - sid_key = ioread32be(sid_data->reg_base + round_down(offset, 4)); - sid_key >>= (offset % 4) * 8; - - return sid_key; /* Only return the last byte */ -} - -static ssize_t sid_read(struct file *fd, struct kobject *kobj, - struct bin_attribute *attr, char *buf, - loff_t pos, size_t size) -{ - struct platform_device *pdev; - struct sunxi_sid_data *sid_data; - int i; - - pdev = to_platform_device(kobj_to_dev(kobj)); - sid_data = platform_get_drvdata(pdev); - - if (pos < 0 || pos >= sid_data->keysize) - return 0; - if (size > sid_data->keysize - pos) - size = sid_data->keysize - pos; - - for (i = 0; i < size; i++) - buf[i] = sunxi_sid_read_byte(sid_data, pos + i); - - return i; -} - -static struct bin_attribute sid_bin_attr = { - .attr = { .name = "eeprom", .mode = S_IRUGO, }, - .read = sid_read, -}; - -static int sunxi_sid_remove(struct platform_device *pdev) -{ - device_remove_bin_file(&pdev->dev, &sid_bin_attr); - dev_dbg(&pdev->dev, "driver unloaded\n"); - - return 0; -} - -static const struct of_device_id sunxi_sid_of_match[] = { - { .compatible = "allwinner,sun4i-a10-sid", .data = (void *)16}, - { .compatible = "allwinner,sun7i-a20-sid", .data = (void *)512}, - {/* sentinel */}, -}; -MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); - -static int sunxi_sid_probe(struct platform_device *pdev) -{ - struct sunxi_sid_data *sid_data; - struct resource *res; - const struct of_device_id *of_dev_id; - u8 *entropy; - unsigned int i; - - sid_data = devm_kzalloc(&pdev->dev, sizeof(struct sunxi_sid_data), - GFP_KERNEL); - if (!sid_data) - return -ENOMEM; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - sid_data->reg_base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(sid_data->reg_base)) - return PTR_ERR(sid_data->reg_base); - - of_dev_id = of_match_device(sunxi_sid_of_match, &pdev->dev); - if (!of_dev_id) - return -ENODEV; - sid_data->keysize = (int)of_dev_id->data; - - platform_set_drvdata(pdev, sid_data); - - sid_bin_attr.size = sid_data->keysize; - if (device_create_bin_file(&pdev->dev, &sid_bin_attr)) - return -ENODEV; - - entropy = kzalloc(sizeof(u8) * sid_data->keysize, GFP_KERNEL); - for (i = 0; i < sid_data->keysize; i++) - entropy[i] = sunxi_sid_read_byte(sid_data, i); - add_device_randomness(entropy, sid_data->keysize); - kfree(entropy); - - dev_dbg(&pdev->dev, "loaded\n"); - - return 0; -} - -static struct platform_driver sunxi_sid_driver = { - .probe = sunxi_sid_probe, - .remove = sunxi_sid_remove, - .driver = { - .name = DRV_NAME, - .of_match_table = sunxi_sid_of_match, - }, -}; -module_platform_driver(sunxi_sid_driver); - -MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); -MODULE_DESCRIPTION("Allwinner sunxi security id driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/misc/isl29003.c b/drivers/misc/isl29003.c index 12c30b486b27..976df0013633 100644 --- a/drivers/misc/isl29003.c +++ b/drivers/misc/isl29003.c @@ -465,7 +465,6 @@ MODULE_DEVICE_TABLE(i2c, isl29003_id); static struct i2c_driver isl29003_driver = { .driver = { .name = ISL29003_DRV_NAME, - .owner = THIS_MODULE, .pm = ISL29003_PM_OPS, }, .probe = isl29003_probe, diff --git a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c index e3e7f1dc27ba..0c3bb7e3ee80 100644 --- a/drivers/misc/lis3lv02d/lis3lv02d_i2c.c +++ b/drivers/misc/lis3lv02d/lis3lv02d_i2c.c @@ -274,7 +274,6 @@ static const struct dev_pm_ops lis3_pm_ops = { static struct i2c_driver lis3lv02d_i2c_driver = { .driver = { .name = DRV_NAME, - .owner = THIS_MODULE, .pm = &lis3_pm_ops, .of_match_table = of_match_ptr(lis3lv02d_i2c_dt_ids), }, diff --git a/drivers/misc/mei/Makefile b/drivers/misc/mei/Makefile index 518914a82b83..01447ca21c26 100644 --- a/drivers/misc/mei/Makefile +++ b/drivers/misc/mei/Makefile @@ -11,7 +11,7 @@ mei-objs += main.o mei-objs += amthif.o mei-objs += wd.o mei-objs += bus.o -mei-objs += nfc.o +mei-objs += bus-fixup.o mei-$(CONFIG_DEBUG_FS) += debugfs.o obj-$(CONFIG_INTEL_MEI_ME) += mei-me.o diff --git a/drivers/misc/mei/bus-fixup.c b/drivers/misc/mei/bus-fixup.c new file mode 100644 index 000000000000..3e536ca85f7d --- /dev/null +++ b/drivers/misc/mei/bus-fixup.c @@ -0,0 +1,306 @@ +/* + * + * Intel Management Engine Interface (Intel MEI) Linux driver + * Copyright (c) 2003-2013, Intel Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + */ + +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/device.h> +#include <linux/slab.h> +#include <linux/uuid.h> + +#include <linux/mei_cl_bus.h> + +#include "mei_dev.h" +#include "client.h" + +#define MEI_UUID_NFC_INFO UUID_LE(0xd2de1625, 0x382d, 0x417d, \ + 0x48, 0xa4, 0xef, 0xab, 0xba, 0x8a, 0x12, 0x06) + +static const uuid_le mei_nfc_info_guid = MEI_UUID_NFC_INFO; + +#define MEI_UUID_NFC_HCI UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, \ + 0x94, 0xd4, 0x50, 0x26, 0x67, 0x23, 0x77, 0x5c) + +#define MEI_UUID_ANY NULL_UUID_LE + +/** + * number_of_connections - determine whether an client be on the bus + * according number of connections + * We support only clients: + * 1. with single connection + * 2. and fixed clients (max_number_of_connections == 0) + * + * @cldev: me clients device + */ +static void number_of_connections(struct mei_cl_device *cldev) +{ + dev_dbg(&cldev->dev, "running hook %s on %pUl\n", + __func__, mei_me_cl_uuid(cldev->me_cl)); + + if (cldev->me_cl->props.max_number_of_connections > 1) + cldev->do_match = 0; +} + +/** + * blacklist - blacklist a client from the bus + * + * @cldev: me clients device + */ +static void blacklist(struct mei_cl_device *cldev) +{ + dev_dbg(&cldev->dev, "running hook %s on %pUl\n", + __func__, mei_me_cl_uuid(cldev->me_cl)); + cldev->do_match = 0; +} + +struct mei_nfc_cmd { + u8 command; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; + u8 sub_command; + u8 data[]; +} __packed; + +struct mei_nfc_reply { + u8 command; + u8 status; + u16 req_id; + u32 reserved; + u16 data_size; + u8 sub_command; + u8 reply_status; + u8 data[]; +} __packed; + +struct mei_nfc_if_version { + u8 radio_version_sw[3]; + u8 reserved[3]; + u8 radio_version_hw[3]; + u8 i2c_addr; + u8 fw_ivn; + u8 vendor_id; + u8 radio_type; +} __packed; + + +#define MEI_NFC_CMD_MAINTENANCE 0x00 +#define MEI_NFC_SUBCMD_IF_VERSION 0x01 + +/* Vendors */ +#define MEI_NFC_VENDOR_INSIDE 0x00 +#define MEI_NFC_VENDOR_NXP 0x01 + +/* Radio types */ +#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 +#define MEI_NFC_VENDOR_NXP_PN544 0x01 + +/** + * mei_nfc_if_version - get NFC interface version + * + * @cl: host client (nfc info) + * @ver: NFC interface version to be filled in + * + * Return: 0 on success; < 0 otherwise + */ +static int mei_nfc_if_version(struct mei_cl *cl, + struct mei_nfc_if_version *ver) +{ + struct mei_device *bus; + struct mei_nfc_cmd cmd = { + .command = MEI_NFC_CMD_MAINTENANCE, + .data_size = 1, + .sub_command = MEI_NFC_SUBCMD_IF_VERSION, + }; + struct mei_nfc_reply *reply = NULL; + size_t if_version_length; + int bytes_recv, ret; + + bus = cl->dev; + + WARN_ON(mutex_is_locked(&bus->device_lock)); + + ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1); + if (ret < 0) { + dev_err(bus->dev, "Could not send IF version cmd\n"); + return ret; + } + + /* to be sure on the stack we alloc memory */ + if_version_length = sizeof(struct mei_nfc_reply) + + sizeof(struct mei_nfc_if_version); + + reply = kzalloc(if_version_length, GFP_KERNEL); + if (!reply) + return -ENOMEM; + + ret = 0; + bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); + if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { + dev_err(bus->dev, "Could not read IF version\n"); + ret = -EIO; + goto err; + } + + memcpy(ver, reply->data, sizeof(struct mei_nfc_if_version)); + + dev_info(bus->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", + ver->fw_ivn, ver->vendor_id, ver->radio_type); + +err: + kfree(reply); + return ret; +} + +/** + * mei_nfc_radio_name - derive nfc radio name from the interface version + * + * @ver: NFC radio version + * + * Return: radio name string + */ +static const char *mei_nfc_radio_name(struct mei_nfc_if_version *ver) +{ + + if (ver->vendor_id == MEI_NFC_VENDOR_INSIDE) { + if (ver->radio_type == MEI_NFC_VENDOR_INSIDE_UREAD) + return "microread"; + } + + if (ver->vendor_id == MEI_NFC_VENDOR_NXP) { + if (ver->radio_type == MEI_NFC_VENDOR_NXP_PN544) + return "pn544"; + } + + return NULL; +} + +/** + * mei_nfc - The nfc fixup function. The function retrieves nfc radio + * name and set is as device attribute so we can load + * the proper device driver for it + * + * @cldev: me client device (nfc) + */ +static void mei_nfc(struct mei_cl_device *cldev) +{ + struct mei_device *bus; + struct mei_cl *cl; + struct mei_me_client *me_cl = NULL; + struct mei_nfc_if_version ver; + const char *radio_name = NULL; + int ret; + + bus = cldev->bus; + + dev_dbg(bus->dev, "running hook %s: %pUl match=%d\n", + __func__, mei_me_cl_uuid(cldev->me_cl), cldev->do_match); + + mutex_lock(&bus->device_lock); + /* we need to connect to INFO GUID */ + cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + if (IS_ERR(cl)) { + ret = PTR_ERR(cl); + cl = NULL; + dev_err(bus->dev, "nfc hook alloc failed %d\n", ret); + goto out; + } + + me_cl = mei_me_cl_by_uuid(bus, &mei_nfc_info_guid); + if (!me_cl) { + ret = -ENOTTY; + dev_err(bus->dev, "Cannot find nfc info %d\n", ret); + goto out; + } + + ret = mei_cl_connect(cl, me_cl, NULL); + if (ret < 0) { + dev_err(&cldev->dev, "Can't connect to the NFC INFO ME ret = %d\n", + ret); + goto out; + } + + mutex_unlock(&bus->device_lock); + + ret = mei_nfc_if_version(cl, &ver); + if (ret) + goto disconnect; + + radio_name = mei_nfc_radio_name(&ver); + + if (!radio_name) { + ret = -ENOENT; + dev_err(&cldev->dev, "Can't get the NFC interface version ret = %d\n", + ret); + goto disconnect; + } + + dev_dbg(bus->dev, "nfc radio %s\n", radio_name); + strlcpy(cldev->name, radio_name, sizeof(cldev->name)); + +disconnect: + mutex_lock(&bus->device_lock); + if (mei_cl_disconnect(cl) < 0) + dev_err(bus->dev, "Can't disconnect the NFC INFO ME\n"); + + mei_cl_flush_queues(cl, NULL); + +out: + mei_cl_unlink(cl); + mutex_unlock(&bus->device_lock); + mei_me_cl_put(me_cl); + kfree(cl); + + if (ret) + cldev->do_match = 0; + + dev_dbg(bus->dev, "end of fixup match = %d\n", cldev->do_match); +} + +#define MEI_FIXUP(_uuid, _hook) { _uuid, _hook } + +static struct mei_fixup { + + const uuid_le uuid; + void (*hook)(struct mei_cl_device *cldev); +} mei_fixups[] = { + MEI_FIXUP(MEI_UUID_ANY, number_of_connections), + MEI_FIXUP(MEI_UUID_NFC_INFO, blacklist), + MEI_FIXUP(MEI_UUID_NFC_HCI, mei_nfc), +}; + +/** + * mei_cl_dev_fixup - run fixup handlers + * + * @cldev: me client device + */ +void mei_cl_dev_fixup(struct mei_cl_device *cldev) +{ + struct mei_fixup *f; + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + int i; + + for (i = 0; i < ARRAY_SIZE(mei_fixups); i++) { + + f = &mei_fixups[i]; + if (uuid_le_cmp(f->uuid, MEI_UUID_ANY) == 0 || + uuid_le_cmp(f->uuid, *uuid) == 0) + f->hook(cldev); + } +} + diff --git a/drivers/misc/mei/bus.c b/drivers/misc/mei/bus.c index 458aa5a09c52..eef1c6b46ad8 100644 --- a/drivers/misc/mei/bus.c +++ b/drivers/misc/mei/bus.c @@ -30,276 +30,29 @@ #define to_mei_cl_driver(d) container_of(d, struct mei_cl_driver, driver) #define to_mei_cl_device(d) container_of(d, struct mei_cl_device, dev) -static int mei_cl_device_match(struct device *dev, struct device_driver *drv) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - struct mei_cl_driver *driver = to_mei_cl_driver(drv); - const struct mei_cl_device_id *id; - const uuid_le *uuid; - const char *name; - - if (!device) - return 0; - - uuid = mei_me_cl_uuid(device->me_cl); - name = device->name; - - if (!driver || !driver->id_table) - return 0; - - id = driver->id_table; - - while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { - - if (!uuid_le_cmp(*uuid, id->uuid)) { - if (id->name[0]) { - if (!strncmp(name, id->name, sizeof(id->name))) - return 1; - } else { - return 1; - } - } - - id++; - } - - return 0; -} - -static int mei_cl_device_probe(struct device *dev) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - struct mei_cl_driver *driver; - struct mei_cl_device_id id; - - if (!device) - return 0; - - driver = to_mei_cl_driver(dev->driver); - if (!driver || !driver->probe) - return -ENODEV; - - dev_dbg(dev, "Device probe\n"); - - strlcpy(id.name, device->name, sizeof(id.name)); - - return driver->probe(device, &id); -} - -static int mei_cl_device_remove(struct device *dev) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - struct mei_cl_driver *driver; - - if (!device || !dev->driver) - return 0; - - if (device->event_cb) { - device->event_cb = NULL; - cancel_work_sync(&device->event_work); - } - - driver = to_mei_cl_driver(dev->driver); - if (!driver->remove) { - dev->driver = NULL; - - return 0; - } - - return driver->remove(device); -} - -static ssize_t name_show(struct device *dev, struct device_attribute *a, - char *buf) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - size_t len; - - len = snprintf(buf, PAGE_SIZE, "%s", device->name); - - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; -} -static DEVICE_ATTR_RO(name); - -static ssize_t uuid_show(struct device *dev, struct device_attribute *a, - char *buf) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(device->me_cl); - size_t len; - - len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); - - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; -} -static DEVICE_ATTR_RO(uuid); - -static ssize_t modalias_show(struct device *dev, struct device_attribute *a, - char *buf) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(device->me_cl); - size_t len; - - len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":", - device->name, MEI_CL_UUID_ARGS(uuid->b)); - - return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; -} -static DEVICE_ATTR_RO(modalias); - -static struct attribute *mei_cl_dev_attrs[] = { - &dev_attr_name.attr, - &dev_attr_uuid.attr, - &dev_attr_modalias.attr, - NULL, -}; -ATTRIBUTE_GROUPS(mei_cl_dev); - -static int mei_cl_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - const uuid_le *uuid = mei_me_cl_uuid(device->me_cl); - - if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) - return -ENOMEM; - - if (add_uevent_var(env, "MEI_CL_NAME=%s", device->name)) - return -ENOMEM; - - if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":", - device->name, MEI_CL_UUID_ARGS(uuid->b))) - return -ENOMEM; - - return 0; -} - -static struct bus_type mei_cl_bus_type = { - .name = "mei", - .dev_groups = mei_cl_dev_groups, - .match = mei_cl_device_match, - .probe = mei_cl_device_probe, - .remove = mei_cl_device_remove, - .uevent = mei_cl_uevent, -}; - -static void mei_cl_dev_release(struct device *dev) -{ - struct mei_cl_device *device = to_mei_cl_device(dev); - - if (!device) - return; - - mei_me_cl_put(device->me_cl); - kfree(device); -} - -static struct device_type mei_cl_device_type = { - .release = mei_cl_dev_release, -}; - -struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev, - uuid_le uuid) -{ - struct mei_cl *cl; - - list_for_each_entry(cl, &dev->device_list, device_link) { - if (cl->device && cl->device->me_cl && - !uuid_le_cmp(uuid, *mei_me_cl_uuid(cl->device->me_cl))) - return cl; - } - - return NULL; -} - -struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, - struct mei_me_client *me_cl, - struct mei_cl *cl, - char *name) -{ - struct mei_cl_device *device; - int status; - - device = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); - if (!device) - return NULL; - - device->me_cl = mei_me_cl_get(me_cl); - if (!device->me_cl) { - kfree(device); - return NULL; - } - - device->cl = cl; - device->dev.parent = dev->dev; - device->dev.bus = &mei_cl_bus_type; - device->dev.type = &mei_cl_device_type; - - strlcpy(device->name, name, sizeof(device->name)); - - dev_set_name(&device->dev, "mei:%s:%pUl", name, mei_me_cl_uuid(me_cl)); - - status = device_register(&device->dev); - if (status) { - dev_err(dev->dev, "Failed to register MEI device\n"); - mei_me_cl_put(device->me_cl); - kfree(device); - return NULL; - } - - cl->device = device; - - dev_dbg(&device->dev, "client %s registered\n", name); - - return device; -} -EXPORT_SYMBOL_GPL(mei_cl_add_device); - -void mei_cl_remove_device(struct mei_cl_device *device) -{ - device_unregister(&device->dev); -} -EXPORT_SYMBOL_GPL(mei_cl_remove_device); - -int __mei_cl_driver_register(struct mei_cl_driver *driver, struct module *owner) -{ - int err; - - driver->driver.name = driver->name; - driver->driver.owner = owner; - driver->driver.bus = &mei_cl_bus_type; - - err = driver_register(&driver->driver); - if (err) - return err; - - pr_debug("mei: driver [%s] registered\n", driver->driver.name); - - return 0; -} -EXPORT_SYMBOL_GPL(__mei_cl_driver_register); - -void mei_cl_driver_unregister(struct mei_cl_driver *driver) -{ - driver_unregister(&driver->driver); - - pr_debug("mei: driver [%s] unregistered\n", driver->driver.name); -} -EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); - +/** + * __mei_cl_send - internal client send (write) + * + * @cl: host client + * @buf: buffer to send + * @length: buffer length + * @blocking: wait for write completion + * + * Return: written size bytes or < 0 on error + */ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking) { - struct mei_device *dev; + struct mei_device *bus; struct mei_cl_cb *cb = NULL; ssize_t rets; if (WARN_ON(!cl || !cl->dev)) return -ENODEV; - dev = cl->dev; + bus = cl->dev; - mutex_lock(&dev->device_lock); + mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { rets = -ENODEV; goto out; @@ -327,16 +80,25 @@ ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, rets = mei_cl_write(cl, cb, blocking); out: - mutex_unlock(&dev->device_lock); + mutex_unlock(&bus->device_lock); if (rets < 0) mei_io_cb_free(cb); return rets; } +/** + * __mei_cl_recv - internal client receive (read) + * + * @cl: host client + * @buf: buffer to send + * @length: buffer length + * + * Return: read size in bytes of < 0 on error + */ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) { - struct mei_device *dev; + struct mei_device *bus; struct mei_cl_cb *cb; size_t r_length; ssize_t rets; @@ -344,9 +106,9 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (WARN_ON(!cl || !cl->dev)) return -ENODEV; - dev = cl->dev; + bus = cl->dev; - mutex_lock(&dev->device_lock); + mutex_lock(&bus->device_lock); cb = mei_cl_read_cb(cl, NULL); if (cb) @@ -356,9 +118,10 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) if (rets && rets != -EBUSY) goto out; + /* wait on event only if there is no other waiter */ if (list_empty(&cl->rd_completed) && !waitqueue_active(&cl->rx_wait)) { - mutex_unlock(&dev->device_lock); + mutex_unlock(&bus->device_lock); if (wait_event_interruptible(cl->rx_wait, (!list_empty(&cl->rd_completed)) || @@ -369,7 +132,7 @@ ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length) return -ERESTARTSYS; } - mutex_lock(&dev->device_lock); + mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { rets = -EBUSY; @@ -396,14 +159,23 @@ copy: free: mei_io_cb_free(cb); out: - mutex_unlock(&dev->device_lock); + mutex_unlock(&bus->device_lock); return rets; } -ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) +/** + * mei_cl_send - me device send (write) + * + * @cldev: me client device + * @buf: buffer to send + * @length: buffer length + * + * Return: written size in bytes or < 0 on error + */ +ssize_t mei_cl_send(struct mei_cl_device *cldev, u8 *buf, size_t length) { - struct mei_cl *cl = device->cl; + struct mei_cl *cl = cldev->cl; if (cl == NULL) return -ENODEV; @@ -412,9 +184,18 @@ ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length) } EXPORT_SYMBOL_GPL(mei_cl_send); -ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) +/** + * mei_cl_recv - client receive (read) + * + * @cldev: me client device + * @buf: buffer to send + * @length: buffer length + * + * Return: read size in bytes of < 0 on error + */ +ssize_t mei_cl_recv(struct mei_cl_device *cldev, u8 *buf, size_t length) { - struct mei_cl *cl = device->cl; + struct mei_cl *cl = cldev->cl; if (cl == NULL) return -ENODEV; @@ -423,134 +204,697 @@ ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length) } EXPORT_SYMBOL_GPL(mei_cl_recv); +/** + * mei_bus_event_work - dispatch rx event for a bus device + * and schedule new work + * + * @work: work + */ static void mei_bus_event_work(struct work_struct *work) { - struct mei_cl_device *device; + struct mei_cl_device *cldev; - device = container_of(work, struct mei_cl_device, event_work); + cldev = container_of(work, struct mei_cl_device, event_work); - if (device->event_cb) - device->event_cb(device, device->events, device->event_context); + if (cldev->event_cb) + cldev->event_cb(cldev, cldev->events, cldev->event_context); - device->events = 0; + cldev->events = 0; /* Prepare for the next read */ - mei_cl_read_start(device->cl, 0, NULL); + if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) + mei_cl_read_start(cldev->cl, 0, NULL); } -int mei_cl_register_event_cb(struct mei_cl_device *device, +/** + * mei_cl_bus_notify_event - schedule notify cb on bus client + * + * @cl: host client + */ +void mei_cl_bus_notify_event(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (!cldev || !cldev->event_cb) + return; + + if (!(cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF))) + return; + + if (!cl->notify_ev) + return; + + set_bit(MEI_CL_EVENT_NOTIF, &cldev->events); + + schedule_work(&cldev->event_work); + + cl->notify_ev = false; +} + +/** + * mei_cl_bus_rx_event - schedule rx evenet + * + * @cl: host client + */ +void mei_cl_bus_rx_event(struct mei_cl *cl) +{ + struct mei_cl_device *cldev = cl->cldev; + + if (!cldev || !cldev->event_cb) + return; + + if (!(cldev->events_mask & BIT(MEI_CL_EVENT_RX))) + return; + + set_bit(MEI_CL_EVENT_RX, &cldev->events); + + schedule_work(&cldev->event_work); +} + +/** + * mei_cl_register_event_cb - register event callback + * + * @cldev: me client devices + * @event_cb: callback function + * @events_mask: requested events bitmask + * @context: driver context data + * + * Return: 0 on success + * -EALREADY if an callback is already registered + * <0 on other errors + */ +int mei_cl_register_event_cb(struct mei_cl_device *cldev, + unsigned long events_mask, mei_cl_event_cb_t event_cb, void *context) { - if (device->event_cb) + int ret; + + if (cldev->event_cb) return -EALREADY; - device->events = 0; - device->event_cb = event_cb; - device->event_context = context; - INIT_WORK(&device->event_work, mei_bus_event_work); + cldev->events = 0; + cldev->events_mask = events_mask; + cldev->event_cb = event_cb; + cldev->event_context = context; + INIT_WORK(&cldev->event_work, mei_bus_event_work); - mei_cl_read_start(device->cl, 0, NULL); + if (cldev->events_mask & BIT(MEI_CL_EVENT_RX)) { + ret = mei_cl_read_start(cldev->cl, 0, NULL); + if (ret && ret != -EBUSY) + return ret; + } + + if (cldev->events_mask & BIT(MEI_CL_EVENT_NOTIF)) { + mutex_lock(&cldev->cl->dev->device_lock); + ret = mei_cl_notify_request(cldev->cl, NULL, event_cb ? 1 : 0); + mutex_unlock(&cldev->cl->dev->device_lock); + if (ret) + return ret; + } return 0; } EXPORT_SYMBOL_GPL(mei_cl_register_event_cb); -void *mei_cl_get_drvdata(const struct mei_cl_device *device) +/** + * mei_cl_get_drvdata - driver data getter + * + * @cldev: mei client device + * + * Return: driver private data + */ +void *mei_cl_get_drvdata(const struct mei_cl_device *cldev) { - return dev_get_drvdata(&device->dev); + return dev_get_drvdata(&cldev->dev); } EXPORT_SYMBOL_GPL(mei_cl_get_drvdata); -void mei_cl_set_drvdata(struct mei_cl_device *device, void *data) +/** + * mei_cl_set_drvdata - driver data setter + * + * @cldev: mei client device + * @data: data to store + */ +void mei_cl_set_drvdata(struct mei_cl_device *cldev, void *data) { - dev_set_drvdata(&device->dev, data); + dev_set_drvdata(&cldev->dev, data); } EXPORT_SYMBOL_GPL(mei_cl_set_drvdata); -int mei_cl_enable_device(struct mei_cl_device *device) +/** + * mei_cl_enable_device - enable me client device + * create connection with me client + * + * @cldev: me client device + * + * Return: 0 on success and < 0 on error + */ +int mei_cl_enable_device(struct mei_cl_device *cldev) { - int err; - struct mei_device *dev; - struct mei_cl *cl = device->cl; - - if (cl == NULL) - return -ENODEV; - - dev = cl->dev; - - mutex_lock(&dev->device_lock); + struct mei_device *bus = cldev->bus; + struct mei_cl *cl; + int ret; + + cl = cldev->cl; + + if (!cl) { + mutex_lock(&bus->device_lock); + cl = mei_cl_alloc_linked(bus, MEI_HOST_CLIENT_ID_ANY); + mutex_unlock(&bus->device_lock); + if (IS_ERR(cl)) + return PTR_ERR(cl); + /* update pointers */ + cldev->cl = cl; + cl->cldev = cldev; + } + mutex_lock(&bus->device_lock); if (mei_cl_is_connected(cl)) { - mutex_unlock(&dev->device_lock); - dev_warn(dev->dev, "Already connected"); - return -EBUSY; + ret = 0; + goto out; } - err = mei_cl_connect(cl, device->me_cl, NULL); - if (err < 0) { - mutex_unlock(&dev->device_lock); - dev_err(dev->dev, "Could not connect to the ME client"); - - return err; + if (!mei_me_cl_is_active(cldev->me_cl)) { + dev_err(&cldev->dev, "me client is not active\n"); + ret = -ENOTTY; + goto out; } - mutex_unlock(&dev->device_lock); + ret = mei_cl_connect(cl, cldev->me_cl, NULL); + if (ret < 0) + dev_err(&cldev->dev, "cannot connect\n"); - if (device->event_cb) - mei_cl_read_start(device->cl, 0, NULL); +out: + mutex_unlock(&bus->device_lock); - return 0; + return ret; } EXPORT_SYMBOL_GPL(mei_cl_enable_device); -int mei_cl_disable_device(struct mei_cl_device *device) +/** + * mei_cl_disable_device - disable me client device + * disconnect form the me client + * + * @cldev: me client device + * + * Return: 0 on success and < 0 on error + */ +int mei_cl_disable_device(struct mei_cl_device *cldev) { + struct mei_device *bus; + struct mei_cl *cl; int err; - struct mei_device *dev; - struct mei_cl *cl = device->cl; - if (cl == NULL) + if (!cldev || !cldev->cl) return -ENODEV; - dev = cl->dev; + cl = cldev->cl; - device->event_cb = NULL; + bus = cldev->bus; - mutex_lock(&dev->device_lock); + cldev->event_cb = NULL; + + mutex_lock(&bus->device_lock); if (!mei_cl_is_connected(cl)) { - dev_err(dev->dev, "Already disconnected"); + dev_err(bus->dev, "Already disconnected"); err = 0; goto out; } err = mei_cl_disconnect(cl); - if (err < 0) { - dev_err(dev->dev, "Could not disconnect from the ME client"); - goto out; - } + if (err < 0) + dev_err(bus->dev, "Could not disconnect from the ME client"); +out: /* Flush queues and remove any pending read */ mei_cl_flush_queues(cl, NULL); + mei_cl_unlink(cl); -out: - mutex_unlock(&dev->device_lock); - return err; + kfree(cl); + cldev->cl = NULL; + mutex_unlock(&bus->device_lock); + return err; } EXPORT_SYMBOL_GPL(mei_cl_disable_device); -void mei_cl_bus_rx_event(struct mei_cl *cl) +/** + * mei_cl_device_find - find matching entry in the driver id table + * + * @cldev: me client device + * @cldrv: me client driver + * + * Return: id on success; NULL if no id is matching + */ +static const +struct mei_cl_device_id *mei_cl_device_find(struct mei_cl_device *cldev, + struct mei_cl_driver *cldrv) { - struct mei_cl_device *device = cl->device; + const struct mei_cl_device_id *id; + const uuid_le *uuid; + + uuid = mei_me_cl_uuid(cldev->me_cl); + + id = cldrv->id_table; + while (uuid_le_cmp(NULL_UUID_LE, id->uuid)) { + if (!uuid_le_cmp(*uuid, id->uuid)) { + + if (!cldev->name[0]) + return id; + + if (!strncmp(cldev->name, id->name, sizeof(id->name))) + return id; + } + + id++; + } + + return NULL; +} + +/** + * mei_cl_device_match - device match function + * + * @dev: device + * @drv: driver + * + * Return: 1 if matching device was found 0 otherwise + */ +static int mei_cl_device_match(struct device *dev, struct device_driver *drv) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_cl_driver *cldrv = to_mei_cl_driver(drv); + const struct mei_cl_device_id *found_id; + + if (!cldev) + return 0; + + if (!cldev->do_match) + return 0; + + if (!cldrv || !cldrv->id_table) + return 0; + + found_id = mei_cl_device_find(cldev, cldrv); + if (found_id) + return 1; + + return 0; +} + +/** + * mei_cl_device_probe - bus probe function + * + * @dev: device + * + * Return: 0 on success; < 0 otherwise + */ +static int mei_cl_device_probe(struct device *dev) +{ + struct mei_cl_device *cldev; + struct mei_cl_driver *cldrv; + const struct mei_cl_device_id *id; + + cldev = to_mei_cl_device(dev); + cldrv = to_mei_cl_driver(dev->driver); + + if (!cldev) + return 0; + + if (!cldrv || !cldrv->probe) + return -ENODEV; + + id = mei_cl_device_find(cldev, cldrv); + if (!id) + return -ENODEV; + + __module_get(THIS_MODULE); + + return cldrv->probe(cldev, id); +} + +/** + * mei_cl_device_remove - remove device from the bus + * + * @dev: device + * + * Return: 0 on success; < 0 otherwise + */ +static int mei_cl_device_remove(struct device *dev) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + struct mei_cl_driver *cldrv; + int ret = 0; + + if (!cldev || !dev->driver) + return 0; + + if (cldev->event_cb) { + cldev->event_cb = NULL; + cancel_work_sync(&cldev->event_work); + } + + cldrv = to_mei_cl_driver(dev->driver); + if (cldrv->remove) + ret = cldrv->remove(cldev); + + module_put(THIS_MODULE); + dev->driver = NULL; + return ret; + +} + +static ssize_t name_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + size_t len; + + len = snprintf(buf, PAGE_SIZE, "%s", cldev->name); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(name); + +static ssize_t uuid_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + size_t len; + + len = snprintf(buf, PAGE_SIZE, "%pUl", uuid); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(uuid); + +static ssize_t modalias_show(struct device *dev, struct device_attribute *a, + char *buf) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + size_t len; + + len = snprintf(buf, PAGE_SIZE, "mei:%s:" MEI_CL_UUID_FMT ":", + cldev->name, MEI_CL_UUID_ARGS(uuid->b)); + + return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len; +} +static DEVICE_ATTR_RO(modalias); + +static struct attribute *mei_cl_dev_attrs[] = { + &dev_attr_name.attr, + &dev_attr_uuid.attr, + &dev_attr_modalias.attr, + NULL, +}; +ATTRIBUTE_GROUPS(mei_cl_dev); + +/** + * mei_cl_device_uevent - me client bus uevent handler + * + * @dev: device + * @env: uevent kobject + * + * Return: 0 on success -ENOMEM on when add_uevent_var fails + */ +static int mei_cl_device_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + const uuid_le *uuid = mei_me_cl_uuid(cldev->me_cl); + + if (add_uevent_var(env, "MEI_CL_UUID=%pUl", uuid)) + return -ENOMEM; + + if (add_uevent_var(env, "MEI_CL_NAME=%s", cldev->name)) + return -ENOMEM; + + if (add_uevent_var(env, "MODALIAS=mei:%s:" MEI_CL_UUID_FMT ":", + cldev->name, MEI_CL_UUID_ARGS(uuid->b))) + return -ENOMEM; - if (!device || !device->event_cb) + return 0; +} + +static struct bus_type mei_cl_bus_type = { + .name = "mei", + .dev_groups = mei_cl_dev_groups, + .match = mei_cl_device_match, + .probe = mei_cl_device_probe, + .remove = mei_cl_device_remove, + .uevent = mei_cl_device_uevent, +}; + +static struct mei_device *mei_dev_bus_get(struct mei_device *bus) +{ + if (bus) + get_device(bus->dev); + + return bus; +} + +static void mei_dev_bus_put(struct mei_device *bus) +{ + if (bus) + put_device(bus->dev); +} + +static void mei_cl_dev_release(struct device *dev) +{ + struct mei_cl_device *cldev = to_mei_cl_device(dev); + + if (!cldev) + return; + + mei_me_cl_put(cldev->me_cl); + mei_dev_bus_put(cldev->bus); + kfree(cldev); +} + +static struct device_type mei_cl_device_type = { + .release = mei_cl_dev_release, +}; + +/** + * mei_cl_dev_alloc - initialize and allocate mei client device + * + * @bus: mei device + * @me_cl: me client + * + * Return: allocated device structur or NULL on allocation failure + */ +static struct mei_cl_device *mei_cl_dev_alloc(struct mei_device *bus, + struct mei_me_client *me_cl) +{ + struct mei_cl_device *cldev; + + cldev = kzalloc(sizeof(struct mei_cl_device), GFP_KERNEL); + if (!cldev) + return NULL; + + device_initialize(&cldev->dev); + cldev->dev.parent = bus->dev; + cldev->dev.bus = &mei_cl_bus_type; + cldev->dev.type = &mei_cl_device_type; + cldev->bus = mei_dev_bus_get(bus); + cldev->me_cl = mei_me_cl_get(me_cl); + cldev->is_added = 0; + INIT_LIST_HEAD(&cldev->bus_list); + + return cldev; +} + +/** + * mei_cl_dev_setup - setup me client device + * run fix up routines and set the device name + * + * @bus: mei device + * @cldev: me client device + * + * Return: true if the device is eligible for enumeration + */ +static bool mei_cl_dev_setup(struct mei_device *bus, + struct mei_cl_device *cldev) +{ + cldev->do_match = 1; + mei_cl_dev_fixup(cldev); + + if (cldev->do_match) + dev_set_name(&cldev->dev, "mei:%s:%pUl", + cldev->name, mei_me_cl_uuid(cldev->me_cl)); + + return cldev->do_match == 1; +} + +/** + * mei_cl_bus_dev_add - add me client devices + * + * @cldev: me client device + * + * Return: 0 on success; < 0 on failre + */ +static int mei_cl_bus_dev_add(struct mei_cl_device *cldev) +{ + int ret; + + dev_dbg(cldev->bus->dev, "adding %pUL\n", mei_me_cl_uuid(cldev->me_cl)); + ret = device_add(&cldev->dev); + if (!ret) + cldev->is_added = 1; + + return ret; +} + +/** + * mei_cl_bus_dev_stop - stop the driver + * + * @cldev: me client device + */ +static void mei_cl_bus_dev_stop(struct mei_cl_device *cldev) +{ + if (cldev->is_added) + device_release_driver(&cldev->dev); +} + +/** + * mei_cl_bus_dev_destroy - destroy me client devices object + * + * @cldev: me client device + */ +static void mei_cl_bus_dev_destroy(struct mei_cl_device *cldev) +{ + if (!cldev->is_added) + return; + + device_del(&cldev->dev); + + mutex_lock(&cldev->bus->cl_bus_lock); + list_del_init(&cldev->bus_list); + mutex_unlock(&cldev->bus->cl_bus_lock); + + cldev->is_added = 0; + put_device(&cldev->dev); +} + +/** + * mei_cl_bus_remove_device - remove a devices form the bus + * + * @cldev: me client device + */ +static void mei_cl_bus_remove_device(struct mei_cl_device *cldev) +{ + mei_cl_bus_dev_stop(cldev); + mei_cl_bus_dev_destroy(cldev); +} + +/** + * mei_cl_bus_remove_devices - remove all devices form the bus + * + * @bus: mei device + */ +void mei_cl_bus_remove_devices(struct mei_device *bus) +{ + struct mei_cl_device *cldev, *next; + + list_for_each_entry_safe(cldev, next, &bus->device_list, bus_list) + mei_cl_bus_remove_device(cldev); +} + + +/** + * mei_cl_dev_init - allocate and initializes an mei client devices + * based on me client + * + * @bus: mei device + * @me_cl: me client + */ +static void mei_cl_dev_init(struct mei_device *bus, struct mei_me_client *me_cl) +{ + struct mei_cl_device *cldev; + + dev_dbg(bus->dev, "initializing %pUl", mei_me_cl_uuid(me_cl)); + + if (me_cl->bus_added) return; - set_bit(MEI_CL_EVENT_RX, &device->events); + cldev = mei_cl_dev_alloc(bus, me_cl); + if (!cldev) + return; + + mutex_lock(&cldev->bus->cl_bus_lock); + me_cl->bus_added = true; + list_add_tail(&cldev->bus_list, &bus->device_list); + mutex_unlock(&cldev->bus->cl_bus_lock); + +} + +/** + * mei_cl_bus_rescan - scan me clients list and add create + * devices for eligible clients + * + * @bus: mei device + */ +void mei_cl_bus_rescan(struct mei_device *bus) +{ + struct mei_cl_device *cldev, *n; + struct mei_me_client *me_cl; + + down_read(&bus->me_clients_rwsem); + list_for_each_entry(me_cl, &bus->me_clients, list) + mei_cl_dev_init(bus, me_cl); + up_read(&bus->me_clients_rwsem); + + mutex_lock(&bus->cl_bus_lock); + list_for_each_entry_safe(cldev, n, &bus->device_list, bus_list) { + + if (!mei_me_cl_is_active(cldev->me_cl)) { + mei_cl_bus_remove_device(cldev); + continue; + } + + if (cldev->is_added) + continue; + + if (mei_cl_dev_setup(bus, cldev)) + mei_cl_bus_dev_add(cldev); + else { + list_del_init(&cldev->bus_list); + put_device(&cldev->dev); + } + } + mutex_unlock(&bus->cl_bus_lock); + + dev_dbg(bus->dev, "rescan end"); +} + +int __mei_cl_driver_register(struct mei_cl_driver *cldrv, struct module *owner) +{ + int err; + + cldrv->driver.name = cldrv->name; + cldrv->driver.owner = owner; + cldrv->driver.bus = &mei_cl_bus_type; + + err = driver_register(&cldrv->driver); + if (err) + return err; - schedule_work(&device->event_work); + pr_debug("mei: driver [%s] registered\n", cldrv->driver.name); + + return 0; } +EXPORT_SYMBOL_GPL(__mei_cl_driver_register); + +void mei_cl_driver_unregister(struct mei_cl_driver *cldrv) +{ + driver_unregister(&cldrv->driver); + + pr_debug("mei: driver [%s] unregistered\n", cldrv->driver.name); +} +EXPORT_SYMBOL_GPL(mei_cl_driver_unregister); + int __init mei_cl_bus_init(void) { diff --git a/drivers/misc/mei/client.c b/drivers/misc/mei/client.c index 6decbe136ea7..a6c87c713193 100644 --- a/drivers/misc/mei/client.c +++ b/drivers/misc/mei/client.c @@ -555,10 +555,10 @@ void mei_cl_init(struct mei_cl *cl, struct mei_device *dev) init_waitqueue_head(&cl->wait); init_waitqueue_head(&cl->rx_wait); init_waitqueue_head(&cl->tx_wait); + init_waitqueue_head(&cl->ev_wait); INIT_LIST_HEAD(&cl->rd_completed); INIT_LIST_HEAD(&cl->rd_pending); INIT_LIST_HEAD(&cl->link); - INIT_LIST_HEAD(&cl->device_link); cl->writing_state = MEI_IDLE; cl->state = MEI_FILE_INITIALIZING; cl->dev = dev; @@ -690,16 +690,12 @@ void mei_host_client_init(struct work_struct *work) mei_wd_host_init(dev, me_cl); mei_me_cl_put(me_cl); - me_cl = mei_me_cl_by_uuid(dev, &mei_nfc_guid); - if (me_cl) - mei_nfc_host_init(dev, me_cl); - mei_me_cl_put(me_cl); - - dev->dev_state = MEI_DEV_ENABLED; dev->reset_count = 0; mutex_unlock(&dev->device_lock); + mei_cl_bus_rescan(dev); + pm_runtime_mark_last_busy(dev->dev); dev_dbg(dev->dev, "rpm: autosuspend\n"); pm_runtime_autosuspend(dev->dev); @@ -841,45 +837,22 @@ int mei_cl_irq_disconnect(struct mei_cl *cl, struct mei_cl_cb *cb, return ret; } - - /** - * mei_cl_disconnect - disconnect host client from the me one + * __mei_cl_disconnect - disconnect host client from the me one + * internal function runtime pm has to be already acquired * * @cl: host client * - * Locking: called under "dev->device_lock" lock - * * Return: 0 on success, <0 on failure. */ -int mei_cl_disconnect(struct mei_cl *cl) +static int __mei_cl_disconnect(struct mei_cl *cl) { struct mei_device *dev; struct mei_cl_cb *cb; int rets; - if (WARN_ON(!cl || !cl->dev)) - return -ENODEV; - dev = cl->dev; - cl_dbg(dev, cl, "disconnecting"); - - if (!mei_cl_is_connected(cl)) - return 0; - - if (mei_cl_is_fixed_address(cl)) { - mei_cl_set_disconnected(cl); - return 0; - } - - rets = pm_runtime_get(dev->dev); - if (rets < 0 && rets != -EINPROGRESS) { - pm_runtime_put_noidle(dev->dev); - cl_err(dev, cl, "rpm: get failed %d\n", rets); - return rets; - } - cl->state = MEI_FILE_DISCONNECTING; cb = mei_io_cb_init(cl, MEI_FOP_DISCONNECT, NULL); @@ -915,11 +888,52 @@ out: if (!rets) cl_dbg(dev, cl, "successfully disconnected from FW client.\n"); + mei_io_cb_free(cb); + return rets; +} + +/** + * mei_cl_disconnect - disconnect host client from the me one + * + * @cl: host client + * + * Locking: called under "dev->device_lock" lock + * + * Return: 0 on success, <0 on failure. + */ +int mei_cl_disconnect(struct mei_cl *cl) +{ + struct mei_device *dev; + int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + cl_dbg(dev, cl, "disconnecting"); + + if (!mei_cl_is_connected(cl)) + return 0; + + if (mei_cl_is_fixed_address(cl)) { + mei_cl_set_disconnected(cl); + return 0; + } + + rets = pm_runtime_get(dev->dev); + if (rets < 0 && rets != -EINPROGRESS) { + pm_runtime_put_noidle(dev->dev); + cl_err(dev, cl, "rpm: get failed %d\n", rets); + return rets; + } + + rets = __mei_cl_disconnect(cl); + cl_dbg(dev, cl, "rpm: autosuspend\n"); pm_runtime_mark_last_busy(dev->dev); pm_runtime_put_autosuspend(dev->dev); - mei_io_cb_free(cb); return rets; } @@ -1064,11 +1078,23 @@ int mei_cl_connect(struct mei_cl *cl, struct mei_me_client *me_cl, mutex_unlock(&dev->device_lock); wait_event_timeout(cl->wait, (cl->state == MEI_FILE_CONNECTED || + cl->state == MEI_FILE_DISCONNECT_REQUIRED || cl->state == MEI_FILE_DISCONNECT_REPLY), mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); mutex_lock(&dev->device_lock); if (!mei_cl_is_connected(cl)) { + if (cl->state == MEI_FILE_DISCONNECT_REQUIRED) { + mei_io_list_flush(&dev->ctrl_rd_list, cl); + mei_io_list_flush(&dev->ctrl_wr_list, cl); + /* ignore disconnect return valuue; + * in case of failure reset will be invoked + */ + __mei_cl_disconnect(cl); + rets = -EFAULT; + goto out; + } + /* timeout or something went really wrong */ if (!cl->status) cl->status = -EFAULT; @@ -1181,6 +1207,221 @@ int mei_cl_flow_ctrl_reduce(struct mei_cl *cl) } /** + * mei_cl_notify_fop2req - convert fop to proper request + * + * @fop: client notification start response command + * + * Return: MEI_HBM_NOTIFICATION_START/STOP + */ +u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop) +{ + if (fop == MEI_FOP_NOTIFY_START) + return MEI_HBM_NOTIFICATION_START; + else + return MEI_HBM_NOTIFICATION_STOP; +} + +/** + * mei_cl_notify_req2fop - convert notification request top file operation type + * + * @req: hbm notification request type + * + * Return: MEI_FOP_NOTIFY_START/STOP + */ +enum mei_cb_file_ops mei_cl_notify_req2fop(u8 req) +{ + if (req == MEI_HBM_NOTIFICATION_START) + return MEI_FOP_NOTIFY_START; + else + return MEI_FOP_NOTIFY_STOP; +} + +/** + * mei_cl_irq_notify - send notification request in irq_thread context + * + * @cl: client + * @cb: callback block. + * @cmpl_list: complete list. + * + * Return: 0 on such and error otherwise. + */ +int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list) +{ + struct mei_device *dev = cl->dev; + u32 msg_slots; + int slots; + int ret; + bool request; + + msg_slots = mei_data2slots(sizeof(struct hbm_client_connect_request)); + slots = mei_hbuf_empty_slots(dev); + + if (slots < msg_slots) + return -EMSGSIZE; + + request = mei_cl_notify_fop2req(cb->fop_type); + ret = mei_hbm_cl_notify_req(dev, cl, request); + if (ret) { + cl->status = ret; + list_move_tail(&cb->list, &cmpl_list->list); + return ret; + } + + list_move_tail(&cb->list, &dev->ctrl_rd_list.list); + return 0; +} + +/** + * mei_cl_notify_request - send notification stop/start request + * + * @cl: host client + * @file: associate request with file + * @request: 1 for start or 0 for stop + * + * Locking: called under "dev->device_lock" lock + * + * Return: 0 on such and error otherwise. + */ +int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request) +{ + struct mei_device *dev; + struct mei_cl_cb *cb; + enum mei_cb_file_ops fop_type; + int rets; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + if (!dev->hbm_f_ev_supported) { + cl_dbg(dev, cl, "notifications not supported\n"); + return -EOPNOTSUPP; + } + + rets = pm_runtime_get(dev->dev); + if (rets < 0 && rets != -EINPROGRESS) { + pm_runtime_put_noidle(dev->dev); + cl_err(dev, cl, "rpm: get failed %d\n", rets); + return rets; + } + + fop_type = mei_cl_notify_req2fop(request); + cb = mei_io_cb_init(cl, fop_type, file); + if (!cb) { + rets = -ENOMEM; + goto out; + } + + if (mei_hbuf_acquire(dev)) { + if (mei_hbm_cl_notify_req(dev, cl, request)) { + rets = -ENODEV; + goto out; + } + list_add_tail(&cb->list, &dev->ctrl_rd_list.list); + } else { + list_add_tail(&cb->list, &dev->ctrl_wr_list.list); + } + + mutex_unlock(&dev->device_lock); + wait_event_timeout(cl->wait, cl->notify_en == request, + mei_secs_to_jiffies(MEI_CL_CONNECT_TIMEOUT)); + mutex_lock(&dev->device_lock); + + if (cl->notify_en != request) { + mei_io_list_flush(&dev->ctrl_rd_list, cl); + mei_io_list_flush(&dev->ctrl_wr_list, cl); + if (!cl->status) + cl->status = -EFAULT; + } + + rets = cl->status; + +out: + cl_dbg(dev, cl, "rpm: autosuspend\n"); + pm_runtime_mark_last_busy(dev->dev); + pm_runtime_put_autosuspend(dev->dev); + + mei_io_cb_free(cb); + return rets; +} + +/** + * mei_cl_notify - raise notification + * + * @cl: host client + * + * Locking: called under "dev->device_lock" lock + */ +void mei_cl_notify(struct mei_cl *cl) +{ + struct mei_device *dev; + + if (!cl || !cl->dev) + return; + + dev = cl->dev; + + if (!cl->notify_en) + return; + + cl_dbg(dev, cl, "notify event"); + cl->notify_ev = true; + wake_up_interruptible_all(&cl->ev_wait); + + if (cl->ev_async) + kill_fasync(&cl->ev_async, SIGIO, POLL_PRI); + + mei_cl_bus_notify_event(cl); +} + +/** + * mei_cl_notify_get - get or wait for notification event + * + * @cl: host client + * @block: this request is blocking + * @notify_ev: true if notification event was received + * + * Locking: called under "dev->device_lock" lock + * + * Return: 0 on such and error otherwise. + */ +int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev) +{ + struct mei_device *dev; + int rets; + + *notify_ev = false; + + if (WARN_ON(!cl || !cl->dev)) + return -ENODEV; + + dev = cl->dev; + + if (!mei_cl_is_connected(cl)) + return -ENODEV; + + if (cl->notify_ev) + goto out; + + if (!block) + return -EAGAIN; + + mutex_unlock(&dev->device_lock); + rets = wait_event_interruptible(cl->ev_wait, cl->notify_ev); + mutex_lock(&dev->device_lock); + + if (rets < 0) + return rets; + +out: + *notify_ev = cl->notify_ev; + cl->notify_ev = false; + return 0; +} + +/** * mei_cl_read_start - the start read client message function. * * @cl: host client @@ -1356,6 +1597,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) struct mei_device *dev; struct mei_msg_data *buf; struct mei_msg_hdr mei_hdr; + int size; int rets; @@ -1367,10 +1609,10 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) dev = cl->dev; - buf = &cb->buf; + size = buf->size; - cl_dbg(dev, cl, "size=%d\n", buf->size); + cl_dbg(dev, cl, "size=%d\n", size); rets = pm_runtime_get(dev->dev); if (rets < 0 && rets != -EINPROGRESS) { @@ -1394,21 +1636,21 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking) if (rets == 0) { cl_dbg(dev, cl, "No flow control credentials: not sending.\n"); - rets = buf->size; + rets = size; goto out; } if (!mei_hbuf_acquire(dev)) { cl_dbg(dev, cl, "Cannot acquire the host buffer: not sending.\n"); - rets = buf->size; + rets = size; goto out; } /* Check for a maximum length */ - if (buf->size > mei_hbuf_max_len(dev)) { + if (size > mei_hbuf_max_len(dev)) { mei_hdr.length = mei_hbuf_max_len(dev); mei_hdr.msg_complete = 0; } else { - mei_hdr.length = buf->size; + mei_hdr.length = size; mei_hdr.msg_complete = 1; } @@ -1430,6 +1672,7 @@ out: else list_add_tail(&cb->list, &dev->write_list.list); + cb = NULL; if (blocking && cl->writing_state != MEI_WRITE_COMPLETE) { mutex_unlock(&dev->device_lock); @@ -1444,7 +1687,7 @@ out: } } - rets = buf->size; + rets = size; err: cl_dbg(dev, cl, "rpm: autosuspend\n"); pm_runtime_mark_last_busy(dev->dev); @@ -1486,6 +1729,8 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb) case MEI_FOP_CONNECT: case MEI_FOP_DISCONNECT: + case MEI_FOP_NOTIFY_STOP: + case MEI_FOP_NOTIFY_START: if (waitqueue_active(&cl->wait)) wake_up(&cl->wait); @@ -1528,6 +1773,12 @@ void mei_cl_all_wakeup(struct mei_device *dev) cl_dbg(dev, cl, "Waking up writing client!\n"); wake_up_interruptible(&cl->tx_wait); } + + /* synchronized under device mutex */ + if (waitqueue_active(&cl->ev_wait)) { + cl_dbg(dev, cl, "Waking up waiting for event clients!\n"); + wake_up_interruptible(&cl->ev_wait); + } } } diff --git a/drivers/misc/mei/client.h b/drivers/misc/mei/client.h index 8d7f057f1045..1c7cad07d731 100644 --- a/drivers/misc/mei/client.h +++ b/drivers/misc/mei/client.h @@ -219,6 +219,14 @@ void mei_cl_complete(struct mei_cl *cl, struct mei_cl_cb *cb); void mei_host_client_init(struct work_struct *work); +u8 mei_cl_notify_fop2req(enum mei_cb_file_ops fop); +enum mei_cb_file_ops mei_cl_notify_req2fop(u8 request); +int mei_cl_notify_request(struct mei_cl *cl, struct file *file, u8 request); +int mei_cl_irq_notify(struct mei_cl *cl, struct mei_cl_cb *cb, + struct mei_cl_cb *cmpl_list); +int mei_cl_notify_get(struct mei_cl *cl, bool block, bool *notify_ev); +void mei_cl_notify(struct mei_cl *cl); + void mei_cl_all_disconnect(struct mei_device *dev); void mei_cl_all_wakeup(struct mei_device *dev); void mei_cl_all_write_clear(struct mei_device *dev); diff --git a/drivers/misc/mei/debugfs.c b/drivers/misc/mei/debugfs.c index eb868341247f..4b469cf9e60f 100644 --- a/drivers/misc/mei/debugfs.c +++ b/drivers/misc/mei/debugfs.c @@ -154,6 +154,12 @@ static ssize_t mei_dbgfs_read_devstate(struct file *fp, char __user *ubuf, pos += scnprintf(buf + pos, bufsz - pos, "hbm features:\n"); pos += scnprintf(buf + pos, bufsz - pos, "\tPG: %01d\n", dev->hbm_f_pg_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tDC: %01d\n", + dev->hbm_f_dc_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tDOT: %01d\n", + dev->hbm_f_dot_supported); + pos += scnprintf(buf + pos, bufsz - pos, "\tEV: %01d\n", + dev->hbm_f_ev_supported); } pos += scnprintf(buf + pos, bufsz - pos, "pg: %s, %s\n", diff --git a/drivers/misc/mei/hbm.c b/drivers/misc/mei/hbm.c index a4f283165a33..8eec887c8f70 100644 --- a/drivers/misc/mei/hbm.c +++ b/drivers/misc/mei/hbm.c @@ -52,6 +52,7 @@ static const char *mei_cl_conn_status_str(enum mei_cl_connect_status status) MEI_CL_CS(ALREADY_STARTED); MEI_CL_CS(OUT_OF_RESOURCES); MEI_CL_CS(MESSAGE_SMALL); + MEI_CL_CS(NOT_ALLOWED); default: return "unknown"; } #undef MEI_CL_CCS @@ -89,6 +90,7 @@ static int mei_cl_conn_status_to_errno(enum mei_cl_connect_status status) case MEI_CL_CONN_ALREADY_STARTED: return -EBUSY; case MEI_CL_CONN_OUT_OF_RESOURCES: return -EBUSY; case MEI_CL_CONN_MESSAGE_SMALL: return -EINVAL; + case MEI_CL_CONN_NOT_ALLOWED: return -EBUSY; default: return -EINVAL; } } @@ -299,6 +301,7 @@ static int mei_hbm_enum_clients_req(struct mei_device *dev) enum_req = (struct hbm_host_enum_request *)dev->wr_msg.data; memset(enum_req, 0, len); enum_req->hbm_cmd = HOST_ENUM_REQ_CMD; + enum_req->allow_add = dev->hbm_f_dc_supported; ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); if (ret) { @@ -344,6 +347,180 @@ static int mei_hbm_me_cl_add(struct mei_device *dev, } /** + * mei_hbm_add_cl_resp - send response to fw on client add request + * + * @dev: the device structure + * @addr: me address + * @status: response status + * + * Return: 0 on success and < 0 on failure + */ +static int mei_hbm_add_cl_resp(struct mei_device *dev, u8 addr, u8 status) +{ + struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; + struct hbm_add_client_response *resp; + const size_t len = sizeof(struct hbm_add_client_response); + int ret; + + dev_dbg(dev->dev, "adding client response\n"); + + resp = (struct hbm_add_client_response *)dev->wr_msg.data; + + mei_hbm_hdr(mei_hdr, len); + memset(resp, 0, sizeof(struct hbm_add_client_response)); + + resp->hbm_cmd = MEI_HBM_ADD_CLIENT_RES_CMD; + resp->me_addr = addr; + resp->status = status; + + ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); + if (ret) + dev_err(dev->dev, "add client response write failed: ret = %d\n", + ret); + return ret; +} + +/** + * mei_hbm_fw_add_cl_req - request from the fw to add a client + * + * @dev: the device structure + * @req: add client request + * + * Return: 0 on success and < 0 on failure + */ +static int mei_hbm_fw_add_cl_req(struct mei_device *dev, + struct hbm_add_client_request *req) +{ + int ret; + u8 status = MEI_HBMS_SUCCESS; + + BUILD_BUG_ON(sizeof(struct hbm_add_client_request) != + sizeof(struct hbm_props_response)); + + ret = mei_hbm_me_cl_add(dev, (struct hbm_props_response *)req); + if (ret) + status = !MEI_HBMS_SUCCESS; + + return mei_hbm_add_cl_resp(dev, req->me_addr, status); +} + +/** + * mei_hbm_cl_notify_req - send notification request + * + * @dev: the device structure + * @cl: a client to disconnect from + * @start: true for start false for stop + * + * Return: 0 on success and -EIO on write failure + */ +int mei_hbm_cl_notify_req(struct mei_device *dev, + struct mei_cl *cl, u8 start) +{ + + struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr; + struct hbm_notification_request *req; + const size_t len = sizeof(struct hbm_notification_request); + int ret; + + mei_hbm_hdr(mei_hdr, len); + mei_hbm_cl_hdr(cl, MEI_HBM_NOTIFY_REQ_CMD, dev->wr_msg.data, len); + + req = (struct hbm_notification_request *)dev->wr_msg.data; + req->start = start; + + ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data); + if (ret) + dev_err(dev->dev, "notify request failed: ret = %d\n", ret); + + return ret; +} + +/** + * notify_res_to_fop - convert notification response to the proper + * notification FOP + * + * @cmd: client notification start response command + * + * Return: MEI_FOP_NOTIFY_START or MEI_FOP_NOTIFY_STOP; + */ +static inline enum mei_cb_file_ops notify_res_to_fop(struct mei_hbm_cl_cmd *cmd) +{ + struct hbm_notification_response *rs = + (struct hbm_notification_response *)cmd; + + return mei_cl_notify_req2fop(rs->start); +} + +/** + * mei_hbm_cl_notify_start_res - update the client state according + * notify start response + * + * @dev: the device structure + * @cl: mei host client + * @cmd: client notification start response command + */ +static void mei_hbm_cl_notify_start_res(struct mei_device *dev, + struct mei_cl *cl, + struct mei_hbm_cl_cmd *cmd) +{ + struct hbm_notification_response *rs = + (struct hbm_notification_response *)cmd; + + cl_dbg(dev, cl, "hbm: notify start response status=%d\n", rs->status); + + if (rs->status == MEI_HBMS_SUCCESS || + rs->status == MEI_HBMS_ALREADY_STARTED) { + cl->notify_en = true; + cl->status = 0; + } else { + cl->status = -EINVAL; + } +} + +/** + * mei_hbm_cl_notify_stop_res - update the client state according + * notify stop response + * + * @dev: the device structure + * @cl: mei host client + * @cmd: client notification stop response command + */ +static void mei_hbm_cl_notify_stop_res(struct mei_device *dev, + struct mei_cl *cl, + struct mei_hbm_cl_cmd *cmd) +{ + struct hbm_notification_response *rs = + (struct hbm_notification_response *)cmd; + + cl_dbg(dev, cl, "hbm: notify stop response status=%d\n", rs->status); + + if (rs->status == MEI_HBMS_SUCCESS || + rs->status == MEI_HBMS_NOT_STARTED) { + cl->notify_en = false; + cl->status = 0; + } else { + /* TODO: spec is not clear yet about other possible issues */ + cl->status = -EINVAL; + } +} + +/** + * mei_hbm_cl_notify - signal notification event + * + * @dev: the device structure + * @cmd: notification client message + */ +static void mei_hbm_cl_notify(struct mei_device *dev, + struct mei_hbm_cl_cmd *cmd) +{ + struct mei_cl *cl; + + cl = mei_hbm_cl_find_by_cmd(dev, cmd); + if (cl) + mei_cl_notify(cl); +} + +/** * mei_hbm_prop_req - request property for a single client * * @dev: the device structure @@ -610,8 +787,11 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev, struct mei_cl *cl, if (rs->status == MEI_CL_CONN_SUCCESS) cl->state = MEI_FILE_CONNECTED; - else + else { cl->state = MEI_FILE_DISCONNECT_REPLY; + if (rs->status == MEI_CL_CONN_NOT_FOUND) + mei_me_cl_del(dev, cl->me_cl); + } cl->status = mei_cl_conn_status_to_errno(rs->status); } @@ -654,6 +834,12 @@ static void mei_hbm_cl_res(struct mei_device *dev, case MEI_FOP_DISCONNECT: mei_hbm_cl_disconnect_res(dev, cl, rs); break; + case MEI_FOP_NOTIFY_START: + mei_hbm_cl_notify_start_res(dev, cl, rs); + break; + case MEI_FOP_NOTIFY_STOP: + mei_hbm_cl_notify_stop_res(dev, cl, rs); + break; default: return; } @@ -694,6 +880,79 @@ static int mei_hbm_fw_disconnect_req(struct mei_device *dev, } /** + * mei_hbm_pg_enter_res - PG enter response received + * + * @dev: the device structure. + * + * Return: 0 on success, -EPROTO on state mismatch + */ +static int mei_hbm_pg_enter_res(struct mei_device *dev) +{ + if (mei_pg_state(dev) != MEI_PG_OFF || + dev->pg_event != MEI_PG_EVENT_WAIT) { + dev_err(dev->dev, "hbm: pg entry response: state mismatch [%s, %d]\n", + mei_pg_state_str(mei_pg_state(dev)), dev->pg_event); + return -EPROTO; + } + + dev->pg_event = MEI_PG_EVENT_RECEIVED; + wake_up(&dev->wait_pg); + + return 0; +} + +/** + * mei_hbm_pg_resume - process with PG resume + * + * @dev: the device structure. + */ +void mei_hbm_pg_resume(struct mei_device *dev) +{ + pm_request_resume(dev->dev); +} +EXPORT_SYMBOL_GPL(mei_hbm_pg_resume); + +/** + * mei_hbm_pg_exit_res - PG exit response received + * + * @dev: the device structure. + * + * Return: 0 on success, -EPROTO on state mismatch + */ +static int mei_hbm_pg_exit_res(struct mei_device *dev) +{ + if (mei_pg_state(dev) != MEI_PG_ON || + (dev->pg_event != MEI_PG_EVENT_WAIT && + dev->pg_event != MEI_PG_EVENT_IDLE)) { + dev_err(dev->dev, "hbm: pg exit response: state mismatch [%s, %d]\n", + mei_pg_state_str(mei_pg_state(dev)), dev->pg_event); + return -EPROTO; + } + + switch (dev->pg_event) { + case MEI_PG_EVENT_WAIT: + dev->pg_event = MEI_PG_EVENT_RECEIVED; + wake_up(&dev->wait_pg); + break; + case MEI_PG_EVENT_IDLE: + /* + * If the driver is not waiting on this then + * this is HW initiated exit from PG. + * Start runtime pm resume sequence to exit from PG. + */ + dev->pg_event = MEI_PG_EVENT_RECEIVED; + mei_hbm_pg_resume(dev); + break; + default: + WARN(1, "hbm: pg exit response: unexpected pg event = %d\n", + dev->pg_event); + return -EPROTO; + } + + return 0; +} + +/** * mei_hbm_config_features - check what hbm features and commands * are supported by the fw * @@ -709,6 +968,17 @@ static void mei_hbm_config_features(struct mei_device *dev) if (dev->version.major_version == HBM_MAJOR_VERSION_PGI && dev->version.minor_version >= HBM_MINOR_VERSION_PGI) dev->hbm_f_pg_supported = 1; + + if (dev->version.major_version >= HBM_MAJOR_VERSION_DC) + dev->hbm_f_dc_supported = 1; + + /* disconnect on connect timeout instead of link reset */ + if (dev->version.major_version >= HBM_MAJOR_VERSION_DOT) + dev->hbm_f_dot_supported = 1; + + /* Notification Event Support */ + if (dev->version.major_version >= HBM_MAJOR_VERSION_EV) + dev->hbm_f_ev_supported = 1; } /** @@ -740,6 +1010,8 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) struct hbm_host_version_response *version_res; struct hbm_props_response *props_res; struct hbm_host_enum_response *enum_res; + struct hbm_add_client_request *add_cl_req; + int ret; struct mei_hbm_cl_cmd *cl_cmd; struct hbm_client_connect_request *disconnect_req; @@ -828,24 +1100,17 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) break; case MEI_PG_ISOLATION_ENTRY_RES_CMD: - dev_dbg(dev->dev, "power gate isolation entry response received\n"); - dev->pg_event = MEI_PG_EVENT_RECEIVED; - if (waitqueue_active(&dev->wait_pg)) - wake_up(&dev->wait_pg); + dev_dbg(dev->dev, "hbm: power gate isolation entry response received\n"); + ret = mei_hbm_pg_enter_res(dev); + if (ret) + return ret; break; case MEI_PG_ISOLATION_EXIT_REQ_CMD: - dev_dbg(dev->dev, "power gate isolation exit request received\n"); - dev->pg_event = MEI_PG_EVENT_RECEIVED; - if (waitqueue_active(&dev->wait_pg)) - wake_up(&dev->wait_pg); - else - /* - * If the driver is not waiting on this then - * this is HW initiated exit from PG. - * Start runtime pm resume sequence to exit from PG. - */ - pm_request_resume(dev->dev); + dev_dbg(dev->dev, "hbm: power gate isolation exit request received\n"); + ret = mei_hbm_pg_exit_res(dev); + if (ret) + return ret; break; case HOST_CLIENT_PROPERTIES_RES_CMD: @@ -937,6 +1202,39 @@ int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr) return -EIO; } break; + + case MEI_HBM_ADD_CLIENT_REQ_CMD: + dev_dbg(dev->dev, "hbm: add client request received\n"); + /* + * after the host receives the enum_resp + * message clients may be added or removed + */ + if (dev->hbm_state <= MEI_HBM_ENUM_CLIENTS && + dev->hbm_state >= MEI_HBM_STOPPED) { + dev_err(dev->dev, "hbm: add client: state mismatch, [%d, %d]\n", + dev->dev_state, dev->hbm_state); + return -EPROTO; + } + add_cl_req = (struct hbm_add_client_request *)mei_msg; + ret = mei_hbm_fw_add_cl_req(dev, add_cl_req); + if (ret) { + dev_err(dev->dev, "hbm: add client: failed to send response %d\n", + ret); + return -EIO; + } + dev_dbg(dev->dev, "hbm: add client request processed\n"); + break; + + case MEI_HBM_NOTIFY_RES_CMD: + dev_dbg(dev->dev, "hbm: notify response received\n"); + mei_hbm_cl_res(dev, cl_cmd, notify_res_to_fop(cl_cmd)); + break; + + case MEI_HBM_NOTIFICATION_CMD: + dev_dbg(dev->dev, "hbm: notification\n"); + mei_hbm_cl_notify(dev, cl_cmd); + break; + default: BUG(); break; diff --git a/drivers/misc/mei/hbm.h b/drivers/misc/mei/hbm.h index 2544db7d1649..a2025a5083a3 100644 --- a/drivers/misc/mei/hbm.h +++ b/drivers/misc/mei/hbm.h @@ -54,6 +54,9 @@ int mei_hbm_cl_disconnect_rsp(struct mei_device *dev, struct mei_cl *cl); int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl); bool mei_hbm_version_is_supported(struct mei_device *dev); int mei_hbm_pg(struct mei_device *dev, u8 pg_cmd); +void mei_hbm_pg_resume(struct mei_device *dev); +int mei_hbm_cl_notify_req(struct mei_device *dev, + struct mei_cl *cl, u8 request); #endif /* _MEI_HBM_H_ */ diff --git a/drivers/misc/mei/hw-me-regs.h b/drivers/misc/mei/hw-me-regs.h index 9eb7ed70ace2..a8a68acd3267 100644 --- a/drivers/misc/mei/hw-me-regs.h +++ b/drivers/misc/mei/hw-me-regs.h @@ -117,12 +117,17 @@ #define MEI_DEV_ID_WPT_LP 0x9CBA /* Wildcat Point LP */ #define MEI_DEV_ID_WPT_LP_2 0x9CBB /* Wildcat Point LP 2 */ +#define MEI_DEV_ID_SPT 0x9D3A /* Sunrise Point */ +#define MEI_DEV_ID_SPT_2 0x9D3B /* Sunrise Point 2 */ +#define MEI_DEV_ID_SPT_H 0xA13A /* Sunrise Point H */ +#define MEI_DEV_ID_SPT_H_2 0xA13B /* Sunrise Point H 2 */ /* * MEI HW Section */ /* Host Firmware Status Registers in PCI Config Space */ #define PCI_CFG_HFS_1 0x40 +# define PCI_CFG_HFS_1_D0I3_MSK 0x80000000 #define PCI_CFG_HFS_2 0x48 #define PCI_CFG_HFS_3 0x60 #define PCI_CFG_HFS_4 0x64 @@ -140,7 +145,8 @@ #define ME_CSR_HA 0xC /* H_HGC_CSR - PGI register */ #define H_HPG_CSR 0x10 - +/* H_D0I3C - D0I3 Control */ +#define H_D0I3C 0x800 /* register bits of H_CSR (Host Control Status register) */ /* Host Circular Buffer Depth - maximum number of 32-bit entries in CB */ @@ -159,7 +165,14 @@ #define H_IS 0x00000002 /* Host Interrupt Enable */ #define H_IE 0x00000001 +/* Host D0I3 Interrupt Enable */ +#define H_D0I3C_IE 0x00000020 +/* Host D0I3 Interrupt Status */ +#define H_D0I3C_IS 0x00000040 +/* H_CSR masks */ +#define H_CSR_IE_MASK (H_IE | H_D0I3C_IE) +#define H_CSR_IS_MASK (H_IS | H_D0I3C_IS) /* register bits of ME_CSR_HA (ME Control Status Host Access register) */ /* ME CB (Circular Buffer) Depth HRA (Host Read Access) - host read only @@ -183,8 +196,14 @@ access to ME_CBD */ #define ME_IE_HRA 0x00000001 -/* register bits - H_HPG_CSR */ -#define H_HPG_CSR_PGIHEXR 0x00000001 -#define H_HPG_CSR_PGI 0x00000002 +/* H_HPG_CSR register bits */ +#define H_HPG_CSR_PGIHEXR 0x00000001 +#define H_HPG_CSR_PGI 0x00000002 + +/* H_D0I3C register bits */ +#define H_D0I3C_CIP 0x00000001 +#define H_D0I3C_IR 0x00000002 +#define H_D0I3C_I3 0x00000004 +#define H_D0I3C_RR 0x00000008 #endif /* _MEI_HW_MEI_REGS_H_ */ diff --git a/drivers/misc/mei/hw-me.c b/drivers/misc/mei/hw-me.c index 43d7101ff993..65511d39d89b 100644 --- a/drivers/misc/mei/hw-me.c +++ b/drivers/misc/mei/hw-me.c @@ -134,11 +134,40 @@ static inline void mei_hcsr_write(struct mei_device *dev, u32 reg) */ static inline void mei_hcsr_set(struct mei_device *dev, u32 reg) { - reg &= ~H_IS; + reg &= ~H_CSR_IS_MASK; mei_hcsr_write(dev, reg); } /** + * mei_me_d0i3c_read - Reads 32bit data from the D0I3C register + * + * @dev: the device structure + * + * Return: H_D0I3C register value (u32) + */ +static inline u32 mei_me_d0i3c_read(const struct mei_device *dev) +{ + u32 reg; + + reg = mei_me_reg_read(to_me_hw(dev), H_D0I3C); + trace_mei_reg_read(dev->dev, "H_D0I3C", H_CSR, reg); + + return reg; +} + +/** + * mei_me_d0i3c_write - writes H_D0I3C register to device + * + * @dev: the device structure + * @reg: new register value + */ +static inline void mei_me_d0i3c_write(struct mei_device *dev, u32 reg) +{ + trace_mei_reg_write(dev->dev, "H_D0I3C", H_CSR, reg); + mei_me_reg_write(to_me_hw(dev), H_D0I3C, reg); +} + +/** * mei_me_fw_status - read fw status register from pci config space * * @dev: mei device @@ -176,12 +205,25 @@ static int mei_me_fw_status(struct mei_device *dev, */ static void mei_me_hw_config(struct mei_device *dev) { + struct pci_dev *pdev = to_pci_dev(dev->dev); struct mei_me_hw *hw = to_me_hw(dev); - u32 hcsr = mei_hcsr_read(dev); + u32 hcsr, reg; + /* Doesn't change in runtime */ + hcsr = mei_hcsr_read(dev); dev->hbuf_depth = (hcsr & H_CBD) >> 24; + reg = 0; + pci_read_config_dword(pdev, PCI_CFG_HFS_1, ®); + hw->d0i3_supported = + ((reg & PCI_CFG_HFS_1_D0I3_MSK) == PCI_CFG_HFS_1_D0I3_MSK); + hw->pg_state = MEI_PG_OFF; + if (hw->d0i3_supported) { + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) + hw->pg_state = MEI_PG_ON; + } } /** @@ -208,7 +250,7 @@ static void mei_me_intr_clear(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - if ((hcsr & H_IS) == H_IS) + if (hcsr & H_CSR_IS_MASK) mei_hcsr_write(dev, hcsr); } /** @@ -220,7 +262,7 @@ static void mei_me_intr_enable(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - hcsr |= H_IE; + hcsr |= H_CSR_IE_MASK; mei_hcsr_set(dev, hcsr); } @@ -233,7 +275,7 @@ static void mei_me_intr_disable(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - hcsr &= ~H_IE; + hcsr &= ~H_CSR_IE_MASK; mei_hcsr_set(dev, hcsr); } @@ -253,57 +295,6 @@ static void mei_me_hw_reset_release(struct mei_device *dev) /* complete this write before we set host ready on another CPU */ mmiowb(); } -/** - * mei_me_hw_reset - resets fw via mei csr register. - * - * @dev: the device structure - * @intr_enable: if interrupt should be enabled after reset. - * - * Return: always 0 - */ -static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) -{ - u32 hcsr = mei_hcsr_read(dev); - - /* H_RST may be found lit before reset is started, - * for example if preceding reset flow hasn't completed. - * In that case asserting H_RST will be ignored, therefore - * we need to clean H_RST bit to start a successful reset sequence. - */ - if ((hcsr & H_RST) == H_RST) { - dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr); - hcsr &= ~H_RST; - mei_hcsr_set(dev, hcsr); - hcsr = mei_hcsr_read(dev); - } - - hcsr |= H_RST | H_IG | H_IS; - - if (intr_enable) - hcsr |= H_IE; - else - hcsr &= ~H_IE; - - dev->recvd_hw_ready = false; - mei_hcsr_write(dev, hcsr); - - /* - * Host reads the H_CSR once to ensure that the - * posted write to H_CSR completes. - */ - hcsr = mei_hcsr_read(dev); - - if ((hcsr & H_RST) == 0) - dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr); - - if ((hcsr & H_RDY) == H_RDY) - dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr); - - if (intr_enable == false) - mei_me_hw_reset_release(dev); - - return 0; -} /** * mei_me_host_set_ready - enable device @@ -314,7 +305,7 @@ static void mei_me_host_set_ready(struct mei_device *dev) { u32 hcsr = mei_hcsr_read(dev); - hcsr |= H_IE | H_IG | H_RDY; + hcsr |= H_CSR_IE_MASK | H_IG | H_RDY; mei_hcsr_set(dev, hcsr); } @@ -601,13 +592,13 @@ static void mei_me_pg_unset(struct mei_device *dev) } /** - * mei_me_pg_enter_sync - perform pg entry procedure + * mei_me_pg_legacy_enter_sync - perform legacy pg entry procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise */ -int mei_me_pg_enter_sync(struct mei_device *dev) +static int mei_me_pg_legacy_enter_sync(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); @@ -638,13 +629,13 @@ int mei_me_pg_enter_sync(struct mei_device *dev) } /** - * mei_me_pg_exit_sync - perform pg exit procedure + * mei_me_pg_legacy_exit_sync - perform legacy pg exit procedure * * @dev: the device structure * * Return: 0 on success an error code otherwise */ -int mei_me_pg_exit_sync(struct mei_device *dev) +static int mei_me_pg_legacy_exit_sync(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); unsigned long timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); @@ -712,8 +703,12 @@ static bool mei_me_pg_in_transition(struct mei_device *dev) */ static bool mei_me_pg_is_enabled(struct mei_device *dev) { + struct mei_me_hw *hw = to_me_hw(dev); u32 reg = mei_me_mecsr_read(dev); + if (hw->d0i3_supported) + return true; + if ((reg & ME_PGIC_HRA) == 0) goto notsupported; @@ -723,7 +718,8 @@ static bool mei_me_pg_is_enabled(struct mei_device *dev) return true; notsupported: - dev_dbg(dev->dev, "pg: not supported: HGP = %d hbm version %d.%d ?= %d.%d\n", + dev_dbg(dev->dev, "pg: not supported: d0i3 = %d HGP = %d hbm version %d.%d ?= %d.%d\n", + hw->d0i3_supported, !!(reg & ME_PGIC_HRA), dev->version.major_version, dev->version.minor_version, @@ -734,11 +730,211 @@ notsupported: } /** - * mei_me_pg_intr - perform pg processing in interrupt thread handler + * mei_me_d0i3_set - write d0i3 register bit on mei device. * * @dev: the device structure + * @intr: ask for interrupt + * + * Return: D0I3C register value */ -static void mei_me_pg_intr(struct mei_device *dev) +static u32 mei_me_d0i3_set(struct mei_device *dev, bool intr) +{ + u32 reg = mei_me_d0i3c_read(dev); + + reg |= H_D0I3C_I3; + if (intr) + reg |= H_D0I3C_IR; + else + reg &= ~H_D0I3C_IR; + mei_me_d0i3c_write(dev, reg); + /* read it to ensure HW consistency */ + reg = mei_me_d0i3c_read(dev); + return reg; +} + +/** + * mei_me_d0i3_unset - clean d0i3 register bit on mei device. + * + * @dev: the device structure + * + * Return: D0I3C register value + */ +static u32 mei_me_d0i3_unset(struct mei_device *dev) +{ + u32 reg = mei_me_d0i3c_read(dev); + + reg &= ~H_D0I3C_I3; + reg |= H_D0I3C_IR; + mei_me_d0i3c_write(dev, reg); + /* read it to ensure HW consistency */ + reg = mei_me_d0i3c_read(dev); + return reg; +} + +/** + * mei_me_d0i3_enter_sync - perform d0i3 entry procedure + * + * @dev: the device structure + * + * Return: 0 on success an error code otherwise + */ +static int mei_me_d0i3_enter_sync(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + unsigned long d0i3_timeout = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); + unsigned long pgi_timeout = mei_secs_to_jiffies(MEI_PGI_TIMEOUT); + int ret; + u32 reg; + + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + /* we are in d0i3, nothing to do */ + dev_dbg(dev->dev, "d0i3 set not needed\n"); + ret = 0; + goto on; + } + + /* PGI entry procedure */ + dev->pg_event = MEI_PG_EVENT_WAIT; + + ret = mei_hbm_pg(dev, MEI_PG_ISOLATION_ENTRY_REQ_CMD); + if (ret) + /* FIXME: should we reset here? */ + goto out; + + mutex_unlock(&dev->device_lock); + wait_event_timeout(dev->wait_pg, + dev->pg_event == MEI_PG_EVENT_RECEIVED, pgi_timeout); + mutex_lock(&dev->device_lock); + + if (dev->pg_event != MEI_PG_EVENT_RECEIVED) { + ret = -ETIME; + goto out; + } + /* end PGI entry procedure */ + + dev->pg_event = MEI_PG_EVENT_INTR_WAIT; + + reg = mei_me_d0i3_set(dev, true); + if (!(reg & H_D0I3C_CIP)) { + dev_dbg(dev->dev, "d0i3 enter wait not needed\n"); + ret = 0; + goto on; + } + + mutex_unlock(&dev->device_lock); + wait_event_timeout(dev->wait_pg, + dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, d0i3_timeout); + mutex_lock(&dev->device_lock); + + if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) { + reg = mei_me_d0i3c_read(dev); + if (!(reg & H_D0I3C_I3)) { + ret = -ETIME; + goto out; + } + } + + ret = 0; +on: + hw->pg_state = MEI_PG_ON; +out: + dev->pg_event = MEI_PG_EVENT_IDLE; + dev_dbg(dev->dev, "d0i3 enter ret = %d\n", ret); + return ret; +} + +/** + * mei_me_d0i3_enter - perform d0i3 entry procedure + * no hbm PG handshake + * no waiting for confirmation; runs with interrupts + * disabled + * + * @dev: the device structure + * + * Return: 0 on success an error code otherwise + */ +static int mei_me_d0i3_enter(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + u32 reg; + + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + /* we are in d0i3, nothing to do */ + dev_dbg(dev->dev, "already d0i3 : set not needed\n"); + goto on; + } + + mei_me_d0i3_set(dev, false); +on: + hw->pg_state = MEI_PG_ON; + dev->pg_event = MEI_PG_EVENT_IDLE; + dev_dbg(dev->dev, "d0i3 enter\n"); + return 0; +} + +/** + * mei_me_d0i3_exit_sync - perform d0i3 exit procedure + * + * @dev: the device structure + * + * Return: 0 on success an error code otherwise + */ +static int mei_me_d0i3_exit_sync(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + unsigned long timeout = mei_secs_to_jiffies(MEI_D0I3_TIMEOUT); + int ret; + u32 reg; + + dev->pg_event = MEI_PG_EVENT_INTR_WAIT; + + reg = mei_me_d0i3c_read(dev); + if (!(reg & H_D0I3C_I3)) { + /* we are not in d0i3, nothing to do */ + dev_dbg(dev->dev, "d0i3 exit not needed\n"); + ret = 0; + goto off; + } + + reg = mei_me_d0i3_unset(dev); + if (!(reg & H_D0I3C_CIP)) { + dev_dbg(dev->dev, "d0i3 exit wait not needed\n"); + ret = 0; + goto off; + } + + mutex_unlock(&dev->device_lock); + wait_event_timeout(dev->wait_pg, + dev->pg_event == MEI_PG_EVENT_INTR_RECEIVED, timeout); + mutex_lock(&dev->device_lock); + + if (dev->pg_event != MEI_PG_EVENT_INTR_RECEIVED) { + reg = mei_me_d0i3c_read(dev); + if (reg & H_D0I3C_I3) { + ret = -ETIME; + goto out; + } + } + + ret = 0; +off: + hw->pg_state = MEI_PG_OFF; +out: + dev->pg_event = MEI_PG_EVENT_IDLE; + + dev_dbg(dev->dev, "d0i3 exit ret = %d\n", ret); + return ret; +} + +/** + * mei_me_pg_legacy_intr - perform legacy pg processing + * in interrupt thread handler + * + * @dev: the device structure + */ +static void mei_me_pg_legacy_intr(struct mei_device *dev) { struct mei_me_hw *hw = to_me_hw(dev); @@ -752,6 +948,162 @@ static void mei_me_pg_intr(struct mei_device *dev) } /** + * mei_me_d0i3_intr - perform d0i3 processing in interrupt thread handler + * + * @dev: the device structure + */ +static void mei_me_d0i3_intr(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + + if (dev->pg_event == MEI_PG_EVENT_INTR_WAIT && + (hw->intr_source & H_D0I3C_IS)) { + dev->pg_event = MEI_PG_EVENT_INTR_RECEIVED; + if (hw->pg_state == MEI_PG_ON) { + hw->pg_state = MEI_PG_OFF; + if (dev->hbm_state != MEI_HBM_IDLE) { + /* + * force H_RDY because it could be + * wiped off during PG + */ + dev_dbg(dev->dev, "d0i3 set host ready\n"); + mei_me_host_set_ready(dev); + } + } else { + hw->pg_state = MEI_PG_ON; + } + + wake_up(&dev->wait_pg); + } + + if (hw->pg_state == MEI_PG_ON && (hw->intr_source & H_IS)) { + /* + * HW sent some data and we are in D0i3, so + * we got here because of HW initiated exit from D0i3. + * Start runtime pm resume sequence to exit low power state. + */ + dev_dbg(dev->dev, "d0i3 want resume\n"); + mei_hbm_pg_resume(dev); + } +} + +/** + * mei_me_pg_intr - perform pg processing in interrupt thread handler + * + * @dev: the device structure + */ +static void mei_me_pg_intr(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + + if (hw->d0i3_supported) + mei_me_d0i3_intr(dev); + else + mei_me_pg_legacy_intr(dev); +} + +/** + * mei_me_pg_enter_sync - perform runtime pm entry procedure + * + * @dev: the device structure + * + * Return: 0 on success an error code otherwise + */ +int mei_me_pg_enter_sync(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + + if (hw->d0i3_supported) + return mei_me_d0i3_enter_sync(dev); + else + return mei_me_pg_legacy_enter_sync(dev); +} + +/** + * mei_me_pg_exit_sync - perform runtime pm exit procedure + * + * @dev: the device structure + * + * Return: 0 on success an error code otherwise + */ +int mei_me_pg_exit_sync(struct mei_device *dev) +{ + struct mei_me_hw *hw = to_me_hw(dev); + + if (hw->d0i3_supported) + return mei_me_d0i3_exit_sync(dev); + else + return mei_me_pg_legacy_exit_sync(dev); +} + +/** + * mei_me_hw_reset - resets fw via mei csr register. + * + * @dev: the device structure + * @intr_enable: if interrupt should be enabled after reset. + * + * Return: 0 on success an error code otherwise + */ +static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable) +{ + struct mei_me_hw *hw = to_me_hw(dev); + int ret; + u32 hcsr; + + if (intr_enable) { + mei_me_intr_enable(dev); + if (hw->d0i3_supported) { + ret = mei_me_d0i3_exit_sync(dev); + if (ret) + return ret; + } + } + + hcsr = mei_hcsr_read(dev); + /* H_RST may be found lit before reset is started, + * for example if preceding reset flow hasn't completed. + * In that case asserting H_RST will be ignored, therefore + * we need to clean H_RST bit to start a successful reset sequence. + */ + if ((hcsr & H_RST) == H_RST) { + dev_warn(dev->dev, "H_RST is set = 0x%08X", hcsr); + hcsr &= ~H_RST; + mei_hcsr_set(dev, hcsr); + hcsr = mei_hcsr_read(dev); + } + + hcsr |= H_RST | H_IG | H_CSR_IS_MASK; + + if (!intr_enable) + hcsr &= ~H_CSR_IE_MASK; + + dev->recvd_hw_ready = false; + mei_hcsr_write(dev, hcsr); + + /* + * Host reads the H_CSR once to ensure that the + * posted write to H_CSR completes. + */ + hcsr = mei_hcsr_read(dev); + + if ((hcsr & H_RST) == 0) + dev_warn(dev->dev, "H_RST is not set = 0x%08X", hcsr); + + if ((hcsr & H_RDY) == H_RDY) + dev_warn(dev->dev, "H_RDY is not cleared 0x%08X", hcsr); + + if (!intr_enable) { + mei_me_hw_reset_release(dev); + if (hw->d0i3_supported) { + ret = mei_me_d0i3_enter(dev); + if (ret) + return ret; + } + } + return 0; +} + +/** * mei_me_irq_quick_handler - The ISR of the MEI device * * @irq: The irq number @@ -759,16 +1111,20 @@ static void mei_me_pg_intr(struct mei_device *dev) * * Return: irqreturn_t */ - irqreturn_t mei_me_irq_quick_handler(int irq, void *dev_id) { - struct mei_device *dev = (struct mei_device *) dev_id; - u32 hcsr = mei_hcsr_read(dev); + struct mei_device *dev = (struct mei_device *)dev_id; + struct mei_me_hw *hw = to_me_hw(dev); + u32 hcsr; - if ((hcsr & H_IS) != H_IS) + hcsr = mei_hcsr_read(dev); + if (!(hcsr & H_CSR_IS_MASK)) return IRQ_NONE; - /* clear H_IS bit in H_CSR */ + hw->intr_source = hcsr & H_CSR_IS_MASK; + dev_dbg(dev->dev, "interrupt source 0x%08X.\n", hw->intr_source); + + /* clear H_IS and H_D0I3C_IS bits in H_CSR to clear the interrupts */ mei_hcsr_write(dev, hcsr); return IRQ_WAKE_THREAD; @@ -796,11 +1152,6 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id) mutex_lock(&dev->device_lock); mei_io_list_init(&complete_list); - /* Ack the interrupt here - * In case of MSI we don't go through the quick handler */ - if (pci_dev_msi_enabled(to_pci_dev(dev->dev))) - mei_clear_interrupts(dev); - /* check if ME wants a reset */ if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) { dev_warn(dev->dev, "FW not ready: resetting.\n"); diff --git a/drivers/misc/mei/hw-me.h b/drivers/misc/mei/hw-me.h index 6022d52af6f6..2ee14dc1b2ea 100644 --- a/drivers/misc/mei/hw-me.h +++ b/drivers/misc/mei/hw-me.h @@ -50,13 +50,17 @@ struct mei_cfg { * struct mei_me_hw - me hw specific data * * @cfg: per device generation config and ops - * @mem_addr: io memory address - * @pg_state: power gating state + * @mem_addr: io memory address + * @intr_source: interrupt source + * @pg_state: power gating state + * @d0i3_supported: di03 support */ struct mei_me_hw { const struct mei_cfg *cfg; void __iomem *mem_addr; + u32 intr_source; enum mei_pg_state pg_state; + bool d0i3_supported; }; #define to_me_hw(dev) (struct mei_me_hw *)((dev)->hw) diff --git a/drivers/misc/mei/hw.h b/drivers/misc/mei/hw.h index 16fef6dc4dd7..4cebde85924f 100644 --- a/drivers/misc/mei/hw.h +++ b/drivers/misc/mei/hw.h @@ -31,14 +31,15 @@ #define MEI_IAMTHIF_STALL_TIMER 12 /* HPS */ #define MEI_IAMTHIF_READ_TIMER 10 /* HPS */ -#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */ -#define MEI_HBM_TIMEOUT 1 /* 1 second */ +#define MEI_PGI_TIMEOUT 1 /* PG Isolation time response 1 sec */ +#define MEI_D0I3_TIMEOUT 5 /* D0i3 set/unset max response time */ +#define MEI_HBM_TIMEOUT 1 /* 1 second */ /* * MEI Version */ -#define HBM_MINOR_VERSION 1 -#define HBM_MAJOR_VERSION 1 +#define HBM_MINOR_VERSION 0 +#define HBM_MAJOR_VERSION 2 /* * MEI version with PGI support @@ -46,6 +47,24 @@ #define HBM_MINOR_VERSION_PGI 1 #define HBM_MAJOR_VERSION_PGI 1 +/* + * MEI version with Dynamic clients support + */ +#define HBM_MINOR_VERSION_DC 0 +#define HBM_MAJOR_VERSION_DC 2 + +/* + * MEI version with disconnect on connection timeout support + */ +#define HBM_MINOR_VERSION_DOT 0 +#define HBM_MAJOR_VERSION_DOT 2 + +/* + * MEI version with notifcation support + */ +#define HBM_MINOR_VERSION_EV 0 +#define HBM_MAJOR_VERSION_EV 2 + /* Host bus message command opcode */ #define MEI_HBM_CMD_OP_MSK 0x7f /* Host bus message command RESPONSE */ @@ -81,6 +100,13 @@ #define MEI_PG_ISOLATION_EXIT_REQ_CMD 0x0b #define MEI_PG_ISOLATION_EXIT_RES_CMD 0x8b +#define MEI_HBM_ADD_CLIENT_REQ_CMD 0x0f +#define MEI_HBM_ADD_CLIENT_RES_CMD 0x8f + +#define MEI_HBM_NOTIFY_REQ_CMD 0x10 +#define MEI_HBM_NOTIFY_RES_CMD 0x90 +#define MEI_HBM_NOTIFICATION_CMD 0x11 + /* * MEI Stop Reason * used by hbm_host_stop_request.reason @@ -136,6 +162,7 @@ enum mei_cl_connect_status { MEI_CL_CONN_ALREADY_STARTED = MEI_HBMS_ALREADY_EXISTS, MEI_CL_CONN_OUT_OF_RESOURCES = MEI_HBMS_REJECTED, MEI_CL_CONN_MESSAGE_SMALL = MEI_HBMS_INVALID_PARAMETER, + MEI_CL_CONN_NOT_ALLOWED = MEI_HBMS_NOT_ALLOWED, }; /* @@ -213,9 +240,17 @@ struct hbm_me_stop_request { u8 reserved[2]; } __packed; +/** + * struct hbm_host_enum_request - enumeration request from host to fw + * + * @hbm_cmd: bus message command header + * @allow_add: allow dynamic clients add HBM version >= 2.0 + * @reserved: reserved + */ struct hbm_host_enum_request { u8 hbm_cmd; - u8 reserved[3]; + u8 allow_add; + u8 reserved[2]; } __packed; struct hbm_host_enum_response { @@ -248,6 +283,38 @@ struct hbm_props_response { } __packed; /** + * struct hbm_add_client_request - request to add a client + * might be sent by fw after enumeration has already completed + * + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @reserved: reserved + * @client_properties: client properties + */ +struct hbm_add_client_request { + u8 hbm_cmd; + u8 me_addr; + u8 reserved[2]; + struct mei_client_properties client_properties; +} __packed; + +/** + * struct hbm_add_client_response - response to add a client + * sent by the host to report client addition status to fw + * + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @status: if HBMS_SUCCESS then the client can now accept connections. + * @reserved: reserved + */ +struct hbm_add_client_response { + u8 hbm_cmd; + u8 me_addr; + u8 status; + u8 reserved[1]; +} __packed; + +/** * struct hbm_power_gate - power gate request/response * * @hbm_cmd: bus message command header @@ -298,5 +365,62 @@ struct hbm_flow_control { u8 reserved[MEI_FC_MESSAGE_RESERVED_LENGTH]; } __packed; +#define MEI_HBM_NOTIFICATION_START 1 +#define MEI_HBM_NOTIFICATION_STOP 0 +/** + * struct hbm_notification_request - start/stop notification request + * + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @start: start = 1 or stop = 0 asynchronous notifications + */ +struct hbm_notification_request { + u8 hbm_cmd; + u8 me_addr; + u8 host_addr; + u8 start; +} __packed; + +/** + * struct hbm_notification_response - start/stop notification response + * + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: - address of the client in the driver + * @status: (mei_hbm_status) response status for the request + * - MEI_HBMS_SUCCESS: successful stop/start + * - MEI_HBMS_CLIENT_NOT_FOUND: if the connection could not be found. + * - MEI_HBMS_ALREADY_STARTED: for start requests for a previously + * started notification. + * - MEI_HBMS_NOT_STARTED: for stop request for a connected client for whom + * asynchronous notifications are currently disabled. + * + * @start: start = 1 or stop = 0 asynchronous notifications + * @reserved: reserved + */ +struct hbm_notification_response { + u8 hbm_cmd; + u8 me_addr; + u8 host_addr; + u8 status; + u8 start; + u8 reserved[3]; +} __packed; + +/** + * struct hbm_notification - notification event + * + * @hbm_cmd: bus message command header + * @me_addr: address of the client in ME + * @host_addr: address of the client in the driver + * @reserved: reserved for alignment + */ +struct hbm_notification { + u8 hbm_cmd; + u8 me_addr; + u8 host_addr; + u8 reserved[1]; +} __packed; #endif diff --git a/drivers/misc/mei/init.c b/drivers/misc/mei/init.c index 00c3865ca3b1..e374661652cd 100644 --- a/drivers/misc/mei/init.c +++ b/drivers/misc/mei/init.c @@ -331,7 +331,7 @@ void mei_stop(struct mei_device *dev) mei_cancel_work(dev); - mei_nfc_host_exit(dev); + mei_cl_bus_remove_devices(dev); mutex_lock(&dev->device_lock); @@ -390,6 +390,7 @@ void mei_device_init(struct mei_device *dev, INIT_LIST_HEAD(&dev->me_clients); mutex_init(&dev->device_lock); init_rwsem(&dev->me_clients_rwsem); + mutex_init(&dev->cl_bus_lock); init_waitqueue_head(&dev->wait_hw_ready); init_waitqueue_head(&dev->wait_pg); init_waitqueue_head(&dev->wait_hbm_start); diff --git a/drivers/misc/mei/interrupt.c b/drivers/misc/mei/interrupt.c index 3f3405269c39..c418d7888994 100644 --- a/drivers/misc/mei/interrupt.c +++ b/drivers/misc/mei/interrupt.c @@ -403,6 +403,13 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) if (ret) return ret; break; + + case MEI_FOP_NOTIFY_START: + case MEI_FOP_NOTIFY_STOP: + ret = mei_cl_irq_notify(cl, cb, cmpl_list); + if (ret) + return ret; + break; default: BUG(); } @@ -424,6 +431,24 @@ int mei_irq_write_handler(struct mei_device *dev, struct mei_cl_cb *cmpl_list) EXPORT_SYMBOL_GPL(mei_irq_write_handler); +/** + * mei_connect_timeout - connect/disconnect timeouts + * + * @cl: host client + */ +static void mei_connect_timeout(struct mei_cl *cl) +{ + struct mei_device *dev = cl->dev; + + if (cl->state == MEI_FILE_CONNECTING) { + if (dev->hbm_f_dot_supported) { + cl->state = MEI_FILE_DISCONNECT_REQUIRED; + wake_up(&cl->wait); + return; + } + } + mei_reset(dev); +} /** * mei_timer - timer function. @@ -464,7 +489,7 @@ void mei_timer(struct work_struct *work) if (cl->timer_count) { if (--cl->timer_count == 0) { dev_err(dev->dev, "timer: connect/disconnect timeout.\n"); - mei_reset(dev); + mei_connect_timeout(cl); goto out; } } diff --git a/drivers/misc/mei/main.c b/drivers/misc/mei/main.c index e9513d651cd3..b2f2486b3d75 100644 --- a/drivers/misc/mei/main.c +++ b/drivers/misc/mei/main.c @@ -446,6 +446,45 @@ end: } /** + * mei_ioctl_client_notify_request - + * propagate event notification request to client + * + * @file: pointer to file structure + * @request: 0 - disable, 1 - enable + * + * Return: 0 on success , <0 on error + */ +static int mei_ioctl_client_notify_request(struct file *file, u32 request) +{ + struct mei_cl *cl = file->private_data; + + return mei_cl_notify_request(cl, file, request); +} + +/** + * mei_ioctl_client_notify_get - wait for notification request + * + * @file: pointer to file structure + * @notify_get: 0 - disable, 1 - enable + * + * Return: 0 on success , <0 on error + */ +static int mei_ioctl_client_notify_get(struct file *file, u32 *notify_get) +{ + struct mei_cl *cl = file->private_data; + bool notify_ev; + bool block = (file->f_flags & O_NONBLOCK) == 0; + int rets; + + rets = mei_cl_notify_get(cl, block, ¬ify_ev); + if (rets) + return rets; + + *notify_get = notify_ev ? 1 : 0; + return 0; +} + +/** * mei_ioctl - the IOCTL function * * @file: pointer to file structure @@ -459,6 +498,7 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) struct mei_device *dev; struct mei_cl *cl = file->private_data; struct mei_connect_client_data connect_data; + u32 notify_get, notify_req; int rets; @@ -499,6 +539,33 @@ static long mei_ioctl(struct file *file, unsigned int cmd, unsigned long data) break; + case IOCTL_MEI_NOTIFY_SET: + dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_SET.\n"); + if (copy_from_user(¬ify_req, + (char __user *)data, sizeof(notify_req))) { + dev_dbg(dev->dev, "failed to copy data from userland\n"); + rets = -EFAULT; + goto out; + } + rets = mei_ioctl_client_notify_request(file, notify_req); + break; + + case IOCTL_MEI_NOTIFY_GET: + dev_dbg(dev->dev, ": IOCTL_MEI_NOTIFY_GET.\n"); + rets = mei_ioctl_client_notify_get(file, ¬ify_get); + if (rets) + goto out; + + dev_dbg(dev->dev, "copy connect data to user\n"); + if (copy_to_user((char __user *)data, + ¬ify_get, sizeof(notify_get))) { + dev_dbg(dev->dev, "failed to copy data to userland\n"); + rets = -EFAULT; + goto out; + + } + break; + default: dev_err(dev->dev, ": unsupported ioctl %d.\n", cmd); rets = -ENOIOCTLCMD; @@ -541,6 +608,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) struct mei_cl *cl = file->private_data; struct mei_device *dev; unsigned int mask = 0; + bool notify_en; if (WARN_ON(!cl || !cl->dev)) return POLLERR; @@ -549,6 +617,7 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) mutex_lock(&dev->device_lock); + notify_en = cl->notify_en && (req_events & POLLPRI); if (dev->dev_state != MEI_DEV_ENABLED || !mei_cl_is_connected(cl)) { @@ -561,6 +630,12 @@ static unsigned int mei_poll(struct file *file, poll_table *wait) goto out; } + if (notify_en) { + poll_wait(file, &cl->ev_wait, wait); + if (cl->notify_ev) + mask |= POLLPRI; + } + if (req_events & (POLLIN | POLLRDNORM)) { poll_wait(file, &cl->rx_wait, wait); @@ -576,6 +651,26 @@ out: } /** + * mei_fasync - asynchronous io support + * + * @fd: file descriptor + * @file: pointer to file structure + * @band: band bitmap + * + * Return: poll mask + */ +static int mei_fasync(int fd, struct file *file, int band) +{ + + struct mei_cl *cl = file->private_data; + + if (!mei_cl_is_connected(cl)) + return POLLERR; + + return fasync_helper(fd, file, band, &cl->ev_async); +} + +/** * fw_status_show - mei device attribute show method * * @device: device pointer @@ -627,6 +722,7 @@ static const struct file_operations mei_fops = { .release = mei_release, .write = mei_write, .poll = mei_poll, + .fasync = mei_fasync, .llseek = no_llseek }; diff --git a/drivers/misc/mei/mei_dev.h b/drivers/misc/mei/mei_dev.h index 453f6a333b42..e25ee16c658e 100644 --- a/drivers/misc/mei/mei_dev.h +++ b/drivers/misc/mei/mei_dev.h @@ -89,6 +89,7 @@ enum file_state { MEI_FILE_CONNECTED, MEI_FILE_DISCONNECTING, MEI_FILE_DISCONNECT_REPLY, + MEI_FILE_DISCONNECT_REQUIRED, MEI_FILE_DISCONNECTED, }; @@ -135,6 +136,8 @@ enum mei_wd_states { * @MEI_FOP_CONNECT: connect * @MEI_FOP_DISCONNECT: disconnect * @MEI_FOP_DISCONNECT_RSP: disconnect response + * @MEI_FOP_NOTIFY_START: start notification + * @MEI_FOP_NOTIFY_STOP: stop notification */ enum mei_cb_file_ops { MEI_FOP_READ = 0, @@ -142,6 +145,8 @@ enum mei_cb_file_ops { MEI_FOP_CONNECT, MEI_FOP_DISCONNECT, MEI_FOP_DISCONNECT_RSP, + MEI_FOP_NOTIFY_START, + MEI_FOP_NOTIFY_STOP, }; /* @@ -178,7 +183,7 @@ struct mei_fw_status { * @client_id: me client id * @mei_flow_ctrl_creds: flow control credits * @connect_count: number connections to this client - * @reserved: reserved + * @bus_added: added to bus */ struct mei_me_client { struct list_head list; @@ -187,7 +192,7 @@ struct mei_me_client { u8 client_id; u8 mei_flow_ctrl_creds; u8 connect_count; - u8 reserved; + u8 bus_added; }; @@ -230,18 +235,21 @@ struct mei_cl_cb { * @tx_wait: wait queue for tx completion * @rx_wait: wait queue for rx completion * @wait: wait queue for management operation + * @ev_wait: notification wait queue + * @ev_async: event async notification * @status: connection status * @me_cl: fw client connected * @host_client_id: host id * @mei_flow_ctrl_creds: transmit flow credentials * @timer_count: watchdog timer for operation completion * @reserved: reserved for alignment + * @notify_en: notification - enabled/disabled + * @notify_ev: pending notification event * @writing_state: state of the tx * @rd_pending: pending read credits * @rd_completed: completed read * - * @device: device on the mei client bus - * @device_link: link to bus clients + * @cldev: device on the mei client bus */ struct mei_cl { struct list_head link; @@ -250,19 +258,21 @@ struct mei_cl { wait_queue_head_t tx_wait; wait_queue_head_t rx_wait; wait_queue_head_t wait; + wait_queue_head_t ev_wait; + struct fasync_struct *ev_async; int status; struct mei_me_client *me_cl; u8 host_client_id; u8 mei_flow_ctrl_creds; u8 timer_count; u8 reserved; + u8 notify_en; + u8 notify_ev; enum mei_file_transaction_states writing_state; struct list_head rd_pending; struct list_head rd_completed; - /* MEI CL bus data */ - struct mei_cl_device *device; - struct list_head device_link; + struct mei_cl_device *cldev; }; /** struct mei_hw_ops @@ -329,21 +339,16 @@ struct mei_hw_ops { }; /* MEI bus API*/ - -struct mei_cl_device *mei_cl_add_device(struct mei_device *dev, - struct mei_me_client *me_cl, - struct mei_cl *cl, - char *name); -void mei_cl_remove_device(struct mei_cl_device *device); - +void mei_cl_bus_rescan(struct mei_device *bus); +void mei_cl_dev_fixup(struct mei_cl_device *dev); ssize_t __mei_cl_send(struct mei_cl *cl, u8 *buf, size_t length, bool blocking); ssize_t __mei_cl_recv(struct mei_cl *cl, u8 *buf, size_t length); void mei_cl_bus_rx_event(struct mei_cl *cl); -void mei_cl_bus_remove_devices(struct mei_device *dev); +void mei_cl_bus_notify_event(struct mei_cl *cl); +void mei_cl_bus_remove_devices(struct mei_device *bus); int mei_cl_bus_init(void); void mei_cl_bus_exit(void); -struct mei_cl *mei_cl_bus_find_cl_by_uuid(struct mei_device *dev, uuid_le uuid); /** * enum mei_pg_event - power gating transition events @@ -416,7 +421,10 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @wr_msg : the buffer for hbm control messages * * @version : HBM protocol version in use - * @hbm_f_pg_supported : hbm feature pgi protocol + * @hbm_f_pg_supported : hbm feature pgi protocol + * @hbm_f_dc_supported : hbm feature dynamic clients + * @hbm_f_dot_supported : hbm feature disconnect on timeout + * @hbm_f_ev_supported : hbm feature event notification * * @me_clients_rwsem: rw lock over me_clients list * @me_clients : list of FW clients @@ -447,6 +455,7 @@ const char *mei_pg_state_str(enum mei_pg_state state); * @reset_work : work item for the device reset * * @device_list : mei client bus list + * @cl_bus_lock : client bus list lock * * @dbgfs_dir : debugfs mei root directory * @@ -509,6 +518,9 @@ struct mei_device { struct hbm_version version; unsigned int hbm_f_pg_supported:1; + unsigned int hbm_f_dc_supported:1; + unsigned int hbm_f_dot_supported:1; + unsigned int hbm_f_ev_supported:1; struct rw_semaphore me_clients_rwsem; struct list_head me_clients; @@ -543,6 +555,7 @@ struct mei_device { /* List of bus devices */ struct list_head device_list; + struct mutex cl_bus_lock; #if IS_ENABLED(CONFIG_DEBUG_FS) struct dentry *dbgfs_dir; diff --git a/drivers/misc/mei/nfc.c b/drivers/misc/mei/nfc.c deleted file mode 100644 index 290ef3037437..000000000000 --- a/drivers/misc/mei/nfc.c +++ /dev/null @@ -1,415 +0,0 @@ -/* - * - * Intel Management Engine Interface (Intel MEI) Linux driver - * Copyright (c) 2003-2013, Intel Corporation. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms and conditions of the GNU General Public License, - * version 2, as published by the Free Software Foundation. - * - * This program is distributed in the hope it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for - * more details. - * - */ - -#include <linux/kernel.h> -#include <linux/sched.h> -#include <linux/module.h> -#include <linux/moduleparam.h> -#include <linux/device.h> -#include <linux/slab.h> - -#include <linux/mei_cl_bus.h> - -#include "mei_dev.h" -#include "client.h" - -struct mei_nfc_cmd { - u8 command; - u8 status; - u16 req_id; - u32 reserved; - u16 data_size; - u8 sub_command; - u8 data[]; -} __packed; - -struct mei_nfc_reply { - u8 command; - u8 status; - u16 req_id; - u32 reserved; - u16 data_size; - u8 sub_command; - u8 reply_status; - u8 data[]; -} __packed; - -struct mei_nfc_if_version { - u8 radio_version_sw[3]; - u8 reserved[3]; - u8 radio_version_hw[3]; - u8 i2c_addr; - u8 fw_ivn; - u8 vendor_id; - u8 radio_type; -} __packed; - -struct mei_nfc_connect { - u8 fw_ivn; - u8 vendor_id; -} __packed; - -struct mei_nfc_connect_resp { - u8 fw_ivn; - u8 vendor_id; - u16 me_major; - u16 me_minor; - u16 me_hotfix; - u16 me_build; -} __packed; - -struct mei_nfc_hci_hdr { - u8 cmd; - u8 status; - u16 req_id; - u32 reserved; - u16 data_size; -} __packed; - -#define MEI_NFC_CMD_MAINTENANCE 0x00 -#define MEI_NFC_CMD_HCI_SEND 0x01 -#define MEI_NFC_CMD_HCI_RECV 0x02 - -#define MEI_NFC_SUBCMD_CONNECT 0x00 -#define MEI_NFC_SUBCMD_IF_VERSION 0x01 - -#define MEI_NFC_HEADER_SIZE 10 - -/** - * struct mei_nfc_dev - NFC mei device - * - * @me_cl: NFC me client - * @cl: NFC host client - * @cl_info: NFC info host client - * @init_work: perform connection to the info client - * @fw_ivn: NFC Interface Version Number - * @vendor_id: NFC manufacturer ID - * @radio_type: NFC radio type - * @bus_name: bus name - * - */ -struct mei_nfc_dev { - struct mei_me_client *me_cl; - struct mei_cl *cl; - struct mei_cl *cl_info; - struct work_struct init_work; - u8 fw_ivn; - u8 vendor_id; - u8 radio_type; - char *bus_name; -}; - -/* UUIDs for NFC F/W clients */ -const uuid_le mei_nfc_guid = UUID_LE(0x0bb17a78, 0x2a8e, 0x4c50, - 0x94, 0xd4, 0x50, 0x26, - 0x67, 0x23, 0x77, 0x5c); - -static const uuid_le mei_nfc_info_guid = UUID_LE(0xd2de1625, 0x382d, 0x417d, - 0x48, 0xa4, 0xef, 0xab, - 0xba, 0x8a, 0x12, 0x06); - -/* Vendors */ -#define MEI_NFC_VENDOR_INSIDE 0x00 -#define MEI_NFC_VENDOR_NXP 0x01 - -/* Radio types */ -#define MEI_NFC_VENDOR_INSIDE_UREAD 0x00 -#define MEI_NFC_VENDOR_NXP_PN544 0x01 - -static void mei_nfc_free(struct mei_nfc_dev *ndev) -{ - if (!ndev) - return; - - if (ndev->cl) { - list_del(&ndev->cl->device_link); - mei_cl_unlink(ndev->cl); - kfree(ndev->cl); - } - - if (ndev->cl_info) { - list_del(&ndev->cl_info->device_link); - mei_cl_unlink(ndev->cl_info); - kfree(ndev->cl_info); - } - - mei_me_cl_put(ndev->me_cl); - kfree(ndev); -} - -static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev) -{ - struct mei_device *dev; - - if (!ndev->cl) - return -ENODEV; - - dev = ndev->cl->dev; - - switch (ndev->vendor_id) { - case MEI_NFC_VENDOR_INSIDE: - switch (ndev->radio_type) { - case MEI_NFC_VENDOR_INSIDE_UREAD: - ndev->bus_name = "microread"; - return 0; - - default: - dev_err(dev->dev, "Unknown radio type 0x%x\n", - ndev->radio_type); - - return -EINVAL; - } - - case MEI_NFC_VENDOR_NXP: - switch (ndev->radio_type) { - case MEI_NFC_VENDOR_NXP_PN544: - ndev->bus_name = "pn544"; - return 0; - default: - dev_err(dev->dev, "Unknown radio type 0x%x\n", - ndev->radio_type); - - return -EINVAL; - } - - default: - dev_err(dev->dev, "Unknown vendor ID 0x%x\n", - ndev->vendor_id); - - return -EINVAL; - } - - return 0; -} - -static int mei_nfc_if_version(struct mei_nfc_dev *ndev) -{ - struct mei_device *dev; - struct mei_cl *cl; - - struct mei_nfc_cmd cmd; - struct mei_nfc_reply *reply = NULL; - struct mei_nfc_if_version *version; - size_t if_version_length; - int bytes_recv, ret; - - cl = ndev->cl_info; - dev = cl->dev; - - memset(&cmd, 0, sizeof(struct mei_nfc_cmd)); - cmd.command = MEI_NFC_CMD_MAINTENANCE; - cmd.data_size = 1; - cmd.sub_command = MEI_NFC_SUBCMD_IF_VERSION; - - ret = __mei_cl_send(cl, (u8 *)&cmd, sizeof(struct mei_nfc_cmd), 1); - if (ret < 0) { - dev_err(dev->dev, "Could not send IF version cmd\n"); - return ret; - } - - /* to be sure on the stack we alloc memory */ - if_version_length = sizeof(struct mei_nfc_reply) + - sizeof(struct mei_nfc_if_version); - - reply = kzalloc(if_version_length, GFP_KERNEL); - if (!reply) - return -ENOMEM; - - bytes_recv = __mei_cl_recv(cl, (u8 *)reply, if_version_length); - if (bytes_recv < 0 || bytes_recv < sizeof(struct mei_nfc_reply)) { - dev_err(dev->dev, "Could not read IF version\n"); - ret = -EIO; - goto err; - } - - version = (struct mei_nfc_if_version *)reply->data; - - ndev->fw_ivn = version->fw_ivn; - ndev->vendor_id = version->vendor_id; - ndev->radio_type = version->radio_type; - -err: - kfree(reply); - return ret; -} - -static void mei_nfc_init(struct work_struct *work) -{ - struct mei_device *dev; - struct mei_cl_device *cldev; - struct mei_nfc_dev *ndev; - struct mei_cl *cl_info; - struct mei_me_client *me_cl_info; - - ndev = container_of(work, struct mei_nfc_dev, init_work); - - cl_info = ndev->cl_info; - dev = cl_info->dev; - - mutex_lock(&dev->device_lock); - - /* check for valid client id */ - me_cl_info = mei_me_cl_by_uuid(dev, &mei_nfc_info_guid); - if (!me_cl_info) { - mutex_unlock(&dev->device_lock); - dev_info(dev->dev, "nfc: failed to find the info client\n"); - goto err; - } - - if (mei_cl_connect(cl_info, me_cl_info, NULL) < 0) { - mei_me_cl_put(me_cl_info); - mutex_unlock(&dev->device_lock); - dev_err(dev->dev, "Could not connect to the NFC INFO ME client"); - - goto err; - } - mei_me_cl_put(me_cl_info); - mutex_unlock(&dev->device_lock); - - if (mei_nfc_if_version(ndev) < 0) { - dev_err(dev->dev, "Could not get the NFC interface version"); - - goto err; - } - - dev_info(dev->dev, "NFC MEI VERSION: IVN 0x%x Vendor ID 0x%x Type 0x%x\n", - ndev->fw_ivn, ndev->vendor_id, ndev->radio_type); - - mutex_lock(&dev->device_lock); - - if (mei_cl_disconnect(cl_info) < 0) { - mutex_unlock(&dev->device_lock); - dev_err(dev->dev, "Could not disconnect the NFC INFO ME client"); - - goto err; - } - - mutex_unlock(&dev->device_lock); - - if (mei_nfc_build_bus_name(ndev) < 0) { - dev_err(dev->dev, "Could not build the bus ID name\n"); - return; - } - - cldev = mei_cl_add_device(dev, ndev->me_cl, ndev->cl, - ndev->bus_name); - if (!cldev) { - dev_err(dev->dev, "Could not add the NFC device to the MEI bus\n"); - - goto err; - } - - cldev->priv_data = ndev; - - - return; - -err: - mutex_lock(&dev->device_lock); - mei_nfc_free(ndev); - mutex_unlock(&dev->device_lock); - -} - - -int mei_nfc_host_init(struct mei_device *dev, struct mei_me_client *me_cl) -{ - struct mei_nfc_dev *ndev; - struct mei_cl *cl_info, *cl; - int ret; - - - /* in case of internal reset bail out - * as the device is already setup - */ - cl = mei_cl_bus_find_cl_by_uuid(dev, mei_nfc_guid); - if (cl) - return 0; - - ndev = kzalloc(sizeof(struct mei_nfc_dev), GFP_KERNEL); - if (!ndev) { - ret = -ENOMEM; - goto err; - } - - ndev->me_cl = mei_me_cl_get(me_cl); - if (!ndev->me_cl) { - ret = -ENODEV; - goto err; - } - - cl_info = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); - if (IS_ERR(cl_info)) { - ret = PTR_ERR(cl_info); - goto err; - } - - list_add_tail(&cl_info->device_link, &dev->device_list); - - ndev->cl_info = cl_info; - - cl = mei_cl_alloc_linked(dev, MEI_HOST_CLIENT_ID_ANY); - if (IS_ERR(cl)) { - ret = PTR_ERR(cl); - goto err; - } - - list_add_tail(&cl->device_link, &dev->device_list); - - ndev->cl = cl; - - INIT_WORK(&ndev->init_work, mei_nfc_init); - schedule_work(&ndev->init_work); - - return 0; - -err: - mei_nfc_free(ndev); - - return ret; -} - -void mei_nfc_host_exit(struct mei_device *dev) -{ - struct mei_nfc_dev *ndev; - struct mei_cl *cl; - struct mei_cl_device *cldev; - - cl = mei_cl_bus_find_cl_by_uuid(dev, mei_nfc_guid); - if (!cl) - return; - - cldev = cl->device; - if (!cldev) - return; - - ndev = (struct mei_nfc_dev *)cldev->priv_data; - if (ndev) - cancel_work_sync(&ndev->init_work); - - cldev->priv_data = NULL; - - /* Need to remove the device here - * since mei_nfc_free will unlink the clients - */ - mei_cl_remove_device(cldev); - - mutex_lock(&dev->device_lock); - mei_nfc_free(ndev); - mutex_unlock(&dev->device_lock); -} - - diff --git a/drivers/misc/mei/pci-me.c b/drivers/misc/mei/pci-me.c index 23f71f5ce4fb..27678d8154e0 100644 --- a/drivers/misc/mei/pci-me.c +++ b/drivers/misc/mei/pci-me.c @@ -82,6 +82,11 @@ static const struct pci_device_id mei_me_pci_tbl[] = { {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP, mei_me_pch8_cfg)}, {MEI_PCI_DEVICE(MEI_DEV_ID_WPT_LP_2, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_2, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H, mei_me_pch8_cfg)}, + {MEI_PCI_DEVICE(MEI_DEV_ID_SPT_H_2, mei_me_pch8_cfg)}, + /* required last entry */ {0, } }; @@ -128,6 +133,7 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) const struct mei_cfg *cfg = (struct mei_cfg *)(ent->driver_data); struct mei_device *dev; struct mei_me_hw *hw; + unsigned int irqflags; int err; @@ -180,17 +186,12 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent) pci_enable_msi(pdev); /* request and enable interrupt */ - if (pci_dev_msi_enabled(pdev)) - err = request_threaded_irq(pdev->irq, - NULL, - mei_me_irq_thread_handler, - IRQF_ONESHOT, KBUILD_MODNAME, dev); - else - err = request_threaded_irq(pdev->irq, + irqflags = pci_dev_msi_enabled(pdev) ? IRQF_ONESHOT : IRQF_SHARED; + + err = request_threaded_irq(pdev->irq, mei_me_irq_quick_handler, mei_me_irq_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); - + irqflags, KBUILD_MODNAME, dev); if (err) { dev_err(&pdev->dev, "request_threaded_irq failure. irq = %d\n", pdev->irq); @@ -319,6 +320,7 @@ static int mei_me_pci_resume(struct device *device) { struct pci_dev *pdev = to_pci_dev(device); struct mei_device *dev; + unsigned int irqflags; int err; dev = pci_get_drvdata(pdev); @@ -327,17 +329,13 @@ static int mei_me_pci_resume(struct device *device) pci_enable_msi(pdev); + irqflags = pci_dev_msi_enabled(pdev) ? IRQF_ONESHOT : IRQF_SHARED; + /* request and enable interrupt */ - if (pci_dev_msi_enabled(pdev)) - err = request_threaded_irq(pdev->irq, - NULL, - mei_me_irq_thread_handler, - IRQF_ONESHOT, KBUILD_MODNAME, dev); - else - err = request_threaded_irq(pdev->irq, + err = request_threaded_irq(pdev->irq, mei_me_irq_quick_handler, mei_me_irq_thread_handler, - IRQF_SHARED, KBUILD_MODNAME, dev); + irqflags, KBUILD_MODNAME, dev); if (err) { dev_err(&pdev->dev, "request_threaded_irq failed: irq = %d.\n", diff --git a/drivers/misc/qcom-coincell.c b/drivers/misc/qcom-coincell.c new file mode 100644 index 000000000000..7b4a2da487a5 --- /dev/null +++ b/drivers/misc/qcom-coincell.c @@ -0,0 +1,152 @@ +/* Copyright (c) 2013, The Linux Foundation. All rights reserved. + * Copyright (c) 2015, Sony Mobile Communications 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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/of_device.h> +#include <linux/platform_device.h> + +struct qcom_coincell { + struct device *dev; + struct regmap *regmap; + u32 base_addr; +}; + +#define QCOM_COINCELL_REG_RSET 0x44 +#define QCOM_COINCELL_REG_VSET 0x45 +#define QCOM_COINCELL_REG_ENABLE 0x46 + +#define QCOM_COINCELL_ENABLE BIT(7) + +static const int qcom_rset_map[] = { 2100, 1700, 1200, 800 }; +static const int qcom_vset_map[] = { 2500, 3200, 3100, 3000 }; +/* NOTE: for pm8921 and others, voltage of 2500 is 16 (10000b), not 0 */ + +/* if enable==0, rset and vset are ignored */ +static int qcom_coincell_chgr_config(struct qcom_coincell *chgr, int rset, + int vset, bool enable) +{ + int i, j, rc; + + /* if disabling, just do that and skip other operations */ + if (!enable) + return regmap_write(chgr->regmap, + chgr->base_addr + QCOM_COINCELL_REG_ENABLE, 0); + + /* find index for current-limiting resistor */ + for (i = 0; i < ARRAY_SIZE(qcom_rset_map); i++) + if (rset == qcom_rset_map[i]) + break; + + if (i >= ARRAY_SIZE(qcom_rset_map)) { + dev_err(chgr->dev, "invalid rset-ohms value %d\n", rset); + return -EINVAL; + } + + /* find index for charge voltage */ + for (j = 0; j < ARRAY_SIZE(qcom_vset_map); j++) + if (vset == qcom_vset_map[j]) + break; + + if (j >= ARRAY_SIZE(qcom_vset_map)) { + dev_err(chgr->dev, "invalid vset-millivolts value %d\n", vset); + return -EINVAL; + } + + rc = regmap_write(chgr->regmap, + chgr->base_addr + QCOM_COINCELL_REG_RSET, i); + if (rc) { + /* + * This is mainly to flag a bad base_addr (reg) from dts. + * Other failures writing to the registers should be + * extremely rare, or indicative of problems that + * should be reported elsewhere (eg. spmi failure). + */ + dev_err(chgr->dev, "could not write to RSET register\n"); + return rc; + } + + rc = regmap_write(chgr->regmap, + chgr->base_addr + QCOM_COINCELL_REG_VSET, j); + if (rc) + return rc; + + /* set 'enable' register */ + return regmap_write(chgr->regmap, + chgr->base_addr + QCOM_COINCELL_REG_ENABLE, + QCOM_COINCELL_ENABLE); +} + +static int qcom_coincell_probe(struct platform_device *pdev) +{ + struct device_node *node = pdev->dev.of_node; + struct qcom_coincell chgr; + u32 rset, vset; + bool enable; + int rc; + + chgr.dev = &pdev->dev; + + chgr.regmap = dev_get_regmap(pdev->dev.parent, NULL); + if (!chgr.regmap) { + dev_err(chgr.dev, "Unable to get regmap\n"); + return -EINVAL; + } + + rc = of_property_read_u32(node, "reg", &chgr.base_addr); + if (rc) + return rc; + + enable = !of_property_read_bool(node, "qcom,charger-disable"); + + if (enable) { + rc = of_property_read_u32(node, "qcom,rset-ohms", &rset); + if (rc) { + dev_err(chgr.dev, + "can't find 'qcom,rset-ohms' in DT block"); + return rc; + } + + rc = of_property_read_u32(node, "qcom,vset-millivolts", &vset); + if (rc) { + dev_err(chgr.dev, + "can't find 'qcom,vset-millivolts' in DT block"); + return rc; + } + } + + return qcom_coincell_chgr_config(&chgr, rset, vset, enable); +} + +static const struct of_device_id qcom_coincell_match_table[] = { + { .compatible = "qcom,pm8941-coincell", }, + {} +}; + +MODULE_DEVICE_TABLE(of, qcom_coincell_match_table); + +static struct platform_driver qcom_coincell_driver = { + .driver = { + .name = "qcom-spmi-coincell", + .of_match_table = qcom_coincell_match_table, + }, + .probe = qcom_coincell_probe, +}; + +module_platform_driver(qcom_coincell_driver); + +MODULE_DESCRIPTION("Qualcomm PMIC coincell charger driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/misc/ti-st/st_kim.c b/drivers/misc/ti-st/st_kim.c index 5027b8ffae43..71b64550b591 100644 --- a/drivers/misc/ti-st/st_kim.c +++ b/drivers/misc/ti-st/st_kim.c @@ -36,8 +36,6 @@ #include <linux/skbuff.h> #include <linux/ti_wilink_st.h> #include <linux/module.h> -#include <linux/of.h> -#include <linux/of_device.h> #define MAX_ST_DEVICES 3 /* Imagine 1 on each UART for now */ static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; @@ -45,9 +43,6 @@ static struct platform_device *st_kim_devices[MAX_ST_DEVICES]; /**********************************************************************/ /* internal functions */ -struct ti_st_plat_data *dt_pdata; -static struct ti_st_plat_data *get_platform_data(struct device *dev); - /** * st_get_plat_device - * function which returns the reference to the platform device @@ -469,12 +464,7 @@ long st_kim_start(void *kim_data) struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; pr_info(" %s", __func__); - if (kim_gdata->kim_pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else { - pdata = kim_gdata->kim_pdev->dev.platform_data; - } + pdata = kim_gdata->kim_pdev->dev.platform_data; do { /* platform specific enabling code here */ @@ -482,9 +472,9 @@ long st_kim_start(void *kim_data) pdata->chip_enable(kim_gdata); /* Configure BT nShutdown to HIGH state */ - gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); mdelay(5); /* FIXME: a proper toggle */ - gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); + gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH); mdelay(100); /* re-initialize the completion */ reinit_completion(&kim_gdata->ldisc_installed); @@ -534,18 +524,12 @@ long st_kim_stop(void *kim_data) { long err = 0; struct kim_data_s *kim_gdata = (struct kim_data_s *)kim_data; - struct ti_st_plat_data *pdata; + struct ti_st_plat_data *pdata = + kim_gdata->kim_pdev->dev.platform_data; struct tty_struct *tty = kim_gdata->core_data->tty; reinit_completion(&kim_gdata->ldisc_installed); - if (kim_gdata->kim_pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else - pdata = kim_gdata->kim_pdev->dev.platform_data; - - if (tty) { /* can be called before ldisc is installed */ /* Flush any pending characters in the driver and discipline. */ tty_ldisc_flush(tty); @@ -566,11 +550,11 @@ long st_kim_stop(void *kim_data) } /* By default configure BT nShutdown to LOW state */ - gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); mdelay(1); - gpio_set_value(kim_gdata->nshutdown, GPIO_HIGH); + gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_HIGH); mdelay(1); - gpio_set_value(kim_gdata->nshutdown, GPIO_LOW); + gpio_set_value_cansleep(kim_gdata->nshutdown, GPIO_LOW); /* platform specific disable */ if (pdata->chip_disable) @@ -737,52 +721,13 @@ static const struct file_operations list_debugfs_fops = { * board-*.c file */ -static const struct of_device_id kim_of_match[] = { -{ - .compatible = "kim", - }, - {} -}; -MODULE_DEVICE_TABLE(of, kim_of_match); - -static struct ti_st_plat_data *get_platform_data(struct device *dev) -{ - struct device_node *np = dev->of_node; - const u32 *dt_property; - int len; - - dt_pdata = kzalloc(sizeof(*dt_pdata), GFP_KERNEL); - if (!dt_pdata) - return NULL; - - dt_property = of_get_property(np, "dev_name", &len); - if (dt_property) - memcpy(&dt_pdata->dev_name, dt_property, len); - of_property_read_u32(np, "nshutdown_gpio", - &dt_pdata->nshutdown_gpio); - of_property_read_u32(np, "flow_cntrl", &dt_pdata->flow_cntrl); - of_property_read_u32(np, "baud_rate", &dt_pdata->baud_rate); - - return dt_pdata; -} - static struct dentry *kim_debugfs_dir; static int kim_probe(struct platform_device *pdev) { struct kim_data_s *kim_gdata; - struct ti_st_plat_data *pdata; + struct ti_st_plat_data *pdata = pdev->dev.platform_data; int err; - if (pdev->dev.of_node) - pdata = get_platform_data(&pdev->dev); - else - pdata = pdev->dev.platform_data; - - if (pdata == NULL) { - dev_err(&pdev->dev, "Platform Data is missing\n"); - return -ENXIO; - } - if ((pdev->id != -1) && (pdev->id < MAX_ST_DEVICES)) { /* multiple devices could exist */ st_kim_devices[pdev->id] = pdev; @@ -863,16 +808,9 @@ err_core_init: static int kim_remove(struct platform_device *pdev) { /* free the GPIOs requested */ - struct ti_st_plat_data *pdata; + struct ti_st_plat_data *pdata = pdev->dev.platform_data; struct kim_data_s *kim_gdata; - if (pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else { - pdata = pdev->dev.platform_data; - } - kim_gdata = platform_get_drvdata(pdev); /* Free the Bluetooth/FM/GPIO @@ -890,22 +828,12 @@ static int kim_remove(struct platform_device *pdev) kfree(kim_gdata); kim_gdata = NULL; - kfree(dt_pdata); - dt_pdata = NULL; - return 0; } static int kim_suspend(struct platform_device *pdev, pm_message_t state) { - struct ti_st_plat_data *pdata; - - if (pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else { - pdata = pdev->dev.platform_data; - } + struct ti_st_plat_data *pdata = pdev->dev.platform_data; if (pdata->suspend) return pdata->suspend(pdev, state); @@ -915,14 +843,7 @@ static int kim_suspend(struct platform_device *pdev, pm_message_t state) static int kim_resume(struct platform_device *pdev) { - struct ti_st_plat_data *pdata; - - if (pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else { - pdata = pdev->dev.platform_data; - } + struct ti_st_plat_data *pdata = pdev->dev.platform_data; if (pdata->resume) return pdata->resume(pdev); @@ -939,8 +860,6 @@ static struct platform_driver kim_platform_driver = { .resume = kim_resume, .driver = { .name = "kim", - .owner = THIS_MODULE, - .of_match_table = of_match_ptr(kim_of_match), }, }; diff --git a/drivers/misc/ti-st/st_ll.c b/drivers/misc/ti-st/st_ll.c index 518e1b7f2f95..93b4d67cc4a3 100644 --- a/drivers/misc/ti-st/st_ll.c +++ b/drivers/misc/ti-st/st_ll.c @@ -26,7 +26,6 @@ #include <linux/ti_wilink_st.h> /**********************************************************************/ - /* internal functions */ static void send_ll_cmd(struct st_data_s *st_data, unsigned char cmd) @@ -54,13 +53,7 @@ static void ll_device_want_to_sleep(struct st_data_s *st_data) /* communicate to platform about chip asleep */ kim_data = st_data->kim_data; - if (kim_data->kim_pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else { - pdata = kim_data->kim_pdev->dev.platform_data; - } - + pdata = kim_data->kim_pdev->dev.platform_data; if (pdata->chip_asleep) pdata->chip_asleep(NULL); } @@ -93,13 +86,7 @@ static void ll_device_want_to_wakeup(struct st_data_s *st_data) /* communicate to platform about chip wakeup */ kim_data = st_data->kim_data; - if (kim_data->kim_pdev->dev.of_node) { - pr_debug("use device tree data"); - pdata = dt_pdata; - } else { - pdata = kim_data->kim_pdev->dev.platform_data; - } - + pdata = kim_data->kim_pdev->dev.platform_data; if (pdata->chip_awake) pdata->chip_awake(NULL); } diff --git a/drivers/misc/tsl2550.c b/drivers/misc/tsl2550.c index b00335652e52..87a13374fdc0 100644 --- a/drivers/misc/tsl2550.c +++ b/drivers/misc/tsl2550.c @@ -446,7 +446,6 @@ MODULE_DEVICE_TABLE(i2c, tsl2550_id); static struct i2c_driver tsl2550_driver = { .driver = { .name = TSL2550_DRV_NAME, - .owner = THIS_MODULE, .pm = TSL2550_PM_OPS, }, .probe = tsl2550_probe, diff --git a/drivers/misc/vmw_balloon.c b/drivers/misc/vmw_balloon.c index 191617492181..ffb56340d0c7 100644 --- a/drivers/misc/vmw_balloon.c +++ b/drivers/misc/vmw_balloon.c @@ -46,7 +46,7 @@ MODULE_AUTHOR("VMware, Inc."); MODULE_DESCRIPTION("VMware Memory Control (Balloon) Driver"); -MODULE_VERSION("1.2.1.3-k"); +MODULE_VERSION("1.3.0.0-k"); MODULE_ALIAS("dmi:*:svnVMware*:*"); MODULE_ALIAS("vmware_vmmemctl"); MODULE_LICENSE("GPL"); @@ -110,9 +110,18 @@ MODULE_LICENSE("GPL"); */ #define VMW_BALLOON_HV_PORT 0x5670 #define VMW_BALLOON_HV_MAGIC 0x456c6d6f -#define VMW_BALLOON_PROTOCOL_VERSION 2 #define VMW_BALLOON_GUEST_ID 1 /* Linux */ +enum vmwballoon_capabilities { + /* + * Bit 0 is reserved and not associated to any capability. + */ + VMW_BALLOON_BASIC_CMDS = (1 << 1), + VMW_BALLOON_BATCHED_CMDS = (1 << 2) +}; + +#define VMW_BALLOON_CAPABILITIES (VMW_BALLOON_BASIC_CMDS) + #define VMW_BALLOON_CMD_START 0 #define VMW_BALLOON_CMD_GET_TARGET 1 #define VMW_BALLOON_CMD_LOCK 2 @@ -120,32 +129,36 @@ MODULE_LICENSE("GPL"); #define VMW_BALLOON_CMD_GUEST_ID 4 /* error codes */ -#define VMW_BALLOON_SUCCESS 0 -#define VMW_BALLOON_FAILURE -1 -#define VMW_BALLOON_ERROR_CMD_INVALID 1 -#define VMW_BALLOON_ERROR_PPN_INVALID 2 -#define VMW_BALLOON_ERROR_PPN_LOCKED 3 -#define VMW_BALLOON_ERROR_PPN_UNLOCKED 4 -#define VMW_BALLOON_ERROR_PPN_PINNED 5 -#define VMW_BALLOON_ERROR_PPN_NOTNEEDED 6 -#define VMW_BALLOON_ERROR_RESET 7 -#define VMW_BALLOON_ERROR_BUSY 8 - -#define VMWARE_BALLOON_CMD(cmd, data, result) \ -({ \ - unsigned long __stat, __dummy1, __dummy2; \ - __asm__ __volatile__ ("inl %%dx" : \ - "=a"(__stat), \ - "=c"(__dummy1), \ - "=d"(__dummy2), \ - "=b"(result) : \ - "0"(VMW_BALLOON_HV_MAGIC), \ - "1"(VMW_BALLOON_CMD_##cmd), \ - "2"(VMW_BALLOON_HV_PORT), \ - "3"(data) : \ - "memory"); \ - result &= -1UL; \ - __stat & -1UL; \ +#define VMW_BALLOON_SUCCESS 0 +#define VMW_BALLOON_FAILURE -1 +#define VMW_BALLOON_ERROR_CMD_INVALID 1 +#define VMW_BALLOON_ERROR_PPN_INVALID 2 +#define VMW_BALLOON_ERROR_PPN_LOCKED 3 +#define VMW_BALLOON_ERROR_PPN_UNLOCKED 4 +#define VMW_BALLOON_ERROR_PPN_PINNED 5 +#define VMW_BALLOON_ERROR_PPN_NOTNEEDED 6 +#define VMW_BALLOON_ERROR_RESET 7 +#define VMW_BALLOON_ERROR_BUSY 8 + +#define VMW_BALLOON_SUCCESS_WITH_CAPABILITIES (0x03000000) + +#define VMWARE_BALLOON_CMD(cmd, data, result) \ +({ \ + unsigned long __status, __dummy1, __dummy2; \ + __asm__ __volatile__ ("inl %%dx" : \ + "=a"(__status), \ + "=c"(__dummy1), \ + "=d"(__dummy2), \ + "=b"(result) : \ + "0"(VMW_BALLOON_HV_MAGIC), \ + "1"(VMW_BALLOON_CMD_##cmd), \ + "2"(VMW_BALLOON_HV_PORT), \ + "3"(data) : \ + "memory"); \ + if (VMW_BALLOON_CMD_##cmd == VMW_BALLOON_CMD_START) \ + result = __dummy1; \ + result &= -1UL; \ + __status & -1UL; \ }) #ifdef CONFIG_DEBUG_FS @@ -223,11 +236,12 @@ static struct vmballoon balloon; */ static bool vmballoon_send_start(struct vmballoon *b) { - unsigned long status, dummy; + unsigned long status, capabilities; STATS_INC(b->stats.start); - status = VMWARE_BALLOON_CMD(START, VMW_BALLOON_PROTOCOL_VERSION, dummy); + status = VMWARE_BALLOON_CMD(START, VMW_BALLOON_CAPABILITIES, + capabilities); if (status == VMW_BALLOON_SUCCESS) return true; @@ -402,55 +416,37 @@ static void vmballoon_reset(struct vmballoon *b) } /* - * Allocate (or reserve) a page for the balloon and notify the host. If host - * refuses the page put it on "refuse" list and allocate another one until host - * is satisfied. "Refused" pages are released at the end of inflation cycle - * (when we allocate b->rate_alloc pages). + * Notify the host of a ballooned page. If host rejects the page put it on the + * refuse list, those refused page are then released at the end of the + * inflation cycle. */ -static int vmballoon_reserve_page(struct vmballoon *b, bool can_sleep) +static int vmballoon_lock_page(struct vmballoon *b, struct page *page) { - struct page *page; - gfp_t flags; - unsigned int hv_status; - int locked; - flags = can_sleep ? VMW_PAGE_ALLOC_CANSLEEP : VMW_PAGE_ALLOC_NOSLEEP; - - do { - if (!can_sleep) - STATS_INC(b->stats.alloc); - else - STATS_INC(b->stats.sleep_alloc); + int locked, hv_status; - page = alloc_page(flags); - if (!page) { - if (!can_sleep) - STATS_INC(b->stats.alloc_fail); - else - STATS_INC(b->stats.sleep_alloc_fail); - return -ENOMEM; - } + locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status); + if (locked > 0) { + STATS_INC(b->stats.refused_alloc); - /* inform monitor */ - locked = vmballoon_send_lock_page(b, page_to_pfn(page), &hv_status); - if (locked > 0) { - STATS_INC(b->stats.refused_alloc); - - if (hv_status == VMW_BALLOON_ERROR_RESET || - hv_status == VMW_BALLOON_ERROR_PPN_NOTNEEDED) { - __free_page(page); - return -EIO; - } + if (hv_status == VMW_BALLOON_ERROR_RESET || + hv_status == VMW_BALLOON_ERROR_PPN_NOTNEEDED) { + __free_page(page); + return -EIO; + } - /* - * Place page on the list of non-balloonable pages - * and retry allocation, unless we already accumulated - * too many of them, in which case take a breather. - */ + /* + * Place page on the list of non-balloonable pages + * and retry allocation, unless we already accumulated + * too many of them, in which case take a breather. + */ + if (b->n_refused_pages < VMW_BALLOON_MAX_REFUSED) { + b->n_refused_pages++; list_add(&page->lru, &b->refused_pages); - if (++b->n_refused_pages >= VMW_BALLOON_MAX_REFUSED) - return -EIO; + } else { + __free_page(page); } - } while (locked != 0); + return -EIO; + } /* track allocated page */ list_add(&page->lru, &b->pages); @@ -512,7 +508,7 @@ static void vmballoon_inflate(struct vmballoon *b) unsigned int i; unsigned int allocations = 0; int error = 0; - bool alloc_can_sleep = false; + gfp_t flags = VMW_PAGE_ALLOC_NOSLEEP; pr_debug("%s - size: %d, target %d\n", __func__, b->size, b->target); @@ -543,19 +539,16 @@ static void vmballoon_inflate(struct vmballoon *b) __func__, goal, rate, b->rate_alloc); for (i = 0; i < goal; i++) { + struct page *page; - error = vmballoon_reserve_page(b, alloc_can_sleep); - if (error) { - if (error != -ENOMEM) { - /* - * Not a page allocation failure, stop this - * cycle. Maybe we'll get new target from - * the host soon. - */ - break; - } + if (flags == VMW_PAGE_ALLOC_NOSLEEP) + STATS_INC(b->stats.alloc); + else + STATS_INC(b->stats.sleep_alloc); - if (alloc_can_sleep) { + page = alloc_page(flags); + if (!page) { + if (flags == VMW_PAGE_ALLOC_CANSLEEP) { /* * CANSLEEP page allocation failed, so guest * is under severe memory pressure. Quickly @@ -563,8 +556,10 @@ static void vmballoon_inflate(struct vmballoon *b) */ b->rate_alloc = max(b->rate_alloc / 2, VMW_BALLOON_RATE_ALLOC_MIN); + STATS_INC(b->stats.sleep_alloc_fail); break; } + STATS_INC(b->stats.alloc_fail); /* * NOSLEEP page allocation failed, so the guest is @@ -579,11 +574,16 @@ static void vmballoon_inflate(struct vmballoon *b) if (i >= b->rate_alloc) break; - alloc_can_sleep = true; + flags = VMW_PAGE_ALLOC_CANSLEEP; /* Lower rate for sleeping allocations. */ rate = b->rate_alloc; + continue; } + error = vmballoon_lock_page(b, page); + if (error) + break; + if (++allocations > VMW_BALLOON_YIELD_THRESHOLD) { cond_resched(); allocations = 0; diff --git a/drivers/misc/vmw_vmci/vmci_host.c b/drivers/misc/vmw_vmci/vmci_host.c index a721b5d8a9da..9ec262a52656 100644 --- a/drivers/misc/vmw_vmci/vmci_host.c +++ b/drivers/misc/vmw_vmci/vmci_host.c @@ -1031,14 +1031,9 @@ int __init vmci_host_init(void) void __exit vmci_host_exit(void) { - int error; - vmci_host_device_initialized = false; - error = misc_deregister(&vmci_host_miscdev); - if (error) - pr_warn("Error unregistering character device: %d\n", error); - + misc_deregister(&vmci_host_miscdev); vmci_ctx_destroy(host_context); vmci_qp_broker_exit(); diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index e1ccefce9a9d..a98dd4f1b0e3 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -786,6 +786,7 @@ static bool bond_should_notify_peers(struct bonding *bond) slave ? slave->dev->name : "NULL"); if (!slave || !bond->send_peer_notif || + !netif_carrier_ok(bond->dev) || test_bit(__LINK_STATE_LINKWATCH_PENDING, &slave->dev->state)) return false; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb.c b/drivers/net/can/usb/peak_usb/pcan_usb.c index 6b94007ae052..838545ce468d 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb.c @@ -854,6 +854,18 @@ static int pcan_usb_probe(struct usb_interface *intf) /* * describe the PCAN-USB adapter */ +static const struct can_bittiming_const pcan_usb_const = { + .name = "pcan_usb", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 64, + .brp_inc = 1, +}; + const struct peak_usb_adapter pcan_usb = { .name = "PCAN-USB", .device_id = PCAN_USB_PRODUCT_ID, @@ -862,17 +874,7 @@ const struct peak_usb_adapter pcan_usb = { .clock = { .freq = PCAN_USB_CRYSTAL_HZ / 2 , }, - .bittiming_const = { - .name = "pcan_usb", - .tseg1_min = 1, - .tseg1_max = 16, - .tseg2_min = 1, - .tseg2_max = 8, - .sjw_max = 4, - .brp_min = 1, - .brp_max = 64, - .brp_inc = 1, - }, + .bittiming_const = &pcan_usb_const, /* size of device private data */ .sizeof_dev_private = sizeof(struct pcan_usb), diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.c b/drivers/net/can/usb/peak_usb/pcan_usb_core.c index 7921cff93a63..5a2e341a6d1e 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.c @@ -792,9 +792,9 @@ static int peak_usb_create_dev(const struct peak_usb_adapter *peak_usb_adapter, dev->ep_msg_out = peak_usb_adapter->ep_msg_out[ctrl_idx]; dev->can.clock = peak_usb_adapter->clock; - dev->can.bittiming_const = &peak_usb_adapter->bittiming_const; + dev->can.bittiming_const = peak_usb_adapter->bittiming_const; dev->can.do_set_bittiming = peak_usb_set_bittiming; - dev->can.data_bittiming_const = &peak_usb_adapter->data_bittiming_const; + dev->can.data_bittiming_const = peak_usb_adapter->data_bittiming_const; dev->can.do_set_data_bittiming = peak_usb_set_data_bittiming; dev->can.do_set_mode = peak_usb_set_mode; dev->can.do_get_berr_counter = peak_usb_adapter->do_get_berr_counter; diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_core.h b/drivers/net/can/usb/peak_usb/pcan_usb_core.h index 9e624f05ad4d..506fe506c9d3 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_core.h +++ b/drivers/net/can/usb/peak_usb/pcan_usb_core.h @@ -48,8 +48,8 @@ struct peak_usb_adapter { u32 device_id; u32 ctrlmode_supported; struct can_clock clock; - const struct can_bittiming_const bittiming_const; - const struct can_bittiming_const data_bittiming_const; + const struct can_bittiming_const * const bittiming_const; + const struct can_bittiming_const * const data_bittiming_const; unsigned int ctrl_count; int (*intf_probe)(struct usb_interface *intf); diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c index 09d14e70abd7..ce44a033f63b 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_fd.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_fd.c @@ -990,6 +990,30 @@ static void pcan_usb_fd_free(struct peak_usb_device *dev) } /* describes the PCAN-USB FD adapter */ +static const struct can_bittiming_const pcan_usb_fd_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = 64, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_fd_data_const = { + .name = "pcan_usb_fd", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + const struct peak_usb_adapter pcan_usb_fd = { .name = "PCAN-USB FD", .device_id = PCAN_USBFD_PRODUCT_ID, @@ -999,28 +1023,8 @@ const struct peak_usb_adapter pcan_usb_fd = { .clock = { .freq = PCAN_UFD_CRYSTAL_HZ, }, - .bittiming_const = { - .name = "pcan_usb_fd", - .tseg1_min = 1, - .tseg1_max = 64, - .tseg2_min = 1, - .tseg2_max = 16, - .sjw_max = 16, - .brp_min = 1, - .brp_max = 1024, - .brp_inc = 1, - }, - .data_bittiming_const = { - .name = "pcan_usb_fd", - .tseg1_min = 1, - .tseg1_max = 16, - .tseg2_min = 1, - .tseg2_max = 8, - .sjw_max = 4, - .brp_min = 1, - .brp_max = 1024, - .brp_inc = 1, - }, + .bittiming_const = &pcan_usb_fd_const, + .data_bittiming_const = &pcan_usb_fd_data_const, /* size of device private data */ .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), @@ -1058,6 +1062,30 @@ const struct peak_usb_adapter pcan_usb_fd = { }; /* describes the PCAN-USB Pro FD adapter */ +static const struct can_bittiming_const pcan_usb_pro_fd_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = 64, + .tseg2_min = 1, + .tseg2_max = 16, + .sjw_max = 16, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + +static const struct can_bittiming_const pcan_usb_pro_fd_data_const = { + .name = "pcan_usb_pro_fd", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + const struct peak_usb_adapter pcan_usb_pro_fd = { .name = "PCAN-USB Pro FD", .device_id = PCAN_USBPROFD_PRODUCT_ID, @@ -1067,28 +1095,8 @@ const struct peak_usb_adapter pcan_usb_pro_fd = { .clock = { .freq = PCAN_UFD_CRYSTAL_HZ, }, - .bittiming_const = { - .name = "pcan_usb_pro_fd", - .tseg1_min = 1, - .tseg1_max = 64, - .tseg2_min = 1, - .tseg2_max = 16, - .sjw_max = 16, - .brp_min = 1, - .brp_max = 1024, - .brp_inc = 1, - }, - .data_bittiming_const = { - .name = "pcan_usb_pro_fd", - .tseg1_min = 1, - .tseg1_max = 16, - .tseg2_min = 1, - .tseg2_max = 8, - .sjw_max = 4, - .brp_min = 1, - .brp_max = 1024, - .brp_inc = 1, - }, + .bittiming_const = &pcan_usb_pro_fd_const, + .data_bittiming_const = &pcan_usb_pro_fd_data_const, /* size of device private data */ .sizeof_dev_private = sizeof(struct pcan_usb_fd_device), diff --git a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c index 7d61b3279798..bbdd6058cd2f 100644 --- a/drivers/net/can/usb/peak_usb/pcan_usb_pro.c +++ b/drivers/net/can/usb/peak_usb/pcan_usb_pro.c @@ -1004,6 +1004,18 @@ int pcan_usb_pro_probe(struct usb_interface *intf) /* * describe the PCAN-USB Pro adapter */ +static const struct can_bittiming_const pcan_usb_pro_const = { + .name = "pcan_usb_pro", + .tseg1_min = 1, + .tseg1_max = 16, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 1024, + .brp_inc = 1, +}; + const struct peak_usb_adapter pcan_usb_pro = { .name = "PCAN-USB Pro", .device_id = PCAN_USBPRO_PRODUCT_ID, @@ -1012,17 +1024,7 @@ const struct peak_usb_adapter pcan_usb_pro = { .clock = { .freq = PCAN_USBPRO_CRYSTAL_HZ, }, - .bittiming_const = { - .name = "pcan_usb_pro", - .tseg1_min = 1, - .tseg1_max = 16, - .tseg2_min = 1, - .tseg2_max = 8, - .sjw_max = 4, - .brp_min = 1, - .brp_max = 1024, - .brp_inc = 1, - }, + .bittiming_const = &pcan_usb_pro_const, /* size of device private data */ .sizeof_dev_private = sizeof(struct pcan_usb_pro_device), diff --git a/drivers/net/ethernet/3com/3c59x.c b/drivers/net/ethernet/3com/3c59x.c index 2d1ce3c5d0dd..753887d02b46 100644 --- a/drivers/net/ethernet/3com/3c59x.c +++ b/drivers/net/ethernet/3com/3c59x.c @@ -1763,16 +1763,9 @@ vortex_open(struct net_device *dev) vp->rx_ring[i].addr = cpu_to_le32(pci_map_single(VORTEX_PCI(vp), skb->data, PKT_BUF_SZ, PCI_DMA_FROMDEVICE)); } if (i != RX_RING_SIZE) { - int j; pr_emerg("%s: no memory for rx ring\n", dev->name); - for (j = 0; j < i; j++) { - if (vp->rx_skbuff[j]) { - dev_kfree_skb(vp->rx_skbuff[j]); - vp->rx_skbuff[j] = NULL; - } - } retval = -ENOMEM; - goto err_free_irq; + goto err_free_skb; } /* Wrap the ring. */ vp->rx_ring[i-1].next = cpu_to_le32(vp->rx_ring_dma); @@ -1782,7 +1775,13 @@ vortex_open(struct net_device *dev) if (!retval) goto out; -err_free_irq: +err_free_skb: + for (i = 0; i < RX_RING_SIZE; i++) { + if (vp->rx_skbuff[i]) { + dev_kfree_skb(vp->rx_skbuff[i]); + vp->rx_skbuff[i] = NULL; + } + } free_irq(dev->irq, dev); err: if (vortex_debug > 1) diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile index c51014b0464f..b52e0f63f9a3 100644 --- a/drivers/net/ethernet/Makefile +++ b/drivers/net/ethernet/Makefile @@ -65,7 +65,7 @@ obj-$(CONFIG_NET_VENDOR_PASEMI) += pasemi/ obj-$(CONFIG_NET_VENDOR_QLOGIC) += qlogic/ obj-$(CONFIG_NET_VENDOR_QUALCOMM) += qualcomm/ obj-$(CONFIG_NET_VENDOR_REALTEK) += realtek/ -obj-$(CONFIG_SH_ETH) += renesas/ +obj-$(CONFIG_NET_VENDOR_RENESAS) += renesas/ obj-$(CONFIG_NET_VENDOR_RDC) += rdc/ obj-$(CONFIG_NET_VENDOR_ROCKER) += rocker/ obj-$(CONFIG_NET_VENDOR_SAMSUNG) += samsung/ diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c index a626c4315a89..cfa37041ab71 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_hw.c @@ -801,6 +801,9 @@ int xgene_enet_mdio_config(struct xgene_enet_pdata *pdata) void xgene_enet_mdio_remove(struct xgene_enet_pdata *pdata) { + if (pdata->phy_dev) + phy_disconnect(pdata->phy_dev); + mdiobus_unregister(pdata->mdio_bus); mdiobus_free(pdata->mdio_bus); pdata->mdio_bus = NULL; diff --git a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c index 299eb4315fe6..a02ea7f8fdae 100644 --- a/drivers/net/ethernet/apm/xgene/xgene_enet_main.c +++ b/drivers/net/ethernet/apm/xgene/xgene_enet_main.c @@ -1277,9 +1277,10 @@ static int xgene_enet_remove(struct platform_device *pdev) mac_ops->tx_disable(pdata); xgene_enet_napi_del(pdata); - xgene_enet_mdio_remove(pdata); - xgene_enet_delete_desc_rings(pdata); + if (pdata->phy_mode == PHY_INTERFACE_MODE_RGMII) + xgene_enet_mdio_remove(pdata); unregister_netdev(ndev); + xgene_enet_delete_desc_rings(pdata); pdata->port_ops->shutdown(pdata); free_netdev(ndev); diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c index a90d7364334f..f7fbdc9d1325 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_cmn.c @@ -262,9 +262,9 @@ static u16 bnx2x_free_tx_pkt(struct bnx2x *bp, struct bnx2x_fp_txdata *txdata, if (likely(skb)) { (*pkts_compl)++; (*bytes_compl) += skb->len; + dev_kfree_skb_any(skb); } - dev_kfree_skb_any(skb); tx_buf->first_bd = 0; tx_buf->skb = NULL; diff --git a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c index 76b9052a961c..5907c821d131 100644 --- a/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c +++ b/drivers/net/ethernet/broadcom/bnx2x/bnx2x_ethtool.c @@ -1718,6 +1718,22 @@ static int bnx2x_nvram_write(struct bnx2x *bp, u32 offset, u8 *data_buf, offset += sizeof(u32); data_buf += sizeof(u32); written_so_far += sizeof(u32); + + /* At end of each 4Kb page, release nvram lock to allow MFW + * chance to take it for its own use. + */ + if ((cmd_flags & MCPR_NVM_COMMAND_LAST) && + (written_so_far < buf_size)) { + DP(BNX2X_MSG_ETHTOOL | BNX2X_MSG_NVM, + "Releasing NVM lock after offset 0x%x\n", + (u32)(offset - sizeof(u32))); + bnx2x_release_nvram_lock(bp); + usleep_range(1000, 2000); + rc = bnx2x_acquire_nvram_lock(bp); + if (rc) + return rc; + } + cmd_flags = 0; } diff --git a/drivers/net/ethernet/broadcom/genet/bcmgenet.c b/drivers/net/ethernet/broadcom/genet/bcmgenet.c index 64c1e9db6b0b..09ff09f828d0 100644 --- a/drivers/net/ethernet/broadcom/genet/bcmgenet.c +++ b/drivers/net/ethernet/broadcom/genet/bcmgenet.c @@ -2126,6 +2126,8 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) int ret = 0; int timeout = 0; u32 reg; + u32 dma_ctrl; + int i; /* Disable TDMA to stop add more frames in TX DMA */ reg = bcmgenet_tdma_readl(priv, DMA_CTRL); @@ -2169,6 +2171,20 @@ static int bcmgenet_dma_teardown(struct bcmgenet_priv *priv) ret = -ETIMEDOUT; } + dma_ctrl = 0; + for (i = 0; i < priv->hw_params->rx_queues; i++) + dma_ctrl |= (1 << (i + DMA_RING_BUF_EN_SHIFT)); + reg = bcmgenet_rdma_readl(priv, DMA_CTRL); + reg &= ~dma_ctrl; + bcmgenet_rdma_writel(priv, reg, DMA_CTRL); + + dma_ctrl = 0; + for (i = 0; i < priv->hw_params->tx_queues; i++) + dma_ctrl |= (1 << (i + DMA_RING_BUF_EN_SHIFT)); + reg = bcmgenet_tdma_readl(priv, DMA_CTRL); + reg &= ~dma_ctrl; + bcmgenet_tdma_writel(priv, reg, DMA_CTRL); + return ret; } @@ -2820,8 +2836,6 @@ static void bcmgenet_timeout(struct net_device *dev) netif_dbg(priv, tx_err, dev, "bcmgenet_timeout\n"); - bcmgenet_disable_tx_napi(priv); - for (q = 0; q < priv->hw_params->tx_queues; q++) bcmgenet_dump_tx_queue(&priv->tx_rings[q]); bcmgenet_dump_tx_queue(&priv->tx_rings[DESC_INDEX]); @@ -2837,8 +2851,6 @@ static void bcmgenet_timeout(struct net_device *dev) bcmgenet_intrl2_0_writel(priv, int0_enable, INTRL2_CPU_MASK_CLEAR); bcmgenet_intrl2_1_writel(priv, int1_enable, INTRL2_CPU_MASK_CLEAR); - bcmgenet_enable_tx_napi(priv); - dev->trans_start = jiffies; dev->stats.tx_errors++; diff --git a/drivers/net/ethernet/brocade/bna/bnad.c b/drivers/net/ethernet/brocade/bna/bnad.c index 0612b19f6313..506047c38607 100644 --- a/drivers/net/ethernet/brocade/bna/bnad.c +++ b/drivers/net/ethernet/brocade/bna/bnad.c @@ -676,6 +676,7 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget) if (!next_cmpl->valid) break; } + packets++; /* TODO: BNA_CQ_EF_LOCAL ? */ if (unlikely(flags & (BNA_CQ_EF_MAC_ERROR | @@ -692,7 +693,6 @@ bnad_cq_process(struct bnad *bnad, struct bna_ccb *ccb, int budget) else bnad_cq_setup_skb_frags(rcb, skb, sop_ci, nvecs, len); - packets++; rcb->rxq->rx_packets++; rcb->rxq->rx_bytes += totlen; ccb->bytes_per_intr += totlen; diff --git a/drivers/net/ethernet/cavium/Kconfig b/drivers/net/ethernet/cavium/Kconfig index c4d6bbe9458d..02e23e6f1424 100644 --- a/drivers/net/ethernet/cavium/Kconfig +++ b/drivers/net/ethernet/cavium/Kconfig @@ -16,7 +16,6 @@ if NET_VENDOR_CAVIUM config THUNDER_NIC_PF tristate "Thunder Physical function driver" depends on 64BIT - default ARCH_THUNDER select THUNDER_NIC_BGX ---help--- This driver supports Thunder's NIC physical function. @@ -29,14 +28,12 @@ config THUNDER_NIC_PF config THUNDER_NIC_VF tristate "Thunder Virtual function driver" depends on 64BIT - default ARCH_THUNDER ---help--- This driver supports Thunder's NIC virtual function config THUNDER_NIC_BGX tristate "Thunder MAC interface driver (BGX)" depends on 64BIT - default ARCH_THUNDER ---help--- This driver supports programming and controlling of MAC interface from NIC physical function driver. diff --git a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c index a11485fbb33f..c3c7db41819d 100644 --- a/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c +++ b/drivers/net/ethernet/chelsio/cxgb4/cxgb4_debugfs.c @@ -2332,10 +2332,11 @@ int t4_setup_debugfs(struct adapter *adap) EXT_MEM1_SIZE_G(size)); } } else { - if (i & EXT_MEM_ENABLE_F) + if (i & EXT_MEM_ENABLE_F) { size = t4_read_reg(adap, MA_EXT_MEMORY_BAR_A); add_debugfs_mem(adap, "mc", MEM_MC, EXT_MEM_SIZE_G(size)); + } } de = debugfs_create_file_size("flash", S_IRUSR, adap->debugfs_root, adap, diff --git a/drivers/net/ethernet/emulex/benet/be_cmds.h b/drivers/net/ethernet/emulex/benet/be_cmds.h index 2716e6f30d9a..00e3a6b6b822 100644 --- a/drivers/net/ethernet/emulex/benet/be_cmds.h +++ b/drivers/net/ethernet/emulex/benet/be_cmds.h @@ -620,6 +620,11 @@ enum be_if_flags { BE_IF_FLAGS_VLAN_PROMISCUOUS |\ BE_IF_FLAGS_MCAST_PROMISCUOUS) +#define BE_IF_EN_FLAGS (BE_IF_FLAGS_BROADCAST | BE_IF_FLAGS_PASS_L3L4_ERRORS |\ + BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_UNTAGGED) + +#define BE_IF_ALL_FILT_FLAGS (BE_IF_EN_FLAGS | BE_IF_FLAGS_ALL_PROMISCUOUS) + /* An RX interface is an object with one or more MAC addresses and * filtering capabilities. */ struct be_cmd_req_if_create { diff --git a/drivers/net/ethernet/emulex/benet/be_main.c b/drivers/net/ethernet/emulex/benet/be_main.c index 6f642426308c..6ca693b03f33 100644 --- a/drivers/net/ethernet/emulex/benet/be_main.c +++ b/drivers/net/ethernet/emulex/benet/be_main.c @@ -273,6 +273,10 @@ static int be_mac_addr_set(struct net_device *netdev, void *p) if (ether_addr_equal(addr->sa_data, netdev->dev_addr)) return 0; + /* if device is not running, copy MAC to netdev->dev_addr */ + if (!netif_running(netdev)) + goto done; + /* The PMAC_ADD cmd may fail if the VF doesn't have FILTMGMT * privilege or if PF did not provision the new MAC address. * On BE3, this cmd will always fail if the VF doesn't have the @@ -307,9 +311,9 @@ static int be_mac_addr_set(struct net_device *netdev, void *p) status = -EPERM; goto err; } - - memcpy(netdev->dev_addr, addr->sa_data, netdev->addr_len); - dev_info(dev, "MAC address changed to %pM\n", mac); +done: + ether_addr_copy(netdev->dev_addr, addr->sa_data); + dev_info(dev, "MAC address changed to %pM\n", addr->sa_data); return 0; err: dev_warn(dev, "MAC address change to %pM failed\n", addr->sa_data); @@ -2447,10 +2451,24 @@ static void be_eq_clean(struct be_eq_obj *eqo) be_eq_notify(eqo->adapter, eqo->q.id, false, true, num, 0); } -static void be_rx_cq_clean(struct be_rx_obj *rxo) +/* Free posted rx buffers that were not used */ +static void be_rxq_clean(struct be_rx_obj *rxo) { - struct be_rx_page_info *page_info; struct be_queue_info *rxq = &rxo->q; + struct be_rx_page_info *page_info; + + while (atomic_read(&rxq->used) > 0) { + page_info = get_rx_page_info(rxo); + put_page(page_info->page); + memset(page_info, 0, sizeof(*page_info)); + } + BUG_ON(atomic_read(&rxq->used)); + rxq->tail = 0; + rxq->head = 0; +} + +static void be_rx_cq_clean(struct be_rx_obj *rxo) +{ struct be_queue_info *rx_cq = &rxo->cq; struct be_rx_compl_info *rxcp; struct be_adapter *adapter = rxo->adapter; @@ -2487,16 +2505,6 @@ static void be_rx_cq_clean(struct be_rx_obj *rxo) /* After cleanup, leave the CQ in unarmed state */ be_cq_notify(adapter, rx_cq->id, false, 0); - - /* Then free posted rx buffers that were not used */ - while (atomic_read(&rxq->used) > 0) { - page_info = get_rx_page_info(rxo); - put_page(page_info->page); - memset(page_info, 0, sizeof(*page_info)); - } - BUG_ON(atomic_read(&rxq->used)); - rxq->tail = 0; - rxq->head = 0; } static void be_tx_compl_clean(struct be_adapter *adapter) @@ -2576,8 +2584,8 @@ static void be_evt_queues_destroy(struct be_adapter *adapter) be_cmd_q_destroy(adapter, &eqo->q, QTYPE_EQ); napi_hash_del(&eqo->napi); netif_napi_del(&eqo->napi); + free_cpumask_var(eqo->affinity_mask); } - free_cpumask_var(eqo->affinity_mask); be_queue_free(adapter, &eqo->q); } } @@ -2594,13 +2602,7 @@ static int be_evt_queues_create(struct be_adapter *adapter) for_all_evt_queues(adapter, eqo, i) { int numa_node = dev_to_node(&adapter->pdev->dev); - if (!zalloc_cpumask_var(&eqo->affinity_mask, GFP_KERNEL)) - return -ENOMEM; - cpumask_set_cpu(cpumask_local_spread(i, numa_node), - eqo->affinity_mask); - netif_napi_add(adapter->netdev, &eqo->napi, be_poll, - BE_NAPI_WEIGHT); - napi_hash_add(&eqo->napi); + aic = &adapter->aic_obj[i]; eqo->adapter = adapter; eqo->idx = i; @@ -2616,6 +2618,14 @@ static int be_evt_queues_create(struct be_adapter *adapter) rc = be_cmd_eq_create(adapter, eqo); if (rc) return rc; + + if (!zalloc_cpumask_var(&eqo->affinity_mask, GFP_KERNEL)) + return -ENOMEM; + cpumask_set_cpu(cpumask_local_spread(i, numa_node), + eqo->affinity_mask); + netif_napi_add(adapter->netdev, &eqo->napi, be_poll, + BE_NAPI_WEIGHT); + napi_hash_add(&eqo->napi); } return 0; } @@ -3354,13 +3364,54 @@ static void be_rx_qs_destroy(struct be_adapter *adapter) for_all_rx_queues(adapter, rxo, i) { q = &rxo->q; if (q->created) { + /* If RXQs are destroyed while in an "out of buffer" + * state, there is a possibility of an HW stall on + * Lancer. So, post 64 buffers to each queue to relieve + * the "out of buffer" condition. + * Make sure there's space in the RXQ before posting. + */ + if (lancer_chip(adapter)) { + be_rx_cq_clean(rxo); + if (atomic_read(&q->used) == 0) + be_post_rx_frags(rxo, GFP_KERNEL, + MAX_RX_POST); + } + be_cmd_rxq_destroy(adapter, q); be_rx_cq_clean(rxo); + be_rxq_clean(rxo); } be_queue_free(adapter, q); } } +static void be_disable_if_filters(struct be_adapter *adapter) +{ + be_cmd_pmac_del(adapter, adapter->if_handle, + adapter->pmac_id[0], 0); + + be_clear_uc_list(adapter); + + /* The IFACE flags are enabled in the open path and cleared + * in the close path. When a VF gets detached from the host and + * assigned to a VM the following happens: + * - VF's IFACE flags get cleared in the detach path + * - IFACE create is issued by the VF in the attach path + * Due to a bug in the BE3/Skyhawk-R FW + * (Lancer FW doesn't have the bug), the IFACE capability flags + * specified along with the IFACE create cmd issued by a VF are not + * honoured by FW. As a consequence, if a *new* driver + * (that enables/disables IFACE flags in open/close) + * is loaded in the host and an *old* driver is * used by a VM/VF, + * the IFACE gets created *without* the needed flags. + * To avoid this, disable RX-filter flags only for Lancer. + */ + if (lancer_chip(adapter)) { + be_cmd_rx_filter(adapter, BE_IF_ALL_FILT_FLAGS, OFF); + adapter->if_flags &= ~BE_IF_ALL_FILT_FLAGS; + } +} + static int be_close(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -3373,6 +3424,8 @@ static int be_close(struct net_device *netdev) if (!(adapter->flags & BE_FLAGS_SETUP_DONE)) return 0; + be_disable_if_filters(adapter); + be_roce_dev_close(adapter); if (adapter->flags & BE_FLAGS_NAPI_ENABLED) { @@ -3392,7 +3445,6 @@ static int be_close(struct net_device *netdev) be_tx_compl_clean(adapter); be_rx_qs_destroy(adapter); - be_clear_uc_list(adapter); for_all_evt_queues(adapter, eqo, i) { if (msix_enabled(adapter)) @@ -3477,6 +3529,31 @@ static int be_rx_qs_create(struct be_adapter *adapter) return 0; } +static int be_enable_if_filters(struct be_adapter *adapter) +{ + int status; + + status = be_cmd_rx_filter(adapter, BE_IF_EN_FLAGS, ON); + if (status) + return status; + + /* For BE3 VFs, the PF programs the initial MAC address */ + if (!(BEx_chip(adapter) && be_virtfn(adapter))) { + status = be_cmd_pmac_add(adapter, adapter->netdev->dev_addr, + adapter->if_handle, + &adapter->pmac_id[0], 0); + if (status) + return status; + } + + if (adapter->vlans_added) + be_vid_config(adapter); + + be_set_rx_mode(adapter->netdev); + + return 0; +} + static int be_open(struct net_device *netdev) { struct be_adapter *adapter = netdev_priv(netdev); @@ -3490,6 +3567,10 @@ static int be_open(struct net_device *netdev) if (status) goto err; + status = be_enable_if_filters(adapter); + if (status) + goto err; + status = be_irq_register(adapter); if (status) goto err; @@ -3686,16 +3767,6 @@ static void be_cancel_err_detection(struct be_adapter *adapter) } } -static void be_mac_clear(struct be_adapter *adapter) -{ - if (adapter->pmac_id) { - be_cmd_pmac_del(adapter, adapter->if_handle, - adapter->pmac_id[0], 0); - kfree(adapter->pmac_id); - adapter->pmac_id = NULL; - } -} - #ifdef CONFIG_BE2NET_VXLAN static void be_disable_vxlan_offloads(struct be_adapter *adapter) { @@ -3770,8 +3841,8 @@ static int be_clear(struct be_adapter *adapter) #ifdef CONFIG_BE2NET_VXLAN be_disable_vxlan_offloads(adapter); #endif - /* delete the primary mac along with the uc-mac list */ - be_mac_clear(adapter); + kfree(adapter->pmac_id); + adapter->pmac_id = NULL; be_cmd_if_destroy(adapter, adapter->if_handle, 0); @@ -3782,25 +3853,11 @@ static int be_clear(struct be_adapter *adapter) return 0; } -static int be_if_create(struct be_adapter *adapter, u32 *if_handle, - u32 cap_flags, u32 vf) -{ - u32 en_flags; - - en_flags = BE_IF_FLAGS_UNTAGGED | BE_IF_FLAGS_BROADCAST | - BE_IF_FLAGS_MULTICAST | BE_IF_FLAGS_PASS_L3L4_ERRORS | - BE_IF_FLAGS_RSS | BE_IF_FLAGS_DEFQ_RSS; - - en_flags &= cap_flags; - - return be_cmd_if_create(adapter, cap_flags, en_flags, if_handle, vf); -} - static int be_vfs_if_create(struct be_adapter *adapter) { struct be_resources res = {0}; + u32 cap_flags, en_flags, vf; struct be_vf_cfg *vf_cfg; - u32 cap_flags, vf; int status; /* If a FW profile exists, then cap_flags are updated */ @@ -3821,8 +3878,12 @@ static int be_vfs_if_create(struct be_adapter *adapter) } } - status = be_if_create(adapter, &vf_cfg->if_handle, - cap_flags, vf + 1); + en_flags = cap_flags & (BE_IF_FLAGS_UNTAGGED | + BE_IF_FLAGS_BROADCAST | + BE_IF_FLAGS_MULTICAST | + BE_IF_FLAGS_PASS_L3L4_ERRORS); + status = be_cmd_if_create(adapter, cap_flags, en_flags, + &vf_cfg->if_handle, vf + 1); if (status) return status; } @@ -4194,15 +4255,8 @@ static int be_mac_setup(struct be_adapter *adapter) memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN); memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); - } else { - /* Maybe the HW was reset; dev_addr must be re-programmed */ - memcpy(mac, adapter->netdev->dev_addr, ETH_ALEN); } - /* For BE3-R VFs, the PF programs the initial MAC address */ - if (!(BEx_chip(adapter) && be_virtfn(adapter))) - be_cmd_pmac_add(adapter, mac, adapter->if_handle, - &adapter->pmac_id[0], 0); return 0; } @@ -4342,6 +4396,7 @@ static int be_func_init(struct be_adapter *adapter) static int be_setup(struct be_adapter *adapter) { struct device *dev = &adapter->pdev->dev; + u32 en_flags; int status; status = be_func_init(adapter); @@ -4364,8 +4419,11 @@ static int be_setup(struct be_adapter *adapter) if (status) goto err; - status = be_if_create(adapter, &adapter->if_handle, - be_if_cap_flags(adapter), 0); + /* will enable all the needed filter flags in be_open() */ + en_flags = BE_IF_FLAGS_RSS | BE_IF_FLAGS_DEFQ_RSS; + en_flags = en_flags & be_if_cap_flags(adapter); + status = be_cmd_if_create(adapter, be_if_cap_flags(adapter), en_flags, + &adapter->if_handle, 0); if (status) goto err; @@ -4391,11 +4449,6 @@ static int be_setup(struct be_adapter *adapter) dev_err(dev, "Please upgrade firmware to version >= 4.0\n"); } - if (adapter->vlans_added) - be_vid_config(adapter); - - be_set_rx_mode(adapter->netdev); - status = be_cmd_set_flow_control(adapter, adapter->tx_fc, adapter->rx_fc); if (status) @@ -5121,7 +5174,7 @@ static void be_add_vxlan_port(struct net_device *netdev, sa_family_t sa_family, struct device *dev = &adapter->pdev->dev; int status; - if (lancer_chip(adapter) || BEx_chip(adapter)) + if (lancer_chip(adapter) || BEx_chip(adapter) || be_is_mc(adapter)) return; if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) { @@ -5168,7 +5221,7 @@ static void be_del_vxlan_port(struct net_device *netdev, sa_family_t sa_family, { struct be_adapter *adapter = netdev_priv(netdev); - if (lancer_chip(adapter) || BEx_chip(adapter)) + if (lancer_chip(adapter) || BEx_chip(adapter) || be_is_mc(adapter)) return; if (adapter->vxlan_port != port) diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 32e3807c650e..b349e6f36ea7 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -1778,7 +1778,7 @@ static int fec_enet_mdio_read(struct mii_bus *bus, int mii_id, int regnum) return ret; fep->mii_timeout = 0; - init_completion(&fep->mdio_done); + reinit_completion(&fep->mdio_done); /* start a read op */ writel(FEC_MMFR_ST | FEC_MMFR_OP_READ | @@ -1817,7 +1817,7 @@ static int fec_enet_mdio_write(struct mii_bus *bus, int mii_id, int regnum, return ret; fep->mii_timeout = 0; - init_completion(&fep->mdio_done); + reinit_completion(&fep->mdio_done); /* start a write op */ writel(FEC_MMFR_ST | FEC_MMFR_OP_WRITE | @@ -3433,6 +3433,7 @@ fec_probe(struct platform_device *pdev) pm_runtime_set_autosuspend_delay(&pdev->dev, FEC_MDIO_PM_TIMEOUT); pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_get_noresume(&pdev->dev); pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); diff --git a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c index 56316db6c5a6..cf8e54652df9 100644 --- a/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c +++ b/drivers/net/ethernet/freescale/fs_enet/fs_enet-main.c @@ -586,7 +586,8 @@ static int fs_enet_start_xmit(struct sk_buff *skb, struct net_device *dev) frag = skb_shinfo(skb)->frags; while (nr_frags) { CBDC_SC(bdp, - BD_ENET_TX_STATS | BD_ENET_TX_LAST | BD_ENET_TX_TC); + BD_ENET_TX_STATS | BD_ENET_TX_INTR | BD_ENET_TX_LAST | + BD_ENET_TX_TC); CBDS_SC(bdp, BD_ENET_TX_READY); if ((CBDR_SC(bdp) & BD_ENET_TX_WRAP) == 0) diff --git a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c index b34214e2df5f..016743e355de 100644 --- a/drivers/net/ethernet/freescale/fs_enet/mac-fec.c +++ b/drivers/net/ethernet/freescale/fs_enet/mac-fec.c @@ -110,7 +110,7 @@ static int do_pd_setup(struct fs_enet_private *fep) } #define FEC_NAPI_RX_EVENT_MSK (FEC_ENET_RXF | FEC_ENET_RXB) -#define FEC_NAPI_TX_EVENT_MSK (FEC_ENET_TXF | FEC_ENET_TXB) +#define FEC_NAPI_TX_EVENT_MSK (FEC_ENET_TXF) #define FEC_RX_EVENT (FEC_ENET_RXF) #define FEC_TX_EVENT (FEC_ENET_TXF) #define FEC_ERR_EVENT_MSK (FEC_ENET_HBERR | FEC_ENET_BABR | \ diff --git a/drivers/net/ethernet/freescale/gianfar.c b/drivers/net/ethernet/freescale/gianfar.c index 2b7610f341b0..10b3bbbbac8e 100644 --- a/drivers/net/ethernet/freescale/gianfar.c +++ b/drivers/net/ethernet/freescale/gianfar.c @@ -2102,6 +2102,11 @@ int startup_gfar(struct net_device *ndev) /* Start Rx/Tx DMA and enable the interrupts */ gfar_start(priv); + /* force link state update after mac reset */ + priv->oldlink = 0; + priv->oldspeed = 0; + priv->oldduplex = -1; + phy_start(priv->phydev); enable_napi(priv); diff --git a/drivers/net/ethernet/freescale/gianfar_ethtool.c b/drivers/net/ethernet/freescale/gianfar_ethtool.c index 3c0a8f825b63..5b90fcf96265 100644 --- a/drivers/net/ethernet/freescale/gianfar_ethtool.c +++ b/drivers/net/ethernet/freescale/gianfar_ethtool.c @@ -900,27 +900,6 @@ static int gfar_check_filer_hardware(struct gfar_private *priv) return 0; } -static int gfar_comp_asc(const void *a, const void *b) -{ - return memcmp(a, b, 4); -} - -static int gfar_comp_desc(const void *a, const void *b) -{ - return -memcmp(a, b, 4); -} - -static void gfar_swap(void *a, void *b, int size) -{ - u32 *_a = a; - u32 *_b = b; - - swap(_a[0], _b[0]); - swap(_a[1], _b[1]); - swap(_a[2], _b[2]); - swap(_a[3], _b[3]); -} - /* Write a mask to filer cache */ static void gfar_set_mask(u32 mask, struct filer_table *tab) { @@ -1270,310 +1249,6 @@ static int gfar_convert_to_filer(struct ethtool_rx_flow_spec *rule, return 0; } -/* Copy size filer entries */ -static void gfar_copy_filer_entries(struct gfar_filer_entry dst[0], - struct gfar_filer_entry src[0], s32 size) -{ - while (size > 0) { - size--; - dst[size].ctrl = src[size].ctrl; - dst[size].prop = src[size].prop; - } -} - -/* Delete the contents of the filer-table between start and end - * and collapse them - */ -static int gfar_trim_filer_entries(u32 begin, u32 end, struct filer_table *tab) -{ - int length; - - if (end > MAX_FILER_CACHE_IDX || end < begin) - return -EINVAL; - - end++; - length = end - begin; - - /* Copy */ - while (end < tab->index) { - tab->fe[begin].ctrl = tab->fe[end].ctrl; - tab->fe[begin++].prop = tab->fe[end++].prop; - - } - /* Fill up with don't cares */ - while (begin < tab->index) { - tab->fe[begin].ctrl = 0x60; - tab->fe[begin].prop = 0xFFFFFFFF; - begin++; - } - - tab->index -= length; - return 0; -} - -/* Make space on the wanted location */ -static int gfar_expand_filer_entries(u32 begin, u32 length, - struct filer_table *tab) -{ - if (length == 0 || length + tab->index > MAX_FILER_CACHE_IDX || - begin > MAX_FILER_CACHE_IDX) - return -EINVAL; - - gfar_copy_filer_entries(&(tab->fe[begin + length]), &(tab->fe[begin]), - tab->index - length + 1); - - tab->index += length; - return 0; -} - -static int gfar_get_next_cluster_start(int start, struct filer_table *tab) -{ - for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); - start++) { - if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE)) == - (RQFCR_AND | RQFCR_CLE)) - return start; - } - return -1; -} - -static int gfar_get_next_cluster_end(int start, struct filer_table *tab) -{ - for (; (start < tab->index) && (start < MAX_FILER_CACHE_IDX - 1); - start++) { - if ((tab->fe[start].ctrl & (RQFCR_AND | RQFCR_CLE)) == - (RQFCR_CLE)) - return start; - } - return -1; -} - -/* Uses hardwares clustering option to reduce - * the number of filer table entries - */ -static void gfar_cluster_filer(struct filer_table *tab) -{ - s32 i = -1, j, iend, jend; - - while ((i = gfar_get_next_cluster_start(++i, tab)) != -1) { - j = i; - while ((j = gfar_get_next_cluster_start(++j, tab)) != -1) { - /* The cluster entries self and the previous one - * (a mask) must be identical! - */ - if (tab->fe[i].ctrl != tab->fe[j].ctrl) - break; - if (tab->fe[i].prop != tab->fe[j].prop) - break; - if (tab->fe[i - 1].ctrl != tab->fe[j - 1].ctrl) - break; - if (tab->fe[i - 1].prop != tab->fe[j - 1].prop) - break; - iend = gfar_get_next_cluster_end(i, tab); - jend = gfar_get_next_cluster_end(j, tab); - if (jend == -1 || iend == -1) - break; - - /* First we make some free space, where our cluster - * element should be. Then we copy it there and finally - * delete in from its old location. - */ - if (gfar_expand_filer_entries(iend, (jend - j), tab) == - -EINVAL) - break; - - gfar_copy_filer_entries(&(tab->fe[iend + 1]), - &(tab->fe[jend + 1]), jend - j); - - if (gfar_trim_filer_entries(jend - 1, - jend + (jend - j), - tab) == -EINVAL) - return; - - /* Mask out cluster bit */ - tab->fe[iend].ctrl &= ~(RQFCR_CLE); - } - } -} - -/* Swaps the masked bits of a1<>a2 and b1<>b2 */ -static void gfar_swap_bits(struct gfar_filer_entry *a1, - struct gfar_filer_entry *a2, - struct gfar_filer_entry *b1, - struct gfar_filer_entry *b2, u32 mask) -{ - u32 temp[4]; - temp[0] = a1->ctrl & mask; - temp[1] = a2->ctrl & mask; - temp[2] = b1->ctrl & mask; - temp[3] = b2->ctrl & mask; - - a1->ctrl &= ~mask; - a2->ctrl &= ~mask; - b1->ctrl &= ~mask; - b2->ctrl &= ~mask; - - a1->ctrl |= temp[1]; - a2->ctrl |= temp[0]; - b1->ctrl |= temp[3]; - b2->ctrl |= temp[2]; -} - -/* Generate a list consisting of masks values with their start and - * end of validity and block as indicator for parts belonging - * together (glued by ANDs) in mask_table - */ -static u32 gfar_generate_mask_table(struct gfar_mask_entry *mask_table, - struct filer_table *tab) -{ - u32 i, and_index = 0, block_index = 1; - - for (i = 0; i < tab->index; i++) { - - /* LSByte of control = 0 sets a mask */ - if (!(tab->fe[i].ctrl & 0xF)) { - mask_table[and_index].mask = tab->fe[i].prop; - mask_table[and_index].start = i; - mask_table[and_index].block = block_index; - if (and_index >= 1) - mask_table[and_index - 1].end = i - 1; - and_index++; - } - /* cluster starts and ends will be separated because they should - * hold their position - */ - if (tab->fe[i].ctrl & RQFCR_CLE) - block_index++; - /* A not set AND indicates the end of a depended block */ - if (!(tab->fe[i].ctrl & RQFCR_AND)) - block_index++; - } - - mask_table[and_index - 1].end = i - 1; - - return and_index; -} - -/* Sorts the entries of mask_table by the values of the masks. - * Important: The 0xFF80 flags of the first and last entry of a - * block must hold their position (which queue, CLusterEnable, ReJEct, - * AND) - */ -static void gfar_sort_mask_table(struct gfar_mask_entry *mask_table, - struct filer_table *temp_table, u32 and_index) -{ - /* Pointer to compare function (_asc or _desc) */ - int (*gfar_comp)(const void *, const void *); - - u32 i, size = 0, start = 0, prev = 1; - u32 old_first, old_last, new_first, new_last; - - gfar_comp = &gfar_comp_desc; - - for (i = 0; i < and_index; i++) { - if (prev != mask_table[i].block) { - old_first = mask_table[start].start + 1; - old_last = mask_table[i - 1].end; - sort(mask_table + start, size, - sizeof(struct gfar_mask_entry), - gfar_comp, &gfar_swap); - - /* Toggle order for every block. This makes the - * thing more efficient! - */ - if (gfar_comp == gfar_comp_desc) - gfar_comp = &gfar_comp_asc; - else - gfar_comp = &gfar_comp_desc; - - new_first = mask_table[start].start + 1; - new_last = mask_table[i - 1].end; - - gfar_swap_bits(&temp_table->fe[new_first], - &temp_table->fe[old_first], - &temp_table->fe[new_last], - &temp_table->fe[old_last], - RQFCR_QUEUE | RQFCR_CLE | - RQFCR_RJE | RQFCR_AND); - - start = i; - size = 0; - } - size++; - prev = mask_table[i].block; - } -} - -/* Reduces the number of masks needed in the filer table to save entries - * This is done by sorting the masks of a depended block. A depended block is - * identified by gluing ANDs or CLE. The sorting order toggles after every - * block. Of course entries in scope of a mask must change their location with - * it. - */ -static int gfar_optimize_filer_masks(struct filer_table *tab) -{ - struct filer_table *temp_table; - struct gfar_mask_entry *mask_table; - - u32 and_index = 0, previous_mask = 0, i = 0, j = 0, size = 0; - s32 ret = 0; - - /* We need a copy of the filer table because - * we want to change its order - */ - temp_table = kmemdup(tab, sizeof(*temp_table), GFP_KERNEL); - if (temp_table == NULL) - return -ENOMEM; - - mask_table = kcalloc(MAX_FILER_CACHE_IDX / 2 + 1, - sizeof(struct gfar_mask_entry), GFP_KERNEL); - - if (mask_table == NULL) { - ret = -ENOMEM; - goto end; - } - - and_index = gfar_generate_mask_table(mask_table, tab); - - gfar_sort_mask_table(mask_table, temp_table, and_index); - - /* Now we can copy the data from our duplicated filer table to - * the real one in the order the mask table says - */ - for (i = 0; i < and_index; i++) { - size = mask_table[i].end - mask_table[i].start + 1; - gfar_copy_filer_entries(&(tab->fe[j]), - &(temp_table->fe[mask_table[i].start]), size); - j += size; - } - - /* And finally we just have to check for duplicated masks and drop the - * second ones - */ - for (i = 0; i < tab->index && i < MAX_FILER_CACHE_IDX; i++) { - if (tab->fe[i].ctrl == 0x80) { - previous_mask = i++; - break; - } - } - for (; i < tab->index && i < MAX_FILER_CACHE_IDX; i++) { - if (tab->fe[i].ctrl == 0x80) { - if (tab->fe[i].prop == tab->fe[previous_mask].prop) { - /* Two identical ones found! - * So drop the second one! - */ - gfar_trim_filer_entries(i, i, tab); - } else - /* Not identical! */ - previous_mask = i; - } - } - - kfree(mask_table); -end: kfree(temp_table); - return ret; -} - /* Write the bit-pattern from software's buffer to hardware registers */ static int gfar_write_filer_table(struct gfar_private *priv, struct filer_table *tab) @@ -1583,11 +1258,10 @@ static int gfar_write_filer_table(struct gfar_private *priv, return -EBUSY; /* Fill regular entries */ - for (; i < MAX_FILER_IDX - 1 && (tab->fe[i].ctrl | tab->fe[i].prop); - i++) + for (; i < MAX_FILER_IDX && (tab->fe[i].ctrl | tab->fe[i].prop); i++) gfar_write_filer(priv, i, tab->fe[i].ctrl, tab->fe[i].prop); /* Fill the rest with fall-troughs */ - for (; i < MAX_FILER_IDX - 1; i++) + for (; i < MAX_FILER_IDX; i++) gfar_write_filer(priv, i, 0x60, 0xFFFFFFFF); /* Last entry must be default accept * because that's what people expect @@ -1621,7 +1295,6 @@ static int gfar_process_filer_changes(struct gfar_private *priv) { struct ethtool_flow_spec_container *j; struct filer_table *tab; - s32 i = 0; s32 ret = 0; /* So index is set to zero, too! */ @@ -1646,17 +1319,6 @@ static int gfar_process_filer_changes(struct gfar_private *priv) } } - i = tab->index; - - /* Optimizations to save entries */ - gfar_cluster_filer(tab); - gfar_optimize_filer_masks(tab); - - pr_debug("\tSummary:\n" - "\tData on hardware: %d\n" - "\tCompression rate: %d%%\n", - tab->index, 100 - (100 * tab->index) / i); - /* Write everything to hardware */ ret = gfar_write_filer_table(priv, tab); if (ret == -EBUSY) { @@ -1722,13 +1384,14 @@ static int gfar_add_cls(struct gfar_private *priv, } process: + priv->rx_list.count++; ret = gfar_process_filer_changes(priv); if (ret) goto clean_list; - priv->rx_list.count++; return ret; clean_list: + priv->rx_list.count--; list_del(&temp->list); clean_mem: kfree(temp); diff --git a/drivers/net/ethernet/intel/fm10k/fm10k_main.c b/drivers/net/ethernet/intel/fm10k/fm10k_main.c index 982fdcdc795b..b5b2925103ec 100644 --- a/drivers/net/ethernet/intel/fm10k/fm10k_main.c +++ b/drivers/net/ethernet/intel/fm10k/fm10k_main.c @@ -216,7 +216,7 @@ static void fm10k_reuse_rx_page(struct fm10k_ring *rx_ring, static inline bool fm10k_page_is_reserved(struct page *page) { - return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc; + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); } static bool fm10k_can_reuse_rx_page(struct fm10k_rx_buffer *rx_buffer, diff --git a/drivers/net/ethernet/intel/igb/igb_main.c b/drivers/net/ethernet/intel/igb/igb_main.c index 2f70a9b152bd..830466c49987 100644 --- a/drivers/net/ethernet/intel/igb/igb_main.c +++ b/drivers/net/ethernet/intel/igb/igb_main.c @@ -6566,7 +6566,7 @@ static void igb_reuse_rx_page(struct igb_ring *rx_ring, static inline bool igb_page_is_reserved(struct page *page) { - return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc; + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); } static bool igb_can_reuse_rx_page(struct igb_rx_buffer *rx_buffer, diff --git a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c index 9aa6104e34ea..ae21e0b06c3a 100644 --- a/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c +++ b/drivers/net/ethernet/intel/ixgbe/ixgbe_main.c @@ -1832,7 +1832,7 @@ static void ixgbe_reuse_rx_page(struct ixgbe_ring *rx_ring, static inline bool ixgbe_page_is_reserved(struct page *page) { - return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc; + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); } /** diff --git a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c index e71cdde9cb01..1d7b00b038a2 100644 --- a/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c +++ b/drivers/net/ethernet/intel/ixgbevf/ixgbevf_main.c @@ -765,7 +765,7 @@ static void ixgbevf_reuse_rx_page(struct ixgbevf_ring *rx_ring, static inline bool ixgbevf_page_is_reserved(struct page *page) { - return (page_to_nid(page) != numa_mem_id()) || page->pfmemalloc; + return (page_to_nid(page) != numa_mem_id()) || page_is_pfmemalloc(page); } /** diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c index 3e8b1bfb1f2e..d9884fd15b45 100644 --- a/drivers/net/ethernet/marvell/mvpp2.c +++ b/drivers/net/ethernet/marvell/mvpp2.c @@ -27,6 +27,8 @@ #include <linux/of_address.h> #include <linux/phy.h> #include <linux/clk.h> +#include <linux/hrtimer.h> +#include <linux/ktime.h> #include <uapi/linux/ppp_defs.h> #include <net/ip.h> #include <net/ipv6.h> @@ -299,6 +301,7 @@ /* Coalescing */ #define MVPP2_TXDONE_COAL_PKTS_THRESH 15 +#define MVPP2_TXDONE_HRTIMER_PERIOD_NS 1000000UL #define MVPP2_RX_COAL_PKTS 32 #define MVPP2_RX_COAL_USEC 100 @@ -660,6 +663,14 @@ struct mvpp2_pcpu_stats { u64 tx_bytes; }; +/* Per-CPU port control */ +struct mvpp2_port_pcpu { + struct hrtimer tx_done_timer; + bool timer_scheduled; + /* Tasklet for egress finalization */ + struct tasklet_struct tx_done_tasklet; +}; + struct mvpp2_port { u8 id; @@ -679,6 +690,9 @@ struct mvpp2_port { u32 pending_cause_rx; struct napi_struct napi; + /* Per-CPU port control */ + struct mvpp2_port_pcpu __percpu *pcpu; + /* Flags */ unsigned long flags; @@ -776,6 +790,9 @@ struct mvpp2_txq_pcpu { /* Array of transmitted skb */ struct sk_buff **tx_skb; + /* Array of transmitted buffers' physical addresses */ + dma_addr_t *tx_buffs; + /* Index of last TX DMA descriptor that was inserted */ int txq_put_index; @@ -913,8 +930,6 @@ struct mvpp2_bm_pool { /* Occupied buffers indicator */ atomic_t in_use; int in_use_thresh; - - spinlock_t lock; }; struct mvpp2_buff_hdr { @@ -963,9 +978,13 @@ static void mvpp2_txq_inc_get(struct mvpp2_txq_pcpu *txq_pcpu) } static void mvpp2_txq_inc_put(struct mvpp2_txq_pcpu *txq_pcpu, - struct sk_buff *skb) + struct sk_buff *skb, + struct mvpp2_tx_desc *tx_desc) { txq_pcpu->tx_skb[txq_pcpu->txq_put_index] = skb; + if (skb) + txq_pcpu->tx_buffs[txq_pcpu->txq_put_index] = + tx_desc->buf_phys_addr; txq_pcpu->txq_put_index++; if (txq_pcpu->txq_put_index == txq_pcpu->size) txq_pcpu->txq_put_index = 0; @@ -3376,7 +3395,6 @@ static int mvpp2_bm_pool_create(struct platform_device *pdev, bm_pool->pkt_size = 0; bm_pool->buf_num = 0; atomic_set(&bm_pool->in_use, 0); - spin_lock_init(&bm_pool->lock); return 0; } @@ -3647,7 +3665,6 @@ static struct mvpp2_bm_pool * mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type, int pkt_size) { - unsigned long flags = 0; struct mvpp2_bm_pool *new_pool = &port->priv->bm_pools[pool]; int num; @@ -3656,8 +3673,6 @@ mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type, return NULL; } - spin_lock_irqsave(&new_pool->lock, flags); - if (new_pool->type == MVPP2_BM_FREE) new_pool->type = type; @@ -3686,8 +3701,6 @@ mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type, if (num != pkts_num) { WARN(1, "pool %d: %d of %d allocated\n", new_pool->id, num, pkts_num); - /* We need to undo the bufs_add() allocations */ - spin_unlock_irqrestore(&new_pool->lock, flags); return NULL; } } @@ -3695,15 +3708,12 @@ mvpp2_bm_pool_use(struct mvpp2_port *port, int pool, enum mvpp2_bm_type type, mvpp2_bm_pool_bufsize_set(port->priv, new_pool, MVPP2_RX_BUF_SIZE(new_pool->pkt_size)); - spin_unlock_irqrestore(&new_pool->lock, flags); - return new_pool; } /* Initialize pools for swf */ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) { - unsigned long flags = 0; int rxq; if (!port->pool_long) { @@ -3714,9 +3724,7 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) if (!port->pool_long) return -ENOMEM; - spin_lock_irqsave(&port->pool_long->lock, flags); port->pool_long->port_map |= (1 << port->id); - spin_unlock_irqrestore(&port->pool_long->lock, flags); for (rxq = 0; rxq < rxq_number; rxq++) mvpp2_rxq_long_pool_set(port, rxq, port->pool_long->id); @@ -3730,9 +3738,7 @@ static int mvpp2_swf_bm_pool_init(struct mvpp2_port *port) if (!port->pool_short) return -ENOMEM; - spin_lock_irqsave(&port->pool_short->lock, flags); port->pool_short->port_map |= (1 << port->id); - spin_unlock_irqrestore(&port->pool_short->lock, flags); for (rxq = 0; rxq < rxq_number; rxq++) mvpp2_rxq_short_pool_set(port, rxq, @@ -3806,7 +3812,6 @@ static void mvpp2_interrupts_unmask(void *arg) mvpp2_write(port->priv, MVPP2_ISR_RX_TX_MASK_REG(port->id), (MVPP2_CAUSE_MISC_SUM_MASK | - MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK | MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK)); } @@ -4382,23 +4387,6 @@ static void mvpp2_rx_time_coal_set(struct mvpp2_port *port, rxq->time_coal = usec; } -/* Set threshold for TX_DONE pkts coalescing */ -static void mvpp2_tx_done_pkts_coal_set(void *arg) -{ - struct mvpp2_port *port = arg; - int queue; - u32 val; - - for (queue = 0; queue < txq_number; queue++) { - struct mvpp2_tx_queue *txq = port->txqs[queue]; - - val = (txq->done_pkts_coal << MVPP2_TRANSMITTED_THRESH_OFFSET) & - MVPP2_TRANSMITTED_THRESH_MASK; - mvpp2_write(port->priv, MVPP2_TXQ_NUM_REG, txq->id); - mvpp2_write(port->priv, MVPP2_TXQ_THRESH_REG, val); - } -} - /* Free Tx queue skbuffs */ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, @@ -4407,8 +4395,8 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, int i; for (i = 0; i < num; i++) { - struct mvpp2_tx_desc *tx_desc = txq->descs + - txq_pcpu->txq_get_index; + dma_addr_t buf_phys_addr = + txq_pcpu->tx_buffs[txq_pcpu->txq_get_index]; struct sk_buff *skb = txq_pcpu->tx_skb[txq_pcpu->txq_get_index]; mvpp2_txq_inc_get(txq_pcpu); @@ -4416,8 +4404,8 @@ static void mvpp2_txq_bufs_free(struct mvpp2_port *port, if (!skb) continue; - dma_unmap_single(port->dev->dev.parent, tx_desc->buf_phys_addr, - tx_desc->data_size, DMA_TO_DEVICE); + dma_unmap_single(port->dev->dev.parent, buf_phys_addr, + skb_headlen(skb), DMA_TO_DEVICE); dev_kfree_skb_any(skb); } } @@ -4433,7 +4421,7 @@ static inline struct mvpp2_rx_queue *mvpp2_get_rx_queue(struct mvpp2_port *port, static inline struct mvpp2_tx_queue *mvpp2_get_tx_queue(struct mvpp2_port *port, u32 cause) { - int queue = fls(cause >> 16) - 1; + int queue = fls(cause) - 1; return port->txqs[queue]; } @@ -4460,6 +4448,29 @@ static void mvpp2_txq_done(struct mvpp2_port *port, struct mvpp2_tx_queue *txq, netif_tx_wake_queue(nq); } +static unsigned int mvpp2_tx_done(struct mvpp2_port *port, u32 cause) +{ + struct mvpp2_tx_queue *txq; + struct mvpp2_txq_pcpu *txq_pcpu; + unsigned int tx_todo = 0; + + while (cause) { + txq = mvpp2_get_tx_queue(port, cause); + if (!txq) + break; + + txq_pcpu = this_cpu_ptr(txq->pcpu); + + if (txq_pcpu->count) { + mvpp2_txq_done(port, txq, txq_pcpu); + tx_todo += txq_pcpu->count; + } + + cause &= ~(1 << txq->log_id); + } + return tx_todo; +} + /* Rx/Tx queue initialization/cleanup methods */ /* Allocate and initialize descriptors for aggr TXQ */ @@ -4649,12 +4660,13 @@ static int mvpp2_txq_init(struct mvpp2_port *port, txq_pcpu->tx_skb = kmalloc(txq_pcpu->size * sizeof(*txq_pcpu->tx_skb), GFP_KERNEL); - if (!txq_pcpu->tx_skb) { - dma_free_coherent(port->dev->dev.parent, - txq->size * MVPP2_DESC_ALIGNED_SIZE, - txq->descs, txq->descs_phys); - return -ENOMEM; - } + if (!txq_pcpu->tx_skb) + goto error; + + txq_pcpu->tx_buffs = kmalloc(txq_pcpu->size * + sizeof(dma_addr_t), GFP_KERNEL); + if (!txq_pcpu->tx_buffs) + goto error; txq_pcpu->count = 0; txq_pcpu->reserved_num = 0; @@ -4663,6 +4675,19 @@ static int mvpp2_txq_init(struct mvpp2_port *port, } return 0; + +error: + for_each_present_cpu(cpu) { + txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); + kfree(txq_pcpu->tx_skb); + kfree(txq_pcpu->tx_buffs); + } + + dma_free_coherent(port->dev->dev.parent, + txq->size * MVPP2_DESC_ALIGNED_SIZE, + txq->descs, txq->descs_phys); + + return -ENOMEM; } /* Free allocated TXQ resources */ @@ -4675,6 +4700,7 @@ static void mvpp2_txq_deinit(struct mvpp2_port *port, for_each_present_cpu(cpu) { txq_pcpu = per_cpu_ptr(txq->pcpu, cpu); kfree(txq_pcpu->tx_skb); + kfree(txq_pcpu->tx_buffs); } if (txq->descs) @@ -4805,7 +4831,6 @@ static int mvpp2_setup_txqs(struct mvpp2_port *port) goto err_cleanup; } - on_each_cpu(mvpp2_tx_done_pkts_coal_set, port, 1); on_each_cpu(mvpp2_txq_sent_counter_clear, port, 1); return 0; @@ -4887,6 +4912,49 @@ static void mvpp2_link_event(struct net_device *dev) } } +static void mvpp2_timer_set(struct mvpp2_port_pcpu *port_pcpu) +{ + ktime_t interval; + + if (!port_pcpu->timer_scheduled) { + port_pcpu->timer_scheduled = true; + interval = ktime_set(0, MVPP2_TXDONE_HRTIMER_PERIOD_NS); + hrtimer_start(&port_pcpu->tx_done_timer, interval, + HRTIMER_MODE_REL_PINNED); + } +} + +static void mvpp2_tx_proc_cb(unsigned long data) +{ + struct net_device *dev = (struct net_device *)data; + struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu); + unsigned int tx_todo, cause; + + if (!netif_running(dev)) + return; + port_pcpu->timer_scheduled = false; + + /* Process all the Tx queues */ + cause = (1 << txq_number) - 1; + tx_todo = mvpp2_tx_done(port, cause); + + /* Set the timer in case not all the packets were processed */ + if (tx_todo) + mvpp2_timer_set(port_pcpu); +} + +static enum hrtimer_restart mvpp2_hr_timer_cb(struct hrtimer *timer) +{ + struct mvpp2_port_pcpu *port_pcpu = container_of(timer, + struct mvpp2_port_pcpu, + tx_done_timer); + + tasklet_schedule(&port_pcpu->tx_done_tasklet); + + return HRTIMER_NORESTART; +} + /* Main RX/TX processing routines */ /* Display more error info */ @@ -5144,11 +5212,11 @@ static int mvpp2_tx_frag_process(struct mvpp2_port *port, struct sk_buff *skb, if (i == (skb_shinfo(skb)->nr_frags - 1)) { /* Last descriptor */ tx_desc->command = MVPP2_TXD_L_DESC; - mvpp2_txq_inc_put(txq_pcpu, skb); + mvpp2_txq_inc_put(txq_pcpu, skb, tx_desc); } else { /* Descriptor in the middle: Not First, Not Last */ tx_desc->command = 0; - mvpp2_txq_inc_put(txq_pcpu, NULL); + mvpp2_txq_inc_put(txq_pcpu, NULL, tx_desc); } } @@ -5214,12 +5282,12 @@ static int mvpp2_tx(struct sk_buff *skb, struct net_device *dev) /* First and Last descriptor */ tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_L_DESC; tx_desc->command = tx_cmd; - mvpp2_txq_inc_put(txq_pcpu, skb); + mvpp2_txq_inc_put(txq_pcpu, skb, tx_desc); } else { /* First but not Last */ tx_cmd |= MVPP2_TXD_F_DESC | MVPP2_TXD_PADDING_DISABLE; tx_desc->command = tx_cmd; - mvpp2_txq_inc_put(txq_pcpu, NULL); + mvpp2_txq_inc_put(txq_pcpu, NULL, tx_desc); /* Continue with other skb fragments */ if (mvpp2_tx_frag_process(port, skb, aggr_txq, txq)) { @@ -5255,6 +5323,17 @@ out: dev_kfree_skb_any(skb); } + /* Finalize TX processing */ + if (txq_pcpu->count >= txq->done_pkts_coal) + mvpp2_txq_done(port, txq, txq_pcpu); + + /* Set the timer in case not all frags were processed */ + if (txq_pcpu->count <= frags && txq_pcpu->count > 0) { + struct mvpp2_port_pcpu *port_pcpu = this_cpu_ptr(port->pcpu); + + mvpp2_timer_set(port_pcpu); + } + return NETDEV_TX_OK; } @@ -5268,10 +5347,11 @@ static inline void mvpp2_cause_error(struct net_device *dev, int cause) netdev_err(dev, "tx fifo underrun error\n"); } -static void mvpp2_txq_done_percpu(void *arg) +static int mvpp2_poll(struct napi_struct *napi, int budget) { - struct mvpp2_port *port = arg; - u32 cause_rx_tx, cause_tx, cause_misc; + u32 cause_rx_tx, cause_rx, cause_misc; + int rx_done = 0; + struct mvpp2_port *port = netdev_priv(napi->dev); /* Rx/Tx cause register * @@ -5285,7 +5365,7 @@ static void mvpp2_txq_done_percpu(void *arg) */ cause_rx_tx = mvpp2_read(port->priv, MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); - cause_tx = cause_rx_tx & MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; + cause_rx_tx &= ~MVPP2_CAUSE_TXQ_OCCUP_DESC_ALL_MASK; cause_misc = cause_rx_tx & MVPP2_CAUSE_MISC_SUM_MASK; if (cause_misc) { @@ -5297,26 +5377,6 @@ static void mvpp2_txq_done_percpu(void *arg) cause_rx_tx & ~MVPP2_CAUSE_MISC_SUM_MASK); } - /* Release TX descriptors */ - if (cause_tx) { - struct mvpp2_tx_queue *txq = mvpp2_get_tx_queue(port, cause_tx); - struct mvpp2_txq_pcpu *txq_pcpu = this_cpu_ptr(txq->pcpu); - - if (txq_pcpu->count) - mvpp2_txq_done(port, txq, txq_pcpu); - } -} - -static int mvpp2_poll(struct napi_struct *napi, int budget) -{ - u32 cause_rx_tx, cause_rx; - int rx_done = 0; - struct mvpp2_port *port = netdev_priv(napi->dev); - - on_each_cpu(mvpp2_txq_done_percpu, port, 1); - - cause_rx_tx = mvpp2_read(port->priv, - MVPP2_ISR_RX_TX_CAUSE_REG(port->id)); cause_rx = cause_rx_tx & MVPP2_CAUSE_RXQ_OCCUP_DESC_ALL_MASK; /* Process RX packets */ @@ -5561,6 +5621,8 @@ err_cleanup_rxqs: static int mvpp2_stop(struct net_device *dev) { struct mvpp2_port *port = netdev_priv(dev); + struct mvpp2_port_pcpu *port_pcpu; + int cpu; mvpp2_stop_dev(port); mvpp2_phy_disconnect(port); @@ -5569,6 +5631,13 @@ static int mvpp2_stop(struct net_device *dev) on_each_cpu(mvpp2_interrupts_mask, port, 1); free_irq(port->irq, port); + for_each_present_cpu(cpu) { + port_pcpu = per_cpu_ptr(port->pcpu, cpu); + + hrtimer_cancel(&port_pcpu->tx_done_timer); + port_pcpu->timer_scheduled = false; + tasklet_kill(&port_pcpu->tx_done_tasklet); + } mvpp2_cleanup_rxqs(port); mvpp2_cleanup_txqs(port); @@ -5784,7 +5853,6 @@ static int mvpp2_ethtool_set_coalesce(struct net_device *dev, txq->done_pkts_coal = c->tx_max_coalesced_frames; } - on_each_cpu(mvpp2_tx_done_pkts_coal_set, port, 1); return 0; } @@ -6035,6 +6103,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, { struct device_node *phy_node; struct mvpp2_port *port; + struct mvpp2_port_pcpu *port_pcpu; struct net_device *dev; struct resource *res; const char *dt_mac_addr; @@ -6044,7 +6113,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, int features; int phy_mode; int priv_common_regs_num = 2; - int err, i; + int err, i, cpu; dev = alloc_etherdev_mqs(sizeof(struct mvpp2_port), txq_number, rxq_number); @@ -6135,6 +6204,24 @@ static int mvpp2_port_probe(struct platform_device *pdev, } mvpp2_port_power_up(port); + port->pcpu = alloc_percpu(struct mvpp2_port_pcpu); + if (!port->pcpu) { + err = -ENOMEM; + goto err_free_txq_pcpu; + } + + for_each_present_cpu(cpu) { + port_pcpu = per_cpu_ptr(port->pcpu, cpu); + + hrtimer_init(&port_pcpu->tx_done_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_PINNED); + port_pcpu->tx_done_timer.function = mvpp2_hr_timer_cb; + port_pcpu->timer_scheduled = false; + + tasklet_init(&port_pcpu->tx_done_tasklet, mvpp2_tx_proc_cb, + (unsigned long)dev); + } + netif_napi_add(dev, &port->napi, mvpp2_poll, NAPI_POLL_WEIGHT); features = NETIF_F_SG | NETIF_F_IP_CSUM; dev->features = features | NETIF_F_RXCSUM; @@ -6144,7 +6231,7 @@ static int mvpp2_port_probe(struct platform_device *pdev, err = register_netdev(dev); if (err < 0) { dev_err(&pdev->dev, "failed to register netdev\n"); - goto err_free_txq_pcpu; + goto err_free_port_pcpu; } netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr); @@ -6153,6 +6240,8 @@ static int mvpp2_port_probe(struct platform_device *pdev, priv->port_list[id] = port; return 0; +err_free_port_pcpu: + free_percpu(port->pcpu); err_free_txq_pcpu: for (i = 0; i < txq_number; i++) free_percpu(port->txqs[i]->pcpu); @@ -6171,6 +6260,7 @@ static void mvpp2_port_remove(struct mvpp2_port *port) int i; unregister_netdev(port->dev); + free_percpu(port->pcpu); free_percpu(port->stats); for (i = 0; i < txq_number; i++) free_percpu(port->txqs[i]->pcpu); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/main.c b/drivers/net/ethernet/mellanox/mlx5/core/main.c index afad529838de..06e3e1e54c35 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/main.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/main.c @@ -391,6 +391,8 @@ static int handle_hca_cap(struct mlx5_core_dev *dev) /* disable cmdif checksum */ MLX5_SET(cmd_hca_cap, set_hca_cap, cmdif_checksum, 0); + MLX5_SET(cmd_hca_cap, set_hca_cap, log_uar_page_sz, PAGE_SHIFT - 12); + err = set_caps(dev, set_ctx, set_sz); query_ex: diff --git a/drivers/net/ethernet/micrel/ks8842.c b/drivers/net/ethernet/micrel/ks8842.c index f78909a00f15..09d2e16fd6b0 100644 --- a/drivers/net/ethernet/micrel/ks8842.c +++ b/drivers/net/ethernet/micrel/ks8842.c @@ -952,9 +952,8 @@ static int ks8842_alloc_dma_bufs(struct net_device *netdev) sg_dma_address(&tx_ctl->sg) = dma_map_single(adapter->dev, tx_ctl->buf, DMA_BUFFER_SIZE, DMA_TO_DEVICE); - err = dma_mapping_error(adapter->dev, - sg_dma_address(&tx_ctl->sg)); - if (err) { + if (dma_mapping_error(adapter->dev, sg_dma_address(&tx_ctl->sg))) { + err = -ENOMEM; sg_dma_address(&tx_ctl->sg) = 0; goto err; } diff --git a/drivers/net/ethernet/realtek/r8169.c b/drivers/net/ethernet/realtek/r8169.c index 3df51faf18ae..f790f61ea78a 100644 --- a/drivers/net/ethernet/realtek/r8169.c +++ b/drivers/net/ethernet/realtek/r8169.c @@ -4875,10 +4875,12 @@ static void rtl_init_rxcfg(struct rtl8169_private *tp) case RTL_GIGA_MAC_VER_46: case RTL_GIGA_MAC_VER_47: case RTL_GIGA_MAC_VER_48: + RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST | RX_EARLY_OFF); + break; case RTL_GIGA_MAC_VER_49: case RTL_GIGA_MAC_VER_50: case RTL_GIGA_MAC_VER_51: - RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST | RX_EARLY_OFF); + RTL_W32(RxConfig, RX128_INT_EN | RX_MULTI_EN | RX_DMA_BURST | RX_EARLY_OFF); break; default: RTL_W32(RxConfig, RX128_INT_EN | RX_DMA_BURST); diff --git a/drivers/net/ethernet/rocker/rocker.c b/drivers/net/ethernet/rocker/rocker.c index 2d8578cade03..2e7f9a2834be 100644 --- a/drivers/net/ethernet/rocker/rocker.c +++ b/drivers/net/ethernet/rocker/rocker.c @@ -4821,6 +4821,7 @@ static void rocker_remove_ports(const struct rocker *rocker) rocker_port_ig_tbl(rocker_port, SWITCHDEV_TRANS_NONE, ROCKER_OP_FLAG_REMOVE); unregister_netdev(rocker_port->dev); + free_netdev(rocker_port->dev); } kfree(rocker->ports); } diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c index 605cc8948594..b1a4ea21c91c 100644 --- a/drivers/net/ethernet/sfc/ef10.c +++ b/drivers/net/ethernet/sfc/ef10.c @@ -1282,7 +1282,12 @@ static size_t efx_ef10_update_stats_common(struct efx_nic *efx, u64 *full_stats, } } - if (core_stats) { + if (!core_stats) + return stats_count; + + if (nic_data->datapath_caps & + 1 << MC_CMD_GET_CAPABILITIES_OUT_EVB_LBN) { + /* Use vadaptor stats. */ core_stats->rx_packets = stats[EF10_STAT_rx_unicast] + stats[EF10_STAT_rx_multicast] + stats[EF10_STAT_rx_broadcast]; @@ -1302,6 +1307,26 @@ static size_t efx_ef10_update_stats_common(struct efx_nic *efx, u64 *full_stats, core_stats->rx_fifo_errors = stats[EF10_STAT_rx_overflow]; core_stats->rx_errors = core_stats->rx_crc_errors; core_stats->tx_errors = stats[EF10_STAT_tx_bad]; + } else { + /* Use port stats. */ + core_stats->rx_packets = stats[EF10_STAT_port_rx_packets]; + core_stats->tx_packets = stats[EF10_STAT_port_tx_packets]; + core_stats->rx_bytes = stats[EF10_STAT_port_rx_bytes]; + core_stats->tx_bytes = stats[EF10_STAT_port_tx_bytes]; + core_stats->rx_dropped = stats[EF10_STAT_port_rx_nodesc_drops] + + stats[GENERIC_STAT_rx_nodesc_trunc] + + stats[GENERIC_STAT_rx_noskb_drops]; + core_stats->multicast = stats[EF10_STAT_port_rx_multicast]; + core_stats->rx_length_errors = + stats[EF10_STAT_port_rx_gtjumbo] + + stats[EF10_STAT_port_rx_length_error]; + core_stats->rx_crc_errors = stats[EF10_STAT_port_rx_bad]; + core_stats->rx_frame_errors = + stats[EF10_STAT_port_rx_align_error]; + core_stats->rx_fifo_errors = stats[EF10_STAT_port_rx_overflow]; + core_stats->rx_errors = (core_stats->rx_length_errors + + core_stats->rx_crc_errors + + core_stats->rx_frame_errors); } return stats_count; diff --git a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c index 7e3129e7f143..f0e4bb4e3ec5 100644 --- a/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c +++ b/drivers/net/ethernet/stmicro/stmmac/dwmac-ipq806x.c @@ -42,7 +42,7 @@ #define NSS_COMMON_CLK_DIV_MASK 0x7f #define NSS_COMMON_CLK_SRC_CTRL 0x14 -#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (1 << x) +#define NSS_COMMON_CLK_SRC_CTRL_OFFSET(x) (x) /* Mode is coded on 1 bit but is different depending on the MAC ID: * MAC0: QSGMII=0 RGMII=1 * MAC1: QSGMII=0 SGMII=0 RGMII=1 @@ -291,7 +291,7 @@ static void *ipq806x_gmac_setup(struct platform_device *pdev) /* Configure the clock src according to the mode */ regmap_read(gmac->nss_common, NSS_COMMON_CLK_SRC_CTRL, &val); - val &= ~NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id); + val &= ~(1 << NSS_COMMON_CLK_SRC_CTRL_OFFSET(gmac->id)); switch (gmac->phy_mode) { case PHY_INTERFACE_MODE_RGMII: val |= NSS_COMMON_CLK_SRC_CTRL_RGMII(gmac->id) << diff --git a/drivers/net/ethernet/ti/netcp.h b/drivers/net/ethernet/ti/netcp.h index a8a730641bbb..bb1bb72121c0 100644 --- a/drivers/net/ethernet/ti/netcp.h +++ b/drivers/net/ethernet/ti/netcp.h @@ -85,7 +85,6 @@ struct netcp_intf { struct list_head rxhook_list_head; unsigned int rx_queue_id; void *rx_fdq[KNAV_DMA_FDQ_PER_CHAN]; - u32 rx_buffer_sizes[KNAV_DMA_FDQ_PER_CHAN]; struct napi_struct rx_napi; struct napi_struct tx_napi; diff --git a/drivers/net/ethernet/ti/netcp_core.c b/drivers/net/ethernet/ti/netcp_core.c index 9749dfd78c43..4755838c6137 100644 --- a/drivers/net/ethernet/ti/netcp_core.c +++ b/drivers/net/ethernet/ti/netcp_core.c @@ -34,6 +34,7 @@ #define NETCP_SOP_OFFSET (NET_IP_ALIGN + NET_SKB_PAD) #define NETCP_NAPI_WEIGHT 64 #define NETCP_TX_TIMEOUT (5 * HZ) +#define NETCP_PACKET_SIZE (ETH_FRAME_LEN + ETH_FCS_LEN) #define NETCP_MIN_PACKET_SIZE ETH_ZLEN #define NETCP_MAX_MCAST_ADDR 16 @@ -804,30 +805,28 @@ static void netcp_allocate_rx_buf(struct netcp_intf *netcp, int fdq) if (likely(fdq == 0)) { unsigned int primary_buf_len; /* Allocate a primary receive queue entry */ - buf_len = netcp->rx_buffer_sizes[0] + NETCP_SOP_OFFSET; + buf_len = NETCP_PACKET_SIZE + NETCP_SOP_OFFSET; primary_buf_len = SKB_DATA_ALIGN(buf_len) + SKB_DATA_ALIGN(sizeof(struct skb_shared_info)); - if (primary_buf_len <= PAGE_SIZE) { - bufptr = netdev_alloc_frag(primary_buf_len); - pad[1] = primary_buf_len; - } else { - bufptr = kmalloc(primary_buf_len, GFP_ATOMIC | - GFP_DMA32 | __GFP_COLD); - pad[1] = 0; - } + bufptr = netdev_alloc_frag(primary_buf_len); + pad[1] = primary_buf_len; if (unlikely(!bufptr)) { - dev_warn_ratelimited(netcp->ndev_dev, "Primary RX buffer alloc failed\n"); + dev_warn_ratelimited(netcp->ndev_dev, + "Primary RX buffer alloc failed\n"); goto fail; } dma = dma_map_single(netcp->dev, bufptr, buf_len, DMA_TO_DEVICE); + if (unlikely(dma_mapping_error(netcp->dev, dma))) + goto fail; + pad[0] = (u32)bufptr; } else { /* Allocate a secondary receive queue entry */ - page = alloc_page(GFP_ATOMIC | GFP_DMA32 | __GFP_COLD); + page = alloc_page(GFP_ATOMIC | GFP_DMA | __GFP_COLD); if (unlikely(!page)) { dev_warn_ratelimited(netcp->ndev_dev, "Secondary page alloc failed\n"); goto fail; @@ -1010,7 +1009,7 @@ netcp_tx_map_skb(struct sk_buff *skb, struct netcp_intf *netcp) /* Map the linear buffer */ dma_addr = dma_map_single(dev, skb->data, pkt_len, DMA_TO_DEVICE); - if (unlikely(!dma_addr)) { + if (unlikely(dma_mapping_error(dev, dma_addr))) { dev_err(netcp->ndev_dev, "Failed to map skb buffer\n"); return NULL; } @@ -1546,8 +1545,8 @@ static int netcp_setup_navigator_resources(struct net_device *ndev) knav_queue_disable_notify(netcp->rx_queue); /* open Rx FDQs */ - for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && - netcp->rx_queue_depths[i] && netcp->rx_buffer_sizes[i]; ++i) { + for (i = 0; i < KNAV_DMA_FDQ_PER_CHAN && netcp->rx_queue_depths[i]; + ++i) { snprintf(name, sizeof(name), "rx-fdq-%s-%d", ndev->name, i); netcp->rx_fdq[i] = knav_queue_open(name, KNAV_QUEUE_GP, 0); if (IS_ERR_OR_NULL(netcp->rx_fdq[i])) { @@ -1941,14 +1940,6 @@ static int netcp_create_interface(struct netcp_device *netcp_device, netcp->rx_queue_depths[0] = 128; } - ret = of_property_read_u32_array(node_interface, "rx-buffer-size", - netcp->rx_buffer_sizes, - KNAV_DMA_FDQ_PER_CHAN); - if (ret) { - dev_err(dev, "missing \"rx-buffer-size\" parameter\n"); - netcp->rx_buffer_sizes[0] = 1536; - } - ret = of_property_read_u32_array(node_interface, "rx-pool", temp, 2); if (ret < 0) { dev_err(dev, "missing \"rx-pool\" parameter\n"); diff --git a/drivers/net/hamradio/mkiss.c b/drivers/net/hamradio/mkiss.c index 2ffbf13471d0..216bfd350169 100644 --- a/drivers/net/hamradio/mkiss.c +++ b/drivers/net/hamradio/mkiss.c @@ -728,11 +728,12 @@ static int mkiss_open(struct tty_struct *tty) dev->type = ARPHRD_AX25; /* Perform the low-level AX25 initialization. */ - if ((err = ax_open(ax->dev))) { + err = ax_open(ax->dev); + if (err) goto out_free_netdev; - } - if (register_netdev(dev)) + err = register_netdev(dev); + if (err) goto out_free_buffers; /* after register_netdev() - because else printk smashes the kernel */ diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 3cc316cb7e6b..d8757bf9ad75 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -102,6 +102,12 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, netdev_dbg(ndev, "%s: %d byte payload received\n", __func__, len); + if (len < 0) { + ndev->stats.rx_errors++; + ndev->stats.rx_length_errors++; + goto enqueue_again; + } + skb_put(skb, len); skb->protocol = eth_type_trans(skb, ndev); skb->ip_summed = CHECKSUM_NONE; @@ -121,6 +127,7 @@ static void ntb_netdev_rx_handler(struct ntb_transport_qp *qp, void *qp_data, return; } +enqueue_again: rc = ntb_transport_rx_enqueue(qp, skb, skb->data, ndev->mtu + ETH_HLEN); if (rc) { dev_kfree_skb(skb); @@ -184,7 +191,7 @@ static int ntb_netdev_open(struct net_device *ndev) rc = ntb_transport_rx_enqueue(dev->qp, skb, skb->data, ndev->mtu + ETH_HLEN); - if (rc == -EINVAL) { + if (rc) { dev_kfree_skb(skb); goto err; } diff --git a/drivers/net/phy/fixed_phy.c b/drivers/net/phy/fixed_phy.c index 1960b46add65..d7a65247f952 100644 --- a/drivers/net/phy/fixed_phy.c +++ b/drivers/net/phy/fixed_phy.c @@ -290,6 +290,15 @@ struct phy_device *fixed_phy_register(unsigned int irq, return ERR_PTR(-EINVAL); } + /* propagate the fixed link values to struct phy_device */ + phy->link = status->link; + if (status->link) { + phy->speed = status->speed; + phy->duplex = status->duplex; + phy->pause = status->pause; + phy->asym_pause = status->asym_pause; + } + of_node_get(np); phy->dev.of_node = np; diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c index b2197b506acb..34fe339f4e80 100644 --- a/drivers/net/phy/phy.c +++ b/drivers/net/phy/phy.c @@ -811,6 +811,7 @@ void phy_state_machine(struct work_struct *work) bool needs_aneg = false, do_suspend = false; enum phy_state old_state; int err = 0; + int old_link; mutex_lock(&phydev->lock); @@ -896,11 +897,18 @@ void phy_state_machine(struct work_struct *work) phydev->adjust_link(phydev->attached_dev); break; case PHY_RUNNING: - /* Only register a CHANGE if we are - * polling or ignoring interrupts + /* Only register a CHANGE if we are polling or ignoring + * interrupts and link changed since latest checking. */ - if (!phy_interrupt_is_valid(phydev)) - phydev->state = PHY_CHANGELINK; + if (!phy_interrupt_is_valid(phydev)) { + old_link = phydev->link; + err = phy_read_status(phydev); + if (err) + break; + + if (old_link != phydev->link) + phydev->state = PHY_CHANGELINK; + } break; case PHY_CHANGELINK: err = phy_read_status(phydev); @@ -1030,10 +1038,14 @@ int phy_read_mmd_indirect(struct phy_device *phydev, int prtad, int value = -1; if (phydrv->read_mmd_indirect == NULL) { - mmd_phy_indirect(phydev->bus, prtad, devad, addr); + struct mii_bus *bus = phydev->bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); /* Read the content of the MMD's selected register */ - value = phydev->bus->read(phydev->bus, addr, MII_MMD_DATA); + value = bus->read(bus, addr, MII_MMD_DATA); + mutex_unlock(&bus->mdio_lock); } else { value = phydrv->read_mmd_indirect(phydev, prtad, devad, addr); } @@ -1063,10 +1075,14 @@ void phy_write_mmd_indirect(struct phy_device *phydev, int prtad, struct phy_driver *phydrv = phydev->drv; if (phydrv->write_mmd_indirect == NULL) { - mmd_phy_indirect(phydev->bus, prtad, devad, addr); + struct mii_bus *bus = phydev->bus; + + mutex_lock(&bus->mdio_lock); + mmd_phy_indirect(bus, prtad, devad, addr); /* Write the data into MMD's selected register */ - phydev->bus->write(phydev->bus, addr, MII_MMD_DATA, data); + bus->write(bus, addr, MII_MMD_DATA, data); + mutex_unlock(&bus->mdio_lock); } else { phydrv->write_mmd_indirect(phydev, prtad, devad, addr, data); } diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c index 0302483de240..55f01788df5e 100644 --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c @@ -176,7 +176,7 @@ struct phy_device *phy_device_create(struct mii_bus *bus, int addr, int phy_id, if (c45_ids) dev->c45_ids = *c45_ids; dev->bus = bus; - dev->dev.parent = bus->parent; + dev->dev.parent = &bus->dev; dev->dev.bus = &mdio_bus_type; dev->irq = bus->irq != NULL ? bus->irq[addr] : PHY_POLL; dev_set_name(&dev->dev, PHY_ID_FMT, bus->id, addr); diff --git a/drivers/net/phy/smsc.c b/drivers/net/phy/smsc.c index c0f6479e19d4..70b08958763a 100644 --- a/drivers/net/phy/smsc.c +++ b/drivers/net/phy/smsc.c @@ -91,19 +91,18 @@ static int lan911x_config_init(struct phy_device *phydev) } /* - * The LAN8710/LAN8720 requires a minimum of 2 link pulses within 64ms of each - * other in order to set the ENERGYON bit and exit EDPD mode. If a link partner - * does send the pulses within this interval, the PHY will remained powered - * down. - * - * This workaround will manually toggle the PHY on/off upon calls to read_status - * in order to generate link test pulses if the link is down. If a link partner - * is present, it will respond to the pulses, which will cause the ENERGYON bit - * to be set and will cause the EDPD mode to be exited. + * The LAN87xx suffers from rare absence of the ENERGYON-bit when Ethernet cable + * plugs in while LAN87xx is in Energy Detect Power-Down mode. This leads to + * unstable detection of plugging in Ethernet cable. + * This workaround disables Energy Detect Power-Down mode and waiting for + * response on link pulses to detect presence of plugged Ethernet cable. + * The Energy Detect Power-Down mode is enabled again in the end of procedure to + * save approximately 220 mW of power if cable is unplugged. */ static int lan87xx_read_status(struct phy_device *phydev) { int err = genphy_read_status(phydev); + int i; if (!phydev->link) { /* Disable EDPD to wake up PHY */ @@ -116,8 +115,16 @@ static int lan87xx_read_status(struct phy_device *phydev) if (rc < 0) return rc; - /* Sleep 64 ms to allow ~5 link test pulses to be sent */ - msleep(64); + /* Wait max 640 ms to detect energy */ + for (i = 0; i < 64; i++) { + /* Sleep to allow link test pulses to be sent */ + msleep(10); + rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); + if (rc < 0) + return rc; + if (rc & MII_LAN83C185_ENERGYON) + break; + } /* Re-enable EDPD */ rc = phy_read(phydev, MII_LAN83C185_CTRL_STATUS); @@ -191,7 +198,7 @@ static struct phy_driver smsc_phy_driver[] = { /* basic functions */ .config_aneg = genphy_config_aneg, - .read_status = genphy_read_status, + .read_status = lan87xx_read_status, .config_init = smsc_phy_config_init, .soft_reset = smsc_phy_reset, diff --git a/drivers/net/ppp/ppp_generic.c b/drivers/net/ppp/ppp_generic.c index 9d15566521a7..fa8f5046afe9 100644 --- a/drivers/net/ppp/ppp_generic.c +++ b/drivers/net/ppp/ppp_generic.c @@ -269,9 +269,9 @@ static void ppp_ccp_peek(struct ppp *ppp, struct sk_buff *skb, int inbound); static void ppp_ccp_closed(struct ppp *ppp); static struct compressor *find_compressor(int type); static void ppp_get_stats(struct ppp *ppp, struct ppp_stats *st); -static struct ppp *ppp_create_interface(struct net *net, int unit, int *retp); +static struct ppp *ppp_create_interface(struct net *net, int unit, + struct file *file, int *retp); static void init_ppp_file(struct ppp_file *pf, int kind); -static void ppp_shutdown_interface(struct ppp *ppp); static void ppp_destroy_interface(struct ppp *ppp); static struct ppp *ppp_find_unit(struct ppp_net *pn, int unit); static struct channel *ppp_find_channel(struct ppp_net *pn, int unit); @@ -392,8 +392,10 @@ static int ppp_release(struct inode *unused, struct file *file) file->private_data = NULL; if (pf->kind == INTERFACE) { ppp = PF_TO_PPP(pf); + rtnl_lock(); if (file == ppp->owner) - ppp_shutdown_interface(ppp); + unregister_netdevice(ppp->dev); + rtnl_unlock(); } if (atomic_dec_and_test(&pf->refcnt)) { switch (pf->kind) { @@ -593,8 +595,10 @@ static long ppp_ioctl(struct file *file, unsigned int cmd, unsigned long arg) mutex_lock(&ppp_mutex); if (pf->kind == INTERFACE) { ppp = PF_TO_PPP(pf); + rtnl_lock(); if (file == ppp->owner) - ppp_shutdown_interface(ppp); + unregister_netdevice(ppp->dev); + rtnl_unlock(); } if (atomic_long_read(&file->f_count) < 2) { ppp_release(NULL, file); @@ -838,11 +842,10 @@ static int ppp_unattached_ioctl(struct net *net, struct ppp_file *pf, /* Create a new ppp unit */ if (get_user(unit, p)) break; - ppp = ppp_create_interface(net, unit, &err); + ppp = ppp_create_interface(net, unit, file, &err); if (!ppp) break; file->private_data = &ppp->file; - ppp->owner = file; err = -EFAULT; if (put_user(ppp->file.index, p)) break; @@ -916,6 +919,16 @@ static __net_init int ppp_init_net(struct net *net) static __net_exit void ppp_exit_net(struct net *net) { struct ppp_net *pn = net_generic(net, ppp_net_id); + struct ppp *ppp; + LIST_HEAD(list); + int id; + + rtnl_lock(); + idr_for_each_entry(&pn->units_idr, ppp, id) + unregister_netdevice_queue(ppp->dev, &list); + + unregister_netdevice_many(&list); + rtnl_unlock(); idr_destroy(&pn->units_idr); } @@ -1088,8 +1101,28 @@ static int ppp_dev_init(struct net_device *dev) return 0; } +static void ppp_dev_uninit(struct net_device *dev) +{ + struct ppp *ppp = netdev_priv(dev); + struct ppp_net *pn = ppp_pernet(ppp->ppp_net); + + ppp_lock(ppp); + ppp->closing = 1; + ppp_unlock(ppp); + + mutex_lock(&pn->all_ppp_mutex); + unit_put(&pn->units_idr, ppp->file.index); + mutex_unlock(&pn->all_ppp_mutex); + + ppp->owner = NULL; + + ppp->file.dead = 1; + wake_up_interruptible(&ppp->file.rwait); +} + static const struct net_device_ops ppp_netdev_ops = { .ndo_init = ppp_dev_init, + .ndo_uninit = ppp_dev_uninit, .ndo_start_xmit = ppp_start_xmit, .ndo_do_ioctl = ppp_net_ioctl, .ndo_get_stats64 = ppp_get_stats64, @@ -2667,8 +2700,8 @@ ppp_get_stats(struct ppp *ppp, struct ppp_stats *st) * or if there is already a unit with the requested number. * unit == -1 means allocate a new number. */ -static struct ppp * -ppp_create_interface(struct net *net, int unit, int *retp) +static struct ppp *ppp_create_interface(struct net *net, int unit, + struct file *file, int *retp) { struct ppp *ppp; struct ppp_net *pn; @@ -2688,6 +2721,7 @@ ppp_create_interface(struct net *net, int unit, int *retp) ppp->mru = PPP_MRU; init_ppp_file(&ppp->file, INTERFACE); ppp->file.hdrlen = PPP_HDRLEN - 2; /* don't count proto bytes */ + ppp->owner = file; for (i = 0; i < NUM_NP; ++i) ppp->npmode[i] = NPMODE_PASS; INIT_LIST_HEAD(&ppp->channels); @@ -2776,34 +2810,6 @@ init_ppp_file(struct ppp_file *pf, int kind) } /* - * Take down a ppp interface unit - called when the owning file - * (the one that created the unit) is closed or detached. - */ -static void ppp_shutdown_interface(struct ppp *ppp) -{ - struct ppp_net *pn; - - pn = ppp_pernet(ppp->ppp_net); - mutex_lock(&pn->all_ppp_mutex); - - /* This will call dev_close() for us. */ - ppp_lock(ppp); - if (!ppp->closing) { - ppp->closing = 1; - ppp_unlock(ppp); - unregister_netdev(ppp->dev); - unit_put(&pn->units_idr, ppp->file.index); - } else - ppp_unlock(ppp); - - ppp->file.dead = 1; - ppp->owner = NULL; - wake_up_interruptible(&ppp->file.rwait); - - mutex_unlock(&pn->all_ppp_mutex); -} - -/* * Free the memory used by a ppp unit. This is only called once * there are no channels connected to the unit and no file structs * that reference the unit. diff --git a/drivers/net/rionet.c b/drivers/net/rionet.c index dac7a0d9bb46..01f08a7751f7 100644 --- a/drivers/net/rionet.c +++ b/drivers/net/rionet.c @@ -396,7 +396,7 @@ static int rionet_close(struct net_device *ndev) return 0; } -static int rionet_remove_dev(struct device *dev, struct subsys_interface *sif) +static void rionet_remove_dev(struct device *dev, struct subsys_interface *sif) { struct rio_dev *rdev = to_rio_dev(dev); unsigned char netid = rdev->net->hport->id; @@ -416,8 +416,6 @@ static int rionet_remove_dev(struct device *dev, struct subsys_interface *sif) } } } - - return 0; } static void rionet_get_drvinfo(struct net_device *ndev, diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index 9d43460ce3c7..64a60afbe50c 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c @@ -785,6 +785,7 @@ static const struct usb_device_id products[] = { {QMI_FIXED_INTF(0x413c, 0x81a4, 8)}, /* Dell Wireless 5570e HSPA+ (42Mbps) Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a8, 8)}, /* Dell Wireless 5808 Gobi(TM) 4G LTE Mobile Broadband Card */ {QMI_FIXED_INTF(0x413c, 0x81a9, 8)}, /* Dell Wireless 5808e Gobi(TM) 4G LTE Mobile Broadband Card */ + {QMI_FIXED_INTF(0x03f0, 0x4e1d, 8)}, /* HP lt4111 LTE/EV-DO/HSPA+ Gobi 4G Module */ {QMI_FIXED_INTF(0x03f0, 0x581d, 4)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Module (Huawei me906e) */ /* 4. Gobi 1000 devices */ diff --git a/drivers/net/usb/usbnet.c b/drivers/net/usb/usbnet.c index 3c86b107275a..e0498571ae26 100644 --- a/drivers/net/usb/usbnet.c +++ b/drivers/net/usb/usbnet.c @@ -778,7 +778,7 @@ int usbnet_stop (struct net_device *net) { struct usbnet *dev = netdev_priv(net); struct driver_info *info = dev->driver_info; - int retval, pm; + int retval, pm, mpn; clear_bit(EVENT_DEV_OPEN, &dev->flags); netif_stop_queue (net); @@ -809,6 +809,8 @@ int usbnet_stop (struct net_device *net) usbnet_purge_paused_rxq(dev); + mpn = !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags); + /* deferred work (task, timer, softirq) must also stop. * can't flush_scheduled_work() until we drop rtnl (later), * else workers could deadlock; so make workers a NOP. @@ -819,8 +821,7 @@ int usbnet_stop (struct net_device *net) if (!pm) usb_autopm_put_interface(dev->intf); - if (info->manage_power && - !test_and_clear_bit(EVENT_NO_RUNTIME_PM, &dev->flags)) + if (info->manage_power && mpn) info->manage_power(dev, 0); else usb_autopm_put_interface(dev->intf); diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c index 7fbca37a1adf..237f8e5e493d 100644 --- a/drivers/net/virtio_net.c +++ b/drivers/net/virtio_net.c @@ -1756,9 +1756,9 @@ static int virtnet_probe(struct virtio_device *vdev) /* Do we support "hardware" checksums? */ if (virtio_has_feature(vdev, VIRTIO_NET_F_CSUM)) { /* This opens up the world of extra features. */ - dev->hw_features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; + dev->hw_features |= NETIF_F_HW_CSUM | NETIF_F_SG; if (csum) - dev->features |= NETIF_F_HW_CSUM|NETIF_F_SG|NETIF_F_FRAGLIST; + dev->features |= NETIF_F_HW_CSUM | NETIF_F_SG; if (virtio_has_feature(vdev, VIRTIO_NET_F_GSO)) { dev->hw_features |= NETIF_F_TSO | NETIF_F_UFO diff --git a/drivers/net/vxlan.c b/drivers/net/vxlan.c index 34c519eb1db5..5bc4b1ed67b3 100644 --- a/drivers/net/vxlan.c +++ b/drivers/net/vxlan.c @@ -2216,6 +2216,8 @@ static int vxlan_open(struct net_device *dev) if (vxlan_addr_multicast(&vxlan->default_dst.remote_ip)) { ret = vxlan_igmp_join(vxlan); + if (ret == -EADDRINUSE) + ret = 0; if (ret) { vxlan_sock_release(vs); return ret; diff --git a/drivers/net/wan/cosa.c b/drivers/net/wan/cosa.c index 7193b7304fdd..848ea6a399f2 100644 --- a/drivers/net/wan/cosa.c +++ b/drivers/net/wan/cosa.c @@ -589,7 +589,8 @@ static int cosa_probe(int base, int irq, int dma) chan->netdev->base_addr = chan->cosa->datareg; chan->netdev->irq = chan->cosa->irq; chan->netdev->dma = chan->cosa->dma; - if (register_hdlc_device(chan->netdev)) { + err = register_hdlc_device(chan->netdev); + if (err) { netdev_warn(chan->netdev, "register_hdlc_device() failed\n"); free_netdev(chan->netdev); diff --git a/drivers/net/wireless/b43/tables_nphy.c b/drivers/net/wireless/b43/tables_nphy.c index 25d1cbd34306..b2f0d245bcf3 100644 --- a/drivers/net/wireless/b43/tables_nphy.c +++ b/drivers/net/wireless/b43/tables_nphy.c @@ -3728,7 +3728,7 @@ const u32 *b43_nphy_get_tx_gain_table(struct b43_wldev *dev) switch (phy->rev) { case 6: case 5: - if (sprom->fem.ghz5.extpa_gain == 3) + if (sprom->fem.ghz2.extpa_gain == 3) return b43_ntab_tx_gain_epa_rev3_hi_pwr_2g; /* fall through */ case 4: diff --git a/drivers/net/wireless/iwlwifi/mvm/scan.c b/drivers/net/wireless/iwlwifi/mvm/scan.c index 5000bfcded61..5514ad6d4e54 100644 --- a/drivers/net/wireless/iwlwifi/mvm/scan.c +++ b/drivers/net/wireless/iwlwifi/mvm/scan.c @@ -1023,7 +1023,7 @@ static void iwl_mvm_scan_umac_dwell(struct iwl_mvm *mvm, cmd->scan_priority = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); - if (iwl_mvm_scan_total_iterations(params) == 0) + if (iwl_mvm_scan_total_iterations(params) == 1) cmd->ooc_priority = iwl_mvm_scan_priority(mvm, IWL_SCAN_PRIORITY_EXT_6); else diff --git a/drivers/net/wireless/iwlwifi/pcie/trans.c b/drivers/net/wireless/iwlwifi/pcie/trans.c index 6203c4ad9bba..9e144e71da0b 100644 --- a/drivers/net/wireless/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/iwlwifi/pcie/trans.c @@ -478,10 +478,16 @@ static void iwl_pcie_apm_stop(struct iwl_trans *trans, bool op_mode_leave) if (trans->cfg->device_family == IWL_DEVICE_FAMILY_7000) iwl_set_bits_prph(trans, APMG_PCIDEV_STT_REG, APMG_PCIDEV_STT_VAL_WAKE_ME); - else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) + else if (trans->cfg->device_family == IWL_DEVICE_FAMILY_8000) { + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, CSR_HW_IF_CONFIG_REG_PREPARE | CSR_HW_IF_CONFIG_REG_ENABLE_PME); + mdelay(1); + iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + } mdelay(5); } @@ -575,6 +581,10 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) if (ret >= 0) return 0; + iwl_set_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + msleep(1); + for (iter = 0; iter < 10; iter++) { /* If HW is not ready, prepare the conditions to check again */ iwl_set_bit(trans, CSR_HW_IF_CONFIG_REG, @@ -582,8 +592,10 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) do { ret = iwl_pcie_set_hw_ready(trans); - if (ret >= 0) - return 0; + if (ret >= 0) { + ret = 0; + goto out; + } usleep_range(200, 1000); t += 200; @@ -593,6 +605,10 @@ static int iwl_pcie_prepare_card_hw(struct iwl_trans *trans) IWL_ERR(trans, "Couldn't prepare the card\n"); +out: + iwl_clear_bit(trans, CSR_DBG_LINK_PWR_MGMT_REG, + CSR_RESET_LINK_PWR_MGMT_DISABLED); + return ret; } diff --git a/drivers/net/wireless/iwlwifi/pcie/tx.c b/drivers/net/wireless/iwlwifi/pcie/tx.c index 2b86c2135de3..607acb53c847 100644 --- a/drivers/net/wireless/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/iwlwifi/pcie/tx.c @@ -1875,8 +1875,19 @@ int iwl_trans_pcie_tx(struct iwl_trans *trans, struct sk_buff *skb, /* start timer if queue currently empty */ if (q->read_ptr == q->write_ptr) { - if (txq->wd_timeout) - mod_timer(&txq->stuck_timer, jiffies + txq->wd_timeout); + if (txq->wd_timeout) { + /* + * If the TXQ is active, then set the timer, if not, + * set the timer in remainder so that the timer will + * be armed with the right value when the station will + * wake up. + */ + if (!txq->frozen) + mod_timer(&txq->stuck_timer, + jiffies + txq->wd_timeout); + else + txq->frozen_expiry_remainder = txq->wd_timeout; + } IWL_DEBUG_RPM(trans, "Q: %d first tx - take ref\n", q->id); iwl_trans_pcie_ref(trans); } diff --git a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c index b6cc9ff47fc2..1c6788aecc62 100644 --- a/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_sdio_ops.c @@ -172,6 +172,7 @@ static int rsi_load_ta_instructions(struct rsi_common *common) (struct rsi_91x_sdiodev *)adapter->rsi_dev; u32 len; u32 num_blocks; + const u8 *fw; const struct firmware *fw_entry = NULL; u32 block_size = dev->tx_blk_size; int status = 0; @@ -200,6 +201,10 @@ static int rsi_load_ta_instructions(struct rsi_common *common) return status; } + /* Copy firmware into DMA-accessible memory */ + fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + if (!fw) + return -ENOMEM; len = fw_entry->size; if (len % 4) @@ -210,7 +215,8 @@ static int rsi_load_ta_instructions(struct rsi_common *common) rsi_dbg(INIT_ZONE, "%s: Instruction size:%d\n", __func__, len); rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); - status = rsi_copy_to_card(common, fw_entry->data, len, num_blocks); + status = rsi_copy_to_card(common, fw, len, num_blocks); + kfree(fw); release_firmware(fw_entry); return status; } diff --git a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c index 1106ce76707e..30c2cf7fa93b 100644 --- a/drivers/net/wireless/rsi/rsi_91x_usb_ops.c +++ b/drivers/net/wireless/rsi/rsi_91x_usb_ops.c @@ -146,7 +146,10 @@ static int rsi_load_ta_instructions(struct rsi_common *common) return status; } + /* Copy firmware into DMA-accessible memory */ fw = kmemdup(fw_entry->data, fw_entry->size, GFP_KERNEL); + if (!fw) + return -ENOMEM; len = fw_entry->size; if (len % 4) @@ -158,6 +161,7 @@ static int rsi_load_ta_instructions(struct rsi_common *common) rsi_dbg(INIT_ZONE, "%s: num blocks: %d\n", __func__, num_blocks); status = rsi_copy_to_card(common, fw, len, num_blocks); + kfree(fw); release_firmware(fw_entry); return status; } diff --git a/drivers/net/wireless/rtlwifi/core.c b/drivers/net/wireless/rtlwifi/core.c index 3b3a88b53b11..585d0883c7e5 100644 --- a/drivers/net/wireless/rtlwifi/core.c +++ b/drivers/net/wireless/rtlwifi/core.c @@ -1015,9 +1015,12 @@ static void send_beacon_frame(struct ieee80211_hw *hw, { struct rtl_priv *rtlpriv = rtl_priv(hw); struct sk_buff *skb = ieee80211_beacon_get(hw, vif); + struct rtl_tcb_desc tcb_desc; - if (skb) - rtlpriv->intf_ops->adapter_tx(hw, NULL, skb, NULL); + if (skb) { + memset(&tcb_desc, 0, sizeof(struct rtl_tcb_desc)); + rtlpriv->intf_ops->adapter_tx(hw, NULL, skb, &tcb_desc); + } } static void rtl_op_bss_info_changed(struct ieee80211_hw *hw, diff --git a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c index 1017f02d7bf7..7bf88d9dcdc3 100644 --- a/drivers/net/wireless/rtlwifi/rtl8723be/sw.c +++ b/drivers/net/wireless/rtlwifi/rtl8723be/sw.c @@ -385,6 +385,7 @@ module_param_named(debug, rtl8723be_mod_params.debug, int, 0444); module_param_named(ips, rtl8723be_mod_params.inactiveps, bool, 0444); module_param_named(swlps, rtl8723be_mod_params.swctrl_lps, bool, 0444); module_param_named(fwlps, rtl8723be_mod_params.fwctrl_lps, bool, 0444); +module_param_named(msi, rtl8723be_mod_params.msi_support, bool, 0444); module_param_named(disable_watchdog, rtl8723be_mod_params.disable_watchdog, bool, 0444); MODULE_PARM_DESC(swenc, "Set to 1 for software crypto (default 0)\n"); diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c index 1a83e190fc15..28577a31549d 100644 --- a/drivers/net/xen-netback/interface.c +++ b/drivers/net/xen-netback/interface.c @@ -61,6 +61,12 @@ void xenvif_skb_zerocopy_prepare(struct xenvif_queue *queue, void xenvif_skb_zerocopy_complete(struct xenvif_queue *queue) { atomic_dec(&queue->inflight_packets); + + /* Wake the dealloc thread _after_ decrementing inflight_packets so + * that if kthread_stop() has already been called, the dealloc thread + * does not wait forever with nothing to wake it. + */ + wake_up(&queue->dealloc_wq); } int xenvif_schedulable(struct xenvif *vif) diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 7d50711476fe..3f44b522b831 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -810,23 +810,17 @@ static inline struct sk_buff *xenvif_alloc_skb(unsigned int size) static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif_queue *queue, struct sk_buff *skb, struct xen_netif_tx_request *txp, - struct gnttab_map_grant_ref *gop) + struct gnttab_map_grant_ref *gop, + unsigned int frag_overflow, + struct sk_buff *nskb) { struct skb_shared_info *shinfo = skb_shinfo(skb); skb_frag_t *frags = shinfo->frags; u16 pending_idx = XENVIF_TX_CB(skb)->pending_idx; int start; pending_ring_idx_t index; - unsigned int nr_slots, frag_overflow = 0; + unsigned int nr_slots; - /* At this point shinfo->nr_frags is in fact the number of - * slots, which can be as large as XEN_NETBK_LEGACY_SLOTS_MAX. - */ - if (shinfo->nr_frags > MAX_SKB_FRAGS) { - frag_overflow = shinfo->nr_frags - MAX_SKB_FRAGS; - BUG_ON(frag_overflow > MAX_SKB_FRAGS); - shinfo->nr_frags = MAX_SKB_FRAGS; - } nr_slots = shinfo->nr_frags; /* Skip first skb fragment if it is on same page as header fragment. */ @@ -841,13 +835,6 @@ static struct gnttab_map_grant_ref *xenvif_get_requests(struct xenvif_queue *que } if (frag_overflow) { - struct sk_buff *nskb = xenvif_alloc_skb(0); - if (unlikely(nskb == NULL)) { - if (net_ratelimit()) - netdev_err(queue->vif->dev, - "Can't allocate the frag_list skb.\n"); - return NULL; - } shinfo = skb_shinfo(nskb); frags = shinfo->frags; @@ -1175,9 +1162,10 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, unsigned *copy_ops, unsigned *map_ops) { - struct gnttab_map_grant_ref *gop = queue->tx_map_ops, *request_gop; - struct sk_buff *skb; + struct gnttab_map_grant_ref *gop = queue->tx_map_ops; + struct sk_buff *skb, *nskb; int ret; + unsigned int frag_overflow; while (skb_queue_len(&queue->tx_queue) < budget) { struct xen_netif_tx_request txreq; @@ -1265,6 +1253,29 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, break; } + skb_shinfo(skb)->nr_frags = ret; + if (data_len < txreq.size) + skb_shinfo(skb)->nr_frags++; + /* At this point shinfo->nr_frags is in fact the number of + * slots, which can be as large as XEN_NETBK_LEGACY_SLOTS_MAX. + */ + frag_overflow = 0; + nskb = NULL; + if (skb_shinfo(skb)->nr_frags > MAX_SKB_FRAGS) { + frag_overflow = skb_shinfo(skb)->nr_frags - MAX_SKB_FRAGS; + BUG_ON(frag_overflow > MAX_SKB_FRAGS); + skb_shinfo(skb)->nr_frags = MAX_SKB_FRAGS; + nskb = xenvif_alloc_skb(0); + if (unlikely(nskb == NULL)) { + kfree_skb(skb); + xenvif_tx_err(queue, &txreq, idx); + if (net_ratelimit()) + netdev_err(queue->vif->dev, + "Can't allocate the frag_list skb.\n"); + break; + } + } + if (extras[XEN_NETIF_EXTRA_TYPE_GSO - 1].type) { struct xen_netif_extra_info *gso; gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1]; @@ -1272,6 +1283,7 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, if (xenvif_set_skb_gso(queue->vif, skb, gso)) { /* Failure in xenvif_set_skb_gso is fatal. */ kfree_skb(skb); + kfree_skb(nskb); break; } } @@ -1294,9 +1306,7 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, (*copy_ops)++; - skb_shinfo(skb)->nr_frags = ret; if (data_len < txreq.size) { - skb_shinfo(skb)->nr_frags++; frag_set_pending_idx(&skb_shinfo(skb)->frags[0], pending_idx); xenvif_tx_create_map_op(queue, pending_idx, &txreq, gop); @@ -1310,13 +1320,8 @@ static void xenvif_tx_build_gops(struct xenvif_queue *queue, queue->pending_cons++; - request_gop = xenvif_get_requests(queue, skb, txfrags, gop); - if (request_gop == NULL) { - kfree_skb(skb); - xenvif_tx_err(queue, &txreq, idx); - break; - } - gop = request_gop; + gop = xenvif_get_requests(queue, skb, txfrags, gop, + frag_overflow, nskb); __skb_queue_tail(&queue->tx_queue, skb); @@ -1536,7 +1541,6 @@ void xenvif_zerocopy_callback(struct ubuf_info *ubuf, bool zerocopy_success) smp_wmb(); queue->dealloc_prod++; } while (ubuf); - wake_up(&queue->dealloc_wq); spin_unlock_irqrestore(&queue->callback_lock, flags); if (likely(zerocopy_success)) diff --git a/drivers/nfc/mei_phy.c b/drivers/nfc/mei_phy.c index 2b77ccf77f81..754a9bb0f58d 100644 --- a/drivers/nfc/mei_phy.c +++ b/drivers/nfc/mei_phy.c @@ -355,7 +355,8 @@ static int nfc_mei_phy_enable(void *phy_id) goto err; } - r = mei_cl_register_event_cb(phy->device, nfc_mei_event_cb, phy); + r = mei_cl_register_event_cb(phy->device, BIT(MEI_CL_EVENT_RX), + nfc_mei_event_cb, phy); if (r) { pr_err("Event cb registration failed %d\n", r); goto err; diff --git a/drivers/ntb/ntb.c b/drivers/ntb/ntb.c index 23435f2a5486..2e2530743831 100644 --- a/drivers/ntb/ntb.c +++ b/drivers/ntb/ntb.c @@ -114,7 +114,7 @@ int ntb_register_device(struct ntb_dev *ntb) ntb->dev.bus = &ntb_bus; ntb->dev.parent = &ntb->pdev->dev; ntb->dev.release = ntb_dev_release; - dev_set_name(&ntb->dev, pci_name(ntb->pdev)); + dev_set_name(&ntb->dev, "%s", pci_name(ntb->pdev)); ntb->ctx = NULL; ntb->ctx_ops = NULL; diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index efe3ad4122f2..1c6386d5f79c 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -142,10 +142,11 @@ struct ntb_transport_qp { void (*rx_handler)(struct ntb_transport_qp *qp, void *qp_data, void *data, int len); + struct list_head rx_post_q; struct list_head rx_pend_q; struct list_head rx_free_q; - spinlock_t ntb_rx_pend_q_lock; - spinlock_t ntb_rx_free_q_lock; + /* ntb_rx_q_lock: synchronize access to rx_XXXX_q */ + spinlock_t ntb_rx_q_lock; void *rx_buff; unsigned int rx_index; unsigned int rx_max_entry; @@ -211,6 +212,8 @@ struct ntb_transport_ctx { bool link_is_up; struct delayed_work link_work; struct work_struct link_cleanup; + + struct dentry *debugfs_node_dir; }; enum { @@ -436,13 +439,17 @@ static ssize_t debugfs_read(struct file *filp, char __user *ubuf, size_t count, char *buf; ssize_t ret, out_offset, out_count; + qp = filp->private_data; + + if (!qp || !qp->link_is_up) + return 0; + out_count = 1000; buf = kmalloc(out_count, GFP_KERNEL); if (!buf) return -ENOMEM; - qp = filp->private_data; out_offset = 0; out_offset += snprintf(buf + out_offset, out_count - out_offset, "NTB QP stats\n"); @@ -534,6 +541,27 @@ out: return entry; } +static struct ntb_queue_entry *ntb_list_mv(spinlock_t *lock, + struct list_head *list, + struct list_head *to_list) +{ + struct ntb_queue_entry *entry; + unsigned long flags; + + spin_lock_irqsave(lock, flags); + + if (list_empty(list)) { + entry = NULL; + } else { + entry = list_first_entry(list, struct ntb_queue_entry, entry); + list_move_tail(&entry->entry, to_list); + } + + spin_unlock_irqrestore(lock, flags); + + return entry; +} + static int ntb_transport_setup_qp_mw(struct ntb_transport_ctx *nt, unsigned int qp_num) { @@ -601,13 +629,16 @@ static void ntb_free_mw(struct ntb_transport_ctx *nt, int num_mw) } static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, - unsigned int size) + resource_size_t size) { struct ntb_transport_mw *mw = &nt->mw_vec[num_mw]; struct pci_dev *pdev = nt->ndev->pdev; - unsigned int xlat_size, buff_size; + size_t xlat_size, buff_size; int rc; + if (!size) + return -EINVAL; + xlat_size = round_up(size, mw->xlat_align_size); buff_size = round_up(size, mw->xlat_align); @@ -627,7 +658,7 @@ static int ntb_set_mw(struct ntb_transport_ctx *nt, int num_mw, if (!mw->virt_addr) { mw->xlat_size = 0; mw->buff_size = 0; - dev_err(&pdev->dev, "Unable to alloc MW buff of size %d\n", + dev_err(&pdev->dev, "Unable to alloc MW buff of size %zu\n", buff_size); return -ENOMEM; } @@ -867,6 +898,8 @@ static void ntb_qp_link_work(struct work_struct *work) if (qp->event_handler) qp->event_handler(qp->cb_data, qp->link_is_up); + + tasklet_schedule(&qp->rxc_db_work); } else if (nt->link_is_up) schedule_delayed_work(&qp->link_work, msecs_to_jiffies(NTB_LINK_DOWN_TIMEOUT)); @@ -923,12 +956,12 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, qp->tx_max_frame = min(transport_mtu, tx_size / 2); qp->tx_max_entry = tx_size / qp->tx_max_frame; - if (nt_debugfs_dir) { + if (nt->debugfs_node_dir) { char debugfs_name[4]; snprintf(debugfs_name, 4, "qp%d", qp_num); qp->debugfs_dir = debugfs_create_dir(debugfs_name, - nt_debugfs_dir); + nt->debugfs_node_dir); qp->debugfs_stats = debugfs_create_file("stats", S_IRUSR, qp->debugfs_dir, qp, @@ -941,10 +974,10 @@ static int ntb_transport_init_queue(struct ntb_transport_ctx *nt, INIT_DELAYED_WORK(&qp->link_work, ntb_qp_link_work); INIT_WORK(&qp->link_cleanup, ntb_qp_link_cleanup_work); - spin_lock_init(&qp->ntb_rx_pend_q_lock); - spin_lock_init(&qp->ntb_rx_free_q_lock); + spin_lock_init(&qp->ntb_rx_q_lock); spin_lock_init(&qp->ntb_tx_free_q_lock); + INIT_LIST_HEAD(&qp->rx_post_q); INIT_LIST_HEAD(&qp->rx_pend_q); INIT_LIST_HEAD(&qp->rx_free_q); INIT_LIST_HEAD(&qp->tx_free_q); @@ -1031,6 +1064,12 @@ static int ntb_transport_probe(struct ntb_client *self, struct ntb_dev *ndev) goto err2; } + if (nt_debugfs_dir) { + nt->debugfs_node_dir = + debugfs_create_dir(pci_name(ndev->pdev), + nt_debugfs_dir); + } + for (i = 0; i < qp_count; i++) { rc = ntb_transport_init_queue(nt, i); if (rc) @@ -1107,22 +1146,47 @@ static void ntb_transport_free(struct ntb_client *self, struct ntb_dev *ndev) kfree(nt); } -static void ntb_rx_copy_callback(void *data) +static void ntb_complete_rxc(struct ntb_transport_qp *qp) { - struct ntb_queue_entry *entry = data; - struct ntb_transport_qp *qp = entry->qp; - void *cb_data = entry->cb_data; - unsigned int len = entry->len; - struct ntb_payload_header *hdr = entry->rx_hdr; + struct ntb_queue_entry *entry; + void *cb_data; + unsigned int len; + unsigned long irqflags; + + spin_lock_irqsave(&qp->ntb_rx_q_lock, irqflags); + + while (!list_empty(&qp->rx_post_q)) { + entry = list_first_entry(&qp->rx_post_q, + struct ntb_queue_entry, entry); + if (!(entry->flags & DESC_DONE_FLAG)) + break; + + entry->rx_hdr->flags = 0; + iowrite32(entry->index, &qp->rx_info->entry); - hdr->flags = 0; + cb_data = entry->cb_data; + len = entry->len; - iowrite32(entry->index, &qp->rx_info->entry); + list_move_tail(&entry->entry, &qp->rx_free_q); - ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry, &qp->rx_free_q); + spin_unlock_irqrestore(&qp->ntb_rx_q_lock, irqflags); - if (qp->rx_handler && qp->client_ready) - qp->rx_handler(qp, qp->cb_data, cb_data, len); + if (qp->rx_handler && qp->client_ready) + qp->rx_handler(qp, qp->cb_data, cb_data, len); + + spin_lock_irqsave(&qp->ntb_rx_q_lock, irqflags); + } + + spin_unlock_irqrestore(&qp->ntb_rx_q_lock, irqflags); +} + +static void ntb_rx_copy_callback(void *data) +{ + struct ntb_queue_entry *entry = data; + + entry->flags |= DESC_DONE_FLAG; + + ntb_complete_rxc(entry->qp); } static void ntb_memcpy_rx(struct ntb_queue_entry *entry, void *offset) @@ -1138,19 +1202,18 @@ static void ntb_memcpy_rx(struct ntb_queue_entry *entry, void *offset) ntb_rx_copy_callback(entry); } -static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset, - size_t len) +static void ntb_async_rx(struct ntb_queue_entry *entry, void *offset) { struct dma_async_tx_descriptor *txd; struct ntb_transport_qp *qp = entry->qp; struct dma_chan *chan = qp->dma_chan; struct dma_device *device; - size_t pay_off, buff_off; + size_t pay_off, buff_off, len; struct dmaengine_unmap_data *unmap; dma_cookie_t cookie; void *buf = entry->buf; - entry->len = len; + len = entry->len; if (!chan) goto err; @@ -1226,7 +1289,6 @@ static int ntb_process_rxc(struct ntb_transport_qp *qp) struct ntb_payload_header *hdr; struct ntb_queue_entry *entry; void *offset; - int rc; offset = qp->rx_buff + qp->rx_max_frame * qp->rx_index; hdr = offset + qp->rx_max_frame - sizeof(struct ntb_payload_header); @@ -1255,65 +1317,43 @@ static int ntb_process_rxc(struct ntb_transport_qp *qp) return -EIO; } - entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q); + entry = ntb_list_mv(&qp->ntb_rx_q_lock, &qp->rx_pend_q, &qp->rx_post_q); if (!entry) { dev_dbg(&qp->ndev->pdev->dev, "no receive buffer\n"); qp->rx_err_no_buf++; - - rc = -ENOMEM; - goto err; + return -EAGAIN; } + entry->rx_hdr = hdr; + entry->index = qp->rx_index; + if (hdr->len > entry->len) { dev_dbg(&qp->ndev->pdev->dev, "receive buffer overflow! Wanted %d got %d\n", hdr->len, entry->len); qp->rx_err_oflow++; - rc = -EIO; - goto err; - } + entry->len = -EIO; + entry->flags |= DESC_DONE_FLAG; - dev_dbg(&qp->ndev->pdev->dev, - "RX OK index %u ver %u size %d into buf size %d\n", - qp->rx_index, hdr->ver, hdr->len, entry->len); + ntb_complete_rxc(qp); + } else { + dev_dbg(&qp->ndev->pdev->dev, + "RX OK index %u ver %u size %d into buf size %d\n", + qp->rx_index, hdr->ver, hdr->len, entry->len); - qp->rx_bytes += hdr->len; - qp->rx_pkts++; + qp->rx_bytes += hdr->len; + qp->rx_pkts++; - entry->index = qp->rx_index; - entry->rx_hdr = hdr; + entry->len = hdr->len; - ntb_async_rx(entry, offset, hdr->len); + ntb_async_rx(entry, offset); + } qp->rx_index++; qp->rx_index %= qp->rx_max_entry; return 0; - -err: - /* FIXME: if this syncrhonous update of the rx_index gets ahead of - * asyncrhonous ntb_rx_copy_callback of previous entry, there are three - * scenarios: - * - * 1) The peer might miss this update, but observe the update - * from the memcpy completion callback. In this case, the buffer will - * not be freed on the peer to be reused for a different packet. The - * successful rx of a later packet would clear the condition, but the - * condition could persist if several rx fail in a row. - * - * 2) The peer may observe this update before the asyncrhonous copy of - * prior packets is completed. The peer may overwrite the buffers of - * the prior packets before they are copied. - * - * 3) Both: the peer may observe the update, and then observe the index - * decrement by the asynchronous completion callback. Who knows what - * badness that will cause. - */ - hdr->flags = 0; - iowrite32(qp->rx_index, &qp->rx_info->entry); - - return rc; } static void ntb_transport_rxc_db(unsigned long data) @@ -1333,7 +1373,7 @@ static void ntb_transport_rxc_db(unsigned long data) break; } - if (qp->dma_chan) + if (i && qp->dma_chan) dma_async_issue_pending(qp->dma_chan); if (i == qp->rx_max_entry) { @@ -1609,7 +1649,7 @@ ntb_transport_create_queue(void *data, struct device *client_dev, goto err1; entry->qp = qp; - ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry, + ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_free_q); } @@ -1634,7 +1674,7 @@ err2: while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q))) kfree(entry); err1: - while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) + while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) kfree(entry); if (qp->dma_chan) dma_release_channel(qp->dma_chan); @@ -1652,7 +1692,6 @@ EXPORT_SYMBOL_GPL(ntb_transport_create_queue); */ void ntb_transport_free_queue(struct ntb_transport_qp *qp) { - struct ntb_transport_ctx *nt = qp->transport; struct pci_dev *pdev; struct ntb_queue_entry *entry; u64 qp_bit; @@ -1689,18 +1728,23 @@ void ntb_transport_free_queue(struct ntb_transport_qp *qp) qp->tx_handler = NULL; qp->event_handler = NULL; - while ((entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q))) + while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q))) kfree(entry); - while ((entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q))) { - dev_warn(&pdev->dev, "Freeing item from a non-empty queue\n"); + while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_pend_q))) { + dev_warn(&pdev->dev, "Freeing item from non-empty rx_pend_q\n"); + kfree(entry); + } + + while ((entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_post_q))) { + dev_warn(&pdev->dev, "Freeing item from non-empty rx_post_q\n"); kfree(entry); } while ((entry = ntb_list_rm(&qp->ntb_tx_free_q_lock, &qp->tx_free_q))) kfree(entry); - nt->qp_bitmap_free |= qp_bit; + qp->transport->qp_bitmap_free |= qp_bit; dev_info(&pdev->dev, "NTB Transport QP %d freed\n", qp->qp_num); } @@ -1724,14 +1768,14 @@ void *ntb_transport_rx_remove(struct ntb_transport_qp *qp, unsigned int *len) if (!qp || qp->client_ready) return NULL; - entry = ntb_list_rm(&qp->ntb_rx_pend_q_lock, &qp->rx_pend_q); + entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_pend_q); if (!entry) return NULL; buf = entry->cb_data; *len = entry->len; - ntb_list_add(&qp->ntb_rx_free_q_lock, &entry->entry, &qp->rx_free_q); + ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_free_q); return buf; } @@ -1757,15 +1801,18 @@ int ntb_transport_rx_enqueue(struct ntb_transport_qp *qp, void *cb, void *data, if (!qp) return -EINVAL; - entry = ntb_list_rm(&qp->ntb_rx_free_q_lock, &qp->rx_free_q); + entry = ntb_list_rm(&qp->ntb_rx_q_lock, &qp->rx_free_q); if (!entry) return -ENOMEM; entry->cb_data = cb; entry->buf = data; entry->len = len; + entry->flags = 0; + + ntb_list_add(&qp->ntb_rx_q_lock, &entry->entry, &qp->rx_pend_q); - ntb_list_add(&qp->ntb_rx_pend_q_lock, &entry->entry, &qp->rx_pend_q); + tasklet_schedule(&qp->rxc_db_work); return 0; } diff --git a/drivers/nvmem/Kconfig b/drivers/nvmem/Kconfig new file mode 100644 index 000000000000..8db297821f78 --- /dev/null +++ b/drivers/nvmem/Kconfig @@ -0,0 +1,39 @@ +menuconfig NVMEM + tristate "NVMEM Support" + select REGMAP + help + Support for NVMEM(Non Volatile Memory) devices like EEPROM, EFUSES... + + This framework is designed to provide a generic interface to NVMEM + from both the Linux Kernel and the userspace. + + This driver can also be built as a module. If so, the module + will be called nvmem_core. + + If unsure, say no. + +if NVMEM + +config QCOM_QFPROM + tristate "QCOM QFPROM Support" + depends on ARCH_QCOM || COMPILE_TEST + select REGMAP_MMIO + help + Say y here to enable QFPROM support. The QFPROM provides access + functions for QFPROM data to rest of the drivers via nvmem interface. + + This driver can also be built as a module. If so, the module + will be called nvmem_qfprom. + +config NVMEM_SUNXI_SID + tristate "Allwinner SoCs SID support" + depends on ARCH_SUNXI + select REGMAP_MMIO + help + This is a driver for the 'security ID' available on various Allwinner + devices. + + This driver can also be built as a module. If so, the module + will be called nvmem_sunxi_sid. + +endif diff --git a/drivers/nvmem/Makefile b/drivers/nvmem/Makefile new file mode 100644 index 000000000000..4328b930ad9a --- /dev/null +++ b/drivers/nvmem/Makefile @@ -0,0 +1,12 @@ +# +# Makefile for nvmem drivers. +# + +obj-$(CONFIG_NVMEM) += nvmem_core.o +nvmem_core-y := core.o + +# Devices +obj-$(CONFIG_QCOM_QFPROM) += nvmem_qfprom.o +nvmem_qfprom-y := qfprom.o +obj-$(CONFIG_NVMEM_SUNXI_SID) += nvmem_sunxi_sid.o +nvmem_sunxi_sid-y := sunxi_sid.o diff --git a/drivers/nvmem/core.c b/drivers/nvmem/core.c new file mode 100644 index 000000000000..d3c6676b3c0c --- /dev/null +++ b/drivers/nvmem/core.c @@ -0,0 +1,1083 @@ +/* + * nvmem framework core. + * + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.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 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/export.h> +#include <linux/fs.h> +#include <linux/idr.h> +#include <linux/init.h> +#include <linux/module.h> +#include <linux/nvmem-consumer.h> +#include <linux/nvmem-provider.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/slab.h> + +struct nvmem_device { + const char *name; + struct regmap *regmap; + struct module *owner; + struct device dev; + int stride; + int word_size; + int ncells; + int id; + int users; + size_t size; + bool read_only; +}; + +struct nvmem_cell { + const char *name; + int offset; + int bytes; + int bit_offset; + int nbits; + struct nvmem_device *nvmem; + struct list_head node; +}; + +static DEFINE_MUTEX(nvmem_mutex); +static DEFINE_IDA(nvmem_ida); + +static LIST_HEAD(nvmem_cells); +static DEFINE_MUTEX(nvmem_cells_mutex); + +#define to_nvmem_device(d) container_of(d, struct nvmem_device, dev) + +static ssize_t bin_attr_nvmem_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nvmem_device *nvmem = to_nvmem_device(dev); + int rc; + + /* Stop the user from reading */ + if (pos > nvmem->size) + return 0; + + if (pos + count > nvmem->size) + count = nvmem->size - pos; + + count = round_down(count, nvmem->word_size); + + rc = regmap_raw_read(nvmem->regmap, pos, buf, count); + + if (IS_ERR_VALUE(rc)) + return rc; + + return count; +} + +static ssize_t bin_attr_nvmem_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t pos, size_t count) +{ + struct device *dev = container_of(kobj, struct device, kobj); + struct nvmem_device *nvmem = to_nvmem_device(dev); + int rc; + + /* Stop the user from writing */ + if (pos > nvmem->size) + return 0; + + if (pos + count > nvmem->size) + count = nvmem->size - pos; + + count = round_down(count, nvmem->word_size); + + rc = regmap_raw_write(nvmem->regmap, pos, buf, count); + + if (IS_ERR_VALUE(rc)) + return rc; + + return count; +} + +/* default read/write permissions */ +static struct bin_attribute bin_attr_rw_nvmem = { + .attr = { + .name = "nvmem", + .mode = S_IWUSR | S_IRUGO, + }, + .read = bin_attr_nvmem_read, + .write = bin_attr_nvmem_write, +}; + +static struct bin_attribute *nvmem_bin_rw_attributes[] = { + &bin_attr_rw_nvmem, + NULL, +}; + +static const struct attribute_group nvmem_bin_rw_group = { + .bin_attrs = nvmem_bin_rw_attributes, +}; + +static const struct attribute_group *nvmem_rw_dev_groups[] = { + &nvmem_bin_rw_group, + NULL, +}; + +/* read only permission */ +static struct bin_attribute bin_attr_ro_nvmem = { + .attr = { + .name = "nvmem", + .mode = S_IRUGO, + }, + .read = bin_attr_nvmem_read, +}; + +static struct bin_attribute *nvmem_bin_ro_attributes[] = { + &bin_attr_ro_nvmem, + NULL, +}; + +static const struct attribute_group nvmem_bin_ro_group = { + .bin_attrs = nvmem_bin_ro_attributes, +}; + +static const struct attribute_group *nvmem_ro_dev_groups[] = { + &nvmem_bin_ro_group, + NULL, +}; + +static void nvmem_release(struct device *dev) +{ + struct nvmem_device *nvmem = to_nvmem_device(dev); + + ida_simple_remove(&nvmem_ida, nvmem->id); + kfree(nvmem); +} + +static const struct device_type nvmem_provider_type = { + .release = nvmem_release, +}; + +static struct bus_type nvmem_bus_type = { + .name = "nvmem", +}; + +static int of_nvmem_match(struct device *dev, void *nvmem_np) +{ + return dev->of_node == nvmem_np; +} + +static struct nvmem_device *of_nvmem_find(struct device_node *nvmem_np) +{ + struct device *d; + + if (!nvmem_np) + return NULL; + + d = bus_find_device(&nvmem_bus_type, NULL, nvmem_np, of_nvmem_match); + + if (!d) + return NULL; + + return to_nvmem_device(d); +} + +static struct nvmem_cell *nvmem_find_cell(const char *cell_id) +{ + struct nvmem_cell *p; + + list_for_each_entry(p, &nvmem_cells, node) + if (p && !strcmp(p->name, cell_id)) + return p; + + return NULL; +} + +static void nvmem_cell_drop(struct nvmem_cell *cell) +{ + mutex_lock(&nvmem_cells_mutex); + list_del(&cell->node); + mutex_unlock(&nvmem_cells_mutex); + kfree(cell); +} + +static void nvmem_device_remove_all_cells(const struct nvmem_device *nvmem) +{ + struct nvmem_cell *cell; + struct list_head *p, *n; + + list_for_each_safe(p, n, &nvmem_cells) { + cell = list_entry(p, struct nvmem_cell, node); + if (cell->nvmem == nvmem) + nvmem_cell_drop(cell); + } +} + +static void nvmem_cell_add(struct nvmem_cell *cell) +{ + mutex_lock(&nvmem_cells_mutex); + list_add_tail(&cell->node, &nvmem_cells); + mutex_unlock(&nvmem_cells_mutex); +} + +static int nvmem_cell_info_to_nvmem_cell(struct nvmem_device *nvmem, + const struct nvmem_cell_info *info, + struct nvmem_cell *cell) +{ + cell->nvmem = nvmem; + cell->offset = info->offset; + cell->bytes = info->bytes; + cell->name = info->name; + + cell->bit_offset = info->bit_offset; + cell->nbits = info->nbits; + + if (cell->nbits) + cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, + BITS_PER_BYTE); + + if (!IS_ALIGNED(cell->offset, nvmem->stride)) { + dev_err(&nvmem->dev, + "cell %s unaligned to nvmem stride %d\n", + cell->name, nvmem->stride); + return -EINVAL; + } + + return 0; +} + +static int nvmem_add_cells(struct nvmem_device *nvmem, + const struct nvmem_config *cfg) +{ + struct nvmem_cell **cells; + const struct nvmem_cell_info *info = cfg->cells; + int i, rval; + + cells = kcalloc(cfg->ncells, sizeof(*cells), GFP_KERNEL); + if (!cells) + return -ENOMEM; + + for (i = 0; i < cfg->ncells; i++) { + cells[i] = kzalloc(sizeof(**cells), GFP_KERNEL); + if (!cells[i]) { + rval = -ENOMEM; + goto err; + } + + rval = nvmem_cell_info_to_nvmem_cell(nvmem, &info[i], cells[i]); + if (IS_ERR_VALUE(rval)) { + kfree(cells[i]); + goto err; + } + + nvmem_cell_add(cells[i]); + } + + nvmem->ncells = cfg->ncells; + /* remove tmp array */ + kfree(cells); + + return 0; +err: + while (--i) + nvmem_cell_drop(cells[i]); + + return rval; +} + +/** + * nvmem_register() - Register a nvmem device for given nvmem_config. + * Also creates an binary entry in /sys/bus/nvmem/devices/dev-name/nvmem + * + * @config: nvmem device configuration with which nvmem device is created. + * + * Return: Will be an ERR_PTR() on error or a valid pointer to nvmem_device + * on success. + */ + +struct nvmem_device *nvmem_register(const struct nvmem_config *config) +{ + struct nvmem_device *nvmem; + struct device_node *np; + struct regmap *rm; + int rval; + + if (!config->dev) + return ERR_PTR(-EINVAL); + + rm = dev_get_regmap(config->dev, NULL); + if (!rm) { + dev_err(config->dev, "Regmap not found\n"); + return ERR_PTR(-EINVAL); + } + + nvmem = kzalloc(sizeof(*nvmem), GFP_KERNEL); + if (!nvmem) + return ERR_PTR(-ENOMEM); + + rval = ida_simple_get(&nvmem_ida, 0, 0, GFP_KERNEL); + if (rval < 0) { + kfree(nvmem); + return ERR_PTR(rval); + } + + nvmem->id = rval; + nvmem->regmap = rm; + nvmem->owner = config->owner; + nvmem->stride = regmap_get_reg_stride(rm); + nvmem->word_size = regmap_get_val_bytes(rm); + nvmem->size = regmap_get_max_register(rm) + nvmem->stride; + nvmem->dev.type = &nvmem_provider_type; + nvmem->dev.bus = &nvmem_bus_type; + nvmem->dev.parent = config->dev; + np = config->dev->of_node; + nvmem->dev.of_node = np; + dev_set_name(&nvmem->dev, "%s%d", + config->name ? : "nvmem", config->id); + + nvmem->read_only = of_property_read_bool(np, "read-only") | + config->read_only; + + nvmem->dev.groups = nvmem->read_only ? nvmem_ro_dev_groups : + nvmem_rw_dev_groups; + + device_initialize(&nvmem->dev); + + dev_dbg(&nvmem->dev, "Registering nvmem device %s\n", config->name); + + rval = device_add(&nvmem->dev); + if (rval) { + ida_simple_remove(&nvmem_ida, nvmem->id); + kfree(nvmem); + return ERR_PTR(rval); + } + + if (config->cells) + nvmem_add_cells(nvmem, config); + + return nvmem; +} +EXPORT_SYMBOL_GPL(nvmem_register); + +/** + * nvmem_unregister() - Unregister previously registered nvmem device + * + * @nvmem: Pointer to previously registered nvmem device. + * + * Return: Will be an negative on error or a zero on success. + */ +int nvmem_unregister(struct nvmem_device *nvmem) +{ + mutex_lock(&nvmem_mutex); + if (nvmem->users) { + mutex_unlock(&nvmem_mutex); + return -EBUSY; + } + mutex_unlock(&nvmem_mutex); + + nvmem_device_remove_all_cells(nvmem); + device_del(&nvmem->dev); + + return 0; +} +EXPORT_SYMBOL_GPL(nvmem_unregister); + +static struct nvmem_device *__nvmem_device_get(struct device_node *np, + struct nvmem_cell **cellp, + const char *cell_id) +{ + struct nvmem_device *nvmem = NULL; + + mutex_lock(&nvmem_mutex); + + if (np) { + nvmem = of_nvmem_find(np); + if (!nvmem) { + mutex_unlock(&nvmem_mutex); + return ERR_PTR(-EPROBE_DEFER); + } + } else { + struct nvmem_cell *cell = nvmem_find_cell(cell_id); + + if (cell) { + nvmem = cell->nvmem; + *cellp = cell; + } + + if (!nvmem) { + mutex_unlock(&nvmem_mutex); + return ERR_PTR(-ENOENT); + } + } + + nvmem->users++; + mutex_unlock(&nvmem_mutex); + + if (!try_module_get(nvmem->owner)) { + dev_err(&nvmem->dev, + "could not increase module refcount for cell %s\n", + nvmem->name); + + mutex_lock(&nvmem_mutex); + nvmem->users--; + mutex_unlock(&nvmem_mutex); + + return ERR_PTR(-EINVAL); + } + + return nvmem; +} + +static void __nvmem_device_put(struct nvmem_device *nvmem) +{ + module_put(nvmem->owner); + mutex_lock(&nvmem_mutex); + nvmem->users--; + mutex_unlock(&nvmem_mutex); +} + +static int nvmem_match(struct device *dev, void *data) +{ + return !strcmp(dev_name(dev), data); +} + +static struct nvmem_device *nvmem_find(const char *name) +{ + struct device *d; + + d = bus_find_device(&nvmem_bus_type, NULL, (void *)name, nvmem_match); + + if (!d) + return NULL; + + return to_nvmem_device(d); +} + +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +/** + * of_nvmem_device_get() - Get nvmem device from a given id + * + * @dev node: Device tree node that uses the nvmem device + * @id: nvmem name from nvmem-names property. + * + * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device + * on success. + */ +struct nvmem_device *of_nvmem_device_get(struct device_node *np, const char *id) +{ + + struct device_node *nvmem_np; + int index; + + index = of_property_match_string(np, "nvmem-names", id); + + nvmem_np = of_parse_phandle(np, "nvmem", index); + if (!nvmem_np) + return ERR_PTR(-EINVAL); + + return __nvmem_device_get(nvmem_np, NULL, NULL); +} +EXPORT_SYMBOL_GPL(of_nvmem_device_get); +#endif + +/** + * nvmem_device_get() - Get nvmem device from a given id + * + * @dev : Device that uses the nvmem device + * @id: nvmem name from nvmem-names property. + * + * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_device + * on success. + */ +struct nvmem_device *nvmem_device_get(struct device *dev, const char *dev_name) +{ + if (dev->of_node) { /* try dt first */ + struct nvmem_device *nvmem; + + nvmem = of_nvmem_device_get(dev->of_node, dev_name); + + if (!IS_ERR(nvmem) || PTR_ERR(nvmem) == -EPROBE_DEFER) + return nvmem; + + } + + return nvmem_find(dev_name); +} +EXPORT_SYMBOL_GPL(nvmem_device_get); + +static int devm_nvmem_device_match(struct device *dev, void *res, void *data) +{ + struct nvmem_device **nvmem = res; + + if (WARN_ON(!nvmem || !*nvmem)) + return 0; + + return *nvmem == data; +} + +static void devm_nvmem_device_release(struct device *dev, void *res) +{ + nvmem_device_put(*(struct nvmem_device **)res); +} + +/** + * devm_nvmem_device_put() - put alredy got nvmem device + * + * @nvmem: pointer to nvmem device allocated by devm_nvmem_cell_get(), + * that needs to be released. + */ +void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem) +{ + int ret; + + ret = devres_release(dev, devm_nvmem_device_release, + devm_nvmem_device_match, nvmem); + + WARN_ON(ret); +} +EXPORT_SYMBOL_GPL(devm_nvmem_device_put); + +/** + * nvmem_device_put() - put alredy got nvmem device + * + * @nvmem: pointer to nvmem device that needs to be released. + */ +void nvmem_device_put(struct nvmem_device *nvmem) +{ + __nvmem_device_put(nvmem); +} +EXPORT_SYMBOL_GPL(nvmem_device_put); + +/** + * devm_nvmem_device_get() - Get nvmem cell of device form a given id + * + * @dev node: Device tree node that uses the nvmem cell + * @id: nvmem name in nvmems property. + * + * Return: ERR_PTR() on error or a valid pointer to a struct nvmem_cell + * on success. The nvmem_cell will be freed by the automatically once the + * device is freed. + */ +struct nvmem_device *devm_nvmem_device_get(struct device *dev, const char *id) +{ + struct nvmem_device **ptr, *nvmem; + + ptr = devres_alloc(devm_nvmem_device_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + nvmem = nvmem_device_get(dev, id); + if (!IS_ERR(nvmem)) { + *ptr = nvmem; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return nvmem; +} +EXPORT_SYMBOL_GPL(devm_nvmem_device_get); + +static struct nvmem_cell *nvmem_cell_get_from_list(const char *cell_id) +{ + struct nvmem_cell *cell = NULL; + struct nvmem_device *nvmem; + + nvmem = __nvmem_device_get(NULL, &cell, cell_id); + if (IS_ERR(nvmem)) + return ERR_CAST(nvmem); + + return cell; +} + +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +/** + * of_nvmem_cell_get() - Get a nvmem cell from given device node and cell id + * + * @dev node: Device tree node that uses the nvmem cell + * @id: nvmem cell name from nvmem-cell-names property. + * + * Return: Will be an ERR_PTR() on error or a valid pointer + * to a struct nvmem_cell. The nvmem_cell will be freed by the + * nvmem_cell_put(). + */ +struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *name) +{ + struct device_node *cell_np, *nvmem_np; + struct nvmem_cell *cell; + struct nvmem_device *nvmem; + const __be32 *addr; + int rval, len, index; + + index = of_property_match_string(np, "nvmem-cell-names", name); + + cell_np = of_parse_phandle(np, "nvmem-cells", index); + if (!cell_np) + return ERR_PTR(-EINVAL); + + nvmem_np = of_get_next_parent(cell_np); + if (!nvmem_np) + return ERR_PTR(-EINVAL); + + nvmem = __nvmem_device_get(nvmem_np, NULL, NULL); + if (IS_ERR(nvmem)) + return ERR_CAST(nvmem); + + addr = of_get_property(cell_np, "reg", &len); + if (!addr || (len < 2 * sizeof(u32))) { + dev_err(&nvmem->dev, "nvmem: invalid reg on %s\n", + cell_np->full_name); + rval = -EINVAL; + goto err_mem; + } + + cell = kzalloc(sizeof(*cell), GFP_KERNEL); + if (!cell) { + rval = -ENOMEM; + goto err_mem; + } + + cell->nvmem = nvmem; + cell->offset = be32_to_cpup(addr++); + cell->bytes = be32_to_cpup(addr); + cell->name = cell_np->name; + + addr = of_get_property(cell_np, "bits", &len); + if (addr && len == (2 * sizeof(u32))) { + cell->bit_offset = be32_to_cpup(addr++); + cell->nbits = be32_to_cpup(addr); + } + + if (cell->nbits) + cell->bytes = DIV_ROUND_UP(cell->nbits + cell->bit_offset, + BITS_PER_BYTE); + + if (!IS_ALIGNED(cell->offset, nvmem->stride)) { + dev_err(&nvmem->dev, + "cell %s unaligned to nvmem stride %d\n", + cell->name, nvmem->stride); + rval = -EINVAL; + goto err_sanity; + } + + nvmem_cell_add(cell); + + return cell; + +err_sanity: + kfree(cell); + +err_mem: + __nvmem_device_put(nvmem); + + return ERR_PTR(rval); +} +EXPORT_SYMBOL_GPL(of_nvmem_cell_get); +#endif + +/** + * nvmem_cell_get() - Get nvmem cell of device form a given cell name + * + * @dev node: Device tree node that uses the nvmem cell + * @id: nvmem cell name to get. + * + * Return: Will be an ERR_PTR() on error or a valid pointer + * to a struct nvmem_cell. The nvmem_cell will be freed by the + * nvmem_cell_put(). + */ +struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *cell_id) +{ + struct nvmem_cell *cell; + + if (dev->of_node) { /* try dt first */ + cell = of_nvmem_cell_get(dev->of_node, cell_id); + if (!IS_ERR(cell) || PTR_ERR(cell) == -EPROBE_DEFER) + return cell; + } + + return nvmem_cell_get_from_list(cell_id); +} +EXPORT_SYMBOL_GPL(nvmem_cell_get); + +static void devm_nvmem_cell_release(struct device *dev, void *res) +{ + nvmem_cell_put(*(struct nvmem_cell **)res); +} + +/** + * devm_nvmem_cell_get() - Get nvmem cell of device form a given id + * + * @dev node: Device tree node that uses the nvmem cell + * @id: nvmem id in nvmem-names property. + * + * Return: Will be an ERR_PTR() on error or a valid pointer + * to a struct nvmem_cell. The nvmem_cell will be freed by the + * automatically once the device is freed. + */ +struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *id) +{ + struct nvmem_cell **ptr, *cell; + + ptr = devres_alloc(devm_nvmem_cell_release, sizeof(*ptr), GFP_KERNEL); + if (!ptr) + return ERR_PTR(-ENOMEM); + + cell = nvmem_cell_get(dev, id); + if (!IS_ERR(cell)) { + *ptr = cell; + devres_add(dev, ptr); + } else { + devres_free(ptr); + } + + return cell; +} +EXPORT_SYMBOL_GPL(devm_nvmem_cell_get); + +static int devm_nvmem_cell_match(struct device *dev, void *res, void *data) +{ + struct nvmem_cell **c = res; + + if (WARN_ON(!c || !*c)) + return 0; + + return *c == data; +} + +/** + * devm_nvmem_cell_put() - Release previously allocated nvmem cell + * from devm_nvmem_cell_get. + * + * @cell: Previously allocated nvmem cell by devm_nvmem_cell_get() + */ +void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell) +{ + int ret; + + ret = devres_release(dev, devm_nvmem_cell_release, + devm_nvmem_cell_match, cell); + + WARN_ON(ret); +} +EXPORT_SYMBOL(devm_nvmem_cell_put); + +/** + * nvmem_cell_put() - Release previously allocated nvmem cell. + * + * @cell: Previously allocated nvmem cell by nvmem_cell_get() + */ +void nvmem_cell_put(struct nvmem_cell *cell) +{ + struct nvmem_device *nvmem = cell->nvmem; + + __nvmem_device_put(nvmem); + nvmem_cell_drop(cell); +} +EXPORT_SYMBOL_GPL(nvmem_cell_put); + +static inline void nvmem_shift_read_buffer_in_place(struct nvmem_cell *cell, + void *buf) +{ + u8 *p, *b; + int i, bit_offset = cell->bit_offset; + + p = b = buf; + if (bit_offset) { + /* First shift */ + *b++ >>= bit_offset; + + /* setup rest of the bytes if any */ + for (i = 1; i < cell->bytes; i++) { + /* Get bits from next byte and shift them towards msb */ + *p |= *b << (BITS_PER_BYTE - bit_offset); + + p = b; + *b++ >>= bit_offset; + } + + /* result fits in less bytes */ + if (cell->bytes != DIV_ROUND_UP(cell->nbits, BITS_PER_BYTE)) + *p-- = 0; + } + /* clear msb bits if any leftover in the last byte */ + *p &= GENMASK((cell->nbits%BITS_PER_BYTE) - 1, 0); +} + +static int __nvmem_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell *cell, + void *buf, size_t *len) +{ + int rc; + + rc = regmap_raw_read(nvmem->regmap, cell->offset, buf, cell->bytes); + + if (IS_ERR_VALUE(rc)) + return rc; + + /* shift bits in-place */ + if (cell->bit_offset || cell->bit_offset) + nvmem_shift_read_buffer_in_place(cell, buf); + + *len = cell->bytes; + + return 0; +} + +/** + * nvmem_cell_read() - Read a given nvmem cell + * + * @cell: nvmem cell to be read. + * @len: pointer to length of cell which will be populated on successful read. + * + * Return: ERR_PTR() on error or a valid pointer to a char * buffer on success. + * The buffer should be freed by the consumer with a kfree(). + */ +void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) +{ + struct nvmem_device *nvmem = cell->nvmem; + u8 *buf; + int rc; + + if (!nvmem || !nvmem->regmap) + return ERR_PTR(-EINVAL); + + buf = kzalloc(cell->bytes, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + rc = __nvmem_cell_read(nvmem, cell, buf, len); + if (IS_ERR_VALUE(rc)) { + kfree(buf); + return ERR_PTR(rc); + } + + return buf; +} +EXPORT_SYMBOL_GPL(nvmem_cell_read); + +static inline void *nvmem_cell_prepare_write_buffer(struct nvmem_cell *cell, + u8 *_buf, int len) +{ + struct nvmem_device *nvmem = cell->nvmem; + int i, rc, nbits, bit_offset = cell->bit_offset; + u8 v, *p, *buf, *b, pbyte, pbits; + + nbits = cell->nbits; + buf = kzalloc(cell->bytes, GFP_KERNEL); + if (!buf) + return ERR_PTR(-ENOMEM); + + memcpy(buf, _buf, len); + p = b = buf; + + if (bit_offset) { + pbyte = *b; + *b <<= bit_offset; + + /* setup the first byte with lsb bits from nvmem */ + rc = regmap_raw_read(nvmem->regmap, cell->offset, &v, 1); + *b++ |= GENMASK(bit_offset - 1, 0) & v; + + /* setup rest of the byte if any */ + for (i = 1; i < cell->bytes; i++) { + /* Get last byte bits and shift them towards lsb */ + pbits = pbyte >> (BITS_PER_BYTE - 1 - bit_offset); + pbyte = *b; + p = b; + *b <<= bit_offset; + *b++ |= pbits; + } + } + + /* if it's not end on byte boundary */ + if ((nbits + bit_offset) % BITS_PER_BYTE) { + /* setup the last byte with msb bits from nvmem */ + rc = regmap_raw_read(nvmem->regmap, + cell->offset + cell->bytes - 1, &v, 1); + *p |= GENMASK(7, (nbits + bit_offset) % BITS_PER_BYTE) & v; + + } + + return buf; +} + +/** + * nvmem_cell_write() - Write to a given nvmem cell + * + * @cell: nvmem cell to be written. + * @buf: Buffer to be written. + * @len: length of buffer to be written to nvmem cell. + * + * Return: length of bytes written or negative on failure. + */ +int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len) +{ + struct nvmem_device *nvmem = cell->nvmem; + int rc; + + if (!nvmem || !nvmem->regmap || nvmem->read_only || + (cell->bit_offset == 0 && len != cell->bytes)) + return -EINVAL; + + if (cell->bit_offset || cell->nbits) { + buf = nvmem_cell_prepare_write_buffer(cell, buf, len); + if (IS_ERR(buf)) + return PTR_ERR(buf); + } + + rc = regmap_raw_write(nvmem->regmap, cell->offset, buf, cell->bytes); + + /* free the tmp buffer */ + if (cell->bit_offset) + kfree(buf); + + if (IS_ERR_VALUE(rc)) + return rc; + + return len; +} +EXPORT_SYMBOL_GPL(nvmem_cell_write); + +/** + * nvmem_device_cell_read() - Read a given nvmem device and cell + * + * @nvmem: nvmem device to read from. + * @info: nvmem cell info to be read. + * @buf: buffer pointer which will be populated on successful read. + * + * Return: length of successful bytes read on success and negative + * error code on error. + */ +ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf) +{ + struct nvmem_cell cell; + int rc; + ssize_t len; + + if (!nvmem || !nvmem->regmap) + return -EINVAL; + + rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell); + if (IS_ERR_VALUE(rc)) + return rc; + + rc = __nvmem_cell_read(nvmem, &cell, buf, &len); + if (IS_ERR_VALUE(rc)) + return rc; + + return len; +} +EXPORT_SYMBOL_GPL(nvmem_device_cell_read); + +/** + * nvmem_device_cell_write() - Write cell to a given nvmem device + * + * @nvmem: nvmem device to be written to. + * @info: nvmem cell info to be written + * @buf: buffer to be written to cell. + * + * Return: length of bytes written or negative error code on failure. + * */ +int nvmem_device_cell_write(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf) +{ + struct nvmem_cell cell; + int rc; + + if (!nvmem || !nvmem->regmap) + return -EINVAL; + + rc = nvmem_cell_info_to_nvmem_cell(nvmem, info, &cell); + if (IS_ERR_VALUE(rc)) + return rc; + + return nvmem_cell_write(&cell, buf, cell.bytes); +} +EXPORT_SYMBOL_GPL(nvmem_device_cell_write); + +/** + * nvmem_device_read() - Read from a given nvmem device + * + * @nvmem: nvmem device to read from. + * @offset: offset in nvmem device. + * @bytes: number of bytes to read. + * @buf: buffer pointer which will be populated on successful read. + * + * Return: length of successful bytes read on success and negative + * error code on error. + */ +int nvmem_device_read(struct nvmem_device *nvmem, + unsigned int offset, + size_t bytes, void *buf) +{ + int rc; + + if (!nvmem || !nvmem->regmap) + return -EINVAL; + + rc = regmap_raw_read(nvmem->regmap, offset, buf, bytes); + + if (IS_ERR_VALUE(rc)) + return rc; + + return bytes; +} +EXPORT_SYMBOL_GPL(nvmem_device_read); + +/** + * nvmem_device_write() - Write cell to a given nvmem device + * + * @nvmem: nvmem device to be written to. + * @offset: offset in nvmem device. + * @bytes: number of bytes to write. + * @buf: buffer to be written. + * + * Return: length of bytes written or negative error code on failure. + * */ +int nvmem_device_write(struct nvmem_device *nvmem, + unsigned int offset, + size_t bytes, void *buf) +{ + int rc; + + if (!nvmem || !nvmem->regmap) + return -EINVAL; + + rc = regmap_raw_write(nvmem->regmap, offset, buf, bytes); + + if (IS_ERR_VALUE(rc)) + return rc; + + + return bytes; +} +EXPORT_SYMBOL_GPL(nvmem_device_write); + +static int __init nvmem_init(void) +{ + return bus_register(&nvmem_bus_type); +} + +static void __exit nvmem_exit(void) +{ + bus_unregister(&nvmem_bus_type); +} + +subsys_initcall(nvmem_init); +module_exit(nvmem_exit); + +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org"); +MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com"); +MODULE_DESCRIPTION("nvmem Driver Core"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/qfprom.c b/drivers/nvmem/qfprom.c new file mode 100644 index 000000000000..afb67e7eeee4 --- /dev/null +++ b/drivers/nvmem/qfprom.c @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/device.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +static struct regmap_config qfprom_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 1, +}; + +static struct nvmem_config econfig = { + .name = "qfprom", + .owner = THIS_MODULE, +}; + +static int qfprom_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static int qfprom_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nvmem_device *nvmem; + struct regmap *regmap; + void __iomem *base; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + qfprom_regmap_config.max_register = resource_size(res) - 1; + + regmap = devm_regmap_init_mmio(dev, base, &qfprom_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(regmap); + } + econfig.dev = dev; + nvmem = nvmem_register(&econfig); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static const struct of_device_id qfprom_of_match[] = { + { .compatible = "qcom,qfprom",}, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, qfprom_of_match); + +static struct platform_driver qfprom_driver = { + .probe = qfprom_probe, + .remove = qfprom_remove, + .driver = { + .name = "qcom,qfprom", + .of_match_table = qfprom_of_match, + }, +}; +module_platform_driver(qfprom_driver); +MODULE_AUTHOR("Srinivas Kandagatla <srinivas.kandagatla@linaro.org>"); +MODULE_DESCRIPTION("Qualcomm QFPROM driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/nvmem/sunxi_sid.c b/drivers/nvmem/sunxi_sid.c new file mode 100644 index 000000000000..14777dd5212d --- /dev/null +++ b/drivers/nvmem/sunxi_sid.c @@ -0,0 +1,171 @@ +/* + * Allwinner sunXi SoCs Security ID support. + * + * Copyright (c) 2013 Oliver Schinagl <oliver@schinagl.nl> + * Copyright (C) 2014 Maxime Ripard <maxime.ripard@free-electrons.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + + +#include <linux/device.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/random.h> + + +static struct nvmem_config econfig = { + .name = "sunxi-sid", + .read_only = true, + .owner = THIS_MODULE, +}; + +struct sunxi_sid { + void __iomem *base; +}; + +/* We read the entire key, due to a 32 bit read alignment requirement. Since we + * want to return the requested byte, this results in somewhat slower code and + * uses 4 times more reads as needed but keeps code simpler. Since the SID is + * only very rarely probed, this is not really an issue. + */ +static u8 sunxi_sid_read_byte(const struct sunxi_sid *sid, + const unsigned int offset) +{ + u32 sid_key; + + sid_key = ioread32be(sid->base + round_down(offset, 4)); + sid_key >>= (offset % 4) * 8; + + return sid_key; /* Only return the last byte */ +} + +static int sunxi_sid_read(void *context, + const void *reg, size_t reg_size, + void *val, size_t val_size) +{ + struct sunxi_sid *sid = context; + unsigned int offset = *(u32 *)reg; + u8 *buf = val; + + while (val_size) { + *buf++ = sunxi_sid_read_byte(sid, offset); + val_size--; + offset++; + } + + return 0; +} + +static int sunxi_sid_write(void *context, const void *data, size_t count) +{ + /* Unimplemented, dummy to keep regmap core happy */ + return 0; +} + +static struct regmap_bus sunxi_sid_bus = { + .read = sunxi_sid_read, + .write = sunxi_sid_write, + .reg_format_endian_default = REGMAP_ENDIAN_NATIVE, + .val_format_endian_default = REGMAP_ENDIAN_NATIVE, +}; + +static bool sunxi_sid_writeable_reg(struct device *dev, unsigned int reg) +{ + return false; +} + +static struct regmap_config sunxi_sid_regmap_config = { + .reg_bits = 32, + .val_bits = 8, + .reg_stride = 1, + .writeable_reg = sunxi_sid_writeable_reg, +}; + +static int sunxi_sid_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct resource *res; + struct nvmem_device *nvmem; + struct regmap *regmap; + struct sunxi_sid *sid; + int i, size; + char *randomness; + + sid = devm_kzalloc(dev, sizeof(*sid), GFP_KERNEL); + if (!sid) + return -ENOMEM; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + sid->base = devm_ioremap_resource(dev, res); + if (IS_ERR(sid->base)) + return PTR_ERR(sid->base); + + size = resource_size(res) - 1; + sunxi_sid_regmap_config.max_register = size; + + regmap = devm_regmap_init(dev, &sunxi_sid_bus, sid, + &sunxi_sid_regmap_config); + if (IS_ERR(regmap)) { + dev_err(dev, "regmap init failed\n"); + return PTR_ERR(regmap); + } + + econfig.dev = dev; + nvmem = nvmem_register(&econfig); + if (IS_ERR(nvmem)) + return PTR_ERR(nvmem); + + randomness = kzalloc(sizeof(u8) * size, GFP_KERNEL); + for (i = 0; i < size; i++) + randomness[i] = sunxi_sid_read_byte(sid, i); + + add_device_randomness(randomness, size); + kfree(randomness); + + platform_set_drvdata(pdev, nvmem); + + return 0; +} + +static int sunxi_sid_remove(struct platform_device *pdev) +{ + struct nvmem_device *nvmem = platform_get_drvdata(pdev); + + return nvmem_unregister(nvmem); +} + +static const struct of_device_id sunxi_sid_of_match[] = { + { .compatible = "allwinner,sun4i-a10-sid" }, + { .compatible = "allwinner,sun7i-a20-sid" }, + {/* sentinel */}, +}; +MODULE_DEVICE_TABLE(of, sunxi_sid_of_match); + +static struct platform_driver sunxi_sid_driver = { + .probe = sunxi_sid_probe, + .remove = sunxi_sid_remove, + .driver = { + .name = "eeprom-sunxi-sid", + .of_match_table = sunxi_sid_of_match, + }, +}; +module_platform_driver(sunxi_sid_driver); + +MODULE_AUTHOR("Oliver Schinagl <oliver@schinagl.nl>"); +MODULE_DESCRIPTION("Allwinner sunxi security id driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index 73de4efcbe6e..944f50015ed0 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -2,7 +2,7 @@ # PCI configuration # config PCI_BUS_ADDR_T_64BIT - def_bool y if (ARCH_DMA_ADDR_T_64BIT || 64BIT) + def_bool y if (ARCH_DMA_ADDR_T_64BIT || (64BIT && !PARISC)) depends on PCI config PCI_MSI diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index cefd636681b6..f6ae0d0052eb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -997,7 +997,12 @@ void set_pcie_port_type(struct pci_dev *pdev) else if (type == PCI_EXP_TYPE_UPSTREAM || type == PCI_EXP_TYPE_DOWNSTREAM) { parent = pci_upstream_bridge(pdev); - if (!parent->has_secondary_link) + + /* + * Usually there's an upstream device (Root Port or Switch + * Downstream Port), but we can't assume one exists. + */ + if (parent && !parent->has_secondary_link) pdev->has_secondary_link = 1; } } @@ -1103,7 +1108,7 @@ int pci_cfg_space_size(struct pci_dev *dev) #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) -static void pci_msi_setup_pci_dev(struct pci_dev *dev) +void pci_msi_setup_pci_dev(struct pci_dev *dev) { /* * Disable the MSI hardware to avoid screaming interrupts diff --git a/drivers/platform/chrome/Kconfig b/drivers/platform/chrome/Kconfig index cb1329919527..3271cd1abe7c 100644 --- a/drivers/platform/chrome/Kconfig +++ b/drivers/platform/chrome/Kconfig @@ -4,7 +4,6 @@ menuconfig CHROME_PLATFORMS bool "Platform support for Chrome hardware" - depends on X86 || ARM ---help--- Say Y here to get to see options for platform support for various Chromebooks and Chromeboxes. This option alone does diff --git a/drivers/rtc/rtc-ds1374.c b/drivers/rtc/rtc-ds1374.c index 167783fa7ac1..72c933375233 100644 --- a/drivers/rtc/rtc-ds1374.c +++ b/drivers/rtc/rtc-ds1374.c @@ -666,9 +666,8 @@ static int ds1374_remove(struct i2c_client *client) #ifdef CONFIG_RTC_DRV_DS1374_WDT int res; - res = misc_deregister(&ds1374_miscdev); - if (!res) - ds1374_miscdev.parent = NULL; + misc_deregister(&ds1374_miscdev); + ds1374_miscdev.parent = NULL; unregister_reboot_notifier(&ds1374_wdt_notifier); #endif diff --git a/drivers/scsi/fnic/fnic.h b/drivers/scsi/fnic/fnic.h index 26270c351624..ce129e595b55 100644 --- a/drivers/scsi/fnic/fnic.h +++ b/drivers/scsi/fnic/fnic.h @@ -39,7 +39,7 @@ #define DRV_NAME "fnic" #define DRV_DESCRIPTION "Cisco FCoE HBA Driver" -#define DRV_VERSION "1.6.0.17" +#define DRV_VERSION "1.6.0.17a" #define PFX DRV_NAME ": " #define DFX DRV_NAME "%d: " diff --git a/drivers/scsi/fnic/fnic_scsi.c b/drivers/scsi/fnic/fnic_scsi.c index 155b286f1a9d..25436cd2860c 100644 --- a/drivers/scsi/fnic/fnic_scsi.c +++ b/drivers/scsi/fnic/fnic_scsi.c @@ -425,6 +425,7 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ unsigned long ptr; struct fc_rport_priv *rdata; spinlock_t *io_lock = NULL; + int io_lock_acquired = 0; if (unlikely(fnic_chk_state_flags_locked(fnic, FNIC_FLAGS_IO_BLOCKED))) return SCSI_MLQUEUE_HOST_BUSY; @@ -518,6 +519,7 @@ static int fnic_queuecommand_lck(struct scsi_cmnd *sc, void (*done)(struct scsi_ spin_lock_irqsave(io_lock, flags); /* initialize rest of io_req */ + io_lock_acquired = 1; io_req->port_id = rport->port_id; io_req->start_time = jiffies; CMD_STATE(sc) = FNIC_IOREQ_CMD_PENDING; @@ -571,7 +573,7 @@ out: (((u64)CMD_FLAGS(sc) >> 32) | CMD_STATE(sc))); /* if only we issued IO, will we have the io lock */ - if (CMD_FLAGS(sc) & FNIC_IO_INITIALIZED) + if (io_lock_acquired) spin_unlock_irqrestore(io_lock, flags); atomic_dec(&fnic->in_flight); diff --git a/drivers/scsi/libfc/fc_exch.c b/drivers/scsi/libfc/fc_exch.c index 1b3a09473452..30f9ef0c0d4f 100644 --- a/drivers/scsi/libfc/fc_exch.c +++ b/drivers/scsi/libfc/fc_exch.c @@ -733,8 +733,6 @@ static bool fc_invoke_resp(struct fc_exch *ep, struct fc_seq *sp, if (resp) { resp(sp, fp, arg); res = true; - } else if (!IS_ERR(fp)) { - fc_frame_free(fp); } spin_lock_bh(&ep->ex_lock); @@ -1596,7 +1594,8 @@ static void fc_exch_recv_seq_resp(struct fc_exch_mgr *mp, struct fc_frame *fp) * If new exch resp handler is valid then call that * first. */ - fc_invoke_resp(ep, sp, fp); + if (!fc_invoke_resp(ep, sp, fp)) + fc_frame_free(fp); fc_exch_release(ep); return; @@ -1695,7 +1694,8 @@ static void fc_exch_abts_resp(struct fc_exch *ep, struct fc_frame *fp) fc_exch_hold(ep); if (!rc) fc_exch_delete(ep); - fc_invoke_resp(ep, sp, fp); + if (!fc_invoke_resp(ep, sp, fp)) + fc_frame_free(fp); if (has_rec) fc_exch_timer_set(ep, ep->r_a_tov); fc_exch_release(ep); diff --git a/drivers/scsi/libfc/fc_fcp.c b/drivers/scsi/libfc/fc_fcp.c index c6795941b45d..2d5909c4685c 100644 --- a/drivers/scsi/libfc/fc_fcp.c +++ b/drivers/scsi/libfc/fc_fcp.c @@ -1039,11 +1039,26 @@ restart: fc_fcp_pkt_hold(fsp); spin_unlock_irqrestore(&si->scsi_queue_lock, flags); - if (!fc_fcp_lock_pkt(fsp)) { + spin_lock_bh(&fsp->scsi_pkt_lock); + if (!(fsp->state & FC_SRB_COMPL)) { + fsp->state |= FC_SRB_COMPL; + /* + * TODO: dropping scsi_pkt_lock and then reacquiring + * again around fc_fcp_cleanup_cmd() is required, + * since fc_fcp_cleanup_cmd() calls into + * fc_seq_set_resp() and that func preempts cpu using + * schedule. May be schedule and related code should be + * removed instead of unlocking here to avoid scheduling + * while atomic bug. + */ + spin_unlock_bh(&fsp->scsi_pkt_lock); + fc_fcp_cleanup_cmd(fsp, error); + + spin_lock_bh(&fsp->scsi_pkt_lock); fc_io_compl(fsp); - fc_fcp_unlock_pkt(fsp); } + spin_unlock_bh(&fsp->scsi_pkt_lock); fc_fcp_pkt_release(fsp); spin_lock_irqsave(&si->scsi_queue_lock, flags); diff --git a/drivers/scsi/libiscsi.c b/drivers/scsi/libiscsi.c index 8053f24f0349..98d9bb6ff725 100644 --- a/drivers/scsi/libiscsi.c +++ b/drivers/scsi/libiscsi.c @@ -2941,10 +2941,10 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) { struct iscsi_conn *conn = cls_conn->dd_data; struct iscsi_session *session = conn->session; - unsigned long flags; del_timer_sync(&conn->transport_timer); + mutex_lock(&session->eh_mutex); spin_lock_bh(&session->frwd_lock); conn->c_stage = ISCSI_CONN_CLEANUP_WAIT; if (session->leadconn == conn) { @@ -2956,28 +2956,6 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) } spin_unlock_bh(&session->frwd_lock); - /* - * Block until all in-progress commands for this connection - * time out or fail. - */ - for (;;) { - spin_lock_irqsave(session->host->host_lock, flags); - if (!atomic_read(&session->host->host_busy)) { /* OK for ERL == 0 */ - spin_unlock_irqrestore(session->host->host_lock, flags); - break; - } - spin_unlock_irqrestore(session->host->host_lock, flags); - msleep_interruptible(500); - iscsi_conn_printk(KERN_INFO, conn, "iscsi conn_destroy(): " - "host_busy %d host_failed %d\n", - atomic_read(&session->host->host_busy), - session->host->host_failed); - /* - * force eh_abort() to unblock - */ - wake_up(&conn->ehwait); - } - /* flush queued up work because we free the connection below */ iscsi_suspend_tx(conn); @@ -2994,6 +2972,7 @@ void iscsi_conn_teardown(struct iscsi_cls_conn *cls_conn) if (session->leadconn == conn) session->leadconn = NULL; spin_unlock_bh(&session->frwd_lock); + mutex_unlock(&session->eh_mutex); iscsi_destroy_conn(cls_conn); } diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index cfadccef045c..6457a8a0db9c 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -26,7 +26,6 @@ #include <linux/blkdev.h> #include <linux/delay.h> #include <linux/jiffies.h> -#include <asm/unaligned.h> #include <scsi/scsi.h> #include <scsi/scsi_cmnd.h> @@ -2523,33 +2522,3 @@ void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) } } EXPORT_SYMBOL(scsi_build_sense_buffer); - -/** - * scsi_set_sense_information - set the information field in a - * formatted sense data buffer - * @buf: Where to build sense data - * @info: 64-bit information value to be set - * - **/ -void scsi_set_sense_information(u8 *buf, u64 info) -{ - if ((buf[0] & 0x7f) == 0x72) { - u8 *ucp, len; - - len = buf[7]; - ucp = (char *)scsi_sense_desc_find(buf, len + 8, 0); - if (!ucp) { - buf[7] = len + 0xa; - ucp = buf + 8 + len; - } - ucp[0] = 0; - ucp[1] = 0xa; - ucp[2] = 0x80; /* Valid bit */ - ucp[3] = 0; - put_unaligned_be64(info, &ucp[4]); - } else if ((buf[0] & 0x7f) == 0x70) { - buf[0] |= 0x80; - put_unaligned_be64(info, &buf[3]); - } -} -EXPORT_SYMBOL(scsi_set_sense_information); diff --git a/drivers/scsi/scsi_pm.c b/drivers/scsi/scsi_pm.c index 9e43ae1d2163..e4b799837948 100644 --- a/drivers/scsi/scsi_pm.c +++ b/drivers/scsi/scsi_pm.c @@ -217,15 +217,15 @@ static int sdev_runtime_suspend(struct device *dev) { const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; struct scsi_device *sdev = to_scsi_device(dev); - int err; + int err = 0; - err = blk_pre_runtime_suspend(sdev->request_queue); - if (err) - return err; - if (pm && pm->runtime_suspend) + if (pm && pm->runtime_suspend) { + err = blk_pre_runtime_suspend(sdev->request_queue); + if (err) + return err; err = pm->runtime_suspend(dev); - blk_post_runtime_suspend(sdev->request_queue, err); - + blk_post_runtime_suspend(sdev->request_queue, err); + } return err; } @@ -248,11 +248,11 @@ static int sdev_runtime_resume(struct device *dev) const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int err = 0; - blk_pre_runtime_resume(sdev->request_queue); - if (pm && pm->runtime_resume) + if (pm && pm->runtime_resume) { + blk_pre_runtime_resume(sdev->request_queue); err = pm->runtime_resume(dev); - blk_post_runtime_resume(sdev->request_queue, err); - + blk_post_runtime_resume(sdev->request_queue, err); + } return err; } diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c index 3b2fcb4fada0..a20da8c25b4f 100644 --- a/drivers/scsi/sd.c +++ b/drivers/scsi/sd.c @@ -2770,9 +2770,9 @@ static int sd_revalidate_disk(struct gendisk *disk) max_xfer = sdkp->max_xfer_blocks; max_xfer <<= ilog2(sdp->sector_size) - 9; - max_xfer = min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), - max_xfer); - blk_queue_max_hw_sectors(sdkp->disk->queue, max_xfer); + sdkp->disk->queue->limits.max_sectors = + min_not_zero(queue_max_hw_sectors(sdkp->disk->queue), max_xfer); + set_capacity(disk, sdkp->capacity); sd_config_write_same(sdkp); kfree(buffer); diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 982580af1d16..0d3b70b3bda8 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig @@ -12,7 +12,7 @@ if SPMI config SPMI_MSM_PMIC_ARB tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" - depends on IRQ_DOMAIN + select IRQ_DOMAIN depends on ARCH_QCOM || COMPILE_TEST depends on HAS_IOMEM default ARCH_QCOM diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index d7119db49cfe..a4d8c043a710 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c @@ -575,6 +575,22 @@ static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) return 0; } +static int qpnpint_get_irqchip_state(struct irq_data *d, + enum irqchip_irq_state which, + bool *state) +{ + u8 irq = d->hwirq >> 8; + u8 status = 0; + + if (which != IRQCHIP_STATE_LINE_LEVEL) + return -EINVAL; + + qpnpint_spmi_read(d, QPNPINT_REG_RT_STS, &status, 1); + *state = !!(status & BIT(irq)); + + return 0; +} + static struct irq_chip pmic_arb_irqchip = { .name = "pmic_arb", .irq_enable = qpnpint_irq_enable, @@ -582,6 +598,7 @@ static struct irq_chip pmic_arb_irqchip = { .irq_mask = qpnpint_irq_mask, .irq_unmask = qpnpint_irq_unmask, .irq_set_type = qpnpint_irq_set_type, + .irq_get_irqchip_state = qpnpint_get_irqchip_state, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, }; diff --git a/drivers/spmi/spmi.c b/drivers/spmi/spmi.c index 94938436aef9..11467e17bdd8 100644 --- a/drivers/spmi/spmi.c +++ b/drivers/spmi/spmi.c @@ -22,6 +22,8 @@ #include <linux/pm_runtime.h> #include <dt-bindings/spmi/spmi.h> +#define CREATE_TRACE_POINTS +#include <trace/events/spmi.h> static DEFINE_IDA(ctrl_ida); @@ -96,28 +98,42 @@ EXPORT_SYMBOL_GPL(spmi_device_remove); static inline int spmi_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid) { + int ret; + if (!ctrl || !ctrl->cmd || ctrl->dev.type != &spmi_ctrl_type) return -EINVAL; - return ctrl->cmd(ctrl, opcode, sid); + ret = ctrl->cmd(ctrl, opcode, sid); + trace_spmi_cmd(opcode, sid, ret); + return ret; } static inline int spmi_read_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid, u16 addr, u8 *buf, size_t len) { + int ret; + if (!ctrl || !ctrl->read_cmd || ctrl->dev.type != &spmi_ctrl_type) return -EINVAL; - return ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); + trace_spmi_read_begin(opcode, sid, addr); + ret = ctrl->read_cmd(ctrl, opcode, sid, addr, buf, len); + trace_spmi_read_end(opcode, sid, addr, ret, len, buf); + return ret; } static inline int spmi_write_cmd(struct spmi_controller *ctrl, u8 opcode, u8 sid, u16 addr, const u8 *buf, size_t len) { + int ret; + if (!ctrl || !ctrl->write_cmd || ctrl->dev.type != &spmi_ctrl_type) return -EINVAL; - return ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); + trace_spmi_write_begin(opcode, sid, addr, len, buf); + ret = ctrl->write_cmd(ctrl, opcode, sid, addr, buf, len); + trace_spmi_write_end(opcode, sid, addr, ret); + return ret; } /** diff --git a/drivers/staging/android/ashmem.c b/drivers/staging/android/ashmem.c index cefc208c313a..60200a3da821 100644 --- a/drivers/staging/android/ashmem.c +++ b/drivers/staging/android/ashmem.c @@ -863,14 +863,9 @@ static int __init ashmem_init(void) static void __exit ashmem_exit(void) { - int ret; - unregister_shrinker(&ashmem_shrinker); - ret = misc_deregister(&ashmem_misc); - if (unlikely(ret)) - pr_err("failed to unregister misc device!\n"); - + misc_deregister(&ashmem_misc); kmem_cache_destroy(ashmem_range_cachep); kmem_cache_destroy(ashmem_area_cachep); diff --git a/drivers/staging/android/ion/ion_test.c b/drivers/staging/android/ion/ion_test.c index 7d6e6b6bc894..b8dcf5a26cc4 100644 --- a/drivers/staging/android/ion/ion_test.c +++ b/drivers/staging/android/ion/ion_test.c @@ -269,7 +269,8 @@ static int ion_test_remove(struct platform_device *pdev) if (!testdev) return -ENODATA; - return misc_deregister(&testdev->misc); + misc_deregister(&testdev->misc); + return 0; } static struct platform_device *ion_test_pdev; diff --git a/drivers/staging/lustre/lustre/libcfs/module.c b/drivers/staging/lustre/lustre/libcfs/module.c index 0128268f0251..806f9747a3a2 100644 --- a/drivers/staging/lustre/lustre/libcfs/module.c +++ b/drivers/staging/lustre/lustre/libcfs/module.c @@ -422,9 +422,7 @@ static void exit_libcfs_module(void) cfs_crypto_unregister(); cfs_wi_shutdown(); - rc = misc_deregister(&libcfs_dev); - if (rc) - CERROR("misc_deregister error %d\n", rc); + misc_deregister(&libcfs_dev); cfs_cpu_fini(); diff --git a/drivers/target/iscsi/iscsi_target.c b/drivers/target/iscsi/iscsi_target.c index cd77a064c772..fd092909a457 100644 --- a/drivers/target/iscsi/iscsi_target.c +++ b/drivers/target/iscsi/iscsi_target.c @@ -968,9 +968,9 @@ int iscsit_setup_scsi_cmd(struct iscsi_conn *conn, struct iscsi_cmd *cmd, cmd->cmd_flags |= ICF_NON_IMMEDIATE_UNSOLICITED_DATA; conn->sess->init_task_tag = cmd->init_task_tag = hdr->itt; - if (hdr->flags & ISCSI_FLAG_CMD_READ) { + if (hdr->flags & ISCSI_FLAG_CMD_READ) cmd->targ_xfer_tag = session_get_next_ttt(conn->sess); - } else if (hdr->flags & ISCSI_FLAG_CMD_WRITE) + else cmd->targ_xfer_tag = 0xFFFFFFFF; cmd->cmd_sn = be32_to_cpu(hdr->cmdsn); cmd->exp_stat_sn = be32_to_cpu(hdr->exp_statsn); diff --git a/drivers/target/target_core_configfs.c b/drivers/target/target_core_configfs.c index c2e9fea90b4a..860e84046177 100644 --- a/drivers/target/target_core_configfs.c +++ b/drivers/target/target_core_configfs.c @@ -457,8 +457,15 @@ void target_unregister_template(const struct target_core_fabric_ops *fo) if (!strcmp(t->tf_ops->name, fo->name)) { BUG_ON(atomic_read(&t->tf_access_cnt)); list_del(&t->tf_list); + mutex_unlock(&g_tf_lock); + /* + * Wait for any outstanding fabric se_deve_entry->rcu_head + * callbacks to complete post kfree_rcu(), before allowing + * fabric driver unload of TFO->module to proceed. + */ + rcu_barrier(); kfree(t); - break; + return; } } mutex_unlock(&g_tf_lock); diff --git a/drivers/target/target_core_hba.c b/drivers/target/target_core_hba.c index 62ea4e8e70a8..be9cefc07407 100644 --- a/drivers/target/target_core_hba.c +++ b/drivers/target/target_core_hba.c @@ -84,8 +84,16 @@ void target_backend_unregister(const struct target_backend_ops *ops) list_for_each_entry(tb, &backend_list, list) { if (tb->ops == ops) { list_del(&tb->list); + mutex_unlock(&backend_mutex); + /* + * Wait for any outstanding backend driver ->rcu_head + * callbacks to complete post TBO->free_device() -> + * call_rcu(), before allowing backend driver module + * unload of target_backend_ops->owner to proceed. + */ + rcu_barrier(); kfree(tb); - break; + return; } } mutex_unlock(&backend_mutex); diff --git a/drivers/target/target_core_spc.c b/drivers/target/target_core_spc.c index b5ba1ec3c354..f87d4cef6d39 100644 --- a/drivers/target/target_core_spc.c +++ b/drivers/target/target_core_spc.c @@ -1203,17 +1203,13 @@ sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd) struct se_dev_entry *deve; struct se_session *sess = cmd->se_sess; struct se_node_acl *nacl; + struct scsi_lun slun; unsigned char *buf; u32 lun_count = 0, offset = 8; - - if (cmd->data_length < 16) { - pr_warn("REPORT LUNS allocation length %u too small\n", - cmd->data_length); - return TCM_INVALID_CDB_FIELD; - } + __be32 len; buf = transport_kmap_data_sg(cmd); - if (!buf) + if (cmd->data_length && !buf) return TCM_LOGICAL_UNIT_COMMUNICATION_FAILURE; /* @@ -1221,11 +1217,9 @@ sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd) * coming via a target_core_mod PASSTHROUGH op, and not through * a $FABRIC_MOD. In that case, report LUN=0 only. */ - if (!sess) { - int_to_scsilun(0, (struct scsi_lun *)&buf[offset]); - lun_count = 1; + if (!sess) goto done; - } + nacl = sess->se_node_acl; rcu_read_lock(); @@ -1236,10 +1230,12 @@ sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd) * See SPC2-R20 7.19. */ lun_count++; - if ((offset + 8) > cmd->data_length) + if (offset >= cmd->data_length) continue; - int_to_scsilun(deve->mapped_lun, (struct scsi_lun *)&buf[offset]); + int_to_scsilun(deve->mapped_lun, &slun); + memcpy(buf + offset, &slun, + min(8u, cmd->data_length - offset)); offset += 8; } rcu_read_unlock(); @@ -1248,12 +1244,22 @@ sense_reason_t spc_emulate_report_luns(struct se_cmd *cmd) * See SPC3 r07, page 159. */ done: - lun_count *= 8; - buf[0] = ((lun_count >> 24) & 0xff); - buf[1] = ((lun_count >> 16) & 0xff); - buf[2] = ((lun_count >> 8) & 0xff); - buf[3] = (lun_count & 0xff); - transport_kunmap_data_sg(cmd); + /* + * If no LUNs are accessible, report virtual LUN 0. + */ + if (lun_count == 0) { + int_to_scsilun(0, &slun); + if (cmd->data_length > 8) + memcpy(buf + offset, &slun, + min(8u, cmd->data_length - offset)); + lun_count = 1; + } + + if (buf) { + len = cpu_to_be32(lun_count * 8); + memcpy(buf, &len, min_t(int, sizeof len, cmd->data_length)); + transport_kunmap_data_sg(cmd); + } target_complete_cmd_with_length(cmd, GOOD, 8 + lun_count * 8); return 0; diff --git a/drivers/thermal/cpu_cooling.c b/drivers/thermal/cpu_cooling.c index 6509c61b9648..620dcd405ff6 100644 --- a/drivers/thermal/cpu_cooling.c +++ b/drivers/thermal/cpu_cooling.c @@ -68,7 +68,7 @@ struct power_table { * registered cooling device. * @cpufreq_state: integer value representing the current state of cpufreq * cooling devices. - * @cpufreq_val: integer value representing the absolute value of the clipped + * @clipped_freq: integer value representing the absolute value of the clipped * frequency. * @max_level: maximum cooling level. One less than total number of valid * cpufreq frequencies. @@ -91,7 +91,7 @@ struct cpufreq_cooling_device { int id; struct thermal_cooling_device *cool_dev; unsigned int cpufreq_state; - unsigned int cpufreq_val; + unsigned int clipped_freq; unsigned int max_level; unsigned int *freq_table; /* In descending order */ struct cpumask allowed_cpus; @@ -107,6 +107,9 @@ struct cpufreq_cooling_device { static DEFINE_IDR(cpufreq_idr); static DEFINE_MUTEX(cooling_cpufreq_lock); +static unsigned int cpufreq_dev_count; + +static DEFINE_MUTEX(cooling_list_lock); static LIST_HEAD(cpufreq_dev_list); /** @@ -185,14 +188,14 @@ unsigned long cpufreq_cooling_get_level(unsigned int cpu, unsigned int freq) { struct cpufreq_cooling_device *cpufreq_dev; - mutex_lock(&cooling_cpufreq_lock); + mutex_lock(&cooling_list_lock); list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { if (cpumask_test_cpu(cpu, &cpufreq_dev->allowed_cpus)) { - mutex_unlock(&cooling_cpufreq_lock); + mutex_unlock(&cooling_list_lock); return get_level(cpufreq_dev, freq); } } - mutex_unlock(&cooling_cpufreq_lock); + mutex_unlock(&cooling_list_lock); pr_err("%s: cpu:%d not part of any cooling device\n", __func__, cpu); return THERMAL_CSTATE_INVALID; @@ -215,29 +218,35 @@ static int cpufreq_thermal_notifier(struct notifier_block *nb, unsigned long event, void *data) { struct cpufreq_policy *policy = data; - unsigned long max_freq = 0; + unsigned long clipped_freq; struct cpufreq_cooling_device *cpufreq_dev; - switch (event) { + if (event != CPUFREQ_ADJUST) + return NOTIFY_DONE; - case CPUFREQ_ADJUST: - mutex_lock(&cooling_cpufreq_lock); - list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { - if (!cpumask_test_cpu(policy->cpu, - &cpufreq_dev->allowed_cpus)) - continue; + mutex_lock(&cooling_list_lock); + list_for_each_entry(cpufreq_dev, &cpufreq_dev_list, node) { + if (!cpumask_test_cpu(policy->cpu, &cpufreq_dev->allowed_cpus)) + continue; - max_freq = cpufreq_dev->cpufreq_val; + /* + * policy->max is the maximum allowed frequency defined by user + * and clipped_freq is the maximum that thermal constraints + * allow. + * + * If clipped_freq is lower than policy->max, then we need to + * readjust policy->max. + * + * But, if clipped_freq is greater than policy->max, we don't + * need to do anything. + */ + clipped_freq = cpufreq_dev->clipped_freq; - if (policy->max != max_freq) - cpufreq_verify_within_limits(policy, 0, - max_freq); - } - mutex_unlock(&cooling_cpufreq_lock); + if (policy->max > clipped_freq) + cpufreq_verify_within_limits(policy, 0, clipped_freq); break; - default: - return NOTIFY_DONE; } + mutex_unlock(&cooling_list_lock); return NOTIFY_OK; } @@ -519,7 +528,7 @@ static int cpufreq_set_cur_state(struct thermal_cooling_device *cdev, clip_freq = cpufreq_device->freq_table[state]; cpufreq_device->cpufreq_state = state; - cpufreq_device->cpufreq_val = clip_freq; + cpufreq_device->clipped_freq = clip_freq; cpufreq_update_policy(cpu); @@ -861,17 +870,19 @@ __cpufreq_cooling_register(struct device_node *np, pr_debug("%s: freq:%u KHz\n", __func__, freq); } - cpufreq_dev->cpufreq_val = cpufreq_dev->freq_table[0]; + cpufreq_dev->clipped_freq = cpufreq_dev->freq_table[0]; cpufreq_dev->cool_dev = cool_dev; mutex_lock(&cooling_cpufreq_lock); + mutex_lock(&cooling_list_lock); + list_add(&cpufreq_dev->node, &cpufreq_dev_list); + mutex_unlock(&cooling_list_lock); + /* Register the notifier for first cpufreq cooling device */ - if (list_empty(&cpufreq_dev_list)) + if (!cpufreq_dev_count++) cpufreq_register_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); - list_add(&cpufreq_dev->node, &cpufreq_dev_list); - mutex_unlock(&cooling_cpufreq_lock); return cool_dev; @@ -1013,13 +1024,17 @@ void cpufreq_cooling_unregister(struct thermal_cooling_device *cdev) return; cpufreq_dev = cdev->devdata; - mutex_lock(&cooling_cpufreq_lock); - list_del(&cpufreq_dev->node); /* Unregister the notifier for the last cpufreq cooling device */ - if (list_empty(&cpufreq_dev_list)) + mutex_lock(&cooling_cpufreq_lock); + if (!--cpufreq_dev_count) cpufreq_unregister_notifier(&thermal_cpufreq_notifier_block, CPUFREQ_POLICY_NOTIFIER); + + mutex_lock(&cooling_list_lock); + list_del(&cpufreq_dev->node); + mutex_unlock(&cooling_list_lock); + mutex_unlock(&cooling_cpufreq_lock); thermal_cooling_device_unregister(cpufreq_dev->cool_dev); diff --git a/drivers/thermal/power_allocator.c b/drivers/thermal/power_allocator.c index 63a448f9d93b..251676902869 100644 --- a/drivers/thermal/power_allocator.c +++ b/drivers/thermal/power_allocator.c @@ -258,8 +258,7 @@ static int allocate_power(struct thermal_zone_device *tz, BUILD_BUG_ON(sizeof(*req_power) != sizeof(*granted_power)); BUILD_BUG_ON(sizeof(*req_power) != sizeof(*extra_actor_power)); BUILD_BUG_ON(sizeof(*req_power) != sizeof(*weighted_req_power)); - req_power = devm_kcalloc(&tz->device, num_actors * 5, - sizeof(*req_power), GFP_KERNEL); + req_power = kcalloc(num_actors * 5, sizeof(*req_power), GFP_KERNEL); if (!req_power) { ret = -ENOMEM; goto unlock; @@ -334,7 +333,7 @@ static int allocate_power(struct thermal_zone_device *tz, max_allocatable_power, current_temp, (s32)control_temp - (s32)current_temp); - devm_kfree(&tz->device, req_power); + kfree(req_power); unlock: mutex_unlock(&tz->lock); @@ -426,7 +425,7 @@ static int power_allocator_bind(struct thermal_zone_device *tz) return -EINVAL; } - params = devm_kzalloc(&tz->device, sizeof(*params), GFP_KERNEL); + params = kzalloc(sizeof(*params), GFP_KERNEL); if (!params) return -ENOMEM; @@ -468,14 +467,14 @@ static int power_allocator_bind(struct thermal_zone_device *tz) return 0; free: - devm_kfree(&tz->device, params); + kfree(params); return ret; } static void power_allocator_unbind(struct thermal_zone_device *tz) { dev_dbg(&tz->device, "Unbinding from thermal zone %d\n", tz->id); - devm_kfree(&tz->device, tz->governor_data); + kfree(tz->governor_data); tz->governor_data = NULL; } diff --git a/drivers/uio/Kconfig b/drivers/uio/Kconfig index 48fb1d983f6c..52c98ce1b6fe 100644 --- a/drivers/uio/Kconfig +++ b/drivers/uio/Kconfig @@ -127,7 +127,7 @@ config UIO_FSL_ELBC_GPCM_NETX5152 config UIO_PRUSS tristate "Texas Instruments PRUSS driver" select GENERIC_ALLOCATOR - depends on HAS_IOMEM + depends on HAS_IOMEM && HAS_DMA help PRUSS driver for OMAPL138/DA850/AM18XX devices PRUSS driver requires user space components, examples and user space diff --git a/drivers/uio/uio.c b/drivers/uio/uio.c index 3257d4220d01..8196581f54c2 100644 --- a/drivers/uio/uio.c +++ b/drivers/uio/uio.c @@ -896,6 +896,7 @@ static int __init uio_init(void) static void __exit uio_exit(void) { release_uio_class(); + idr_destroy(&uio_idr); } module_init(uio_init) diff --git a/drivers/uio/uio_fsl_elbc_gpcm.c b/drivers/uio/uio_fsl_elbc_gpcm.c index b6cac91c2ced..2bcf80c159c1 100644 --- a/drivers/uio/uio_fsl_elbc_gpcm.c +++ b/drivers/uio/uio_fsl_elbc_gpcm.c @@ -480,19 +480,7 @@ static struct platform_driver uio_fsl_elbc_gpcm_driver = { .probe = uio_fsl_elbc_gpcm_probe, .remove = uio_fsl_elbc_gpcm_remove, }; - -static int __init uio_fsl_elbc_gpcm_init(void) -{ - return platform_driver_register(&uio_fsl_elbc_gpcm_driver); -} - -static void __exit uio_fsl_elbc_gpcm_exit(void) -{ - platform_driver_unregister(&uio_fsl_elbc_gpcm_driver); -} - -module_init(uio_fsl_elbc_gpcm_init); -module_exit(uio_fsl_elbc_gpcm_exit); +module_platform_driver(uio_fsl_elbc_gpcm_driver); MODULE_LICENSE("GPL"); MODULE_AUTHOR("John Ogness <john.ogness@linutronix.de>"); diff --git a/drivers/vhost/scsi.c b/drivers/vhost/scsi.c index dfcc02c93648..f114a9dbb48f 100644 --- a/drivers/vhost/scsi.c +++ b/drivers/vhost/scsi.c @@ -1573,9 +1573,9 @@ static int __init vhost_scsi_register(void) return misc_register(&vhost_scsi_misc); } -static int vhost_scsi_deregister(void) +static void vhost_scsi_deregister(void) { - return misc_deregister(&vhost_scsi_misc); + misc_deregister(&vhost_scsi_misc); } static char *vhost_scsi_dump_proto_id(struct vhost_scsi_tport *tport) diff --git a/drivers/video/console/fbcon.c b/drivers/video/console/fbcon.c index 658c34bb9076..1aaf89300621 100644 --- a/drivers/video/console/fbcon.c +++ b/drivers/video/console/fbcon.c @@ -1306,10 +1306,11 @@ static void fbcon_cursor(struct vc_data *vc, int mode) int y; int c = scr_readw((u16 *) vc->vc_pos); + ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); + if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) return; - ops->cur_blink_jiffies = msecs_to_jiffies(vc->vc_cur_blink_ms); if (vc->vc_cursor_type & 0x10) fbcon_del_cursor_timer(info); else diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 230790984088..811acfc6048e 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -298,7 +298,7 @@ config FB_ARMCLCD # Helper logic selected only by the ARM Versatile platform family. config PLAT_VERSATILE_CLCD - def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS + def_bool ARCH_VERSATILE || ARCH_REALVIEW || ARCH_VEXPRESS || ARCH_INTEGRATOR depends on ARM depends on FB_ARMCLCD && FB=y diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index 807ee22ef229..e2451bdb4525 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -213,7 +213,7 @@ struct synthvid_msg { struct hvfb_par { struct fb_info *info; - struct resource mem; + struct resource *mem; bool fb_ready; /* fb device is ready */ struct completion wait; u32 synthvid_version; @@ -677,26 +677,18 @@ static void hvfb_get_option(struct fb_info *info) /* Get framebuffer memory from Hyper-V video pci space */ -static int hvfb_getmem(struct fb_info *info) +static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) { struct hvfb_par *par = info->par; struct pci_dev *pdev = NULL; void __iomem *fb_virt; int gen2vm = efi_enabled(EFI_BOOT); + resource_size_t pot_start, pot_end; int ret; - par->mem.name = KBUILD_MODNAME; - par->mem.flags = IORESOURCE_MEM | IORESOURCE_BUSY; if (gen2vm) { - ret = allocate_resource(&hyperv_mmio, &par->mem, - screen_fb_size, - 0, -1, - screen_fb_size, - NULL, NULL); - if (ret != 0) { - pr_err("Unable to allocate framebuffer memory\n"); - return -ENODEV; - } + pot_start = 0; + pot_end = -1; } else { pdev = pci_get_device(PCI_VENDOR_ID_MICROSOFT, PCI_DEVICE_ID_HYPERV_VIDEO, NULL); @@ -709,16 +701,18 @@ static int hvfb_getmem(struct fb_info *info) pci_resource_len(pdev, 0) < screen_fb_size) goto err1; - par->mem.end = pci_resource_end(pdev, 0); - par->mem.start = par->mem.end - screen_fb_size + 1; - ret = request_resource(&pdev->resource[0], &par->mem); - if (ret != 0) { - pr_err("Unable to request framebuffer memory\n"); - goto err1; - } + pot_end = pci_resource_end(pdev, 0); + pot_start = pot_end - screen_fb_size + 1; + } + + ret = vmbus_allocate_mmio(&par->mem, hdev, pot_start, pot_end, + screen_fb_size, 0x100000, true); + if (ret != 0) { + pr_err("Unable to allocate framebuffer memory\n"); + goto err1; } - fb_virt = ioremap(par->mem.start, screen_fb_size); + fb_virt = ioremap(par->mem->start, screen_fb_size); if (!fb_virt) goto err2; @@ -736,7 +730,7 @@ static int hvfb_getmem(struct fb_info *info) info->apertures->ranges[0].size = pci_resource_len(pdev, 0); } - info->fix.smem_start = par->mem.start; + info->fix.smem_start = par->mem->start; info->fix.smem_len = screen_fb_size; info->screen_base = fb_virt; info->screen_size = screen_fb_size; @@ -749,7 +743,8 @@ static int hvfb_getmem(struct fb_info *info) err3: iounmap(fb_virt); err2: - release_resource(&par->mem); + release_mem_region(par->mem->start, screen_fb_size); + par->mem = NULL; err1: if (!gen2vm) pci_dev_put(pdev); @@ -763,7 +758,8 @@ static void hvfb_putmem(struct fb_info *info) struct hvfb_par *par = info->par; iounmap(info->screen_base); - release_resource(&par->mem); + release_mem_region(par->mem->start, screen_fb_size); + par->mem = NULL; } @@ -794,7 +790,7 @@ static int hvfb_probe(struct hv_device *hdev, goto error1; } - ret = hvfb_getmem(info); + ret = hvfb_getmem(hdev, info); if (ret) { pr_err("No memory for framebuffer\n"); goto error2; diff --git a/drivers/video/fbdev/omap2/dss/dss-of.c b/drivers/video/fbdev/omap2/dss/dss-of.c index 928ee639c0c1..bf407b6ba15c 100644 --- a/drivers/video/fbdev/omap2/dss/dss-of.c +++ b/drivers/video/fbdev/omap2/dss/dss-of.c @@ -60,6 +60,8 @@ omapdss_of_get_next_port(const struct device_node *parent, } prev = port; } while (of_node_cmp(port->name, "port") != 0); + + of_node_put(ports); } return port; @@ -94,7 +96,7 @@ struct device_node *dss_of_port_get_parent_device(struct device_node *port) if (!port) return NULL; - np = of_get_next_parent(port); + np = of_get_parent(port); for (i = 0; i < 2 && np; ++i) { struct property *prop; diff --git a/drivers/video/fbdev/pxa3xx-gcu.c b/drivers/video/fbdev/pxa3xx-gcu.c index 86bd457d039d..50bce45e7f3d 100644 --- a/drivers/video/fbdev/pxa3xx-gcu.c +++ b/drivers/video/fbdev/pxa3xx-gcu.c @@ -653,7 +653,7 @@ static int pxa3xx_gcu_probe(struct platform_device *pdev) goto err_free_dma; } - ret = clk_enable(priv->clk); + ret = clk_prepare_enable(priv->clk); if (ret < 0) { dev_err(dev, "failed to enable clock\n"); goto err_misc_deregister; @@ -685,7 +685,7 @@ err_misc_deregister: misc_deregister(&priv->misc_dev); err_disable_clk: - clk_disable(priv->clk); + clk_disable_unprepare(priv->clk); return ret; } diff --git a/drivers/video/of_videomode.c b/drivers/video/of_videomode.c index 111c2d1911d3..b5102aa6090d 100644 --- a/drivers/video/of_videomode.c +++ b/drivers/video/of_videomode.c @@ -44,11 +44,9 @@ int of_get_videomode(struct device_node *np, struct videomode *vm, index = disp->native_mode; ret = videomode_from_timings(disp, vm, index); - if (ret) - return ret; display_timings_release(disp); - return 0; + return ret; } EXPORT_SYMBOL_GPL(of_get_videomode); diff --git a/drivers/w1/masters/ds2482.c b/drivers/w1/masters/ds2482.c index a674409edfb3..b05e8fefbabd 100644 --- a/drivers/w1/masters/ds2482.c +++ b/drivers/w1/masters/ds2482.c @@ -97,7 +97,6 @@ MODULE_DEVICE_TABLE(i2c, ds2482_id); static struct i2c_driver ds2482_driver = { .driver = { - .owner = THIS_MODULE, .name = "ds2482", }, .probe = ds2482_probe, diff --git a/drivers/w1/masters/matrox_w1.c b/drivers/w1/masters/matrox_w1.c index d8667b0212d7..684bc9d861cb 100644 --- a/drivers/w1/masters/matrox_w1.c +++ b/drivers/w1/masters/matrox_w1.c @@ -232,16 +232,4 @@ static void matrox_w1_remove(struct pci_dev *pdev) } kfree(dev); } - -static int __init matrox_w1_init(void) -{ - return pci_register_driver(&matrox_w1_pci_driver); -} - -static void __exit matrox_w1_fini(void) -{ - pci_unregister_driver(&matrox_w1_pci_driver); -} - -module_init(matrox_w1_init); -module_exit(matrox_w1_fini); +module_pci_driver(matrox_w1_pci_driver); diff --git a/drivers/watchdog/at91rm9200_wdt.c b/drivers/watchdog/at91rm9200_wdt.c index 41cecb55766c..9ba1153465ae 100644 --- a/drivers/watchdog/at91rm9200_wdt.c +++ b/drivers/watchdog/at91rm9200_wdt.c @@ -269,9 +269,8 @@ static int at91wdt_remove(struct platform_device *pdev) if (res) dev_warn(dev, "failed to unregister restart handler\n"); - res = misc_deregister(&at91wdt_miscdev); - if (!res) - at91wdt_miscdev.parent = NULL; + misc_deregister(&at91wdt_miscdev); + at91wdt_miscdev.parent = NULL; return res; } diff --git a/drivers/watchdog/ks8695_wdt.c b/drivers/watchdog/ks8695_wdt.c index b7ea39b455c8..1e41818a44bc 100644 --- a/drivers/watchdog/ks8695_wdt.c +++ b/drivers/watchdog/ks8695_wdt.c @@ -254,13 +254,10 @@ static int ks8695wdt_probe(struct platform_device *pdev) static int ks8695wdt_remove(struct platform_device *pdev) { - int res; - - res = misc_deregister(&ks8695wdt_miscdev); - if (!res) - ks8695wdt_miscdev.parent = NULL; + misc_deregister(&ks8695wdt_miscdev); + ks8695wdt_miscdev.parent = NULL; - return res; + return 0; } static void ks8695wdt_shutdown(struct platform_device *pdev) diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index 119beb7f6017..4b541934b6c5 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -428,7 +428,8 @@ static int ts72xx_wdt_probe(struct platform_device *pdev) static int ts72xx_wdt_remove(struct platform_device *pdev) { - return misc_deregister(&ts72xx_wdt_miscdev); + misc_deregister(&ts72xx_wdt_miscdev); + return 0; } static struct platform_driver ts72xx_wdt_driver = { diff --git a/drivers/xen/events/events_base.c b/drivers/xen/events/events_base.c index 1495eccb1617..96093ae369a5 100644 --- a/drivers/xen/events/events_base.c +++ b/drivers/xen/events/events_base.c @@ -452,12 +452,10 @@ static void xen_free_irq(unsigned irq) irq_free_desc(irq); } -static void xen_evtchn_close(unsigned int port, unsigned int cpu) +static void xen_evtchn_close(unsigned int port) { struct evtchn_close close; - xen_evtchn_op_close(port, cpu); - close.port = port; if (HYPERVISOR_event_channel_op(EVTCHNOP_close, &close) != 0) BUG(); @@ -546,7 +544,7 @@ out: err: pr_err("irq%d: Failed to set port to irq mapping (%d)\n", irq, rc); - xen_evtchn_close(evtchn, NR_CPUS); + xen_evtchn_close(evtchn); return 0; } @@ -567,7 +565,7 @@ static void shutdown_pirq(struct irq_data *data) return; mask_evtchn(evtchn); - xen_evtchn_close(evtchn, cpu_from_evtchn(evtchn)); + xen_evtchn_close(evtchn); xen_irq_info_cleanup(info); } @@ -611,7 +609,7 @@ static void __unbind_from_irq(unsigned int irq) if (VALID_EVTCHN(evtchn)) { unsigned int cpu = cpu_from_irq(irq); - xen_evtchn_close(evtchn, cpu); + xen_evtchn_close(evtchn); switch (type_from_irq(irq)) { case IRQT_VIRQ: diff --git a/drivers/xen/events/events_fifo.c b/drivers/xen/events/events_fifo.c index 6df8aac966b9..ed673e1acd61 100644 --- a/drivers/xen/events/events_fifo.c +++ b/drivers/xen/events/events_fifo.c @@ -255,12 +255,6 @@ static void evtchn_fifo_unmask(unsigned port) } } -static bool evtchn_fifo_is_linked(unsigned port) -{ - event_word_t *word = event_word_from_port(port); - return sync_test_bit(EVTCHN_FIFO_BIT(LINKED, word), BM(word)); -} - static uint32_t clear_linked(volatile event_word_t *word) { event_word_t new, old, w; @@ -287,8 +281,7 @@ static void handle_irq_for_port(unsigned port) static void consume_one_event(unsigned cpu, struct evtchn_fifo_control_block *control_block, - unsigned priority, unsigned long *ready, - bool drop) + unsigned priority, unsigned long *ready) { struct evtchn_fifo_queue *q = &per_cpu(cpu_queue, cpu); uint32_t head; @@ -320,15 +313,13 @@ static void consume_one_event(unsigned cpu, if (head == 0) clear_bit(priority, ready); - if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) { - if (likely(!drop)) - handle_irq_for_port(port); - } + if (evtchn_fifo_is_pending(port) && !evtchn_fifo_is_masked(port)) + handle_irq_for_port(port); q->head[priority] = head; } -static void __evtchn_fifo_handle_events(unsigned cpu, bool drop) +static void evtchn_fifo_handle_events(unsigned cpu) { struct evtchn_fifo_control_block *control_block; unsigned long ready; @@ -340,16 +331,11 @@ static void __evtchn_fifo_handle_events(unsigned cpu, bool drop) while (ready) { q = find_first_bit(&ready, EVTCHN_FIFO_MAX_QUEUES); - consume_one_event(cpu, control_block, q, &ready, drop); + consume_one_event(cpu, control_block, q, &ready); ready |= xchg(&control_block->ready, 0); } } -static void evtchn_fifo_handle_events(unsigned cpu) -{ - __evtchn_fifo_handle_events(cpu, false); -} - static void evtchn_fifo_resume(void) { unsigned cpu; @@ -385,26 +371,6 @@ static void evtchn_fifo_resume(void) event_array_pages = 0; } -static void evtchn_fifo_close(unsigned port, unsigned int cpu) -{ - if (cpu == NR_CPUS) - return; - - get_online_cpus(); - if (cpu_online(cpu)) { - if (WARN_ON(irqs_disabled())) - goto out; - - while (evtchn_fifo_is_linked(port)) - cpu_relax(); - } else { - __evtchn_fifo_handle_events(cpu, true); - } - -out: - put_online_cpus(); -} - static const struct evtchn_ops evtchn_ops_fifo = { .max_channels = evtchn_fifo_max_channels, .nr_channels = evtchn_fifo_nr_channels, @@ -418,7 +384,6 @@ static const struct evtchn_ops evtchn_ops_fifo = { .unmask = evtchn_fifo_unmask, .handle_events = evtchn_fifo_handle_events, .resume = evtchn_fifo_resume, - .close = evtchn_fifo_close, }; static int evtchn_fifo_alloc_control_block(unsigned cpu) diff --git a/drivers/xen/events/events_internal.h b/drivers/xen/events/events_internal.h index d18e12315ec0..50c2050a1e32 100644 --- a/drivers/xen/events/events_internal.h +++ b/drivers/xen/events/events_internal.h @@ -68,7 +68,6 @@ struct evtchn_ops { bool (*test_and_set_mask)(unsigned port); void (*mask)(unsigned port); void (*unmask)(unsigned port); - void (*close)(unsigned port, unsigned cpu); void (*handle_events)(unsigned cpu); void (*resume)(void); @@ -146,12 +145,6 @@ static inline void xen_evtchn_resume(void) evtchn_ops->resume(); } -static inline void xen_evtchn_op_close(unsigned port, unsigned cpu) -{ - if (evtchn_ops->close) - return evtchn_ops->close(port, cpu); -} - void xen_evtchn_2l_init(void); int xen_evtchn_fifo_init(void); diff --git a/drivers/xen/xenbus/xenbus_client.c b/drivers/xen/xenbus/xenbus_client.c index 9ad327238ba9..e30353575d5d 100644 --- a/drivers/xen/xenbus/xenbus_client.c +++ b/drivers/xen/xenbus/xenbus_client.c @@ -814,8 +814,10 @@ static int xenbus_unmap_ring_vfree_hvm(struct xenbus_device *dev, void *vaddr) rv = xenbus_unmap_ring(dev, node->handles, node->nr_handles, addrs); - if (!rv) + if (!rv) { vunmap(vaddr); + free_xenballooned_pages(node->nr_handles, node->hvm.pages); + } else WARN(1, "Leaking %p, size %u page(s)\n", vaddr, node->nr_handles); diff --git a/fs/btrfs/super.c b/fs/btrfs/super.c index cd7ef34d2dce..6bad63379a4c 100644 --- a/fs/btrfs/super.c +++ b/fs/btrfs/super.c @@ -2163,8 +2163,7 @@ static int btrfs_interface_init(void) static void btrfs_interface_exit(void) { - if (misc_deregister(&btrfs_misc) < 0) - printk(KERN_INFO "BTRFS: misc_deregister failed for control device\n"); + misc_deregister(&btrfs_misc); } static void btrfs_print_info(void) diff --git a/fs/char_dev.c b/fs/char_dev.c index ea06a3d0364c..24b142569ca9 100644 --- a/fs/char_dev.c +++ b/fs/char_dev.c @@ -274,7 +274,7 @@ out2: } /** - * unregister_chrdev_region() - return a range of device numbers + * unregister_chrdev_region() - unregister a range of device numbers * @from: the first in the range of numbers to unregister * @count: the number of device numbers to unregister * diff --git a/fs/dlm/plock.c b/fs/dlm/plock.c index e0ab3a93eeff..5532f097f6da 100644 --- a/fs/dlm/plock.c +++ b/fs/dlm/plock.c @@ -509,7 +509,6 @@ int dlm_plock_init(void) void dlm_plock_exit(void) { - if (misc_deregister(&plock_dev_misc) < 0) - log_print("dlm_plock_exit: misc_deregister failed"); + misc_deregister(&plock_dev_misc); } diff --git a/fs/dlm/user.c b/fs/dlm/user.c index fb85f32e9eca..75ecc0d3bc85 100644 --- a/fs/dlm/user.c +++ b/fs/dlm/user.c @@ -362,18 +362,15 @@ fail: int dlm_device_deregister(struct dlm_ls *ls) { - int error; - /* The device is not registered. This happens when the lockspace was never used from userspace, or when device_create_lockspace() calls dlm_release_lockspace() after the register fails. */ if (!ls->ls_device.name) return 0; - error = misc_deregister(&ls->ls_device); - if (!error) - kfree(ls->ls_device.name); - return error; + misc_deregister(&ls->ls_device); + kfree(ls->ls_device.name); + return 0; } static int device_user_purge(struct dlm_user_proc *proc, diff --git a/fs/fs-writeback.c b/fs/fs-writeback.c index 518c6294bf6c..5fa588e933d5 100644 --- a/fs/fs-writeback.c +++ b/fs/fs-writeback.c @@ -844,14 +844,15 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, struct wb_iter iter; might_sleep(); - - if (!bdi_has_dirty_io(bdi)) - return; restart: rcu_read_lock(); bdi_for_each_wb(wb, bdi, &iter, next_blkcg_id) { - if (!wb_has_dirty_io(wb) || - (skip_if_busy && writeback_in_progress(wb))) + /* SYNC_ALL writes out I_DIRTY_TIME too */ + if (!wb_has_dirty_io(wb) && + (base_work->sync_mode == WB_SYNC_NONE || + list_empty(&wb->b_dirty_time))) + continue; + if (skip_if_busy && writeback_in_progress(wb)) continue; base_work->nr_pages = wb_split_bdi_pages(wb, nr_pages); @@ -899,8 +900,7 @@ static void bdi_split_work_to_wbs(struct backing_dev_info *bdi, { might_sleep(); - if (bdi_has_dirty_io(bdi) && - (!skip_if_busy || !writeback_in_progress(&bdi->wb))) { + if (!skip_if_busy || !writeback_in_progress(&bdi->wb)) { base_work->auto_free = 0; base_work->single_wait = 0; base_work->single_done = 0; @@ -2275,8 +2275,12 @@ void sync_inodes_sb(struct super_block *sb) }; struct backing_dev_info *bdi = sb->s_bdi; - /* Nothing to do? */ - if (!bdi_has_dirty_io(bdi) || bdi == &noop_backing_dev_info) + /* + * Can't skip on !bdi_has_dirty() because we should wait for !dirty + * inodes under writeback and I_DIRTY_TIME inodes ignored by + * bdi_has_dirty() need to be written out too. + */ + if (bdi == &noop_backing_dev_info) return; WARN_ON(!rwsem_is_locked(&sb->s_umount)); diff --git a/fs/fuse/dev.c b/fs/fuse/dev.c index 80cc1b35d460..ebb5e37455a0 100644 --- a/fs/fuse/dev.c +++ b/fs/fuse/dev.c @@ -2246,7 +2246,15 @@ static long fuse_dev_ioctl(struct file *file, unsigned int cmd, err = -EINVAL; if (old) { - struct fuse_dev *fud = fuse_get_dev(old); + struct fuse_dev *fud = NULL; + + /* + * Check against file->f_op because CUSE + * uses the same ioctl handler. + */ + if (old->f_op == file->f_op && + old->f_cred->user_ns == file->f_cred->user_ns) + fud = fuse_get_dev(old); if (fud) { mutex_lock(&fuse_mutex); diff --git a/fs/namei.c b/fs/namei.c index fbbcf0993312..1c2105ed20c5 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -879,7 +879,7 @@ static inline int may_follow_link(struct nameidata *nd) return 0; /* Allowed if parent directory not sticky and world-writable. */ - parent = nd->path.dentry->d_inode; + parent = nd->inode; if ((parent->i_mode & (S_ISVTX|S_IWOTH)) != (S_ISVTX|S_IWOTH)) return 0; diff --git a/fs/ocfs2/stack_user.c b/fs/ocfs2/stack_user.c index 2768eb1da2b8..ced70c8139f7 100644 --- a/fs/ocfs2/stack_user.c +++ b/fs/ocfs2/stack_user.c @@ -655,14 +655,7 @@ static int ocfs2_control_init(void) static void ocfs2_control_exit(void) { - int rc; - - rc = misc_deregister(&ocfs2_control_device); - if (rc) - printk(KERN_ERR - "ocfs2: Unable to deregister ocfs2_control device " - "(errno %d)\n", - -rc); + misc_deregister(&ocfs2_control_device); } static void fsdlm_lock_ast_wrapper(void *astarg) diff --git a/include/drm/drm_crtc.h b/include/drm/drm_crtc.h index 57ca8cc383a6..3b4d8a4a23fb 100644 --- a/include/drm/drm_crtc.h +++ b/include/drm/drm_crtc.h @@ -743,8 +743,6 @@ struct drm_connector { uint8_t num_h_tile, num_v_tile; uint8_t tile_h_loc, tile_v_loc; uint16_t tile_h_size, tile_v_size; - - struct list_head destroy_list; }; /** diff --git a/include/drm/drm_edid.h b/include/drm/drm_edid.h index 799050198323..53c53c459b15 100644 --- a/include/drm/drm_edid.h +++ b/include/drm/drm_edid.h @@ -348,6 +348,25 @@ static inline int drm_eld_mnl(const uint8_t *eld) } /** + * drm_eld_sad - Get ELD SAD structures. + * @eld: pointer to an eld memory structure with sad_count set + */ +static inline const uint8_t *drm_eld_sad(const uint8_t *eld) +{ + unsigned int ver, mnl; + + ver = (eld[DRM_ELD_VER] & DRM_ELD_VER_MASK) >> DRM_ELD_VER_SHIFT; + if (ver != 2 && ver != 31) + return NULL; + + mnl = drm_eld_mnl(eld); + if (mnl > 16) + return NULL; + + return eld + DRM_ELD_CEA_SAD(mnl, 0); +} + +/** * drm_eld_sad_count - Get ELD SAD count. * @eld: pointer to an eld memory structure with sad_count set */ diff --git a/include/drm/drm_pciids.h b/include/drm/drm_pciids.h index 45c39a37f924..8bc073d297db 100644 --- a/include/drm/drm_pciids.h +++ b/include/drm/drm_pciids.h @@ -172,6 +172,7 @@ {0x1002, 0x6610, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6611, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6613, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_NEW_MEMMAP}, \ + {0x1002, 0x6617, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6620, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6621, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ {0x1002, 0x6623, PCI_ANY_ID, PCI_ANY_ID, 0, 0, CHIP_OLAND|RADEON_IS_MOBILITY|RADEON_NEW_MEMMAP}, \ diff --git a/include/linux/ata.h b/include/linux/ata.h index 6c78956aa470..d2992bfa1706 100644 --- a/include/linux/ata.h +++ b/include/linux/ata.h @@ -385,8 +385,6 @@ enum { SATA_SSP = 0x06, /* Software Settings Preservation */ SATA_DEVSLP = 0x09, /* Device Sleep */ - SETFEATURE_SENSE_DATA = 0xC3, /* Sense Data Reporting feature */ - /* feature values for SET_MAX */ ATA_SET_MAX_ADDR = 0x00, ATA_SET_MAX_PASSWD = 0x01, @@ -530,8 +528,6 @@ struct ata_bmdma_prd { #define ata_id_cdb_intr(id) (((id)[ATA_ID_CONFIG] & 0x60) == 0x20) #define ata_id_has_da(id) ((id)[ATA_ID_SATA_CAPABILITY_2] & (1 << 4)) #define ata_id_has_devslp(id) ((id)[ATA_ID_FEATURE_SUPP] & (1 << 8)) -#define ata_id_has_ncq_autosense(id) \ - ((id)[ATA_ID_FEATURE_SUPP] & (1 << 7)) static inline bool ata_id_has_hipm(const u16 *id) { @@ -720,20 +716,6 @@ static inline bool ata_id_has_read_log_dma_ext(const u16 *id) return false; } -static inline bool ata_id_has_sense_reporting(const u16 *id) -{ - if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) - return false; - return id[ATA_ID_COMMAND_SET_3] & (1 << 6); -} - -static inline bool ata_id_sense_reporting_enabled(const u16 *id) -{ - if (!(id[ATA_ID_CFS_ENABLE_2] & (1 << 15))) - return false; - return id[ATA_ID_COMMAND_SET_4] & (1 << 6); -} - /** * ata_id_major_version - get ATA level of drive * @id: Identify data diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 3486b9082adb..c69e1b932809 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -14,6 +14,7 @@ #define _LINUX_CORESIGHT_H #include <linux/device.h> +#include <linux/sched.h> /* Peripheral id registers (0xFD0-0xFEC) */ #define CORESIGHT_PERIPHIDR4 0xfd0 @@ -248,4 +249,24 @@ static inline struct coresight_platform_data *of_get_coresight_platform_data( struct device *dev, struct device_node *node) { return NULL; } #endif +#ifdef CONFIG_PID_NS +static inline unsigned long +coresight_vpid_to_pid(unsigned long vpid) +{ + struct task_struct *task = NULL; + unsigned long pid = 0; + + rcu_read_lock(); + task = find_task_by_vpid(vpid); + if (task) + pid = task_pid_nr(task); + rcu_read_unlock(); + + return pid; +} +#else +static inline unsigned long +coresight_vpid_to_pid(unsigned long vpid) { return vpid; } +#endif + #endif diff --git a/include/linux/device.h b/include/linux/device.h index a2b4ea70a946..1225f98e9240 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -341,7 +341,7 @@ struct subsys_interface { struct bus_type *subsys; struct list_head node; int (*add_dev)(struct device *dev, struct subsys_interface *sif); - int (*remove_dev)(struct device *dev, struct subsys_interface *sif); + void (*remove_dev)(struct device *dev, struct subsys_interface *sif); }; int subsys_interface_register(struct subsys_interface *sif); diff --git a/include/linux/extcon.h b/include/linux/extcon.h index b16d929fa75f..c0f8c4fc5d45 100644 --- a/include/linux/extcon.h +++ b/include/linux/extcon.h @@ -27,8 +27,6 @@ #define __LINUX_EXTCON_H__ #include <linux/device.h> -#include <linux/notifier.h> -#include <linux/sysfs.h> /* * Define the unique id of supported external connectors @@ -77,8 +75,6 @@ struct extcon_cable; * be attached simulataneously. {0x7, 0} is equivalent to * {0x3, 0x6, 0x5, 0}. If it is {0xFFFFFFFF, 0}, there * can be no simultaneous connections. - * @print_state: An optional callback to override the method to print the - * status of the extcon device. * @dev: Device of this extcon. * @state: Attach/detach state of this extcon. Do not provide at * register-time. @@ -102,9 +98,6 @@ struct extcon_dev { const unsigned int *supported_cable; const u32 *mutually_exclusive; - /* Optional callbacks to override class functions */ - ssize_t (*print_state)(struct extcon_dev *edev, char *buf); - /* Internal data. Please do not set. */ struct device dev; struct raw_notifier_head *nh; diff --git a/include/linux/hyperv.h b/include/linux/hyperv.h index 30d3a1f79450..54733d5b503e 100644 --- a/include/linux/hyperv.h +++ b/include/linux/hyperv.h @@ -977,6 +977,11 @@ int __must_check __vmbus_driver_register(struct hv_driver *hv_driver, const char *mod_name); void vmbus_driver_unregister(struct hv_driver *hv_driver); +int vmbus_allocate_mmio(struct resource **new, struct hv_device *device_obj, + resource_size_t min, resource_size_t max, + resource_size_t size, resource_size_t align, + bool fb_overlap_ok); + /** * VMBUS_DEVICE - macro used to describe a specific hyperv vmbus device * @@ -1233,8 +1238,6 @@ extern bool vmbus_prep_negotiate_resp(struct icmsg_hdr *, void hv_process_channel_removal(struct vmbus_channel *channel, u32 relid); -extern struct resource hyperv_mmio; - /* * Negotiated version with the Host. */ diff --git a/include/linux/irq.h b/include/linux/irq.h index 92188b0225bb..51744bcf74ee 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -484,6 +484,7 @@ extern int irq_chip_set_affinity_parent(struct irq_data *data, extern int irq_chip_set_wake_parent(struct irq_data *data, unsigned int on); extern int irq_chip_set_vcpu_affinity_parent(struct irq_data *data, void *vcpu_info); +extern int irq_chip_set_type_parent(struct irq_data *data, unsigned int type); #endif /* Handling of unhandled and spurious interrupts: */ diff --git a/include/linux/kexec.h b/include/linux/kexec.h index e804306ef5e8..b63218f68c4b 100644 --- a/include/linux/kexec.h +++ b/include/linux/kexec.h @@ -323,6 +323,7 @@ struct pt_regs; struct task_struct; static inline void crash_kexec(struct pt_regs *regs) { } static inline int kexec_should_crash(struct task_struct *p) { return 0; } +#define kexec_in_progress false #endif /* CONFIG_KEXEC */ #endif /* !defined(__ASSEBMLY__) */ diff --git a/include/linux/kvm_host.h b/include/linux/kvm_host.h index 05e99b8ef465..81089cf1f0c1 100644 --- a/include/linux/kvm_host.h +++ b/include/linux/kvm_host.h @@ -139,6 +139,7 @@ static inline bool is_error_page(struct page *page) #define KVM_REQ_DISABLE_IBS 24 #define KVM_REQ_APIC_PAGE_RELOAD 25 #define KVM_REQ_SMI 26 +#define KVM_REQ_HV_CRASH 27 #define KVM_USERSPACE_IRQ_SOURCE_ID 0 #define KVM_IRQFD_RESAMPLE_IRQ_SOURCE_ID 1 @@ -363,9 +364,6 @@ struct kvm { struct kvm_memslots *memslots[KVM_ADDRESS_SPACE_NUM]; struct srcu_struct srcu; struct srcu_struct irq_srcu; -#ifdef CONFIG_KVM_APIC_ARCHITECTURE - u32 bsp_vcpu_id; -#endif struct kvm_vcpu *vcpus[KVM_MAX_VCPUS]; atomic_t online_vcpus; int last_boosted_vcpu; @@ -424,8 +422,15 @@ struct kvm { #define vcpu_unimpl(vcpu, fmt, ...) \ kvm_pr_unimpl("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) +#define vcpu_debug(vcpu, fmt, ...) \ + kvm_debug("vcpu%i " fmt, (vcpu)->vcpu_id, ## __VA_ARGS__) + static inline struct kvm_vcpu *kvm_get_vcpu(struct kvm *kvm, int i) { + /* Pairs with smp_wmb() in kvm_vm_ioctl_create_vcpu, in case + * the caller has read kvm->online_vcpus before (as is the case + * for kvm_for_each_vcpu, for example). + */ smp_rmb(); return kvm->vcpus[i]; } @@ -1055,22 +1060,9 @@ static inline int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args) #endif /* CONFIG_HAVE_KVM_EVENTFD */ #ifdef CONFIG_KVM_APIC_ARCHITECTURE -static inline bool kvm_vcpu_is_reset_bsp(struct kvm_vcpu *vcpu) -{ - return vcpu->kvm->bsp_vcpu_id == vcpu->vcpu_id; -} - -static inline bool kvm_vcpu_is_bsp(struct kvm_vcpu *vcpu) -{ - return (vcpu->arch.apic_base & MSR_IA32_APICBASE_BSP) != 0; -} - bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu); - #else - static inline bool kvm_vcpu_compatible(struct kvm_vcpu *vcpu) { return true; } - #endif static inline void kvm_make_request(int req, struct kvm_vcpu *vcpu) diff --git a/include/linux/mei_cl_bus.h b/include/linux/mei_cl_bus.h index a16b1f9c1aca..0962b2ca628a 100644 --- a/include/linux/mei_cl_bus.h +++ b/include/linux/mei_cl_bus.h @@ -6,6 +6,7 @@ #include <linux/mod_devicetable.h> struct mei_cl_device; +struct mei_device; typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *device, u32 events, void *context); @@ -17,6 +18,8 @@ typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *device, * Drivers for MEI devices will get an mei_cl_device pointer * when being probed and shall use it for doing ME bus I/O. * + * @bus_list: device on the bus list + * @bus: parent mei device * @dev: linux driver model device pointer * @me_cl: me client * @cl: mei client @@ -25,10 +28,16 @@ typedef void (*mei_cl_event_cb_t)(struct mei_cl_device *device, * @event_cb: Drivers register this callback to get asynchronous ME * events (e.g. Rx buffer pending) notifications. * @event_context: event callback run context + * @events_mask: Events bit mask requested by driver. * @events: Events bitmask sent to the driver. + * + * @do_match: wheather device can be matched with a driver + * @is_added: device is already scanned * @priv_data: client private data */ struct mei_cl_device { + struct list_head bus_list; + struct mei_device *bus; struct device dev; struct mei_me_client *me_cl; @@ -38,8 +47,12 @@ struct mei_cl_device { struct work_struct event_work; mei_cl_event_cb_t event_cb; void *event_context; + unsigned long events_mask; unsigned long events; + unsigned int do_match:1; + unsigned int is_added:1; + void *priv_data; }; @@ -65,10 +78,12 @@ ssize_t mei_cl_send(struct mei_cl_device *device, u8 *buf, size_t length); ssize_t mei_cl_recv(struct mei_cl_device *device, u8 *buf, size_t length); int mei_cl_register_event_cb(struct mei_cl_device *device, + unsigned long event_mask, mei_cl_event_cb_t read_cb, void *context); #define MEI_CL_EVENT_RX 0 #define MEI_CL_EVENT_TX 1 +#define MEI_CL_EVENT_NOTIF 2 void *mei_cl_get_drvdata(const struct mei_cl_device *device); void mei_cl_set_drvdata(struct mei_cl_device *device, void *data); diff --git a/include/linux/mfd/palmas.h b/include/linux/mfd/palmas.h index bb270bd03eed..13e1d96935ed 100644 --- a/include/linux/mfd/palmas.h +++ b/include/linux/mfd/palmas.h @@ -21,6 +21,7 @@ #include <linux/regmap.h> #include <linux/regulator/driver.h> #include <linux/extcon.h> +#include <linux/of_gpio.h> #include <linux/usb/phy_companion.h> #define PALMAS_NUM_CLIENTS 3 @@ -551,10 +552,16 @@ struct palmas_usb { int vbus_otg_irq; int vbus_irq; + int gpio_id_irq; + struct gpio_desc *id_gpiod; + unsigned long sw_debounce_jiffies; + struct delayed_work wq_detectid; + enum palmas_usb_state linkstat; int wakeup; bool enable_vbus_detection; bool enable_id_detection; + bool enable_gpio_id_detection; }; #define comparator_to_palmas(x) container_of((x), struct palmas_usb, comparator) diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 819077c32690..81f6e427ba6b 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -67,7 +67,7 @@ struct miscdevice { }; extern int misc_register(struct miscdevice *misc); -extern int misc_deregister(struct miscdevice *misc); +extern void misc_deregister(struct miscdevice *misc); #define MODULE_ALIAS_MISCDEV(minor) \ MODULE_ALIAS("char-major-" __stringify(MISC_MAJOR) \ diff --git a/include/linux/mm.h b/include/linux/mm.h index 2e872f92dbac..bf6f117fcf4d 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1003,6 +1003,34 @@ static inline int page_mapped(struct page *page) } /* + * Return true only if the page has been allocated with + * ALLOC_NO_WATERMARKS and the low watermark was not + * met implying that the system is under some pressure. + */ +static inline bool page_is_pfmemalloc(struct page *page) +{ + /* + * Page index cannot be this large so this must be + * a pfmemalloc page. + */ + return page->index == -1UL; +} + +/* + * Only to be called by the page allocator on a freshly allocated + * page. + */ +static inline void set_page_pfmemalloc(struct page *page) +{ + page->index = -1UL; +} + +static inline void clear_page_pfmemalloc(struct page *page) +{ + page->index = 0; +} + +/* * Different kinds of faults, as returned by handle_mm_fault(). * Used to decide whether a process gets delivered SIGBUS or * just gets major/minor fault counters bumped up. diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 0038ac7466fd..15549578d559 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -63,15 +63,6 @@ struct page { union { pgoff_t index; /* Our offset within mapping. */ void *freelist; /* sl[aou]b first free object */ - bool pfmemalloc; /* If set by the page allocator, - * ALLOC_NO_WATERMARKS was set - * and the low watermark was not - * met implying that the system - * is under some pressure. The - * caller should try ensure - * this page is only used to - * free other pages. - */ }; union { diff --git a/include/linux/nvmem-consumer.h b/include/linux/nvmem-consumer.h new file mode 100644 index 000000000000..9bb77d3ed6e0 --- /dev/null +++ b/include/linux/nvmem-consumer.h @@ -0,0 +1,157 @@ +/* + * nvmem framework consumer. + * + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.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 _LINUX_NVMEM_CONSUMER_H +#define _LINUX_NVMEM_CONSUMER_H + +struct device; +struct device_node; +/* consumer cookie */ +struct nvmem_cell; +struct nvmem_device; + +struct nvmem_cell_info { + const char *name; + unsigned int offset; + unsigned int bytes; + unsigned int bit_offset; + unsigned int nbits; +}; + +#if IS_ENABLED(CONFIG_NVMEM) + +/* Cell based interface */ +struct nvmem_cell *nvmem_cell_get(struct device *dev, const char *name); +struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, const char *name); +void nvmem_cell_put(struct nvmem_cell *cell); +void devm_nvmem_cell_put(struct device *dev, struct nvmem_cell *cell); +void *nvmem_cell_read(struct nvmem_cell *cell, size_t *len); +int nvmem_cell_write(struct nvmem_cell *cell, void *buf, size_t len); + +/* direct nvmem device read/write interface */ +struct nvmem_device *nvmem_device_get(struct device *dev, const char *name); +struct nvmem_device *devm_nvmem_device_get(struct device *dev, + const char *name); +void nvmem_device_put(struct nvmem_device *nvmem); +void devm_nvmem_device_put(struct device *dev, struct nvmem_device *nvmem); +int nvmem_device_read(struct nvmem_device *nvmem, unsigned int offset, + size_t bytes, void *buf); +int nvmem_device_write(struct nvmem_device *nvmem, unsigned int offset, + size_t bytes, void *buf); +ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf); +int nvmem_device_cell_write(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, void *buf); + +#else + +static inline struct nvmem_cell *nvmem_cell_get(struct device *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct nvmem_cell *devm_nvmem_cell_get(struct device *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void devm_nvmem_cell_put(struct device *dev, + struct nvmem_cell *cell) +{ + +} +static inline void nvmem_cell_put(struct nvmem_cell *cell) +{ +} + +static inline char *nvmem_cell_read(struct nvmem_cell *cell, size_t *len) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int nvmem_cell_write(struct nvmem_cell *cell, + const char *buf, size_t len) +{ + return -ENOSYS; +} + +static inline struct nvmem_device *nvmem_device_get(struct device *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct nvmem_device *devm_nvmem_device_get(struct device *dev, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline void nvmem_device_put(struct nvmem_device *nvmem) +{ +} + +static inline void devm_nvmem_device_put(struct device *dev, + struct nvmem_device *nvmem) +{ +} + +static inline ssize_t nvmem_device_cell_read(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, + void *buf) +{ + return -ENOSYS; +} + +static inline int nvmem_device_cell_write(struct nvmem_device *nvmem, + struct nvmem_cell_info *info, + void *buf) +{ + return -ENOSYS; +} + +static inline int nvmem_device_read(struct nvmem_device *nvmem, + unsigned int offset, size_t bytes, + void *buf) +{ + return -ENOSYS; +} + +static inline int nvmem_device_write(struct nvmem_device *nvmem, + unsigned int offset, size_t bytes, + void *buf) +{ + return -ENOSYS; +} +#endif /* CONFIG_NVMEM */ + +#if IS_ENABLED(CONFIG_NVMEM) && IS_ENABLED(CONFIG_OF) +struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *name); +struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name); +#else +static inline struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} + +static inline struct nvmem_device *of_nvmem_device_get(struct device_node *np, + const char *name) +{ + return ERR_PTR(-ENOSYS); +} +#endif /* CONFIG_NVMEM && CONFIG_OF */ + +#endif /* ifndef _LINUX_NVMEM_CONSUMER_H */ diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h new file mode 100644 index 000000000000..0b68caff1b3c --- /dev/null +++ b/include/linux/nvmem-provider.h @@ -0,0 +1,47 @@ +/* + * nvmem framework provider. + * + * Copyright (C) 2015 Srinivas Kandagatla <srinivas.kandagatla@linaro.org> + * Copyright (C) 2013 Maxime Ripard <maxime.ripard@free-electrons.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 _LINUX_NVMEM_PROVIDER_H +#define _LINUX_NVMEM_PROVIDER_H + +struct nvmem_device; +struct nvmem_cell_info; + +struct nvmem_config { + struct device *dev; + const char *name; + int id; + struct module *owner; + const struct nvmem_cell_info *cells; + int ncells; + bool read_only; +}; + +#if IS_ENABLED(CONFIG_NVMEM) + +struct nvmem_device *nvmem_register(const struct nvmem_config *cfg); +int nvmem_unregister(struct nvmem_device *nvmem); + +#else + +static inline struct nvmem_device *nvmem_register(const struct nvmem_config *c) +{ + return ERR_PTR(-ENOSYS); +} + +static inline int nvmem_unregister(struct nvmem_device *nvmem) +{ + return -ENOSYS; +} + +#endif /* CONFIG_NVMEM */ + +#endif /* ifndef _LINUX_NVMEM_PROVIDER_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 8a0321a8fb59..860c751810fc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1202,6 +1202,7 @@ struct msix_entry { u16 entry; /* driver uses to specify entry, OS writes */ }; +void pci_msi_setup_pci_dev(struct pci_dev *dev); #ifdef CONFIG_PCI_MSI int pci_msi_vec_count(struct pci_dev *dev); diff --git a/include/linux/skbuff.h b/include/linux/skbuff.h index d6cdd6e87d53..9b88536487e6 100644 --- a/include/linux/skbuff.h +++ b/include/linux/skbuff.h @@ -1602,20 +1602,16 @@ static inline void __skb_fill_page_desc(struct sk_buff *skb, int i, skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; /* - * Propagate page->pfmemalloc to the skb if we can. The problem is - * that not all callers have unique ownership of the page. If - * pfmemalloc is set, we check the mapping as a mapping implies - * page->index is set (index and pfmemalloc share space). - * If it's a valid mapping, we cannot use page->pfmemalloc but we - * do not lose pfmemalloc information as the pages would not be - * allocated using __GFP_MEMALLOC. + * Propagate page pfmemalloc to the skb if we can. The problem is + * that not all callers have unique ownership of the page but rely + * on page_is_pfmemalloc doing the right thing(tm). */ frag->page.p = page; frag->page_offset = off; skb_frag_size_set(frag, size); page = compound_head(page); - if (page->pfmemalloc && !page->mapping) + if (page_is_pfmemalloc(page)) skb->pfmemalloc = true; } @@ -2263,7 +2259,7 @@ static inline struct page *dev_alloc_page(void) static inline void skb_propagate_pfmemalloc(struct page *page, struct sk_buff *skb) { - if (page && page->pfmemalloc) + if (page_is_pfmemalloc(page)) skb->pfmemalloc = true; } @@ -2884,11 +2880,11 @@ static inline bool skb_defer_rx_timestamp(struct sk_buff *skb) * * PHY drivers may accept clones of transmitted packets for * timestamping via their phy_driver.txtstamp method. These drivers - * must call this function to return the skb back to the stack, with - * or without a timestamp. + * must call this function to return the skb back to the stack with a + * timestamp. * * @skb: clone of the the original outgoing packet - * @hwtstamps: hardware time stamps, may be NULL if not available + * @hwtstamps: hardware time stamps * */ void skb_complete_tx_timestamp(struct sk_buff *skb, diff --git a/include/linux/ti_wilink_st.h b/include/linux/ti_wilink_st.h index c78dcfeaf25f..d4217eff489f 100644 --- a/include/linux/ti_wilink_st.h +++ b/include/linux/ti_wilink_st.h @@ -86,7 +86,6 @@ struct st_proto_s { extern long st_register(struct st_proto_s *); extern long st_unregister(struct st_proto_s *); -extern struct ti_st_plat_data *dt_pdata; /* * header information used by st_core.c diff --git a/include/media/rc-core.h b/include/media/rc-core.h index 45534da57759..644bdc61c387 100644 --- a/include/media/rc-core.h +++ b/include/media/rc-core.h @@ -74,8 +74,6 @@ enum rc_filter_type { * @input_dev: the input child device used to communicate events to userspace * @driver_type: specifies if protocol decoding is done in hardware or software * @idle: used to keep track of RX state - * @encode_wakeup: wakeup filtering uses IR encode API, therefore the allowed - * wakeup protocols is the set of all raw encoders * @allowed_protocols: bitmask with the supported RC_BIT_* protocols * @enabled_protocols: bitmask with the enabled RC_BIT_* protocols * @allowed_wakeup_protocols: bitmask with the supported RC_BIT_* wakeup protocols @@ -136,7 +134,6 @@ struct rc_dev { struct input_dev *input_dev; enum rc_driver_type driver_type; bool idle; - bool encode_wakeup; u64 allowed_protocols; u64 enabled_protocols; u64 allowed_wakeup_protocols; @@ -246,7 +243,6 @@ static inline void init_ir_raw_event(struct ir_raw_event *ev) #define US_TO_NS(usec) ((usec) * 1000) #define MS_TO_US(msec) ((msec) * 1000) #define MS_TO_NS(msec) ((msec) * 1000 * 1000) -#define NS_TO_US(nsec) DIV_ROUND_UP(nsec, 1000L) void ir_raw_event_handle(struct rc_dev *dev); int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev); @@ -254,9 +250,6 @@ int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type); int ir_raw_event_store_with_filter(struct rc_dev *dev, struct ir_raw_event *ev); void ir_raw_event_set_idle(struct rc_dev *dev, bool idle); -int ir_raw_encode_scancode(u64 protocols, - const struct rc_scancode_filter *scancode, - struct ir_raw_event *events, unsigned int max); static inline void ir_raw_event_reset(struct rc_dev *dev) { diff --git a/include/media/videobuf2-core.h b/include/media/videobuf2-core.h index 22a44c2f5963..c192e1b46cdc 100644 --- a/include/media/videobuf2-core.h +++ b/include/media/videobuf2-core.h @@ -139,6 +139,7 @@ enum vb2_io_modes { * @VB2_BUF_STATE_PREPARING: buffer is being prepared in videobuf * @VB2_BUF_STATE_PREPARED: buffer prepared in videobuf and by the driver * @VB2_BUF_STATE_QUEUED: buffer queued in videobuf, but not in driver + * @VB2_BUF_STATE_REQUEUEING: re-queue a buffer to the driver * @VB2_BUF_STATE_ACTIVE: buffer queued in driver and possibly used * in a hardware operation * @VB2_BUF_STATE_DONE: buffer returned from driver to videobuf, but @@ -152,6 +153,7 @@ enum vb2_buffer_state { VB2_BUF_STATE_PREPARING, VB2_BUF_STATE_PREPARED, VB2_BUF_STATE_QUEUED, + VB2_BUF_STATE_REQUEUEING, VB2_BUF_STATE_ACTIVE, VB2_BUF_STATE_DONE, VB2_BUF_STATE_ERROR, diff --git a/include/scsi/scsi_eh.h b/include/scsi/scsi_eh.h index 4942710ef720..8d1d7fa67ec4 100644 --- a/include/scsi/scsi_eh.h +++ b/include/scsi/scsi_eh.h @@ -28,7 +28,6 @@ extern int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len, u64 * info_out); extern void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq); -extern void scsi_set_sense_information(u8 *buf, u64 info); extern int scsi_ioctl_reset(struct scsi_device *, int __user *); diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h index 865a141b118b..427bc41df3ae 100644 --- a/include/sound/soc-topology.h +++ b/include/sound/soc-topology.h @@ -141,6 +141,8 @@ struct snd_soc_tplg_ops { int io_ops_count; }; +#ifdef CONFIG_SND_SOC_TOPOLOGY + /* gets a pointer to data from the firmware block header */ static inline const void *snd_soc_tplg_get_data(struct snd_soc_tplg_hdr *hdr) { @@ -165,4 +167,14 @@ int snd_soc_tplg_widget_bind_event(struct snd_soc_dapm_widget *w, const struct snd_soc_tplg_widget_events *events, int num_events, u16 event_type); +#else + +static inline int snd_soc_tplg_component_remove(struct snd_soc_component *comp, + u32 index) +{ + return 0; +} + +#endif + #endif diff --git a/include/trace/events/spmi.h b/include/trace/events/spmi.h new file mode 100644 index 000000000000..62f005ef4c7e --- /dev/null +++ b/include/trace/events/spmi.h @@ -0,0 +1,135 @@ +#undef TRACE_SYSTEM +#define TRACE_SYSTEM spmi + +#if !defined(_TRACE_SPMI_H) || defined(TRACE_HEADER_MULTI_READ) +#define _TRACE_SPMI_H + +#include <linux/spmi.h> +#include <linux/tracepoint.h> + +/* + * drivers/spmi/spmi.c + */ + +TRACE_EVENT(spmi_write_begin, + TP_PROTO(u8 opcode, u8 sid, u16 addr, u8 len, const u8 *buf), + TP_ARGS(opcode, sid, addr, len, buf), + + TP_STRUCT__entry( + __field ( u8, opcode ) + __field ( u8, sid ) + __field ( u16, addr ) + __field ( u8, len ) + __dynamic_array ( u8, buf, len + 1 ) + ), + + TP_fast_assign( + __entry->opcode = opcode; + __entry->sid = sid; + __entry->addr = addr; + __entry->len = len + 1; + memcpy(__get_dynamic_array(buf), buf, len + 1); + ), + + TP_printk("opc=%d sid=%02d addr=0x%04x len=%d buf=0x[%*phD]", + (int)__entry->opcode, (int)__entry->sid, + (int)__entry->addr, (int)__entry->len, + (int)__entry->len, __get_dynamic_array(buf)) +); + +TRACE_EVENT(spmi_write_end, + TP_PROTO(u8 opcode, u8 sid, u16 addr, int ret), + TP_ARGS(opcode, sid, addr, ret), + + TP_STRUCT__entry( + __field ( u8, opcode ) + __field ( u8, sid ) + __field ( u16, addr ) + __field ( int, ret ) + ), + + TP_fast_assign( + __entry->opcode = opcode; + __entry->sid = sid; + __entry->addr = addr; + __entry->ret = ret; + ), + + TP_printk("opc=%d sid=%02d addr=0x%04x ret=%d", + (int)__entry->opcode, (int)__entry->sid, + (int)__entry->addr, __entry->ret) +); + +TRACE_EVENT(spmi_read_begin, + TP_PROTO(u8 opcode, u8 sid, u16 addr), + TP_ARGS(opcode, sid, addr), + + TP_STRUCT__entry( + __field ( u8, opcode ) + __field ( u8, sid ) + __field ( u16, addr ) + ), + + TP_fast_assign( + __entry->opcode = opcode; + __entry->sid = sid; + __entry->addr = addr; + ), + + TP_printk("opc=%d sid=%02d addr=0x%04x", + (int)__entry->opcode, (int)__entry->sid, + (int)__entry->addr) +); + +TRACE_EVENT(spmi_read_end, + TP_PROTO(u8 opcode, u8 sid, u16 addr, int ret, u8 len, const u8 *buf), + TP_ARGS(opcode, sid, addr, ret, len, buf), + + TP_STRUCT__entry( + __field ( u8, opcode ) + __field ( u8, sid ) + __field ( u16, addr ) + __field ( int, ret ) + __field ( u8, len ) + __dynamic_array ( u8, buf, len + 1 ) + ), + + TP_fast_assign( + __entry->opcode = opcode; + __entry->sid = sid; + __entry->addr = addr; + __entry->ret = ret; + __entry->len = len + 1; + memcpy(__get_dynamic_array(buf), buf, len + 1); + ), + + TP_printk("opc=%d sid=%02d addr=0x%04x ret=%d len=%02d buf=0x[%*phD]", + (int)__entry->opcode, (int)__entry->sid, + (int)__entry->addr, __entry->ret, (int)__entry->len, + (int)__entry->len, __get_dynamic_array(buf)) +); + +TRACE_EVENT(spmi_cmd, + TP_PROTO(u8 opcode, u8 sid, int ret), + TP_ARGS(opcode, sid, ret), + + TP_STRUCT__entry( + __field ( u8, opcode ) + __field ( u8, sid ) + __field ( int, ret ) + ), + + TP_fast_assign( + __entry->opcode = opcode; + __entry->sid = sid; + __entry->ret = ret; + ), + + TP_printk("opc=%d sid=%02d ret=%d", (int)__entry->opcode, + (int)__entry->sid, ret) +); + +#endif /* _TRACE_SPMI_H */ + +/* This part must be outside protection */ +#include <trace/define_trace.h> diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h index 716ad4ae4d4b..0d831f94f8a8 100644 --- a/include/uapi/linux/kvm.h +++ b/include/uapi/linux/kvm.h @@ -317,6 +317,7 @@ struct kvm_run { struct { #define KVM_SYSTEM_EVENT_SHUTDOWN 1 #define KVM_SYSTEM_EVENT_RESET 2 +#define KVM_SYSTEM_EVENT_CRASH 3 __u32 type; __u64 flags; } system_event; @@ -481,6 +482,7 @@ struct kvm_s390_psw { ((ai) << 26)) #define KVM_S390_INT_IO_MIN 0x00000000u #define KVM_S390_INT_IO_MAX 0xfffdffffu +#define KVM_S390_INT_IO_AI_MASK 0x04000000u struct kvm_s390_interrupt { diff --git a/include/uapi/linux/mei.h b/include/uapi/linux/mei.h index bc0d8b69c49e..7c3b64f6a215 100644 --- a/include/uapi/linux/mei.h +++ b/include/uapi/linux/mei.h @@ -107,4 +107,23 @@ struct mei_connect_client_data { }; }; +/** + * DOC: set and unset event notification for a connected client + * + * The IOCTL argument is 1 for enabling event notification and 0 for + * disabling the service + * Return: -EOPNOTSUPP if the devices doesn't support the feature + */ +#define IOCTL_MEI_NOTIFY_SET _IOW('H', 0x02, __u32) + +/** + * DOC: retrieve notification + * + * The IOCTL output argument is 1 if an event was is pending and 0 otherwise + * the ioctl has to be called in order to acknowledge pending event + * + * Return: -EOPNOTSUPP if the devices doesn't support the feature + */ +#define IOCTL_MEI_NOTIFY_GET _IOR('H', 0x03, __u32) + #endif /* _LINUX_MEI_H */ diff --git a/include/uapi/sound/asoc.h b/include/uapi/sound/asoc.h index 51b8066a223b..247c50bd60f0 100644 --- a/include/uapi/sound/asoc.h +++ b/include/uapi/sound/asoc.h @@ -18,6 +18,12 @@ #include <linux/types.h> #include <sound/asound.h> +#ifndef __KERNEL__ +#error This API is an early revision and not enabled in the current +#error kernel release, it will be enabled in a future kernel version +#error with incompatible changes to what is here. +#endif + /* * Maximum number of channels topology kcontrol can represent. */ diff --git a/ipc/sem.c b/ipc/sem.c index bc3d530cb23e..b471e5a3863d 100644 --- a/ipc/sem.c +++ b/ipc/sem.c @@ -253,6 +253,16 @@ static void sem_rcu_free(struct rcu_head *head) } /* + * spin_unlock_wait() and !spin_is_locked() are not memory barriers, they + * are only control barriers. + * The code must pair with spin_unlock(&sem->lock) or + * spin_unlock(&sem_perm.lock), thus just the control barrier is insufficient. + * + * smp_rmb() is sufficient, as writes cannot pass the control barrier. + */ +#define ipc_smp_acquire__after_spin_is_unlocked() smp_rmb() + +/* * Wait until all currently ongoing simple ops have completed. * Caller must own sem_perm.lock. * New simple ops cannot start, because simple ops first check @@ -275,6 +285,7 @@ static void sem_wait_array(struct sem_array *sma) sem = sma->sem_base + i; spin_unlock_wait(&sem->lock); } + ipc_smp_acquire__after_spin_is_unlocked(); } /* @@ -327,13 +338,12 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops, /* Then check that the global lock is free */ if (!spin_is_locked(&sma->sem_perm.lock)) { /* - * The ipc object lock check must be visible on all - * cores before rechecking the complex count. Otherwise - * we can race with another thread that does: + * We need a memory barrier with acquire semantics, + * otherwise we can race with another thread that does: * complex_count++; * spin_unlock(sem_perm.lock); */ - smp_rmb(); + ipc_smp_acquire__after_spin_is_unlocked(); /* * Now repeat the test of complex_count: @@ -2074,17 +2084,28 @@ void exit_sem(struct task_struct *tsk) rcu_read_lock(); un = list_entry_rcu(ulp->list_proc.next, struct sem_undo, list_proc); - if (&un->list_proc == &ulp->list_proc) - semid = -1; - else - semid = un->semid; + if (&un->list_proc == &ulp->list_proc) { + /* + * We must wait for freeary() before freeing this ulp, + * in case we raced with last sem_undo. There is a small + * possibility where we exit while freeary() didn't + * finish unlocking sem_undo_list. + */ + spin_unlock_wait(&ulp->lock); + rcu_read_unlock(); + break; + } + spin_lock(&ulp->lock); + semid = un->semid; + spin_unlock(&ulp->lock); + /* exit_sem raced with IPC_RMID, nothing to do */ if (semid == -1) { rcu_read_unlock(); - break; + continue; } - sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, un->semid); + sma = sem_obtain_object_check(tsk->nsproxy->ipc_ns, semid); /* exit_sem raced with IPC_RMID, nothing to do */ if (IS_ERR(sma)) { rcu_read_unlock(); @@ -2112,9 +2133,11 @@ void exit_sem(struct task_struct *tsk) ipc_assert_locked_object(&sma->sem_perm); list_del(&un->list_id); - spin_lock(&ulp->lock); + /* we are the last process using this ulp, acquiring ulp->lock + * isn't required. Besides that, we are also protected against + * IPC_RMID as we hold sma->sem_perm lock now + */ list_del_rcu(&un->list_proc); - spin_unlock(&ulp->lock); /* perform adjustments registered in un */ for (i = 0; i < sma->sem_nsems; i++) { diff --git a/kernel/cpu.c b/kernel/cpu.c index 5644ec5582b9..964b4efadf41 100644 --- a/kernel/cpu.c +++ b/kernel/cpu.c @@ -191,21 +191,22 @@ void cpu_hotplug_done(void) void cpu_hotplug_disable(void) { cpu_maps_update_begin(); - cpu_hotplug_disabled = 1; + cpu_hotplug_disabled++; cpu_maps_update_done(); } +EXPORT_SYMBOL_GPL(cpu_hotplug_disable); void cpu_hotplug_enable(void) { cpu_maps_update_begin(); - cpu_hotplug_disabled = 0; + WARN_ON(--cpu_hotplug_disabled < 0); cpu_maps_update_done(); } - +EXPORT_SYMBOL_GPL(cpu_hotplug_enable); #endif /* CONFIG_HOTPLUG_CPU */ /* Need to know about CPUs going up/down? */ -int __ref register_cpu_notifier(struct notifier_block *nb) +int register_cpu_notifier(struct notifier_block *nb) { int ret; cpu_maps_update_begin(); @@ -214,7 +215,7 @@ int __ref register_cpu_notifier(struct notifier_block *nb) return ret; } -int __ref __register_cpu_notifier(struct notifier_block *nb) +int __register_cpu_notifier(struct notifier_block *nb) { return raw_notifier_chain_register(&cpu_chain, nb); } @@ -244,7 +245,7 @@ static void cpu_notify_nofail(unsigned long val, void *v) EXPORT_SYMBOL(register_cpu_notifier); EXPORT_SYMBOL(__register_cpu_notifier); -void __ref unregister_cpu_notifier(struct notifier_block *nb) +void unregister_cpu_notifier(struct notifier_block *nb) { cpu_maps_update_begin(); raw_notifier_chain_unregister(&cpu_chain, nb); @@ -252,7 +253,7 @@ void __ref unregister_cpu_notifier(struct notifier_block *nb) } EXPORT_SYMBOL(unregister_cpu_notifier); -void __ref __unregister_cpu_notifier(struct notifier_block *nb) +void __unregister_cpu_notifier(struct notifier_block *nb) { raw_notifier_chain_unregister(&cpu_chain, nb); } @@ -329,7 +330,7 @@ struct take_cpu_down_param { }; /* Take this CPU down. */ -static int __ref take_cpu_down(void *_param) +static int take_cpu_down(void *_param) { struct take_cpu_down_param *param = _param; int err; @@ -348,7 +349,7 @@ static int __ref take_cpu_down(void *_param) } /* Requires cpu_add_remove_lock to be held */ -static int __ref _cpu_down(unsigned int cpu, int tasks_frozen) +static int _cpu_down(unsigned int cpu, int tasks_frozen) { int err, nr_calls = 0; void *hcpu = (void *)(long)cpu; @@ -442,7 +443,7 @@ out_release: return err; } -int __ref cpu_down(unsigned int cpu) +int cpu_down(unsigned int cpu) { int err; @@ -608,13 +609,18 @@ int disable_nonboot_cpus(void) } } - if (!error) { + if (!error) BUG_ON(num_online_cpus() > 1); - /* Make sure the CPUs won't be enabled by someone else */ - cpu_hotplug_disabled = 1; - } else { + else pr_err("Non-boot CPUs are not disabled\n"); - } + + /* + * Make sure the CPUs won't be enabled by someone else. We need to do + * this even in case of failure as all disable_nonboot_cpus() users are + * supposed to do enable_nonboot_cpus() on the failure path. + */ + cpu_hotplug_disabled++; + cpu_maps_update_done(); return error; } @@ -627,13 +633,13 @@ void __weak arch_enable_nonboot_cpus_end(void) { } -void __ref enable_nonboot_cpus(void) +void enable_nonboot_cpus(void) { int cpu, error; /* Allow everyone to use the CPU hotplug again */ cpu_maps_update_begin(); - cpu_hotplug_disabled = 0; + WARN_ON(--cpu_hotplug_disabled < 0); if (cpumask_empty(frozen_cpus)) goto out; diff --git a/kernel/cpuset.c b/kernel/cpuset.c index ee14e3a35a29..f0acff0f66c9 100644 --- a/kernel/cpuset.c +++ b/kernel/cpuset.c @@ -1223,7 +1223,7 @@ static int update_nodemask(struct cpuset *cs, struct cpuset *trialcs, spin_unlock_irq(&callback_lock); /* use trialcs->mems_allowed as a temp variable */ - update_nodemasks_hier(cs, &cs->mems_allowed); + update_nodemasks_hier(cs, &trialcs->mems_allowed); done: return retval; } diff --git a/kernel/events/core.c b/kernel/events/core.c index d3dae3419b99..e6feb5114134 100644 --- a/kernel/events/core.c +++ b/kernel/events/core.c @@ -1868,8 +1868,6 @@ event_sched_in(struct perf_event *event, perf_pmu_disable(event->pmu); - event->tstamp_running += tstamp - event->tstamp_stopped; - perf_set_shadow_time(event, ctx, tstamp); perf_log_itrace_start(event); @@ -1881,6 +1879,8 @@ event_sched_in(struct perf_event *event, goto out; } + event->tstamp_running += tstamp - event->tstamp_stopped; + if (!is_software_event(event)) cpuctx->active_oncpu++; if (!ctx->nr_active++) @@ -3958,28 +3958,21 @@ static void perf_event_for_each(struct perf_event *event, perf_event_for_each_child(sibling, func); } -static int perf_event_period(struct perf_event *event, u64 __user *arg) -{ - struct perf_event_context *ctx = event->ctx; - int ret = 0, active; +struct period_event { + struct perf_event *event; u64 value; +}; - if (!is_sampling_event(event)) - return -EINVAL; - - if (copy_from_user(&value, arg, sizeof(value))) - return -EFAULT; - - if (!value) - return -EINVAL; +static int __perf_event_period(void *info) +{ + struct period_event *pe = info; + struct perf_event *event = pe->event; + struct perf_event_context *ctx = event->ctx; + u64 value = pe->value; + bool active; - raw_spin_lock_irq(&ctx->lock); + raw_spin_lock(&ctx->lock); if (event->attr.freq) { - if (value > sysctl_perf_event_sample_rate) { - ret = -EINVAL; - goto unlock; - } - event->attr.sample_freq = value; } else { event->attr.sample_period = value; @@ -3998,11 +3991,53 @@ static int perf_event_period(struct perf_event *event, u64 __user *arg) event->pmu->start(event, PERF_EF_RELOAD); perf_pmu_enable(ctx->pmu); } + raw_spin_unlock(&ctx->lock); -unlock: + return 0; +} + +static int perf_event_period(struct perf_event *event, u64 __user *arg) +{ + struct period_event pe = { .event = event, }; + struct perf_event_context *ctx = event->ctx; + struct task_struct *task; + u64 value; + + if (!is_sampling_event(event)) + return -EINVAL; + + if (copy_from_user(&value, arg, sizeof(value))) + return -EFAULT; + + if (!value) + return -EINVAL; + + if (event->attr.freq && value > sysctl_perf_event_sample_rate) + return -EINVAL; + + task = ctx->task; + pe.value = value; + + if (!task) { + cpu_function_call(event->cpu, __perf_event_period, &pe); + return 0; + } + +retry: + if (!task_function_call(task, __perf_event_period, &pe)) + return 0; + + raw_spin_lock_irq(&ctx->lock); + if (ctx->is_active) { + raw_spin_unlock_irq(&ctx->lock); + task = ctx->task; + goto retry; + } + + __perf_event_period(&pe); raw_spin_unlock_irq(&ctx->lock); - return ret; + return 0; } static const struct file_operations perf_fops; @@ -4740,12 +4775,20 @@ static const struct file_operations perf_fops = { * to user-space before waking everybody up. */ +static inline struct fasync_struct **perf_event_fasync(struct perf_event *event) +{ + /* only the parent has fasync state */ + if (event->parent) + event = event->parent; + return &event->fasync; +} + void perf_event_wakeup(struct perf_event *event) { ring_buffer_wakeup(event); if (event->pending_kill) { - kill_fasync(&event->fasync, SIGIO, event->pending_kill); + kill_fasync(perf_event_fasync(event), SIGIO, event->pending_kill); event->pending_kill = 0; } } @@ -6124,7 +6167,7 @@ static int __perf_event_overflow(struct perf_event *event, else perf_event_output(event, data, regs); - if (event->fasync && event->pending_kill) { + if (*perf_event_fasync(event) && event->pending_kill) { event->pending_wakeup = 1; irq_work_queue(&event->pending); } diff --git a/kernel/events/ring_buffer.c b/kernel/events/ring_buffer.c index b2be01b1aa9d..c8aa3f75bc4d 100644 --- a/kernel/events/ring_buffer.c +++ b/kernel/events/ring_buffer.c @@ -559,11 +559,13 @@ static void __rb_free_aux(struct ring_buffer *rb) rb->aux_priv = NULL; } - for (pg = 0; pg < rb->aux_nr_pages; pg++) - rb_free_aux_page(rb, pg); + if (rb->aux_nr_pages) { + for (pg = 0; pg < rb->aux_nr_pages; pg++) + rb_free_aux_page(rb, pg); - kfree(rb->aux_pages); - rb->aux_nr_pages = 0; + kfree(rb->aux_pages); + rb->aux_nr_pages = 0; + } } void rb_free_aux(struct ring_buffer *rb) diff --git a/kernel/irq/chip.c b/kernel/irq/chip.c index 27f4332c7f84..ae216824e8ca 100644 --- a/kernel/irq/chip.c +++ b/kernel/irq/chip.c @@ -985,6 +985,23 @@ int irq_chip_set_affinity_parent(struct irq_data *data, } /** + * irq_chip_set_type_parent - Set IRQ type on the parent interrupt + * @data: Pointer to interrupt specific data + * @type: IRQ_TYPE_{LEVEL,EDGE}_* value - see include/linux/irq.h + * + * Conditional, as the underlying parent chip might not implement it. + */ +int irq_chip_set_type_parent(struct irq_data *data, unsigned int type) +{ + data = data->parent_data; + + if (data->chip->irq_set_type) + return data->chip->irq_set_type(data, type); + + return -ENOSYS; +} + +/** * irq_chip_retrigger_hierarchy - Retrigger an interrupt in hardware * @data: Pointer to interrupt specific data * @@ -997,7 +1014,7 @@ int irq_chip_retrigger_hierarchy(struct irq_data *data) if (data->chip && data->chip->irq_retrigger) return data->chip->irq_retrigger(data); - return -ENOSYS; + return 0; } /** diff --git a/kernel/locking/qspinlock_paravirt.h b/kernel/locking/qspinlock_paravirt.h index 04ab18151cc8..df19ae4debd0 100644 --- a/kernel/locking/qspinlock_paravirt.h +++ b/kernel/locking/qspinlock_paravirt.h @@ -4,6 +4,7 @@ #include <linux/hash.h> #include <linux/bootmem.h> +#include <linux/debug_locks.h> /* * Implement paravirt qspinlocks; the general idea is to halt the vcpus instead @@ -286,15 +287,23 @@ __visible void __pv_queued_spin_unlock(struct qspinlock *lock) { struct __qspinlock *l = (void *)lock; struct pv_node *node; + u8 lockval = cmpxchg(&l->locked, _Q_LOCKED_VAL, 0); /* * We must not unlock if SLOW, because in that case we must first * unhash. Otherwise it would be possible to have multiple @lock * entries, which would be BAD. */ - if (likely(cmpxchg(&l->locked, _Q_LOCKED_VAL, 0) == _Q_LOCKED_VAL)) + if (likely(lockval == _Q_LOCKED_VAL)) return; + if (unlikely(lockval != _Q_SLOW_VAL)) { + if (debug_locks_silent) + return; + WARN(1, "pvqspinlock: lock %p has corrupted value 0x%x!\n", lock, atomic_read(&lock->val)); + return; + } + /* * Since the above failed to release, this must be the SLOW path. * Therefore start by looking up the blocked node and unhashing it. diff --git a/kernel/time/timer.c b/kernel/time/timer.c index 5e097fa9faf7..84190f02b521 100644 --- a/kernel/time/timer.c +++ b/kernel/time/timer.c @@ -807,8 +807,8 @@ __mod_timer(struct timer_list *timer, unsigned long expires, spin_unlock(&base->lock); base = new_base; spin_lock(&base->lock); - timer->flags &= ~TIMER_BASEMASK; - timer->flags |= base->cpu; + WRITE_ONCE(timer->flags, + (timer->flags & ~TIMER_BASEMASK) | base->cpu); } } @@ -16,7 +16,7 @@ struct cma { extern struct cma cma_areas[MAX_CMA_AREAS]; extern unsigned cma_area_count; -static unsigned long cma_bitmap_maxno(struct cma *cma) +static inline unsigned long cma_bitmap_maxno(struct cma *cma) { return cma->count >> cma->order_per_bit; } diff --git a/mm/kasan/kasan.c b/mm/kasan/kasan.c index 6c513a63ea84..7b28e9cdf1c7 100644 --- a/mm/kasan/kasan.c +++ b/mm/kasan/kasan.c @@ -2,7 +2,7 @@ * This file contains shadow memory manipulation code. * * Copyright (c) 2014 Samsung Electronics Co., Ltd. - * Author: Andrey Ryabinin <a.ryabinin@samsung.com> + * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> * * Some of code borrowed from https://github.com/xairy/linux by * Andrey Konovalov <adech.fo@gmail.com> diff --git a/mm/kasan/report.c b/mm/kasan/report.c index 680ceedf810a..e07c94fbd0ac 100644 --- a/mm/kasan/report.c +++ b/mm/kasan/report.c @@ -2,7 +2,7 @@ * This file contains error reporting code. * * Copyright (c) 2014 Samsung Electronics Co., Ltd. - * Author: Andrey Ryabinin <a.ryabinin@samsung.com> + * Author: Andrey Ryabinin <ryabinin.a.a@gmail.com> * * Some of code borrowed from https://github.com/xairy/linux by * Andrey Konovalov <adech.fo@gmail.com> diff --git a/mm/memory-failure.c b/mm/memory-failure.c index ea5a93659488..1f4446a90cef 100644 --- a/mm/memory-failure.c +++ b/mm/memory-failure.c @@ -1146,8 +1146,11 @@ int memory_failure(unsigned long pfn, int trapno, int flags) } if (!PageHuge(p) && PageTransHuge(hpage)) { - if (unlikely(split_huge_page(hpage))) { - pr_err("MCE: %#lx: thp split failed\n", pfn); + if (!PageAnon(hpage) || unlikely(split_huge_page(hpage))) { + if (!PageAnon(hpage)) + pr_err("MCE: %#lx: non anonymous thp\n", pfn); + else + pr_err("MCE: %#lx: thp split failed\n", pfn); if (TestClearPageHWPoison(p)) atomic_long_sub(nr_pages, &num_poisoned_pages); put_page(p); @@ -1538,6 +1541,8 @@ static int get_any_page(struct page *page, unsigned long pfn, int flags) */ ret = __get_any_page(page, pfn, 0); if (!PageLRU(page)) { + /* Drop page reference which is from __get_any_page() */ + put_page(page); pr_info("soft_offline: %#lx: unknown non LRU page type %lx\n", pfn, page->flags); return -EIO; @@ -1567,13 +1572,12 @@ static int soft_offline_huge_page(struct page *page, int flags) unlock_page(hpage); ret = isolate_huge_page(hpage, &pagelist); - if (ret) { - /* - * get_any_page() and isolate_huge_page() takes a refcount each, - * so need to drop one here. - */ - put_page(hpage); - } else { + /* + * get_any_page() and isolate_huge_page() takes a refcount each, + * so need to drop one here. + */ + put_page(hpage); + if (!ret) { pr_info("soft offline: %#lx hugepage failed to isolate\n", pfn); return -EBUSY; } diff --git a/mm/memory_hotplug.c b/mm/memory_hotplug.c index 003dbe4b060d..6da82bcb0a8b 100644 --- a/mm/memory_hotplug.c +++ b/mm/memory_hotplug.c @@ -1277,6 +1277,7 @@ int __ref add_memory(int nid, u64 start, u64 size) /* create new memmap entry */ firmware_map_add_hotplug(start, start + size, "System RAM"); + memblock_add_node(start, size, nid); goto out; @@ -2013,6 +2014,8 @@ void __ref remove_memory(int nid, u64 start, u64 size) /* remove memmap entry */ firmware_map_remove(start, start + size, "System RAM"); + memblock_free(start, size); + memblock_remove(start, size); arch_remove_memory(start, size); diff --git a/mm/page_alloc.c b/mm/page_alloc.c index beda41710802..5b5240b7f642 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1343,12 +1343,15 @@ static int prep_new_page(struct page *page, unsigned int order, gfp_t gfp_flags, set_page_owner(page, order, gfp_flags); /* - * page->pfmemalloc is set when ALLOC_NO_WATERMARKS was necessary to + * page is set pfmemalloc when ALLOC_NO_WATERMARKS was necessary to * allocate the page. The expectation is that the caller is taking * steps that will free more memory. The caller should avoid the page * being used for !PFMEMALLOC purposes. */ - page->pfmemalloc = !!(alloc_flags & ALLOC_NO_WATERMARKS); + if (alloc_flags & ALLOC_NO_WATERMARKS) + set_page_pfmemalloc(page); + else + clear_page_pfmemalloc(page); return 0; } @@ -3345,7 +3348,7 @@ refill: atomic_add(size - 1, &page->_count); /* reset page count bias and offset to start of new frag */ - nc->pfmemalloc = page->pfmemalloc; + nc->pfmemalloc = page_is_pfmemalloc(page); nc->pagecnt_bias = size; nc->offset = size; } @@ -5060,6 +5063,10 @@ static unsigned long __meminit zone_spanned_pages_in_node(int nid, { unsigned long zone_start_pfn, zone_end_pfn; + /* When hotadd a new node, the node should be empty */ + if (!node_start_pfn && !node_end_pfn) + return 0; + /* Get the start and end of the zone */ zone_start_pfn = arch_zone_lowest_possible_pfn[zone_type]; zone_end_pfn = arch_zone_highest_possible_pfn[zone_type]; @@ -5123,6 +5130,10 @@ static unsigned long __meminit zone_absent_pages_in_node(int nid, unsigned long zone_high = arch_zone_highest_possible_pfn[zone_type]; unsigned long zone_start_pfn, zone_end_pfn; + /* When hotadd a new node, the node should be empty */ + if (!node_start_pfn && !node_end_pfn) + return 0; + zone_start_pfn = clamp(node_start_pfn, zone_low, zone_high); zone_end_pfn = clamp(node_end_pfn, zone_low, zone_high); diff --git a/mm/slab.c b/mm/slab.c index 200e22412a16..bbd0b47dc6a9 100644 --- a/mm/slab.c +++ b/mm/slab.c @@ -1603,7 +1603,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, } /* Record if ALLOC_NO_WATERMARKS was set when allocating the slab */ - if (unlikely(page->pfmemalloc)) + if (page_is_pfmemalloc(page)) pfmemalloc_active = true; nr_pages = (1 << cachep->gfporder); @@ -1614,7 +1614,7 @@ static struct page *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, add_zone_page_state(page_zone(page), NR_SLAB_UNRECLAIMABLE, nr_pages); __SetPageSlab(page); - if (page->pfmemalloc) + if (page_is_pfmemalloc(page)) SetPageSlabPfmemalloc(page); if (kmemcheck_enabled && !(cachep->flags & SLAB_NOTRACK)) { diff --git a/mm/slub.c b/mm/slub.c index 816df0016555..f68c0e50f3c0 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -1427,7 +1427,7 @@ static struct page *new_slab(struct kmem_cache *s, gfp_t flags, int node) inc_slabs_node(s, page_to_nid(page), page->objects); page->slab_cache = s; __SetPageSlab(page); - if (page->pfmemalloc) + if (page_is_pfmemalloc(page)) SetPageSlabPfmemalloc(page); start = page_address(page); diff --git a/net/9p/client.c b/net/9p/client.c index 498454b3c06c..ea79ee9a7348 100644 --- a/net/9p/client.c +++ b/net/9p/client.c @@ -1541,6 +1541,7 @@ p9_client_read(struct p9_fid *fid, u64 offset, struct iov_iter *to, int *err) struct p9_client *clnt = fid->clnt; struct p9_req_t *req; int total = 0; + *err = 0; p9_debug(P9_DEBUG_9P, ">>> TREAD fid %d offset %llu %d\n", fid->fid, (unsigned long long) offset, (int)iov_iter_count(to)); @@ -1620,6 +1621,7 @@ p9_client_write(struct p9_fid *fid, u64 offset, struct iov_iter *from, int *err) struct p9_client *clnt = fid->clnt; struct p9_req_t *req; int total = 0; + *err = 0; p9_debug(P9_DEBUG_9P, ">>> TWRITE fid %d offset %llu count %zd\n", fid->fid, (unsigned long long) offset, diff --git a/net/batman-adv/distributed-arp-table.c b/net/batman-adv/distributed-arp-table.c index fb54e6aed096..6d0b471eede8 100644 --- a/net/batman-adv/distributed-arp-table.c +++ b/net/batman-adv/distributed-arp-table.c @@ -1138,6 +1138,9 @@ void batadv_dat_snoop_outgoing_arp_reply(struct batadv_priv *bat_priv, * @bat_priv: the bat priv with all the soft interface information * @skb: packet to check * @hdr_size: size of the encapsulation header + * + * Returns true if the packet was snooped and consumed by DAT. False if the + * packet has to be delivered to the interface */ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, struct sk_buff *skb, int hdr_size) @@ -1145,7 +1148,7 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, uint16_t type; __be32 ip_src, ip_dst; uint8_t *hw_src, *hw_dst; - bool ret = false; + bool dropped = false; unsigned short vid; if (!atomic_read(&bat_priv->distributed_arp_table)) @@ -1174,12 +1177,17 @@ bool batadv_dat_snoop_incoming_arp_reply(struct batadv_priv *bat_priv, /* if this REPLY is directed to a client of mine, let's deliver the * packet to the interface */ - ret = !batadv_is_my_client(bat_priv, hw_dst, vid); + dropped = !batadv_is_my_client(bat_priv, hw_dst, vid); + + /* if this REPLY is sent on behalf of a client of mine, let's drop the + * packet because the client will reply by itself + */ + dropped |= batadv_is_my_client(bat_priv, hw_src, vid); out: - if (ret) + if (dropped) kfree_skb(skb); - /* if ret == false -> packet has to be delivered to the interface */ - return ret; + /* if dropped == false -> deliver to the interface */ + return dropped; } /** diff --git a/net/batman-adv/gateway_client.c b/net/batman-adv/gateway_client.c index bb0158620628..cffa92dd9877 100644 --- a/net/batman-adv/gateway_client.c +++ b/net/batman-adv/gateway_client.c @@ -439,6 +439,8 @@ static void batadv_gw_node_add(struct batadv_priv *bat_priv, INIT_HLIST_NODE(&gw_node->list); gw_node->orig_node = orig_node; + gw_node->bandwidth_down = ntohl(gateway->bandwidth_down); + gw_node->bandwidth_up = ntohl(gateway->bandwidth_up); atomic_set(&gw_node->refcount, 1); spin_lock_bh(&bat_priv->gw.list_lock); diff --git a/net/batman-adv/soft-interface.c b/net/batman-adv/soft-interface.c index c002961da75d..a2fc843c2243 100644 --- a/net/batman-adv/soft-interface.c +++ b/net/batman-adv/soft-interface.c @@ -479,6 +479,9 @@ out: */ void batadv_softif_vlan_free_ref(struct batadv_softif_vlan *vlan) { + if (!vlan) + return; + if (atomic_dec_and_test(&vlan->refcount)) { spin_lock_bh(&vlan->bat_priv->softif_vlan_list_lock); hlist_del_rcu(&vlan->list); diff --git a/net/batman-adv/translation-table.c b/net/batman-adv/translation-table.c index b4824951010b..5809b39c1922 100644 --- a/net/batman-adv/translation-table.c +++ b/net/batman-adv/translation-table.c @@ -594,6 +594,12 @@ bool batadv_tt_local_add(struct net_device *soft_iface, const uint8_t *addr, /* increase the refcounter of the related vlan */ vlan = batadv_softif_vlan_get(bat_priv, vid); + if (WARN(!vlan, "adding TT local entry %pM to non-existent VLAN %d", + addr, BATADV_PRINT_VID(vid))) { + kfree(tt_local); + tt_local = NULL; + goto out; + } batadv_dbg(BATADV_DBG_TT, bat_priv, "Creating new local tt entry: %pM (vid: %d, ttvn: %d)\n", @@ -1034,6 +1040,7 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, struct batadv_tt_local_entry *tt_local_entry; uint16_t flags, curr_flags = BATADV_NO_FLAGS; struct batadv_softif_vlan *vlan; + void *tt_entry_exists; tt_local_entry = batadv_tt_local_hash_find(bat_priv, addr, vid); if (!tt_local_entry) @@ -1061,11 +1068,22 @@ uint16_t batadv_tt_local_remove(struct batadv_priv *bat_priv, * immediately purge it */ batadv_tt_local_event(bat_priv, tt_local_entry, BATADV_TT_CLIENT_DEL); - hlist_del_rcu(&tt_local_entry->common.hash_entry); + + tt_entry_exists = batadv_hash_remove(bat_priv->tt.local_hash, + batadv_compare_tt, + batadv_choose_tt, + &tt_local_entry->common); + if (!tt_entry_exists) + goto out; + + /* extra call to free the local tt entry */ batadv_tt_local_entry_free_ref(tt_local_entry); /* decrease the reference held for this vlan */ vlan = batadv_softif_vlan_get(bat_priv, vid); + if (!vlan) + goto out; + batadv_softif_vlan_free_ref(vlan); batadv_softif_vlan_free_ref(vlan); @@ -1166,8 +1184,10 @@ static void batadv_tt_local_table_free(struct batadv_priv *bat_priv) /* decrease the reference held for this vlan */ vlan = batadv_softif_vlan_get(bat_priv, tt_common_entry->vid); - batadv_softif_vlan_free_ref(vlan); - batadv_softif_vlan_free_ref(vlan); + if (vlan) { + batadv_softif_vlan_free_ref(vlan); + batadv_softif_vlan_free_ref(vlan); + } batadv_tt_local_entry_free_ref(tt_local); } @@ -3207,8 +3227,10 @@ static void batadv_tt_local_purge_pending_clients(struct batadv_priv *bat_priv) /* decrease the reference held for this vlan */ vlan = batadv_softif_vlan_get(bat_priv, tt_common->vid); - batadv_softif_vlan_free_ref(vlan); - batadv_softif_vlan_free_ref(vlan); + if (vlan) { + batadv_softif_vlan_free_ref(vlan); + batadv_softif_vlan_free_ref(vlan); + } batadv_tt_local_entry_free_ref(tt_local); } diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c index 7998fb279165..92720f3fe573 100644 --- a/net/bluetooth/mgmt.c +++ b/net/bluetooth/mgmt.c @@ -7820,7 +7820,7 @@ void mgmt_new_ltk(struct hci_dev *hdev, struct smp_ltk *key, bool persistent) /* Make sure we copy only the significant bytes based on the * encryption key size, and set the rest of the value to zeroes. */ - memcpy(ev.key.val, key->val, sizeof(key->enc_size)); + memcpy(ev.key.val, key->val, key->enc_size); memset(ev.key.val + key->enc_size, 0, sizeof(ev.key.val) - key->enc_size); diff --git a/net/bridge/br_multicast.c b/net/bridge/br_multicast.c index 0b39dcc65b94..1285eaf5dc22 100644 --- a/net/bridge/br_multicast.c +++ b/net/bridge/br_multicast.c @@ -1591,7 +1591,7 @@ static int br_multicast_ipv4_rcv(struct net_bridge *br, break; } - if (skb_trimmed) + if (skb_trimmed && skb_trimmed != skb) kfree_skb(skb_trimmed); return err; @@ -1636,7 +1636,7 @@ static int br_multicast_ipv6_rcv(struct net_bridge *br, break; } - if (skb_trimmed) + if (skb_trimmed && skb_trimmed != skb) kfree_skb(skb_trimmed); return err; diff --git a/net/bridge/br_netlink.c b/net/bridge/br_netlink.c index 3da5525eb8a2..4d74a0639c4c 100644 --- a/net/bridge/br_netlink.c +++ b/net/bridge/br_netlink.c @@ -112,6 +112,8 @@ static inline size_t br_port_info_size(void) + nla_total_size(1) /* IFLA_BRPORT_FAST_LEAVE */ + nla_total_size(1) /* IFLA_BRPORT_LEARNING */ + nla_total_size(1) /* IFLA_BRPORT_UNICAST_FLOOD */ + + nla_total_size(1) /* IFLA_BRPORT_PROXYARP */ + + nla_total_size(1) /* IFLA_BRPORT_PROXYARP_WIFI */ + 0; } @@ -506,6 +508,8 @@ static const struct nla_policy br_port_policy[IFLA_BRPORT_MAX + 1] = { [IFLA_BRPORT_FAST_LEAVE]= { .type = NLA_U8 }, [IFLA_BRPORT_LEARNING] = { .type = NLA_U8 }, [IFLA_BRPORT_UNICAST_FLOOD] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROXYARP] = { .type = NLA_U8 }, + [IFLA_BRPORT_PROXYARP_WIFI] = { .type = NLA_U8 }, }; /* Change the state of the port and notify spanning tree */ diff --git a/net/core/datagram.c b/net/core/datagram.c index 4967262b2707..617088aee21d 100644 --- a/net/core/datagram.c +++ b/net/core/datagram.c @@ -131,12 +131,12 @@ out_noerr: goto out; } -static int skb_set_peeked(struct sk_buff *skb) +static struct sk_buff *skb_set_peeked(struct sk_buff *skb) { struct sk_buff *nskb; if (skb->peeked) - return 0; + return skb; /* We have to unshare an skb before modifying it. */ if (!skb_shared(skb)) @@ -144,7 +144,7 @@ static int skb_set_peeked(struct sk_buff *skb) nskb = skb_clone(skb, GFP_ATOMIC); if (!nskb) - return -ENOMEM; + return ERR_PTR(-ENOMEM); skb->prev->next = nskb; skb->next->prev = nskb; @@ -157,7 +157,7 @@ static int skb_set_peeked(struct sk_buff *skb) done: skb->peeked = 1; - return 0; + return skb; } /** @@ -229,8 +229,9 @@ struct sk_buff *__skb_recv_datagram(struct sock *sk, unsigned int flags, continue; } - error = skb_set_peeked(skb); - if (error) + skb = skb_set_peeked(skb); + error = PTR_ERR(skb); + if (IS_ERR(skb)) goto unlock_err; atomic_inc(&skb->users); diff --git a/net/core/pktgen.c b/net/core/pktgen.c index 1ebdf1c0d118..1cbd209192ea 100644 --- a/net/core/pktgen.c +++ b/net/core/pktgen.c @@ -3514,8 +3514,6 @@ static int pktgen_thread_worker(void *arg) set_freezable(); - __set_current_state(TASK_RUNNING); - while (!kthread_should_stop()) { pkt_dev = next_to_run(t); @@ -3560,7 +3558,6 @@ static int pktgen_thread_worker(void *arg) try_to_freeze(); } - set_current_state(TASK_INTERRUPTIBLE); pr_debug("%s stopping all device\n", t->tsk->comm); pktgen_stop(t); diff --git a/net/core/request_sock.c b/net/core/request_sock.c index 87b22c0bc08c..b42f0e26f89e 100644 --- a/net/core/request_sock.c +++ b/net/core/request_sock.c @@ -103,10 +103,16 @@ void reqsk_queue_destroy(struct request_sock_queue *queue) spin_lock_bh(&queue->syn_wait_lock); while ((req = lopt->syn_table[i]) != NULL) { lopt->syn_table[i] = req->dl_next; + /* Because of following del_timer_sync(), + * we must release the spinlock here + * or risk a dead lock. + */ + spin_unlock_bh(&queue->syn_wait_lock); atomic_inc(&lopt->qlen_dec); - if (del_timer(&req->rsk_timer)) + if (del_timer_sync(&req->rsk_timer)) reqsk_put(req); reqsk_put(req); + spin_lock_bh(&queue->syn_wait_lock); } spin_unlock_bh(&queue->syn_wait_lock); } diff --git a/net/core/skbuff.c b/net/core/skbuff.c index b6a19ca0f99e..7b84330e5d30 100644 --- a/net/core/skbuff.c +++ b/net/core/skbuff.c @@ -340,7 +340,7 @@ struct sk_buff *build_skb(void *data, unsigned int frag_size) if (skb && frag_size) { skb->head_frag = 1; - if (virt_to_head_page(data)->pfmemalloc) + if (page_is_pfmemalloc(virt_to_head_page(data))) skb->pfmemalloc = 1; } return skb; @@ -4022,8 +4022,8 @@ EXPORT_SYMBOL(skb_checksum_setup); * Otherwise returns the provided skb. Returns NULL in error cases * (e.g. transport_len exceeds skb length or out-of-memory). * - * Caller needs to set the skb transport header and release the returned skb. - * Provided skb is consumed. + * Caller needs to set the skb transport header and free any returned skb if it + * differs from the provided skb. */ static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb, unsigned int transport_len) @@ -4032,16 +4032,12 @@ static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb, unsigned int len = skb_transport_offset(skb) + transport_len; int ret; - if (skb->len < len) { - kfree_skb(skb); + if (skb->len < len) return NULL; - } else if (skb->len == len) { + else if (skb->len == len) return skb; - } skb_chk = skb_clone(skb, GFP_ATOMIC); - kfree_skb(skb); - if (!skb_chk) return NULL; @@ -4066,8 +4062,8 @@ static struct sk_buff *skb_checksum_maybe_trim(struct sk_buff *skb, * If the skb has data beyond the given transport length, then a * trimmed & cloned skb is checked and returned. * - * Caller needs to set the skb transport header and release the returned skb. - * Provided skb is consumed. + * Caller needs to set the skb transport header and free any returned skb if it + * differs from the provided skb. */ struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb, unsigned int transport_len, @@ -4079,23 +4075,26 @@ struct sk_buff *skb_checksum_trimmed(struct sk_buff *skb, skb_chk = skb_checksum_maybe_trim(skb, transport_len); if (!skb_chk) - return NULL; + goto err; - if (!pskb_may_pull(skb_chk, offset)) { - kfree_skb(skb_chk); - return NULL; - } + if (!pskb_may_pull(skb_chk, offset)) + goto err; __skb_pull(skb_chk, offset); ret = skb_chkf(skb_chk); __skb_push(skb_chk, offset); - if (ret) { - kfree_skb(skb_chk); - return NULL; - } + if (ret) + goto err; return skb_chk; + +err: + if (skb_chk && skb_chk != skb) + kfree_skb(skb_chk); + + return NULL; + } EXPORT_SYMBOL(skb_checksum_trimmed); diff --git a/net/dsa/slave.c b/net/dsa/slave.c index 0917123790ea..35c47ddd04f0 100644 --- a/net/dsa/slave.c +++ b/net/dsa/slave.c @@ -756,7 +756,8 @@ static int dsa_slave_phy_connect(struct dsa_slave_priv *p, return -ENODEV; /* Use already configured phy mode */ - p->phy_interface = p->phy->interface; + if (p->phy_interface == PHY_INTERFACE_MODE_NA) + p->phy_interface = p->phy->interface; phy_connect_direct(slave_dev, p->phy, dsa_slave_adjust_link, p->phy_interface); diff --git a/net/ipv4/fib_trie.c b/net/ipv4/fib_trie.c index 37c4bb89a708..b0c6258ffb79 100644 --- a/net/ipv4/fib_trie.c +++ b/net/ipv4/fib_trie.c @@ -2465,7 +2465,7 @@ static struct key_vector *fib_route_get_idx(struct fib_route_iter *iter, key = l->key + 1; iter->pos++; - if (pos-- <= 0) + if (--pos <= 0) break; l = NULL; diff --git a/net/ipv4/igmp.c b/net/ipv4/igmp.c index 651cdf648ec4..9fdfd9deac11 100644 --- a/net/ipv4/igmp.c +++ b/net/ipv4/igmp.c @@ -1435,33 +1435,35 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) struct sk_buff *skb_chk; unsigned int transport_len; unsigned int len = skb_transport_offset(skb) + sizeof(struct igmphdr); - int ret; + int ret = -EINVAL; transport_len = ntohs(ip_hdr(skb)->tot_len) - ip_hdrlen(skb); - skb_get(skb); skb_chk = skb_checksum_trimmed(skb, transport_len, ip_mc_validate_checksum); if (!skb_chk) - return -EINVAL; + goto err; - if (!pskb_may_pull(skb_chk, len)) { - kfree_skb(skb_chk); - return -EINVAL; - } + if (!pskb_may_pull(skb_chk, len)) + goto err; ret = ip_mc_check_igmp_msg(skb_chk); - if (ret) { - kfree_skb(skb_chk); - return ret; - } + if (ret) + goto err; if (skb_trimmed) *skb_trimmed = skb_chk; - else + /* free now unneeded clone */ + else if (skb_chk != skb) kfree_skb(skb_chk); - return 0; + ret = 0; + +err: + if (ret && skb_chk && skb_chk != skb) + kfree_skb(skb_chk); + + return ret; } /** @@ -1470,7 +1472,7 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) * @skb_trimmed: to store an skb pointer trimmed to IPv4 packet tail (optional) * * Checks whether an IPv4 packet is a valid IGMP packet. If so sets - * skb network and transport headers accordingly and returns zero. + * skb transport header accordingly and returns zero. * * -EINVAL: A broken packet was detected, i.e. it violates some internet * standard @@ -1485,7 +1487,8 @@ static int __ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) * to leave the original skb and its full frame unchanged (which might be * desirable for layer 2 frame jugglers). * - * The caller needs to release a reference count from any returned skb_trimmed. + * Caller needs to set the skb network header and free any returned skb if it + * differs from the provided skb. */ int ip_mc_check_igmp(struct sk_buff *skb, struct sk_buff **skb_trimmed) { diff --git a/net/ipv4/inet_connection_sock.c b/net/ipv4/inet_connection_sock.c index 60021d0d9326..134957159c27 100644 --- a/net/ipv4/inet_connection_sock.c +++ b/net/ipv4/inet_connection_sock.c @@ -593,7 +593,7 @@ static bool reqsk_queue_unlink(struct request_sock_queue *queue, } spin_unlock(&queue->syn_wait_lock); - if (del_timer(&req->rsk_timer)) + if (timer_pending(&req->rsk_timer) && del_timer_sync(&req->rsk_timer)) reqsk_put(req); return found; } diff --git a/net/ipv4/netfilter/ipt_SYNPROXY.c b/net/ipv4/netfilter/ipt_SYNPROXY.c index fe8cc183411e..95ea633e8356 100644 --- a/net/ipv4/netfilter/ipt_SYNPROXY.c +++ b/net/ipv4/netfilter/ipt_SYNPROXY.c @@ -226,7 +226,8 @@ synproxy_send_client_ack(const struct synproxy_net *snet, synproxy_build_options(nth, opts); - synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); + synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, + niph, nth, tcp_hdr_size); } static bool diff --git a/net/ipv4/sysctl_net_ipv4.c b/net/ipv4/sysctl_net_ipv4.c index 433231ccfb17..0330ab2e2b63 100644 --- a/net/ipv4/sysctl_net_ipv4.c +++ b/net/ipv4/sysctl_net_ipv4.c @@ -41,8 +41,6 @@ static int tcp_syn_retries_min = 1; static int tcp_syn_retries_max = MAX_TCP_SYNCNT; static int ip_ping_group_range_min[] = { 0, 0 }; static int ip_ping_group_range_max[] = { GID_T_MAX, GID_T_MAX }; -static int min_sndbuf = SOCK_MIN_SNDBUF; -static int min_rcvbuf = SOCK_MIN_RCVBUF; /* Update system visible IP port range */ static void set_local_port_range(struct net *net, int range[2]) @@ -530,7 +528,7 @@ static struct ctl_table ipv4_table[] = { .maxlen = sizeof(sysctl_tcp_wmem), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &min_sndbuf, + .extra1 = &one, }, { .procname = "tcp_notsent_lowat", @@ -545,7 +543,7 @@ static struct ctl_table ipv4_table[] = { .maxlen = sizeof(sysctl_tcp_rmem), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &min_rcvbuf, + .extra1 = &one, }, { .procname = "tcp_app_win", @@ -758,7 +756,7 @@ static struct ctl_table ipv4_table[] = { .maxlen = sizeof(sysctl_udp_rmem_min), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &min_rcvbuf, + .extra1 = &one }, { .procname = "udp_wmem_min", @@ -766,7 +764,7 @@ static struct ctl_table ipv4_table[] = { .maxlen = sizeof(sysctl_udp_wmem_min), .mode = 0644, .proc_handler = proc_dointvec_minmax, - .extra1 = &min_sndbuf, + .extra1 = &one }, { } }; diff --git a/net/ipv4/tcp_ipv4.c b/net/ipv4/tcp_ipv4.c index d7d4c2b79cf2..0ea2e1c5d395 100644 --- a/net/ipv4/tcp_ipv4.c +++ b/net/ipv4/tcp_ipv4.c @@ -1348,7 +1348,7 @@ static struct sock *tcp_v4_hnd_req(struct sock *sk, struct sk_buff *skb) req = inet_csk_search_req(sk, th->source, iph->saddr, iph->daddr); if (req) { nsk = tcp_check_req(sk, skb, req, false); - if (!nsk) + if (!nsk || nsk == sk) reqsk_put(req); return nsk; } diff --git a/net/ipv4/udp.c b/net/ipv4/udp.c index 83aa604f9273..1b8c5ba7d5f7 100644 --- a/net/ipv4/udp.c +++ b/net/ipv4/udp.c @@ -1995,12 +1995,19 @@ void udp_v4_early_demux(struct sk_buff *skb) skb->sk = sk; skb->destructor = sock_efree; - dst = sk->sk_rx_dst; + dst = READ_ONCE(sk->sk_rx_dst); if (dst) dst = dst_check(dst, 0); - if (dst) - skb_dst_set_noref(skb, dst); + if (dst) { + /* DST_NOCACHE can not be used without taking a reference */ + if (dst->flags & DST_NOCACHE) { + if (likely(atomic_inc_not_zero(&dst->__refcnt))) + skb_dst_set(skb, dst); + } else { + skb_dst_set_noref(skb, dst); + } + } } int udp_rcv(struct sk_buff *skb) diff --git a/net/ipv6/ip6_fib.c b/net/ipv6/ip6_fib.c index 55d19861ab20..548c6237b1e7 100644 --- a/net/ipv6/ip6_fib.c +++ b/net/ipv6/ip6_fib.c @@ -172,6 +172,8 @@ static void rt6_free_pcpu(struct rt6_info *non_pcpu_rt) *ppcpu_rt = NULL; } } + + non_pcpu_rt->rt6i_pcpu = NULL; } static void rt6_release(struct rt6_info *rt) diff --git a/net/ipv6/ip6_gre.c b/net/ipv6/ip6_gre.c index a38d3ac0f18f..69f4f689f06a 100644 --- a/net/ipv6/ip6_gre.c +++ b/net/ipv6/ip6_gre.c @@ -361,6 +361,7 @@ static void ip6gre_tunnel_uninit(struct net_device *dev) struct ip6gre_net *ign = net_generic(t->net, ip6gre_net_id); ip6gre_tunnel_unlink(ign, t); + ip6_tnl_dst_reset(t); dev_put(dev); } diff --git a/net/ipv6/mcast_snoop.c b/net/ipv6/mcast_snoop.c index df8afe5ab31e..9405b04eecc6 100644 --- a/net/ipv6/mcast_snoop.c +++ b/net/ipv6/mcast_snoop.c @@ -143,34 +143,36 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff *skb_chk = NULL; unsigned int transport_len; unsigned int len = skb_transport_offset(skb) + sizeof(struct mld_msg); - int ret; + int ret = -EINVAL; transport_len = ntohs(ipv6_hdr(skb)->payload_len); transport_len -= skb_transport_offset(skb) - sizeof(struct ipv6hdr); - skb_get(skb); skb_chk = skb_checksum_trimmed(skb, transport_len, ipv6_mc_validate_checksum); if (!skb_chk) - return -EINVAL; + goto err; - if (!pskb_may_pull(skb_chk, len)) { - kfree_skb(skb_chk); - return -EINVAL; - } + if (!pskb_may_pull(skb_chk, len)) + goto err; ret = ipv6_mc_check_mld_msg(skb_chk); - if (ret) { - kfree_skb(skb_chk); - return ret; - } + if (ret) + goto err; if (skb_trimmed) *skb_trimmed = skb_chk; - else + /* free now unneeded clone */ + else if (skb_chk != skb) kfree_skb(skb_chk); - return 0; + ret = 0; + +err: + if (ret && skb_chk && skb_chk != skb) + kfree_skb(skb_chk); + + return ret; } /** @@ -179,7 +181,7 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, * @skb_trimmed: to store an skb pointer trimmed to IPv6 packet tail (optional) * * Checks whether an IPv6 packet is a valid MLD packet. If so sets - * skb network and transport headers accordingly and returns zero. + * skb transport header accordingly and returns zero. * * -EINVAL: A broken packet was detected, i.e. it violates some internet * standard @@ -194,7 +196,8 @@ static int __ipv6_mc_check_mld(struct sk_buff *skb, * to leave the original skb and its full frame unchanged (which might be * desirable for layer 2 frame jugglers). * - * The caller needs to release a reference count from any returned skb_trimmed. + * Caller needs to set the skb network header and free any returned skb if it + * differs from the provided skb. */ int ipv6_mc_check_mld(struct sk_buff *skb, struct sk_buff **skb_trimmed) { diff --git a/net/ipv6/netfilter/ip6t_SYNPROXY.c b/net/ipv6/netfilter/ip6t_SYNPROXY.c index 6edb7b106de7..ebbb754c2111 100644 --- a/net/ipv6/netfilter/ip6t_SYNPROXY.c +++ b/net/ipv6/netfilter/ip6t_SYNPROXY.c @@ -37,12 +37,13 @@ synproxy_build_ip(struct sk_buff *skb, const struct in6_addr *saddr, } static void -synproxy_send_tcp(const struct sk_buff *skb, struct sk_buff *nskb, +synproxy_send_tcp(const struct synproxy_net *snet, + const struct sk_buff *skb, struct sk_buff *nskb, struct nf_conntrack *nfct, enum ip_conntrack_info ctinfo, struct ipv6hdr *niph, struct tcphdr *nth, unsigned int tcp_hdr_size) { - struct net *net = nf_ct_net((struct nf_conn *)nfct); + struct net *net = nf_ct_net(snet->tmpl); struct dst_entry *dst; struct flowi6 fl6; @@ -83,7 +84,8 @@ free_nskb: } static void -synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th, +synproxy_send_client_synack(const struct synproxy_net *snet, + const struct sk_buff *skb, const struct tcphdr *th, const struct synproxy_options *opts) { struct sk_buff *nskb; @@ -119,7 +121,7 @@ synproxy_send_client_synack(const struct sk_buff *skb, const struct tcphdr *th, synproxy_build_options(nth, opts); - synproxy_send_tcp(skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, + synproxy_send_tcp(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, niph, nth, tcp_hdr_size); } @@ -163,7 +165,7 @@ synproxy_send_server_syn(const struct synproxy_net *snet, synproxy_build_options(nth, opts); - synproxy_send_tcp(skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, + synproxy_send_tcp(snet, skb, nskb, &snet->tmpl->ct_general, IP_CT_NEW, niph, nth, tcp_hdr_size); } @@ -203,7 +205,7 @@ synproxy_send_server_ack(const struct synproxy_net *snet, synproxy_build_options(nth, opts); - synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); + synproxy_send_tcp(snet, skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); } static void @@ -241,7 +243,8 @@ synproxy_send_client_ack(const struct synproxy_net *snet, synproxy_build_options(nth, opts); - synproxy_send_tcp(skb, nskb, NULL, 0, niph, nth, tcp_hdr_size); + synproxy_send_tcp(snet, skb, nskb, skb->nfct, IP_CT_ESTABLISHED_REPLY, + niph, nth, tcp_hdr_size); } static bool @@ -301,7 +304,7 @@ synproxy_tg6(struct sk_buff *skb, const struct xt_action_param *par) XT_SYNPROXY_OPT_SACK_PERM | XT_SYNPROXY_OPT_ECN); - synproxy_send_client_synack(skb, th, &opts); + synproxy_send_client_synack(snet, skb, th, &opts); return NF_DROP; } else if (th->ack && !(th->fin || th->rst || th->syn)) { diff --git a/net/ipv6/route.c b/net/ipv6/route.c index 6090969937f8..d15586490cec 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -318,8 +318,7 @@ static const struct rt6_info ip6_blk_hole_entry_template = { /* allocate dst with ip6_dst_ops */ static struct rt6_info *__ip6_dst_alloc(struct net *net, struct net_device *dev, - int flags, - struct fib6_table *table) + int flags) { struct rt6_info *rt = dst_alloc(&net->ipv6.ip6_dst_ops, dev, 0, DST_OBSOLETE_FORCE_CHK, flags); @@ -336,10 +335,9 @@ static struct rt6_info *__ip6_dst_alloc(struct net *net, static struct rt6_info *ip6_dst_alloc(struct net *net, struct net_device *dev, - int flags, - struct fib6_table *table) + int flags) { - struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags, table); + struct rt6_info *rt = __ip6_dst_alloc(net, dev, flags); if (rt) { rt->rt6i_pcpu = alloc_percpu_gfp(struct rt6_info *, GFP_ATOMIC); @@ -950,8 +948,7 @@ static struct rt6_info *ip6_rt_cache_alloc(struct rt6_info *ort, if (ort->rt6i_flags & (RTF_CACHE | RTF_PCPU)) ort = (struct rt6_info *)ort->dst.from; - rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, - 0, ort->rt6i_table); + rt = __ip6_dst_alloc(dev_net(ort->dst.dev), ort->dst.dev, 0); if (!rt) return NULL; @@ -983,8 +980,7 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) struct rt6_info *pcpu_rt; pcpu_rt = __ip6_dst_alloc(dev_net(rt->dst.dev), - rt->dst.dev, rt->dst.flags, - rt->rt6i_table); + rt->dst.dev, rt->dst.flags); if (!pcpu_rt) return NULL; @@ -997,32 +993,53 @@ static struct rt6_info *ip6_rt_pcpu_alloc(struct rt6_info *rt) /* It should be called with read_lock_bh(&tb6_lock) acquired */ static struct rt6_info *rt6_get_pcpu_route(struct rt6_info *rt) { - struct rt6_info *pcpu_rt, *prev, **p; + struct rt6_info *pcpu_rt, **p; p = this_cpu_ptr(rt->rt6i_pcpu); pcpu_rt = *p; - if (pcpu_rt) - goto done; + if (pcpu_rt) { + dst_hold(&pcpu_rt->dst); + rt6_dst_from_metrics_check(pcpu_rt); + } + return pcpu_rt; +} + +static struct rt6_info *rt6_make_pcpu_route(struct rt6_info *rt) +{ + struct fib6_table *table = rt->rt6i_table; + struct rt6_info *pcpu_rt, *prev, **p; pcpu_rt = ip6_rt_pcpu_alloc(rt); if (!pcpu_rt) { struct net *net = dev_net(rt->dst.dev); - pcpu_rt = net->ipv6.ip6_null_entry; - goto done; + dst_hold(&net->ipv6.ip6_null_entry->dst); + return net->ipv6.ip6_null_entry; } - prev = cmpxchg(p, NULL, pcpu_rt); - if (prev) { - /* If someone did it before us, return prev instead */ + read_lock_bh(&table->tb6_lock); + if (rt->rt6i_pcpu) { + p = this_cpu_ptr(rt->rt6i_pcpu); + prev = cmpxchg(p, NULL, pcpu_rt); + if (prev) { + /* If someone did it before us, return prev instead */ + dst_destroy(&pcpu_rt->dst); + pcpu_rt = prev; + } + } else { + /* rt has been removed from the fib6 tree + * before we have a chance to acquire the read_lock. + * In this case, don't brother to create a pcpu rt + * since rt is going away anyway. The next + * dst_check() will trigger a re-lookup. + */ dst_destroy(&pcpu_rt->dst); - pcpu_rt = prev; + pcpu_rt = rt; } - -done: dst_hold(&pcpu_rt->dst); rt6_dst_from_metrics_check(pcpu_rt); + read_unlock_bh(&table->tb6_lock); return pcpu_rt; } @@ -1097,9 +1114,22 @@ redo_rt6_select: rt->dst.lastuse = jiffies; rt->dst.__use++; pcpu_rt = rt6_get_pcpu_route(rt); - read_unlock_bh(&table->tb6_lock); + + if (pcpu_rt) { + read_unlock_bh(&table->tb6_lock); + } else { + /* We have to do the read_unlock first + * because rt6_make_pcpu_route() may trigger + * ip6_dst_gc() which will take the write_lock. + */ + dst_hold(&rt->dst); + read_unlock_bh(&table->tb6_lock); + pcpu_rt = rt6_make_pcpu_route(rt); + dst_release(&rt->dst); + } return pcpu_rt; + } } @@ -1555,7 +1585,7 @@ struct dst_entry *icmp6_dst_alloc(struct net_device *dev, if (unlikely(!idev)) return ERR_PTR(-ENODEV); - rt = ip6_dst_alloc(net, dev, 0, NULL); + rt = ip6_dst_alloc(net, dev, 0); if (unlikely(!rt)) { in6_dev_put(idev); dst = ERR_PTR(-ENOMEM); @@ -1742,7 +1772,8 @@ int ip6_route_add(struct fib6_config *cfg) if (!table) goto out; - rt = ip6_dst_alloc(net, NULL, (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT, table); + rt = ip6_dst_alloc(net, NULL, + (cfg->fc_flags & RTF_ADDRCONF) ? 0 : DST_NOCOUNT); if (!rt) { err = -ENOMEM; @@ -1831,6 +1862,7 @@ int ip6_route_add(struct fib6_config *cfg) int gwa_type; gw_addr = &cfg->fc_gateway; + gwa_type = ipv6_addr_type(gw_addr); /* if gw_addr is local we will fail to detect this in case * address is still TENTATIVE (DAD in progress). rt6_lookup() @@ -1838,11 +1870,12 @@ int ip6_route_add(struct fib6_config *cfg) * prefix route was assigned to, which might be non-loopback. */ err = -EINVAL; - if (ipv6_chk_addr_and_flags(net, gw_addr, NULL, 0, 0)) + if (ipv6_chk_addr_and_flags(net, gw_addr, + gwa_type & IPV6_ADDR_LINKLOCAL ? + dev : NULL, 0, 0)) goto out; rt->rt6i_gateway = *gw_addr; - gwa_type = ipv6_addr_type(gw_addr); if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { struct rt6_info *grt; @@ -2397,7 +2430,7 @@ struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, { struct net *net = dev_net(idev->dev); struct rt6_info *rt = ip6_dst_alloc(net, net->loopback_dev, - DST_NOCOUNT, NULL); + DST_NOCOUNT); if (!rt) return ERR_PTR(-ENOMEM); diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index 6748c4277aff..7a6cea5e4274 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -943,7 +943,7 @@ static struct sock *tcp_v6_hnd_req(struct sock *sk, struct sk_buff *skb) &ipv6_hdr(skb)->daddr, tcp_v6_iif(skb)); if (req) { nsk = tcp_check_req(sk, skb, req, false); - if (!nsk) + if (!nsk || nsk == sk) reqsk_put(req); return nsk; } diff --git a/net/key/af_key.c b/net/key/af_key.c index b397f0aa9005..83a70688784b 100644 --- a/net/key/af_key.c +++ b/net/key/af_key.c @@ -219,7 +219,7 @@ static int pfkey_broadcast_one(struct sk_buff *skb, struct sk_buff **skb2, #define BROADCAST_ONE 1 #define BROADCAST_REGISTERED 2 #define BROADCAST_PROMISC_ONLY 4 -static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, +static int pfkey_broadcast(struct sk_buff *skb, int broadcast_flags, struct sock *one_sk, struct net *net) { @@ -244,7 +244,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, * socket. */ if (pfk->promisc) - pfkey_broadcast_one(skb, &skb2, allocation, sk); + pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk); /* the exact target will be processed later */ if (sk == one_sk) @@ -259,7 +259,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, continue; } - err2 = pfkey_broadcast_one(skb, &skb2, allocation, sk); + err2 = pfkey_broadcast_one(skb, &skb2, GFP_ATOMIC, sk); /* Error is cleare after succecful sending to at least one * registered KM */ @@ -269,7 +269,7 @@ static int pfkey_broadcast(struct sk_buff *skb, gfp_t allocation, rcu_read_unlock(); if (one_sk != NULL) - err = pfkey_broadcast_one(skb, &skb2, allocation, one_sk); + err = pfkey_broadcast_one(skb, &skb2, GFP_KERNEL, one_sk); kfree_skb(skb2); kfree_skb(skb); @@ -292,7 +292,7 @@ static int pfkey_do_dump(struct pfkey_sock *pfk) hdr = (struct sadb_msg *) pfk->dump.skb->data; hdr->sadb_msg_seq = 0; hdr->sadb_msg_errno = rc; - pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE, &pfk->sk, sock_net(&pfk->sk)); pfk->dump.skb = NULL; } @@ -333,7 +333,7 @@ static int pfkey_error(const struct sadb_msg *orig, int err, struct sock *sk) hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); - pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ONE, sk, sock_net(sk)); + pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk)); return 0; } @@ -1365,7 +1365,7 @@ static int pfkey_getspi(struct sock *sk, struct sk_buff *skb, const struct sadb_ xfrm_state_put(x); - pfkey_broadcast(resp_skb, GFP_KERNEL, BROADCAST_ONE, sk, net); + pfkey_broadcast(resp_skb, BROADCAST_ONE, sk, net); return 0; } @@ -1452,7 +1452,7 @@ static int key_notify_sa(struct xfrm_state *x, const struct km_event *c) hdr->sadb_msg_seq = c->seq; hdr->sadb_msg_pid = c->portid; - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xs_net(x)); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, xs_net(x)); return 0; } @@ -1565,7 +1565,7 @@ static int pfkey_get(struct sock *sk, struct sk_buff *skb, const struct sadb_msg out_hdr->sadb_msg_reserved = 0; out_hdr->sadb_msg_seq = hdr->sadb_msg_seq; out_hdr->sadb_msg_pid = hdr->sadb_msg_pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk)); + pfkey_broadcast(out_skb, BROADCAST_ONE, sk, sock_net(sk)); return 0; } @@ -1670,7 +1670,7 @@ static int pfkey_register(struct sock *sk, struct sk_buff *skb, const struct sad return -ENOBUFS; } - pfkey_broadcast(supp_skb, GFP_KERNEL, BROADCAST_REGISTERED, sk, sock_net(sk)); + pfkey_broadcast(supp_skb, BROADCAST_REGISTERED, sk, sock_net(sk)); return 0; } @@ -1689,7 +1689,7 @@ static int unicast_flush_resp(struct sock *sk, const struct sadb_msg *ihdr) hdr->sadb_msg_errno = (uint8_t) 0; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); - return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ONE, sk, sock_net(sk)); + return pfkey_broadcast(skb, BROADCAST_ONE, sk, sock_net(sk)); } static int key_notify_sa_flush(const struct km_event *c) @@ -1710,7 +1710,7 @@ static int key_notify_sa_flush(const struct km_event *c) hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); hdr->sadb_msg_reserved = 0; - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, c->net); return 0; } @@ -1767,7 +1767,7 @@ static int dump_sa(struct xfrm_state *x, int count, void *ptr) out_hdr->sadb_msg_pid = pfk->dump.msg_portid; if (pfk->dump.skb) - pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE, &pfk->sk, sock_net(&pfk->sk)); pfk->dump.skb = out_skb; @@ -1847,7 +1847,7 @@ static int pfkey_promisc(struct sock *sk, struct sk_buff *skb, const struct sadb new_hdr->sadb_msg_errno = 0; } - pfkey_broadcast(skb, GFP_KERNEL, BROADCAST_ALL, NULL, sock_net(sk)); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, sock_net(sk)); return 0; } @@ -2181,7 +2181,7 @@ static int key_notify_policy(struct xfrm_policy *xp, int dir, const struct km_ev out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_seq = c->seq; out_hdr->sadb_msg_pid = c->portid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ALL, NULL, xp_net(xp)); + pfkey_broadcast(out_skb, BROADCAST_ALL, NULL, xp_net(xp)); return 0; } @@ -2401,7 +2401,7 @@ static int key_pol_get_resp(struct sock *sk, struct xfrm_policy *xp, const struc out_hdr->sadb_msg_errno = 0; out_hdr->sadb_msg_seq = hdr->sadb_msg_seq; out_hdr->sadb_msg_pid = hdr->sadb_msg_pid; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_ONE, sk, xp_net(xp)); + pfkey_broadcast(out_skb, BROADCAST_ONE, sk, xp_net(xp)); err = 0; out: @@ -2655,7 +2655,7 @@ static int dump_sp(struct xfrm_policy *xp, int dir, int count, void *ptr) out_hdr->sadb_msg_pid = pfk->dump.msg_portid; if (pfk->dump.skb) - pfkey_broadcast(pfk->dump.skb, GFP_ATOMIC, BROADCAST_ONE, + pfkey_broadcast(pfk->dump.skb, BROADCAST_ONE, &pfk->sk, sock_net(&pfk->sk)); pfk->dump.skb = out_skb; @@ -2708,7 +2708,7 @@ static int key_notify_policy_flush(const struct km_event *c) hdr->sadb_msg_satype = SADB_SATYPE_UNSPEC; hdr->sadb_msg_len = (sizeof(struct sadb_msg) / sizeof(uint64_t)); hdr->sadb_msg_reserved = 0; - pfkey_broadcast(skb_out, GFP_ATOMIC, BROADCAST_ALL, NULL, c->net); + pfkey_broadcast(skb_out, BROADCAST_ALL, NULL, c->net); return 0; } @@ -2770,7 +2770,7 @@ static int pfkey_process(struct sock *sk, struct sk_buff *skb, const struct sadb void *ext_hdrs[SADB_EXT_MAX]; int err; - pfkey_broadcast(skb_clone(skb, GFP_KERNEL), GFP_KERNEL, + pfkey_broadcast(skb_clone(skb, GFP_KERNEL), BROADCAST_PROMISC_ONLY, NULL, sock_net(sk)); memset(ext_hdrs, 0, sizeof(ext_hdrs)); @@ -2992,7 +2992,7 @@ static int key_notify_sa_expire(struct xfrm_state *x, const struct km_event *c) out_hdr->sadb_msg_seq = 0; out_hdr->sadb_msg_pid = 0; - pfkey_broadcast(out_skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + pfkey_broadcast(out_skb, BROADCAST_REGISTERED, NULL, xs_net(x)); return 0; } @@ -3182,7 +3182,7 @@ static int pfkey_send_acquire(struct xfrm_state *x, struct xfrm_tmpl *t, struct xfrm_ctx->ctx_len); } - return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x)); } static struct xfrm_policy *pfkey_compile_policy(struct sock *sk, int opt, @@ -3380,7 +3380,7 @@ static int pfkey_send_new_mapping(struct xfrm_state *x, xfrm_address_t *ipaddr, n_port->sadb_x_nat_t_port_port = sport; n_port->sadb_x_nat_t_port_reserved = 0; - return pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_REGISTERED, NULL, xs_net(x)); + return pfkey_broadcast(skb, BROADCAST_REGISTERED, NULL, xs_net(x)); } #ifdef CONFIG_NET_KEY_MIGRATE @@ -3572,7 +3572,7 @@ static int pfkey_send_migrate(const struct xfrm_selector *sel, u8 dir, u8 type, } /* broadcast migrate message to sockets */ - pfkey_broadcast(skb, GFP_ATOMIC, BROADCAST_ALL, NULL, &init_net); + pfkey_broadcast(skb, BROADCAST_ALL, NULL, &init_net); return 0; diff --git a/net/mac80211/rc80211_minstrel.c b/net/mac80211/rc80211_minstrel.c index 247552a7f6c2..3ece7d1034c8 100644 --- a/net/mac80211/rc80211_minstrel.c +++ b/net/mac80211/rc80211_minstrel.c @@ -92,14 +92,15 @@ int minstrel_get_tp_avg(struct minstrel_rate *mr, int prob_ewma) static inline void minstrel_sort_best_tp_rates(struct minstrel_sta_info *mi, int i, u8 *tp_list) { - int j = MAX_THR_RATES; - struct minstrel_rate_stats *tmp_mrs = &mi->r[j - 1].stats; + int j; + struct minstrel_rate_stats *tmp_mrs; struct minstrel_rate_stats *cur_mrs = &mi->r[i].stats; - while (j > 0 && (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) > - minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma))) { - j--; + for (j = MAX_THR_RATES; j > 0; --j) { tmp_mrs = &mi->r[tp_list[j - 1]].stats; + if (minstrel_get_tp_avg(&mi->r[i], cur_mrs->prob_ewma) <= + minstrel_get_tp_avg(&mi->r[tp_list[j - 1]], tmp_mrs->prob_ewma)) + break; } if (j < MAX_THR_RATES - 1) diff --git a/net/netfilter/nf_conntrack_core.c b/net/netfilter/nf_conntrack_core.c index 651039ad1681..3c20d02aee73 100644 --- a/net/netfilter/nf_conntrack_core.c +++ b/net/netfilter/nf_conntrack_core.c @@ -292,7 +292,7 @@ struct nf_conn *nf_ct_tmpl_alloc(struct net *net, u16 zone, gfp_t flags) { struct nf_conn *tmpl; - tmpl = kzalloc(sizeof(struct nf_conn), GFP_KERNEL); + tmpl = kzalloc(sizeof(*tmpl), flags); if (tmpl == NULL) return NULL; @@ -303,7 +303,7 @@ struct nf_conn *nf_ct_tmpl_alloc(struct net *net, u16 zone, gfp_t flags) if (zone) { struct nf_conntrack_zone *nf_ct_zone; - nf_ct_zone = nf_ct_ext_add(tmpl, NF_CT_EXT_ZONE, GFP_ATOMIC); + nf_ct_zone = nf_ct_ext_add(tmpl, NF_CT_EXT_ZONE, flags); if (!nf_ct_zone) goto out_free; nf_ct_zone->id = zone; @@ -1544,10 +1544,8 @@ void *nf_ct_alloc_hashtable(unsigned int *sizep, int nulls) sz = nr_slots * sizeof(struct hlist_nulls_head); hash = (void *)__get_free_pages(GFP_KERNEL | __GFP_NOWARN | __GFP_ZERO, get_order(sz)); - if (!hash) { - printk(KERN_WARNING "nf_conntrack: falling back to vmalloc.\n"); + if (!hash) hash = vzalloc(sz); - } if (hash && nulls) for (i = 0; i < nr_slots; i++) diff --git a/net/netfilter/nf_synproxy_core.c b/net/netfilter/nf_synproxy_core.c index 71f1e9fdfa18..d7f168527903 100644 --- a/net/netfilter/nf_synproxy_core.c +++ b/net/netfilter/nf_synproxy_core.c @@ -353,10 +353,8 @@ static int __net_init synproxy_net_init(struct net *net) int err = -ENOMEM; ct = nf_ct_tmpl_alloc(net, 0, GFP_KERNEL); - if (IS_ERR(ct)) { - err = PTR_ERR(ct); + if (!ct) goto err1; - } if (!nfct_seqadj_ext_add(ct)) goto err2; diff --git a/net/netfilter/xt_CT.c b/net/netfilter/xt_CT.c index c6630030c912..43ddeee404e9 100644 --- a/net/netfilter/xt_CT.c +++ b/net/netfilter/xt_CT.c @@ -202,9 +202,10 @@ static int xt_ct_tg_check(const struct xt_tgchk_param *par, goto err1; ct = nf_ct_tmpl_alloc(par->net, info->zone, GFP_KERNEL); - ret = PTR_ERR(ct); - if (IS_ERR(ct)) + if (!ct) { + ret = -ENOMEM; goto err2; + } ret = 0; if ((info->ct_events || info->exp_events) && diff --git a/net/netlink/af_netlink.c b/net/netlink/af_netlink.c index d8e2e3918ce2..a774985489e2 100644 --- a/net/netlink/af_netlink.c +++ b/net/netlink/af_netlink.c @@ -1096,6 +1096,11 @@ static int netlink_insert(struct sock *sk, u32 portid) err = __netlink_insert(table, sk); if (err) { + /* In case the hashtable backend returns with -EBUSY + * from here, it must not escape to the caller. + */ + if (unlikely(err == -EBUSY)) + err = -EOVERFLOW; if (err == -EEXIST) err = -EADDRINUSE; nlk_sk(sk)->portid = 0; @@ -2396,7 +2401,7 @@ static int netlink_sendmsg(struct socket *sock, struct msghdr *msg, size_t len) * sendmsg(), but that's what we've got... */ if (netlink_tx_is_mmaped(sk) && - msg->msg_iter.type == ITER_IOVEC && + iter_is_iovec(&msg->msg_iter) && msg->msg_iter.nr_segs == 1 && msg->msg_iter.iov->iov_base == NULL) { err = netlink_mmap_sendmsg(sk, msg, dst_portid, dst_group, diff --git a/net/openvswitch/actions.c b/net/openvswitch/actions.c index 8a8c0b8b4f63..ee34f474ad14 100644 --- a/net/openvswitch/actions.c +++ b/net/openvswitch/actions.c @@ -273,28 +273,36 @@ static int set_eth_addr(struct sk_buff *skb, struct sw_flow_key *flow_key, return 0; } -static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, - __be32 *addr, __be32 new_addr) +static void update_ip_l4_checksum(struct sk_buff *skb, struct iphdr *nh, + __be32 addr, __be32 new_addr) { int transport_len = skb->len - skb_transport_offset(skb); + if (nh->frag_off & htons(IP_OFFSET)) + return; + if (nh->protocol == IPPROTO_TCP) { if (likely(transport_len >= sizeof(struct tcphdr))) inet_proto_csum_replace4(&tcp_hdr(skb)->check, skb, - *addr, new_addr, 1); + addr, new_addr, 1); } else if (nh->protocol == IPPROTO_UDP) { if (likely(transport_len >= sizeof(struct udphdr))) { struct udphdr *uh = udp_hdr(skb); if (uh->check || skb->ip_summed == CHECKSUM_PARTIAL) { inet_proto_csum_replace4(&uh->check, skb, - *addr, new_addr, 1); + addr, new_addr, 1); if (!uh->check) uh->check = CSUM_MANGLED_0; } } } +} +static void set_ip_addr(struct sk_buff *skb, struct iphdr *nh, + __be32 *addr, __be32 new_addr) +{ + update_ip_l4_checksum(skb, nh, *addr, new_addr); csum_replace4(&nh->check, *addr, new_addr); skb_clear_hash(skb); *addr = new_addr; diff --git a/net/rds/info.c b/net/rds/info.c index 9a6b4f66187c..140a44a5f7b7 100644 --- a/net/rds/info.c +++ b/net/rds/info.c @@ -176,7 +176,7 @@ int rds_info_getsockopt(struct socket *sock, int optname, char __user *optval, /* check for all kinds of wrapping and the like */ start = (unsigned long)optval; - if (len < 0 || len + PAGE_SIZE - 1 < len || start + len < start) { + if (len < 0 || len > INT_MAX - PAGE_SIZE + 1 || start + len < start) { ret = -EINVAL; goto out; } diff --git a/net/sched/act_mirred.c b/net/sched/act_mirred.c index a42a3b257226..268545050ddb 100644 --- a/net/sched/act_mirred.c +++ b/net/sched/act_mirred.c @@ -98,6 +98,8 @@ static int tcf_mirred_init(struct net *net, struct nlattr *nla, return ret; ret = ACT_P_CREATED; } else { + if (bind) + return 0; if (!ovr) { tcf_hash_release(a, bind); return -EEXIST; diff --git a/net/sched/cls_u32.c b/net/sched/cls_u32.c index cab9e9b43967..4fbb67430ce4 100644 --- a/net/sched/cls_u32.c +++ b/net/sched/cls_u32.c @@ -490,6 +490,19 @@ static bool u32_destroy(struct tcf_proto *tp, bool force) return false; } } + + if (tp_c->refcnt > 1) + return false; + + if (tp_c->refcnt == 1) { + struct tc_u_hnode *ht; + + for (ht = rtnl_dereference(tp_c->hlist); + ht; + ht = rtnl_dereference(ht->next)) + if (!ht_empty(ht)) + return false; + } } if (root_ht && --root_ht->refcnt == 0) diff --git a/net/sched/sch_fq_codel.c b/net/sched/sch_fq_codel.c index 21ca33c9f036..a9ba030435a2 100644 --- a/net/sched/sch_fq_codel.c +++ b/net/sched/sch_fq_codel.c @@ -288,10 +288,26 @@ begin: static void fq_codel_reset(struct Qdisc *sch) { - struct sk_buff *skb; + struct fq_codel_sched_data *q = qdisc_priv(sch); + int i; - while ((skb = fq_codel_dequeue(sch)) != NULL) - kfree_skb(skb); + INIT_LIST_HEAD(&q->new_flows); + INIT_LIST_HEAD(&q->old_flows); + for (i = 0; i < q->flows_cnt; i++) { + struct fq_codel_flow *flow = q->flows + i; + + while (flow->head) { + struct sk_buff *skb = dequeue_head(flow); + + qdisc_qstats_backlog_dec(sch, skb); + kfree_skb(skb); + } + + INIT_LIST_HEAD(&flow->flowchain); + codel_vars_init(&flow->cvars); + } + memset(q->backlogs, 0, q->flows_cnt * sizeof(u32)); + sch->q.qlen = 0; } static const struct nla_policy fq_codel_policy[TCA_FQ_CODEL_MAX + 1] = { diff --git a/net/sctp/sm_make_chunk.c b/net/sctp/sm_make_chunk.c index 06320c8c1c86..a655ddc3f353 100644 --- a/net/sctp/sm_make_chunk.c +++ b/net/sctp/sm_make_chunk.c @@ -3132,11 +3132,18 @@ bool sctp_verify_asconf(const struct sctp_association *asoc, case SCTP_PARAM_IPV4_ADDRESS: if (length != sizeof(sctp_ipv4addr_param_t)) return false; + /* ensure there is only one addr param and it's in the + * beginning of addip_hdr params, or we reject it. + */ + if (param.v != addip->addip_hdr.params) + return false; addr_param_seen = true; break; case SCTP_PARAM_IPV6_ADDRESS: if (length != sizeof(sctp_ipv6addr_param_t)) return false; + if (param.v != addip->addip_hdr.params) + return false; addr_param_seen = true; break; case SCTP_PARAM_ADD_IP: diff --git a/net/sctp/sm_sideeffect.c b/net/sctp/sm_sideeffect.c index fef2acdf4a2e..85e6f03aeb70 100644 --- a/net/sctp/sm_sideeffect.c +++ b/net/sctp/sm_sideeffect.c @@ -702,7 +702,7 @@ static void sctp_cmd_transport_on(sctp_cmd_seq_t *cmds, * outstanding data and rely on the retransmission limit be reached * to shutdown the association. */ - if (t->asoc->state != SCTP_STATE_SHUTDOWN_PENDING) + if (t->asoc->state < SCTP_STATE_SHUTDOWN_PENDING) t->asoc->overall_error_count = 0; /* Clear the hb_sent flag to signal that we had a good diff --git a/scripts/checkkconfigsymbols.py b/scripts/checkkconfigsymbols.py index c89fdcaf06e8..2f4b7ffd5570 100755 --- a/scripts/checkkconfigsymbols.py +++ b/scripts/checkkconfigsymbols.py @@ -2,7 +2,7 @@ """Find Kconfig symbols that are referenced but not defined.""" -# (c) 2014-2015 Valentin Rothberg <Valentin.Rothberg@lip6.fr> +# (c) 2014-2015 Valentin Rothberg <valentinrothberg@gmail.com> # (c) 2014 Stefan Hengelein <stefan.hengelein@fau.de> # # Licensed under the terms of the GNU GPL License version 2 @@ -20,18 +20,20 @@ OPERATORS = r"&|\(|\)|\||\!" FEATURE = r"(?:\w*[A-Z0-9]\w*){2,}" DEF = r"^\s*(?:menu){,1}config\s+(" + FEATURE + r")\s*" EXPR = r"(?:" + OPERATORS + r"|\s|" + FEATURE + r")+" -STMT = r"^\s*(?:if|select|depends\s+on)\s+" + EXPR +DEFAULT = r"default\s+.*?(?:if\s.+){,1}" +STMT = r"^\s*(?:if|select|depends\s+on|(?:" + DEFAULT + r"))\s+" + EXPR SOURCE_FEATURE = r"(?:\W|\b)+[D]{,1}CONFIG_(" + FEATURE + r")" # regex objects REGEX_FILE_KCONFIG = re.compile(r".*Kconfig[\.\w+\-]*$") -REGEX_FEATURE = re.compile(r"(" + FEATURE + r")") +REGEX_FEATURE = re.compile(r'(?!\B"[^"]*)' + FEATURE + r'(?![^"]*"\B)') REGEX_SOURCE_FEATURE = re.compile(SOURCE_FEATURE) REGEX_KCONFIG_DEF = re.compile(DEF) REGEX_KCONFIG_EXPR = re.compile(EXPR) REGEX_KCONFIG_STMT = re.compile(STMT) REGEX_KCONFIG_HELP = re.compile(r"^\s+(help|---help---)\s*$") REGEX_FILTER_FEATURES = re.compile(r"[A-Za-z0-9]$") +REGEX_NUMERIC = re.compile(r"0[xX][0-9a-fA-F]+|[0-9]+") def parse_options(): @@ -58,6 +60,11 @@ def parse_options(): "input format bases on Git log's " "\'commmit1..commit2\'.") + parser.add_option('-f', '--find', dest='find', action='store_true', + default=False, + help="Find and show commits that may cause symbols to be " + "missing. Required to run with --diff.") + parser.add_option('-i', '--ignore', dest='ignore', action='store', default="", help="Ignore files matching this pattern. Note that " @@ -86,6 +93,9 @@ def parse_options(): "'--force' if you\nwant to ignore this warning and " "continue.") + if opts.commit: + opts.find = False + if opts.ignore: try: re.match(opts.ignore, "this/is/just/a/test.c") @@ -128,13 +138,19 @@ def main(): # feature has not been undefined before if not feature in undefined_a: files = sorted(undefined_b.get(feature)) - print "%s\t%s" % (feature, ", ".join(files)) + print "%s\t%s" % (yel(feature), ", ".join(files)) + if opts.find: + commits = find_commits(feature, opts.diff) + print red(commits) # check if there are new files that reference the undefined feature else: files = sorted(undefined_b.get(feature) - undefined_a.get(feature)) if files: - print "%s\t%s" % (feature, ", ".join(files)) + print "%s\t%s" % (yel(feature), ", ".join(files)) + if opts.find: + commits = find_commits(feature, opts.diff) + print red(commits) # reset to head execute("git reset --hard %s" % head) @@ -144,7 +160,21 @@ def main(): undefined = check_symbols(opts.ignore) for feature in sorted(undefined): files = sorted(undefined.get(feature)) - print "%s\t%s" % (feature, ", ".join(files)) + print "%s\t%s" % (yel(feature), ", ".join(files)) + + +def yel(string): + """ + Color %string yellow. + """ + return "\033[33m%s\033[0m" % string + + +def red(string): + """ + Color %string red. + """ + return "\033[31m%s\033[0m" % string def execute(cmd): @@ -156,6 +186,13 @@ def execute(cmd): return stdout +def find_commits(symbol, diff): + """Find commits changing %symbol in the given range of %diff.""" + commits = execute("git log --pretty=oneline --abbrev-commit -G %s %s" + % (symbol, diff)) + return commits + + def tree_is_dirty(): """Return true if the current working tree is dirty (i.e., if any file has been added, deleted, modified, renamed or copied but not committed).""" @@ -279,6 +316,9 @@ def parse_kconfig_file(kfile, defined_features, referenced_features): line = line.strip('\n') features.extend(get_features_in_line(line)) for feature in set(features): + if REGEX_NUMERIC.match(feature): + # ignore numeric values + continue paths = referenced_features.get(feature, set()) paths.add(kfile) referenced_features[feature] = paths diff --git a/scripts/kconfig/streamline_config.pl b/scripts/kconfig/streamline_config.pl index 9cb8522d8d22..f3d3fb42b873 100755 --- a/scripts/kconfig/streamline_config.pl +++ b/scripts/kconfig/streamline_config.pl @@ -137,7 +137,7 @@ my $ksource = ($ARGV[0] ? $ARGV[0] : '.'); my $kconfig = $ARGV[1]; my $lsmod_file = $ENV{'LSMOD'}; -my @makefiles = `find $ksource -name Makefile 2>/dev/null`; +my @makefiles = `find $ksource -name Makefile -or -name Kbuild 2>/dev/null`; chomp @makefiles; my %depends; diff --git a/security/security.c b/security/security.c index 595fffab48b0..994283624bdb 100644 --- a/security/security.c +++ b/security/security.c @@ -380,8 +380,8 @@ int security_inode_init_security(struct inode *inode, struct inode *dir, return 0; if (!initxattrs) - return call_int_hook(inode_init_security, 0, inode, dir, qstr, - NULL, NULL, NULL); + return call_int_hook(inode_init_security, -EOPNOTSUPP, inode, + dir, qstr, NULL, NULL, NULL); memset(new_xattrs, 0, sizeof(new_xattrs)); lsm_xattr = new_xattrs; ret = call_int_hook(inode_init_security, -EOPNOTSUPP, inode, dir, qstr, @@ -409,8 +409,8 @@ int security_old_inode_init_security(struct inode *inode, struct inode *dir, { if (unlikely(IS_PRIVATE(inode))) return -EOPNOTSUPP; - return call_int_hook(inode_init_security, 0, inode, dir, qstr, - name, value, len); + return call_int_hook(inode_init_security, -EOPNOTSUPP, inode, dir, + qstr, name, value, len); } EXPORT_SYMBOL(security_old_inode_init_security); @@ -1281,7 +1281,8 @@ int security_socket_getpeersec_stream(struct socket *sock, char __user *optval, int security_socket_getpeersec_dgram(struct socket *sock, struct sk_buff *skb, u32 *secid) { - return call_int_hook(socket_getpeersec_dgram, 0, sock, skb, secid); + return call_int_hook(socket_getpeersec_dgram, -ENOPROTOOPT, sock, + skb, secid); } EXPORT_SYMBOL(security_socket_getpeersec_dgram); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c index 5de3c5d8c2c0..d1a2cb65e27c 100644 --- a/sound/pci/hda/hda_codec.c +++ b/sound/pci/hda/hda_codec.c @@ -3172,7 +3172,7 @@ static int add_std_chmaps(struct hda_codec *codec) struct snd_pcm_chmap *chmap; const struct snd_pcm_chmap_elem *elem; - if (!pcm || pcm->own_chmap || + if (!pcm || !pcm->pcm || pcm->own_chmap || !hinfo->substreams) continue; elem = hinfo->chmap ? hinfo->chmap : snd_pcm_std_chmaps; diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c index b077bb644434..24f91114a32c 100644 --- a/sound/pci/hda/hda_generic.c +++ b/sound/pci/hda/hda_generic.c @@ -671,7 +671,8 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, } for (i = 0; i < path->depth; i++) { if (path->path[i] == nid) { - if (dir == HDA_OUTPUT || path->idx[i] == idx) + if (dir == HDA_OUTPUT || idx == -1 || + path->idx[i] == idx) return true; break; } @@ -682,7 +683,7 @@ static bool is_active_nid(struct hda_codec *codec, hda_nid_t nid, /* check whether the NID is referred by any active paths */ #define is_active_nid_for_any(codec, nid) \ - is_active_nid(codec, nid, HDA_OUTPUT, 0) + is_active_nid(codec, nid, HDA_OUTPUT, -1) /* get the default amp value for the target state */ static int get_amp_val_to_activate(struct hda_codec *codec, hda_nid_t nid, @@ -883,8 +884,7 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, struct hda_gen_spec *spec = codec->spec; int i; - if (!enable) - path->active = false; + path->active = enable; /* make sure the widget is powered up */ if (enable && (spec->power_down_unused || codec->power_save_node)) @@ -902,9 +902,6 @@ void snd_hda_activate_path(struct hda_codec *codec, struct nid_path *path, if (has_amp_out(codec, path, i)) activate_amp_out(codec, path, i, enable); } - - if (enable) - path->active = true; } EXPORT_SYMBOL_GPL(snd_hda_activate_path); diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c index f788a91b544a..ca03c40609fc 100644 --- a/sound/pci/hda/patch_conexant.c +++ b/sound/pci/hda/patch_conexant.c @@ -200,12 +200,33 @@ static int cx_auto_init(struct hda_codec *codec) return 0; } -#define cx_auto_free snd_hda_gen_free +static void cx_auto_reboot_notify(struct hda_codec *codec) +{ + struct conexant_spec *spec = codec->spec; + + if (codec->core.vendor_id != 0x14f150f2) + return; + + /* Turn the CX20722 codec into D3 to avoid spurious noises + from the internal speaker during (and after) reboot */ + cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, false); + + snd_hda_codec_set_power_to_all(codec, codec->core.afg, AC_PWRST_D3); + snd_hda_codec_write(codec, codec->core.afg, 0, + AC_VERB_SET_POWER_STATE, AC_PWRST_D3); +} + +static void cx_auto_free(struct hda_codec *codec) +{ + cx_auto_reboot_notify(codec); + snd_hda_gen_free(codec); +} static const struct hda_codec_ops cx_auto_patch_ops = { .build_controls = cx_auto_build_controls, .build_pcms = snd_hda_gen_build_pcms, .init = cx_auto_init, + .reboot_notify = cx_auto_reboot_notify, .free = cx_auto_free, .unsol_event = snd_hda_jack_unsol_event, #ifdef CONFIG_PM diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c index 0b9847affbec..374ea53288ca 100644 --- a/sound/pci/hda/patch_realtek.c +++ b/sound/pci/hda/patch_realtek.c @@ -5190,6 +5190,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x06d9, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x06da, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x06de, "Dell", ALC292_FIXUP_DISABLE_AAMIX), + SND_PCI_QUIRK(0x1028, 0x06db, "Dell", ALC292_FIXUP_DISABLE_AAMIX), SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE), SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2), @@ -5291,6 +5292,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad T440s", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x220e, "Thinkpad T440p", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2210, "Thinkpad T540p", ALC292_FIXUP_TPT440_DOCK), + SND_PCI_QUIRK(0x17aa, 0x2211, "Thinkpad W541", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad T440", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad X240", ALC292_FIXUP_TPT440_DOCK), SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST), diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig index 2ae9619443d1..1d651b8a8957 100644 --- a/sound/soc/Kconfig +++ b/sound/soc/Kconfig @@ -30,6 +30,9 @@ config SND_SOC_GENERIC_DMAENGINE_PCM bool select SND_DMAENGINE_PCM +config SND_SOC_TOPOLOGY + bool + # All the supported SoCs source "sound/soc/adi/Kconfig" source "sound/soc/atmel/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile index e189903fabf4..669648b41d30 100644 --- a/sound/soc/Makefile +++ b/sound/soc/Makefile @@ -1,6 +1,9 @@ snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o + +ifneq ($(CONFIG_SND_SOC_TOPOLOGY),) snd-soc-core-objs += soc-topology.o +endif ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),) snd-soc-core-objs += soc-generic-dmaengine-pcm.o diff --git a/sound/usb/card.c b/sound/usb/card.c index 1fab9778807a..0450593980fd 100644 --- a/sound/usb/card.c +++ b/sound/usb/card.c @@ -638,7 +638,7 @@ int snd_usb_autoresume(struct snd_usb_audio *chip) int err = -ENODEV; down_read(&chip->shutdown_rwsem); - if (chip->probing && chip->in_pm) + if (chip->probing || chip->in_pm) err = 0; else if (!chip->shutdown) err = usb_autopm_get_interface(chip->pm_intf); diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c index 754e689596a2..00ebc0ca008e 100644 --- a/sound/usb/quirks.c +++ b/sound/usb/quirks.c @@ -1268,6 +1268,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip, return SNDRV_PCM_FMTBIT_DSD_U32_BE; break; + case USB_ID(0x20b1, 0x000a): /* Gustard DAC-X20U */ case USB_ID(0x20b1, 0x2009): /* DIYINHK DSD DXD 384kHz USB to I2S/DSD */ case USB_ID(0x20b1, 0x2023): /* JLsounds I2SoverUSB */ if (fp->altsetting == 3) diff --git a/tools/hv/lsvmbus b/tools/hv/lsvmbus new file mode 100644 index 000000000000..162a3784d80e --- /dev/null +++ b/tools/hv/lsvmbus @@ -0,0 +1,101 @@ +#!/usr/bin/env python + +import os +from optparse import OptionParser + +parser = OptionParser() +parser.add_option("-v", "--verbose", dest="verbose", + help="print verbose messages. Try -vv, -vvv for \ + more verbose messages", action="count") + +(options, args) = parser.parse_args() + +verbose = 0 +if options.verbose is not None: + verbose = options.verbose + +vmbus_sys_path = '/sys/bus/vmbus/devices' +if not os.path.isdir(vmbus_sys_path): + print "%s doesn't exist: exiting..." % vmbus_sys_path + exit(-1) + +vmbus_dev_dict = { + '{0e0b6031-5213-4934-818b-38d90ced39db}' : '[Operating system shutdown]', + '{9527e630-d0ae-497b-adce-e80ab0175caf}' : '[Time Synchronization]', + '{57164f39-9115-4e78-ab55-382f3bd5422d}' : '[Heartbeat]', + '{a9a0f4e7-5a45-4d96-b827-8a841e8c03e6}' : '[Data Exchange]', + '{35fa2e29-ea23-4236-96ae-3a6ebacba440}' : '[Backup (volume checkpoint)]', + '{34d14be3-dee4-41c8-9ae7-6b174977c192}' : '[Guest services]', + '{525074dc-8985-46e2-8057-a307dc18a502}' : '[Dynamic Memory]', + '{cfa8b69e-5b4a-4cc0-b98b-8ba1a1f3f95a}' : 'Synthetic mouse', + '{f912ad6d-2b17-48ea-bd65-f927a61c7684}' : 'Synthetic keyboard', + '{da0a7802-e377-4aac-8e77-0558eb1073f8}' : 'Synthetic framebuffer adapter', + '{f8615163-df3e-46c5-913f-f2d2f965ed0e}' : 'Synthetic network adapter', + '{32412632-86cb-44a2-9b5c-50d1417354f5}' : 'Synthetic IDE Controller', + '{ba6163d9-04a1-4d29-b605-72e2ffb1dc7f}' : 'Synthetic SCSI Controller', + '{2f9bcc4a-0069-4af3-b76b-6fd0be528cda}' : 'Synthetic fiber channel adapter', + '{8c2eaf3d-32a7-4b09-ab99-bd1f1c86b501}' : 'Synthetic RDMA adapter', + '{276aacf4-ac15-426c-98dd-7521ad3f01fe}' : '[Reserved system device]', + '{f8e65716-3cb3-4a06-9a60-1889c5cccab5}' : '[Reserved system device]', + '{3375baf4-9e15-4b30-b765-67acb10d607b}' : '[Reserved system device]', +} + +def get_vmbus_dev_attr(dev_name, attr): + try: + f = open('%s/%s/%s' % (vmbus_sys_path, dev_name, attr), 'r') + lines = f.readlines() + f.close() + except IOError: + lines = [] + + return lines + +class VMBus_Dev: + pass + + +vmbus_dev_list = [] + +for f in os.listdir(vmbus_sys_path): + vmbus_id = get_vmbus_dev_attr(f, 'id')[0].strip() + class_id = get_vmbus_dev_attr(f, 'class_id')[0].strip() + device_id = get_vmbus_dev_attr(f, 'device_id')[0].strip() + dev_desc = vmbus_dev_dict.get(class_id, 'Unknown') + + chn_vp_mapping = get_vmbus_dev_attr(f, 'channel_vp_mapping') + chn_vp_mapping = [c.strip() for c in chn_vp_mapping] + chn_vp_mapping = sorted(chn_vp_mapping, + key = lambda c : int(c.split(':')[0])) + + chn_vp_mapping = ['\tRel_ID=%s, target_cpu=%s' % + (c.split(':')[0], c.split(':')[1]) + for c in chn_vp_mapping] + d = VMBus_Dev() + d.sysfs_path = '%s/%s' % (vmbus_sys_path, f) + d.vmbus_id = vmbus_id + d.class_id = class_id + d.device_id = device_id + d.dev_desc = dev_desc + d.chn_vp_mapping = '\n'.join(chn_vp_mapping) + if d.chn_vp_mapping: + d.chn_vp_mapping += '\n' + + vmbus_dev_list.append(d) + + +vmbus_dev_list = sorted(vmbus_dev_list, key = lambda d : int(d.vmbus_id)) + +format0 = '%2s: %s' +format1 = '%2s: Class_ID = %s - %s\n%s' +format2 = '%2s: Class_ID = %s - %s\n\tDevice_ID = %s\n\tSysfs path: %s\n%s' + +for d in vmbus_dev_list: + if verbose == 0: + print ('VMBUS ID ' + format0) % (d.vmbus_id, d.dev_desc) + elif verbose == 1: + print ('VMBUS ID ' + format1) % \ + (d.vmbus_id, d.class_id, d.dev_desc, d.chn_vp_mapping) + else: + print ('VMBUS ID ' + format2) % \ + (d.vmbus_id, d.class_id, d.dev_desc, \ + d.device_id, d.sysfs_path, d.chn_vp_mapping) diff --git a/tools/perf/builtin-record.c b/tools/perf/builtin-record.c index de165a1b9240..20b56eb987f8 100644 --- a/tools/perf/builtin-record.c +++ b/tools/perf/builtin-record.c @@ -521,6 +521,15 @@ static int __cmd_record(struct record *rec, int argc, const char **argv) goto out_child; } + /* + * Normally perf_session__new would do this, but it doesn't have the + * evlist. + */ + if (rec->tool.ordered_events && !perf_evlist__sample_id_all(rec->evlist)) { + pr_warning("WARNING: No sample_id_all support, falling back to unordered processing\n"); + rec->tool.ordered_events = false; + } + if (!rec->evlist->nr_groups) perf_header__clear_feat(&session->header, HEADER_GROUP_DESC); @@ -965,9 +974,11 @@ static struct record record = { .tool = { .sample = process_sample_event, .fork = perf_event__process_fork, + .exit = perf_event__process_exit, .comm = perf_event__process_comm, .mmap = perf_event__process_mmap, .mmap2 = perf_event__process_mmap2, + .ordered_events = true, }, }; diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c index ecf319728f25..6135cc07213c 100644 --- a/tools/perf/builtin-top.c +++ b/tools/perf/builtin-top.c @@ -601,8 +601,8 @@ static void display_sig(int sig __maybe_unused) static void display_setup_sig(void) { - signal(SIGSEGV, display_sig); - signal(SIGFPE, display_sig); + signal(SIGSEGV, sighandler_dump_stack); + signal(SIGFPE, sighandler_dump_stack); signal(SIGINT, display_sig); signal(SIGQUIT, display_sig); signal(SIGTERM, display_sig); diff --git a/tools/perf/config/Makefile b/tools/perf/config/Makefile index 094ddaee104c..d31fac19c30b 100644 --- a/tools/perf/config/Makefile +++ b/tools/perf/config/Makefile @@ -638,7 +638,7 @@ ifndef DESTDIR prefix ?= $(HOME) endif bindir_relative = bin -bindir = $(prefix)/$(bindir_relative) +bindir = $(abspath $(prefix)/$(bindir_relative)) mandir = share/man infodir = share/info perfexecdir = libexec/perf-core diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c index 7ff682770fdb..f1a4c833121e 100644 --- a/tools/perf/util/machine.c +++ b/tools/perf/util/machine.c @@ -1387,6 +1387,24 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event event->fork.ptid); int err = 0; + if (dump_trace) + perf_event__fprintf_task(event, stdout); + + /* + * There may be an existing thread that is not actually the parent, + * either because we are processing events out of order, or because the + * (fork) event that would have removed the thread was lost. Assume the + * latter case and continue on as best we can. + */ + if (parent->pid_ != (pid_t)event->fork.ppid) { + dump_printf("removing erroneous parent thread %d/%d\n", + parent->pid_, parent->tid); + machine__remove_thread(machine, parent); + thread__put(parent); + parent = machine__findnew_thread(machine, event->fork.ppid, + event->fork.ptid); + } + /* if a thread currently exists for the thread id remove it */ if (thread != NULL) { machine__remove_thread(machine, thread); @@ -1395,8 +1413,6 @@ int machine__process_fork_event(struct machine *machine, union perf_event *event thread = machine__findnew_thread(machine, event->fork.pid, event->fork.tid); - if (dump_trace) - perf_event__fprintf_task(event, stdout); if (thread == NULL || parent == NULL || thread__fork(thread, parent, sample->time) < 0) { diff --git a/tools/perf/util/stat-shadow.c b/tools/perf/util/stat-shadow.c index 53e8bb7bc852..2a5d8d7698ae 100644 --- a/tools/perf/util/stat-shadow.c +++ b/tools/perf/util/stat-shadow.c @@ -85,7 +85,7 @@ void perf_stat__update_shadow_stats(struct perf_evsel *counter, u64 *count, else if (perf_evsel__match(counter, HARDWARE, HW_CPU_CYCLES)) update_stats(&runtime_cycles_stats[ctx][cpu], count[0]); else if (perf_stat_evsel__is(counter, CYCLES_IN_TX)) - update_stats(&runtime_transaction_stats[ctx][cpu], count[0]); + update_stats(&runtime_cycles_in_tx_stats[ctx][cpu], count[0]); else if (perf_stat_evsel__is(counter, TRANSACTION_START)) update_stats(&runtime_transaction_stats[ctx][cpu], count[0]); else if (perf_stat_evsel__is(counter, ELISION_START)) @@ -398,20 +398,18 @@ void perf_stat__print_shadow_stats(FILE *out, struct perf_evsel *evsel, " # %5.2f%% aborted cycles ", 100.0 * ((total2-avg) / total)); } else if (perf_stat_evsel__is(evsel, TRANSACTION_START) && - avg > 0 && runtime_cycles_in_tx_stats[ctx][cpu].n != 0) { total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); - if (total) + if (avg) ratio = total / avg; fprintf(out, " # %8.0f cycles / transaction ", ratio); } else if (perf_stat_evsel__is(evsel, ELISION_START) && - avg > 0 && runtime_cycles_in_tx_stats[ctx][cpu].n != 0) { total = avg_stats(&runtime_cycles_in_tx_stats[ctx][cpu]); - if (total) + if (avg) ratio = total / avg; fprintf(out, " # %8.0f cycles / elision ", ratio); diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c index 28c4b746baa1..0a9ae8014729 100644 --- a/tools/perf/util/thread.c +++ b/tools/perf/util/thread.c @@ -191,6 +191,12 @@ static int thread__clone_map_groups(struct thread *thread, if (thread->pid_ == parent->pid_) return 0; + if (thread->mg == parent->mg) { + pr_debug("broken map groups on thread %d/%d parent %d/%d\n", + thread->pid_, thread->tid, parent->pid_, parent->tid); + return 0; + } + /* But this one is new process, copy maps. */ for (i = 0; i < MAP__NR_TYPES; ++i) if (map_groups__clone(thread->mg, parent->mg, i) < 0) diff --git a/tools/testing/selftests/firmware/fw_filesystem.sh b/tools/testing/selftests/firmware/fw_filesystem.sh index 3fc6c10c2479..c4366dc74e01 100755 --- a/tools/testing/selftests/firmware/fw_filesystem.sh +++ b/tools/testing/selftests/firmware/fw_filesystem.sh @@ -9,7 +9,15 @@ modprobe test_firmware DIR=/sys/devices/virtual/misc/test_firmware -OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) +# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ +# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that +# as an indicator for CONFIG_FW_LOADER_USER_HELPER. +HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) + +if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) +fi + OLD_FWPATH=$(cat /sys/module/firmware_class/parameters/path) FWPATH=$(mktemp -d) @@ -17,7 +25,9 @@ FW="$FWPATH/test-firmware.bin" test_finish() { - echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout + if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + echo "$OLD_TIMEOUT" >/sys/class/firmware/timeout + fi echo -n "$OLD_PATH" >/sys/module/firmware_class/parameters/path rm -f "$FW" rmdir "$FWPATH" @@ -25,8 +35,11 @@ test_finish() trap "test_finish" EXIT -# Turn down the timeout so failures don't take so long. -echo 1 >/sys/class/firmware/timeout +if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + # Turn down the timeout so failures don't take so long. + echo 1 >/sys/class/firmware/timeout +fi + # Set the kernel search path. echo -n "$FWPATH" >/sys/module/firmware_class/parameters/path @@ -41,7 +54,9 @@ if diff -q "$FW" /dev/test_firmware >/dev/null ; then echo "$0: firmware was not expected to match" >&2 exit 1 else - echo "$0: timeout works" + if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + echo "$0: timeout works" + fi fi # This should succeed via kernel load or will fail after 1 second after diff --git a/tools/testing/selftests/firmware/fw_userhelper.sh b/tools/testing/selftests/firmware/fw_userhelper.sh index 6efbade12139..b9983f8e09f6 100755 --- a/tools/testing/selftests/firmware/fw_userhelper.sh +++ b/tools/testing/selftests/firmware/fw_userhelper.sh @@ -9,7 +9,17 @@ modprobe test_firmware DIR=/sys/devices/virtual/misc/test_firmware -OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) +# CONFIG_FW_LOADER_USER_HELPER has a sysfs class under /sys/class/firmware/ +# These days no one enables CONFIG_FW_LOADER_USER_HELPER so check for that +# as an indicator for CONFIG_FW_LOADER_USER_HELPER. +HAS_FW_LOADER_USER_HELPER=$(if [ -d /sys/class/firmware/ ]; then echo yes; else echo no; fi) + +if [ "$HAS_FW_LOADER_USER_HELPER" = "yes" ]; then + OLD_TIMEOUT=$(cat /sys/class/firmware/timeout) +else + echo "usermode helper disabled so ignoring test" + exit 0 +fi FWPATH=$(mktemp -d) FW="$FWPATH/test-firmware.bin" diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c index 8b8a44453670..d8db2f8fce9c 100644 --- a/virt/kvm/kvm_main.c +++ b/virt/kvm/kvm_main.c @@ -2206,6 +2206,11 @@ static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id) } kvm->vcpus[atomic_read(&kvm->online_vcpus)] = vcpu; + + /* + * Pairs with smp_rmb() in kvm_get_vcpu. Write kvm->vcpus + * before kvm->online_vcpu's incremented value. + */ smp_wmb(); atomic_inc(&kvm->online_vcpus); @@ -2618,9 +2623,6 @@ static long kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg) case KVM_CAP_USER_MEMORY: case KVM_CAP_DESTROY_MEMORY_REGION_WORKS: case KVM_CAP_JOIN_MEMORY_REGIONS_WORKS: -#ifdef CONFIG_KVM_APIC_ARCHITECTURE - case KVM_CAP_SET_BOOT_CPU_ID: -#endif case KVM_CAP_INTERNAL_ERROR_DATA: #ifdef CONFIG_HAVE_KVM_MSI case KVM_CAP_SIGNAL_MSI: @@ -2716,17 +2718,6 @@ static long kvm_vm_ioctl(struct file *filp, r = kvm_ioeventfd(kvm, &data); break; } -#ifdef CONFIG_KVM_APIC_ARCHITECTURE - case KVM_SET_BOOT_CPU_ID: - r = 0; - mutex_lock(&kvm->lock); - if (atomic_read(&kvm->online_vcpus) != 0) - r = -EBUSY; - else - kvm->bsp_vcpu_id = arg; - mutex_unlock(&kvm->lock); - break; -#endif #ifdef CONFIG_HAVE_KVM_MSI case KVM_SIGNAL_MSI: { struct kvm_msi msi; |