mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-01-11 17:10:13 +00:00
Compare commits
28 Commits
c9b47175e9
...
0048fbb401
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0048fbb401 | ||
|
|
2137cb863b | ||
|
|
db28b8ae36 | ||
|
|
2f7041e59b | ||
|
|
565848bb3b | ||
|
|
2c22361ab6 | ||
|
|
7a7e836684 | ||
|
|
429c472701 | ||
|
|
70f62e5217 | ||
|
|
c42ba5a87b | ||
|
|
cd91b502f1 | ||
|
|
275498b881 | ||
|
|
9407d138b8 | ||
|
|
2d98144440 | ||
|
|
4ab2ee3079 | ||
|
|
a311c777f2 | ||
|
|
c65d6881ca | ||
|
|
817d21578d | ||
|
|
16464af831 | ||
|
|
117c537cc7 | ||
|
|
809ef03d6d | ||
|
|
e8ec34ed46 | ||
|
|
3c297278a6 | ||
|
|
d6137f25b1 | ||
|
|
fd1bf704c7 | ||
|
|
6678b3e6ed | ||
|
|
5455d21d2e | ||
|
|
295e3beead |
@ -0,0 +1,59 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/ti,twl4030-keypad.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Texas Instruments TWL4030-family Keypad Controller
|
||||
|
||||
maintainers:
|
||||
- Peter Ujfalusi <peter.ujfalusi@gmail.com>
|
||||
|
||||
description:
|
||||
TWL4030's Keypad controller is used to interface a SoC with a matrix-type
|
||||
keypad device. The keypad controller supports multiple row and column lines.
|
||||
A key can be placed at each intersection of a unique row and a unique column.
|
||||
The keypad controller can sense a key-press and key-release and report the
|
||||
event using a interrupt to the cpu.
|
||||
|
||||
allOf:
|
||||
- $ref: matrix-keymap.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: ti,twl4030-keypad
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- interrupts
|
||||
- keypad,num-rows
|
||||
- keypad,num-columns
|
||||
- linux,keymap
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/input/input.h>
|
||||
|
||||
keypad {
|
||||
compatible = "ti,twl4030-keypad";
|
||||
interrupts = <1>;
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <8>;
|
||||
linux,keymap = <
|
||||
/* row 0 */
|
||||
MATRIX_KEY(0, 0, KEY_1)
|
||||
MATRIX_KEY(0, 1, KEY_2)
|
||||
MATRIX_KEY(0, 2, KEY_3)
|
||||
|
||||
/* ...and so on for a full 8x8 matrix... */
|
||||
|
||||
/* row 7 */
|
||||
MATRIX_KEY(7, 6, KEY_Y)
|
||||
MATRIX_KEY(7, 7, KEY_Z)
|
||||
>;
|
||||
};
|
||||
@ -1,15 +0,0 @@
|
||||
* Microchip AR1020 and AR1021 touchscreen interface (I2C)
|
||||
|
||||
Required properties:
|
||||
- compatible : "microchip,ar1021-i2c"
|
||||
- reg : I2C slave address
|
||||
- interrupts : touch controller interrupt
|
||||
|
||||
Example:
|
||||
|
||||
touchscreen@4d {
|
||||
compatible = "microchip,ar1021-i2c";
|
||||
reg = <0x4d>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <11 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
@ -1,75 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/azoteq,iqs5xx.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Azoteq IQS550/572/525 Trackpad/Touchscreen Controller
|
||||
|
||||
maintainers:
|
||||
- Jeff LaBundy <jeff@labundy.com>
|
||||
|
||||
description: |
|
||||
The Azoteq IQS550, IQS572 and IQS525 trackpad and touchscreen controllers
|
||||
employ projected-capacitance sensing and can track up to five independent
|
||||
contacts.
|
||||
|
||||
Link to datasheet: https://www.azoteq.com/
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- azoteq,iqs550
|
||||
- azoteq,iqs572
|
||||
- azoteq,iqs525
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
touchscreen-size-x: true
|
||||
touchscreen-size-y: true
|
||||
touchscreen-inverted-x: true
|
||||
touchscreen-inverted-y: true
|
||||
touchscreen-swapped-x-y: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@74 {
|
||||
compatible = "azoteq,iqs550";
|
||||
reg = <0x74>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <27 IRQ_TYPE_LEVEL_HIGH>;
|
||||
reset-gpios = <&gpio 22 (GPIO_ACTIVE_LOW |
|
||||
GPIO_PUSH_PULL)>;
|
||||
|
||||
touchscreen-size-x = <800>;
|
||||
touchscreen-size-y = <480>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@ -1,64 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/himax,hx83112b.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Himax hx83112b touchscreen controller
|
||||
|
||||
maintainers:
|
||||
- Job Noorman <job@noorman.info>
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- himax,hx83100a
|
||||
- himax,hx83112b
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
touchscreen-inverted-x: true
|
||||
touchscreen-inverted-y: true
|
||||
touchscreen-size-x: true
|
||||
touchscreen-size-y: true
|
||||
touchscreen-swapped-x-y: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- reset-gpios
|
||||
- touchscreen-size-x
|
||||
- touchscreen-size-y
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
touchscreen@48 {
|
||||
compatible = "himax,hx83112b";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&tlmm>;
|
||||
interrupts = <65 IRQ_TYPE_LEVEL_LOW>;
|
||||
touchscreen-size-x = <1080>;
|
||||
touchscreen-size-y = <2160>;
|
||||
reset-gpios = <&tlmm 64 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@ -1,65 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/hynitron,cstxxx.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Hynitron cstxxx series touchscreen controller
|
||||
|
||||
description: |
|
||||
Bindings for Hynitron cstxxx series multi-touch touchscreen
|
||||
controllers.
|
||||
|
||||
maintainers:
|
||||
- Chris Morgan <macromorgan@hotmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- hynitron,cst340
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
touchscreen-size-x: true
|
||||
touchscreen-size-y: true
|
||||
touchscreen-inverted-x: true
|
||||
touchscreen-inverted-y: true
|
||||
touchscreen-swapped-x-y: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
- reset-gpios
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
touchscreen@1a {
|
||||
compatible = "hynitron,cst340";
|
||||
reg = <0x1a>;
|
||||
interrupt-parent = <&gpio4>;
|
||||
interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
|
||||
reset-gpios = <&gpio4 6 GPIO_ACTIVE_LOW>;
|
||||
touchscreen-size-x = <640>;
|
||||
touchscreen-size-y = <480>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@ -1,76 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/ilitek_ts_i2c.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Ilitek I2C Touchscreen Controller
|
||||
|
||||
maintainers:
|
||||
- Dmitry Torokhov <dmitry.torokhov@gmail.com>
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- ilitek,ili210x
|
||||
- ilitek,ili2117
|
||||
- ilitek,ili2120
|
||||
- ilitek,ili2130
|
||||
- ilitek,ili2131
|
||||
- ilitek,ili2132
|
||||
- ilitek,ili2316
|
||||
- ilitek,ili2322
|
||||
- ilitek,ili2323
|
||||
- ilitek,ili2326
|
||||
- ilitek,ili251x
|
||||
- ilitek,ili2520
|
||||
- ilitek,ili2521
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source:
|
||||
type: boolean
|
||||
description: touchscreen can be used as a wakeup source.
|
||||
|
||||
touchscreen-size-x: true
|
||||
touchscreen-size-y: true
|
||||
touchscreen-inverted-x: true
|
||||
touchscreen-inverted-y: true
|
||||
touchscreen-swapped-x-y: true
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@41 {
|
||||
compatible = "ilitek,ili2520";
|
||||
reg = <0x41>;
|
||||
|
||||
interrupt-parent = <&gpio1>;
|
||||
interrupts = <7 IRQ_TYPE_LEVEL_LOW>;
|
||||
reset-gpios = <&gpio1 8 GPIO_ACTIVE_LOW>;
|
||||
touchscreen-inverted-y;
|
||||
wakeup-source;
|
||||
};
|
||||
};
|
||||
@ -1,46 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/maxim,max11801.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MAXI MAX11801 Resistive touch screen controller with i2c interface
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: maxim,max11801
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@48 {
|
||||
compatible = "maxim,max11801";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <31 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,56 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0 OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/melfas,mip4_ts.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: MELFAS MIP4 Touchscreen
|
||||
|
||||
maintainers:
|
||||
- Ariel D'Alessandro <ariel.dalessandro@collabora.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: melfas,mip4_ts
|
||||
|
||||
reg:
|
||||
description: I2C address of the chip (0x48 or 0x34)
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
ce-gpios:
|
||||
description:
|
||||
GPIO connected to the CE (chip enable) pin of the chip (active high)
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@34 {
|
||||
compatible = "melfas,mip4_ts";
|
||||
reg = <0x34>;
|
||||
|
||||
interrupts-extended = <&tlmm 13 IRQ_TYPE_EDGE_FALLING>;
|
||||
ce-gpios = <&tlmm 12 GPIO_ACTIVE_HIGH>;
|
||||
|
||||
pinctrl-0 = <&touchscreen_default>;
|
||||
pinctrl-names = "default";
|
||||
};
|
||||
};
|
||||
|
||||
...
|
||||
@ -1,20 +0,0 @@
|
||||
* MELFAS MIP4 Touchscreen
|
||||
|
||||
Required properties:
|
||||
- compatible: must be "melfas,mip4_ts"
|
||||
- reg: I2C slave address of the chip (0x48 or 0x34)
|
||||
- interrupts: interrupt to which the chip is connected
|
||||
|
||||
Optional properties:
|
||||
- ce-gpios: GPIO connected to the CE (chip enable) pin of the chip
|
||||
|
||||
Example:
|
||||
i2c@00000000 {
|
||||
touchscreen: melfas_mip4@48 {
|
||||
compatible = "melfas,mip4_ts";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio>;
|
||||
interrupts = <0 IRQ_TYPE_EDGE_FALLING>;
|
||||
ce-gpios = <&gpio 0 GPIO_ACTIVE_HIGH>;
|
||||
};
|
||||
};
|
||||
@ -1,52 +0,0 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/semtech,sx8654.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Semtech SX8654 I2C Touchscreen Controller
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- semtech,sx8650
|
||||
- semtech,sx8654
|
||||
- semtech,sx8655
|
||||
- semtech,sx8656
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/gpio/gpio.h>
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@48 {
|
||||
compatible = "semtech,sx8654";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio6>;
|
||||
interrupts = <3 IRQ_TYPE_EDGE_FALLING>;
|
||||
reset-gpios = <&gpio4 2 GPIO_ACTIVE_LOW>;
|
||||
};
|
||||
};
|
||||
@ -0,0 +1,84 @@
|
||||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/input/touchscreen/trivial-touch.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Trivial touch screen controller with i2c interface
|
||||
|
||||
maintainers:
|
||||
- Frank Li <Frank.Li@nxp.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
# The Azoteq IQS550, IQS572 and IQS525 trackpad and touchscreen controllers
|
||||
- azoteq,iqs550
|
||||
- azoteq,iqs572
|
||||
- azoteq,iqs525
|
||||
# Himax hx83100a touchscreen controller
|
||||
- himax,hx83100a
|
||||
# Himax hx83112b touchscreen controller
|
||||
- himax,hx83112b
|
||||
# Hynitron cstxxx series touchscreen controller
|
||||
- hynitron,cst340
|
||||
# Ilitek I2C Touchscreen Controller
|
||||
- ilitek,ili210x
|
||||
- ilitek,ili2117
|
||||
- ilitek,ili2120
|
||||
- ilitek,ili2130
|
||||
- ilitek,ili2131
|
||||
- ilitek,ili2132
|
||||
- ilitek,ili2316
|
||||
- ilitek,ili2322
|
||||
- ilitek,ili2323
|
||||
- ilitek,ili2326
|
||||
- ilitek,ili251x
|
||||
- ilitek,ili2520
|
||||
- ilitek,ili2521
|
||||
# MAXI MAX11801 Resistive touch screen controller with i2c interface
|
||||
- maxim,max11801
|
||||
# Microchip AR1020 and AR1021 touchscreen interface (I2C)
|
||||
- microchip,ar1021-i2c
|
||||
# Trivial touch screen controller with i2c interface
|
||||
- semtech,sx8650
|
||||
- semtech,sx8654
|
||||
- semtech,sx8655
|
||||
- semtech,sx8656
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
reset-gpios:
|
||||
maxItems: 1
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
allOf:
|
||||
- $ref: touchscreen.yaml
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
- interrupts
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
touchscreen@48 {
|
||||
compatible = "maxim,max11801";
|
||||
reg = <0x48>;
|
||||
interrupt-parent = <&gpio3>;
|
||||
interrupts = <31 IRQ_TYPE_EDGE_FALLING>;
|
||||
};
|
||||
};
|
||||
@ -1,27 +0,0 @@
|
||||
* TWL4030's Keypad Controller device tree bindings
|
||||
|
||||
TWL4030's Keypad controller is used to interface a SoC with a matrix-type
|
||||
keypad device. The keypad controller supports multiple row and column lines.
|
||||
A key can be placed at each intersection of a unique row and a unique column.
|
||||
The keypad controller can sense a key-press and key-release and report the
|
||||
event using a interrupt to the cpu.
|
||||
|
||||
This binding is based on the matrix-keymap binding with the following
|
||||
changes:
|
||||
|
||||
* keypad,num-rows and keypad,num-columns are required.
|
||||
|
||||
Required SoC Specific Properties:
|
||||
- compatible: should be one of the following
|
||||
- "ti,twl4030-keypad": For controllers compatible with twl4030 keypad
|
||||
controller.
|
||||
- interrupt: should be one of the following
|
||||
- <1>: For controllers compatible with twl4030 keypad controller.
|
||||
|
||||
Example:
|
||||
twl_keypad: keypad {
|
||||
compatible = "ti,twl4030-keypad";
|
||||
interrupts = <1>;
|
||||
keypad,num-rows = <8>;
|
||||
keypad,num-columns = <8>;
|
||||
};
|
||||
@ -26455,6 +26455,7 @@ F: include/trace/
|
||||
F: kernel/trace/
|
||||
F: kernel/tracepoint.c
|
||||
F: scripts/tracing/
|
||||
F: scripts/tracepoint-update.c
|
||||
F: tools/testing/selftests/ftrace/
|
||||
|
||||
TRACING MMIO ACCESSES (MMIOTRACE)
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef __ASM_CSKY_REGDEF_H
|
||||
#define __ASM_CSKY_REGDEF_H
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
#ifdef __ASSEMBLER__
|
||||
#define syscallid r1
|
||||
#else
|
||||
#define syscallid "r1"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef __ASM_CSKY_REGDEF_H
|
||||
#define __ASM_CSKY_REGDEF_H
|
||||
|
||||
#ifdef __ASSEMBLY__
|
||||
#ifdef __ASSEMBLER__
|
||||
#define syscallid r7
|
||||
#else
|
||||
#define syscallid "r7"
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef __ASM_CSKY_BARRIER_H
|
||||
#define __ASM_CSKY_BARRIER_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#define nop() asm volatile ("nop\n":::"memory")
|
||||
|
||||
@ -84,5 +84,5 @@
|
||||
|
||||
#include <asm-generic/barrier.h>
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* __ASM_CSKY_BARRIER_H */
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
void dcache_wb_line(unsigned long start);
|
||||
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
|
||||
#define MCOUNT_ADDR ((unsigned long)_mcount)
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
extern void _mcount(unsigned long);
|
||||
|
||||
@ -28,5 +28,5 @@ struct dyn_arch_ftrace {
|
||||
void prepare_ftrace_return(unsigned long *parent, unsigned long self_addr,
|
||||
unsigned long frame_pointer);
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif /* __ASM_CSKY_FTRACE_H */
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef __ASM_CSKY_JUMP_LABEL_H
|
||||
#define __ASM_CSKY_JUMP_LABEL_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
@ -48,5 +48,5 @@ void arch_jump_label_transform_static(struct jump_entry *entry,
|
||||
enum jump_label_type type);
|
||||
#define arch_jump_label_transform_static arch_jump_label_transform_static
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* __ASM_CSKY_JUMP_LABEL_H */
|
||||
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
#define PHYS_OFFSET_OFFSET (CONFIG_DRAM_BASE & (SSEG_SIZE - 1))
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <linux/pfn.h>
|
||||
|
||||
@ -84,5 +84,5 @@ static inline unsigned long virt_to_pfn(const void *kaddr)
|
||||
#include <asm-generic/memory_model.h>
|
||||
#include <asm-generic/getorder.h>
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
#endif /* __ASM_CSKY_PAGE_H */
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#define PS_S 0x80000000 /* Supervisor Mode */
|
||||
|
||||
@ -98,5 +98,5 @@ static inline unsigned long regs_get_register(struct pt_regs *regs,
|
||||
|
||||
asmlinkage int syscall_trace_enter(struct pt_regs *regs);
|
||||
asmlinkage void syscall_trace_exit(struct pt_regs *regs);
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* __ASM_CSKY_PTRACE_H */
|
||||
|
||||
@ -8,5 +8,6 @@
|
||||
extern char _start[];
|
||||
|
||||
asmlinkage void csky_start(unsigned int unused, void *dtb_start);
|
||||
asmlinkage void csky_start_secondary(void);
|
||||
|
||||
#endif /* __ASM_SECTIONS_H */
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef _CSKY_STRING_MM_H_
|
||||
#define _CSKY_STRING_MM_H_
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
#include <linux/types.h>
|
||||
#include <linux/compiler.h>
|
||||
#include <abi/string.h>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef _ASM_CSKY_THREAD_INFO_H
|
||||
#define _ASM_CSKY_THREAD_INFO_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
#include <asm/types.h>
|
||||
#include <asm/page.h>
|
||||
@ -51,7 +51,7 @@ static inline struct thread_info *current_thread_info(void)
|
||||
return (struct thread_info *)(sp & ~(THREAD_SIZE - 1));
|
||||
}
|
||||
|
||||
#endif /* !__ASSEMBLY__ */
|
||||
#endif /* !__ASSEMBLER__ */
|
||||
|
||||
#define TIF_SIGPENDING 0 /* signal pending */
|
||||
#define TIF_NOTIFY_RESUME 1 /* callback before returning to user */
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
#ifndef _CSKY_PTRACE_H
|
||||
#define _CSKY_PTRACE_H
|
||||
|
||||
#ifndef __ASSEMBLY__
|
||||
#ifndef __ASSEMBLER__
|
||||
|
||||
struct pt_regs {
|
||||
unsigned long tls;
|
||||
@ -47,5 +47,5 @@ struct user_fp {
|
||||
unsigned long reserved;
|
||||
};
|
||||
|
||||
#endif /* __ASSEMBLY__ */
|
||||
#endif /* __ASSEMBLER__ */
|
||||
#endif /* _CSKY_PTRACE_H */
|
||||
|
||||
@ -45,8 +45,8 @@ static inline void csky_cmpxchg_fixup(struct pt_regs *regs)
|
||||
if (trap_no(regs) != VEC_TLBMODIFIED)
|
||||
return;
|
||||
|
||||
if (instruction_pointer(regs) == csky_cmpxchg_stw)
|
||||
instruction_pointer_set(regs, csky_cmpxchg_ldw);
|
||||
if (instruction_pointer(regs) == (unsigned long)&csky_cmpxchg_stw)
|
||||
instruction_pointer_set(regs, (unsigned long)&csky_cmpxchg_ldw);
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -103,7 +103,7 @@ static int qnap_mcu_input_probe(struct platform_device *pdev)
|
||||
|
||||
input = devm_input_allocate_device(dev);
|
||||
if (!input)
|
||||
return dev_err_probe(dev, -ENOMEM, "no memory for input device\n");
|
||||
return -ENOMEM;
|
||||
|
||||
idev->input = input;
|
||||
idev->dev = dev;
|
||||
|
||||
@ -923,8 +923,8 @@ static int cyttsp5_i2c_probe(struct i2c_client *client)
|
||||
|
||||
regmap = devm_regmap_init_i2c(client, &config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "regmap allocation failed: %ld\n",
|
||||
PTR_ERR(regmap));
|
||||
dev_err(&client->dev, "regmap allocation failed: %pe\n",
|
||||
regmap);
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
|
||||
@ -389,6 +389,10 @@ static int titsc_parse_dt(struct platform_device *pdev,
|
||||
dev_warn(&pdev->dev,
|
||||
"invalid co-ordinate readouts, resetting it to 5\n");
|
||||
ts_dev->coordinate_readouts = 5;
|
||||
} else if (ts_dev->coordinate_readouts > 6) {
|
||||
dev_warn(&pdev->dev,
|
||||
"co-ordinate readouts too large, limiting to 6\n");
|
||||
ts_dev->coordinate_readouts = 6;
|
||||
}
|
||||
|
||||
err = of_property_read_u32(node, "ti,charge-delay",
|
||||
|
||||
@ -747,8 +747,7 @@ static int zforce_probe(struct i2c_client *client)
|
||||
|
||||
input_dev = devm_input_allocate_device(&client->dev);
|
||||
if (!input_dev)
|
||||
return dev_err_probe(&client->dev, -ENOMEM,
|
||||
"could not allocate input device\n");
|
||||
return -ENOMEM;
|
||||
|
||||
ts->client = client;
|
||||
ts->input = input_dev;
|
||||
|
||||
@ -32,7 +32,7 @@ struct restart_block {
|
||||
u32 val;
|
||||
u32 flags;
|
||||
u32 bitset;
|
||||
u64 time;
|
||||
ktime_t time;
|
||||
u32 __user *uaddr2;
|
||||
} futex;
|
||||
/* For nanosleep */
|
||||
|
||||
@ -738,12 +738,11 @@ int futex_wait(u32 __user *uaddr, unsigned int flags, u32 val, ktime_t *abs_time
|
||||
static long futex_wait_restart(struct restart_block *restart)
|
||||
{
|
||||
u32 __user *uaddr = restart->futex.uaddr;
|
||||
ktime_t t, *tp = NULL;
|
||||
ktime_t *tp = NULL;
|
||||
|
||||
if (restart->futex.flags & FLAGS_HAS_TIMEOUT)
|
||||
tp = &restart->futex.time;
|
||||
|
||||
if (restart->futex.flags & FLAGS_HAS_TIMEOUT) {
|
||||
t = restart->futex.time;
|
||||
tp = &t;
|
||||
}
|
||||
restart->fn = do_no_restart_syscall;
|
||||
|
||||
return (long)futex_wait(uaddr, restart->futex.flags,
|
||||
|
||||
@ -63,7 +63,7 @@ if [ "${CC}" != "${HOSTCC}" ]; then
|
||||
# Clear VPATH and srcroot because the source files reside in the output
|
||||
# directory.
|
||||
# shellcheck disable=SC2016 # $(MAKE) and $(build) will be expanded by Make
|
||||
"${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC='"${CC}"' VPATH= srcroot=. $(build)='"$(realpath --relative-to=. "${destdir}")"/scripts
|
||||
"${MAKE}" run-command KBUILD_RUN_COMMAND='+$(MAKE) HOSTCC="'"${CC}"'" VPATH= srcroot=. $(build)='"$(realpath --relative-to=. "${destdir}")"/scripts
|
||||
|
||||
rm -f "${destdir}/scripts/Kbuild"
|
||||
fi
|
||||
|
||||
@ -210,6 +210,9 @@ static int process_tracepoints(bool mod, void *addr, const char *fname)
|
||||
}
|
||||
|
||||
if (!tracepoint_data_sec) {
|
||||
/* A module may reference only exported tracepoints */
|
||||
if (mod)
|
||||
return 0;
|
||||
fprintf(stderr, "no __tracepoint_strings in file: %s\n", fname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -12,3 +12,4 @@ futex_wait_uninitialized_heap
|
||||
futex_wait_wouldblock
|
||||
futex_waitv
|
||||
futex_numa
|
||||
robust_list
|
||||
|
||||
@ -22,7 +22,8 @@ TEST_GEN_PROGS := \
|
||||
futex_priv_hash \
|
||||
futex_numa_mpol \
|
||||
futex_waitv \
|
||||
futex_numa
|
||||
futex_numa \
|
||||
robust_list
|
||||
|
||||
TEST_PROGS := run.sh
|
||||
|
||||
|
||||
@ -131,11 +131,6 @@ static void test_futex(void *futex_ptr, int err_value)
|
||||
__test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA);
|
||||
}
|
||||
|
||||
static void test_futex_mpol(void *futex_ptr, int err_value)
|
||||
{
|
||||
__test_futex(futex_ptr, err_value, FUTEX2_SIZE_U32 | FUTEX_PRIVATE_FLAG | FUTEX2_NUMA | FUTEX2_MPOL);
|
||||
}
|
||||
|
||||
TEST(futex_numa_mpol)
|
||||
{
|
||||
struct futex32_numa *futex_numa;
|
||||
|
||||
@ -71,6 +71,8 @@ TEST(anon_page)
|
||||
/* Testing an anon page shared memory */
|
||||
shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
|
||||
if (shm_id < 0) {
|
||||
if (errno == ENOSYS)
|
||||
ksft_exit_skip("shmget syscall not supported\n");
|
||||
perror("shmget");
|
||||
exit(1);
|
||||
}
|
||||
@ -108,14 +110,14 @@ TEST(file_backed)
|
||||
/* Testing a file backed shared memory */
|
||||
fd = open(SHM_PATH, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
|
||||
if (fd < 0)
|
||||
ksft_exit_fail_msg("open");
|
||||
ksft_exit_fail_msg("open\n");
|
||||
|
||||
if (ftruncate(fd, sizeof(f_private)))
|
||||
ksft_exit_fail_msg("ftruncate");
|
||||
ksft_exit_fail_msg("ftruncate\n");
|
||||
|
||||
shm = mmap(NULL, sizeof(f_private), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
if (shm == MAP_FAILED)
|
||||
ksft_exit_fail_msg("mmap");
|
||||
ksft_exit_fail_msg("mmap\n");
|
||||
|
||||
memcpy(shm, &f_private, sizeof(f_private));
|
||||
|
||||
|
||||
@ -86,6 +86,8 @@ TEST(shared_waitv)
|
||||
int shm_id = shmget(IPC_PRIVATE, 4096, IPC_CREAT | 0666);
|
||||
|
||||
if (shm_id < 0) {
|
||||
if (errno == ENOSYS)
|
||||
ksft_exit_skip("shmget syscall not supported\n");
|
||||
perror("shmget");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
552
tools/testing/selftests/futex/functional/robust_list.c
Normal file
552
tools/testing/selftests/futex/functional/robust_list.c
Normal file
@ -0,0 +1,552 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* Copyright (C) 2025 Igalia S.L.
|
||||
*
|
||||
* Robust list test by André Almeida <andrealmeid@igalia.com>
|
||||
*
|
||||
* The robust list uAPI allows userspace to create "robust" locks, in the sense
|
||||
* that if the lock holder thread dies, the remaining threads that are waiting
|
||||
* for the lock won't block forever, waiting for a lock that will never be
|
||||
* released.
|
||||
*
|
||||
* This is achieve by userspace setting a list where a thread can enter all the
|
||||
* locks (futexes) that it is holding. The robust list is a linked list, and
|
||||
* userspace register the start of the list with the syscall set_robust_list().
|
||||
* If such thread eventually dies, the kernel will walk this list, waking up one
|
||||
* thread waiting for each futex and marking the futex word with the flag
|
||||
* FUTEX_OWNER_DIED.
|
||||
*
|
||||
* See also
|
||||
* man set_robust_list
|
||||
* Documententation/locking/robust-futex-ABI.rst
|
||||
* Documententation/locking/robust-futexes.rst
|
||||
*/
|
||||
|
||||
#define _GNU_SOURCE
|
||||
|
||||
#include "futextest.h"
|
||||
#include "../../kselftest_harness.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#define STACK_SIZE (1024 * 1024)
|
||||
|
||||
#define FUTEX_TIMEOUT 3
|
||||
|
||||
#define SLEEP_US 100
|
||||
|
||||
static pthread_barrier_t barrier, barrier2;
|
||||
|
||||
static int set_robust_list(struct robust_list_head *head, size_t len)
|
||||
{
|
||||
return syscall(SYS_set_robust_list, head, len);
|
||||
}
|
||||
|
||||
static int get_robust_list(int pid, struct robust_list_head **head, size_t *len_ptr)
|
||||
{
|
||||
return syscall(SYS_get_robust_list, pid, head, len_ptr);
|
||||
}
|
||||
|
||||
/*
|
||||
* Basic lock struct, contains just the futex word and the robust list element
|
||||
* Real implementations have also a *prev to easily walk in the list
|
||||
*/
|
||||
struct lock_struct {
|
||||
_Atomic(unsigned int) futex;
|
||||
struct robust_list list;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper function to spawn a child thread. Returns -1 on error, pid on success
|
||||
*/
|
||||
static int create_child(int (*fn)(void *arg), void *arg)
|
||||
{
|
||||
char *stack;
|
||||
pid_t pid;
|
||||
|
||||
stack = mmap(NULL, STACK_SIZE, PROT_READ | PROT_WRITE,
|
||||
MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
|
||||
if (stack == MAP_FAILED)
|
||||
return -1;
|
||||
|
||||
stack += STACK_SIZE;
|
||||
|
||||
pid = clone(fn, stack, CLONE_VM | SIGCHLD, arg);
|
||||
|
||||
if (pid == -1)
|
||||
return -1;
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper function to prepare and register a robust list
|
||||
*/
|
||||
static int set_list(struct robust_list_head *head)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = set_robust_list(head, sizeof(*head));
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
head->futex_offset = (size_t) offsetof(struct lock_struct, futex) -
|
||||
(size_t) offsetof(struct lock_struct, list);
|
||||
head->list.next = &head->list;
|
||||
head->list_op_pending = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* A basic (and incomplete) mutex lock function with robustness
|
||||
*/
|
||||
static int mutex_lock(struct lock_struct *lock, struct robust_list_head *head, bool error_inject)
|
||||
{
|
||||
_Atomic(unsigned int) *futex = &lock->futex;
|
||||
unsigned int zero = 0;
|
||||
pid_t tid = gettid();
|
||||
int ret = -1;
|
||||
|
||||
/*
|
||||
* Set list_op_pending before starting the lock, so the kernel can catch
|
||||
* the case where the thread died during the lock operation
|
||||
*/
|
||||
head->list_op_pending = &lock->list;
|
||||
|
||||
if (atomic_compare_exchange_strong(futex, &zero, tid)) {
|
||||
/*
|
||||
* We took the lock, insert it in the robust list
|
||||
*/
|
||||
struct robust_list *list = &head->list;
|
||||
|
||||
/* Error injection to test list_op_pending */
|
||||
if (error_inject)
|
||||
return 0;
|
||||
|
||||
while (list->next != &head->list)
|
||||
list = list->next;
|
||||
|
||||
list->next = &lock->list;
|
||||
lock->list.next = &head->list;
|
||||
|
||||
ret = 0;
|
||||
} else {
|
||||
/*
|
||||
* We didn't take the lock, wait until the owner wakes (or dies)
|
||||
*/
|
||||
struct timespec to;
|
||||
|
||||
to.tv_sec = FUTEX_TIMEOUT;
|
||||
to.tv_nsec = 0;
|
||||
|
||||
tid = atomic_load(futex);
|
||||
/* Kernel ignores futexes without the waiters flag */
|
||||
tid |= FUTEX_WAITERS;
|
||||
atomic_store(futex, tid);
|
||||
|
||||
ret = futex_wait((futex_t *) futex, tid, &to, 0);
|
||||
|
||||
/*
|
||||
* A real mutex_lock() implementation would loop here to finally
|
||||
* take the lock. We don't care about that, so we stop here.
|
||||
*/
|
||||
}
|
||||
|
||||
head->list_op_pending = NULL;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* This child thread will succeed taking the lock, and then will exit holding it
|
||||
*/
|
||||
static int child_fn_lock(void *arg)
|
||||
{
|
||||
struct lock_struct *lock = arg;
|
||||
struct robust_list_head head;
|
||||
int ret;
|
||||
|
||||
ret = set_list(&head);
|
||||
if (ret) {
|
||||
ksft_test_result_fail("set_robust_list error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = mutex_lock(lock, &head, false);
|
||||
if (ret) {
|
||||
ksft_test_result_fail("mutex_lock error\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&barrier);
|
||||
|
||||
/*
|
||||
* There's a race here: the parent thread needs to be inside
|
||||
* futex_wait() before the child thread dies, otherwise it will miss the
|
||||
* wakeup from handle_futex_death() that this child will emit. We wait a
|
||||
* little bit just to make sure that this happens.
|
||||
*/
|
||||
usleep(SLEEP_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Spawns a child thread that will set a robust list, take the lock, register it
|
||||
* in the robust list and die. The parent thread will wait on this futex, and
|
||||
* should be waken up when the child exits.
|
||||
*/
|
||||
TEST(test_robustness)
|
||||
{
|
||||
struct lock_struct lock = { .futex = 0 };
|
||||
_Atomic(unsigned int) *futex = &lock.futex;
|
||||
struct robust_list_head head;
|
||||
int ret, pid, wstatus;
|
||||
|
||||
ret = set_list(&head);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
/*
|
||||
* Lets use a barrier to ensure that the child thread takes the lock
|
||||
* before the parent
|
||||
*/
|
||||
ret = pthread_barrier_init(&barrier, NULL, 2);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
pid = create_child(&child_fn_lock, &lock);
|
||||
ASSERT_NE(pid, -1);
|
||||
|
||||
pthread_barrier_wait(&barrier);
|
||||
ret = mutex_lock(&lock, &head, false);
|
||||
|
||||
/*
|
||||
* futex_wait() should return 0 and the futex word should be marked with
|
||||
* FUTEX_OWNER_DIED
|
||||
*/
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ASSERT_TRUE(*futex & FUTEX_OWNER_DIED);
|
||||
|
||||
wait(&wstatus);
|
||||
pthread_barrier_destroy(&barrier);
|
||||
|
||||
/* Pass only if the child hasn't return error */
|
||||
if (!WEXITSTATUS(wstatus))
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* The only valid value for len is sizeof(*head)
|
||||
*/
|
||||
TEST(test_set_robust_list_invalid_size)
|
||||
{
|
||||
struct robust_list_head head;
|
||||
size_t head_size = sizeof(head);
|
||||
int ret;
|
||||
|
||||
ret = set_robust_list(&head, head_size);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = set_robust_list(&head, head_size * 2);
|
||||
ASSERT_EQ(ret, -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
|
||||
ret = set_robust_list(&head, head_size - 1);
|
||||
ASSERT_EQ(ret, -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
|
||||
ret = set_robust_list(&head, 0);
|
||||
ASSERT_EQ(ret, -1);
|
||||
ASSERT_EQ(errno, EINVAL);
|
||||
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test get_robust_list with pid = 0, getting the list of the running thread
|
||||
*/
|
||||
TEST(test_get_robust_list_self)
|
||||
{
|
||||
struct robust_list_head head, head2, *get_head;
|
||||
size_t head_size = sizeof(head), len_ptr;
|
||||
int ret;
|
||||
|
||||
ret = set_robust_list(&head, head_size);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = get_robust_list(0, &get_head, &len_ptr);
|
||||
ASSERT_EQ(ret, 0);
|
||||
ASSERT_EQ(get_head, &head);
|
||||
ASSERT_EQ(head_size, len_ptr);
|
||||
|
||||
ret = set_robust_list(&head2, head_size);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = get_robust_list(0, &get_head, &len_ptr);
|
||||
ASSERT_EQ(ret, 0);
|
||||
ASSERT_EQ(get_head, &head2);
|
||||
ASSERT_EQ(head_size, len_ptr);
|
||||
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
static int child_list(void *arg)
|
||||
{
|
||||
struct robust_list_head *head = arg;
|
||||
int ret;
|
||||
|
||||
ret = set_robust_list(head, sizeof(*head));
|
||||
if (ret) {
|
||||
ksft_test_result_fail("set_robust_list error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* After setting the list head, wait until the main thread can call
|
||||
* get_robust_list() for this thread before exiting.
|
||||
*/
|
||||
pthread_barrier_wait(&barrier);
|
||||
pthread_barrier_wait(&barrier2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test get_robust_list from another thread. We use two barriers here to ensure
|
||||
* that:
|
||||
* 1) the child thread set the list before we try to get it from the
|
||||
* parent
|
||||
* 2) the child thread still alive when we try to get the list from it
|
||||
*/
|
||||
TEST(test_get_robust_list_child)
|
||||
{
|
||||
struct robust_list_head head, *get_head;
|
||||
int ret, wstatus;
|
||||
size_t len_ptr;
|
||||
pid_t tid;
|
||||
|
||||
ret = pthread_barrier_init(&barrier, NULL, 2);
|
||||
ret = pthread_barrier_init(&barrier2, NULL, 2);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
tid = create_child(&child_list, &head);
|
||||
ASSERT_NE(tid, -1);
|
||||
|
||||
pthread_barrier_wait(&barrier);
|
||||
|
||||
ret = get_robust_list(tid, &get_head, &len_ptr);
|
||||
ASSERT_EQ(ret, 0);
|
||||
ASSERT_EQ(&head, get_head);
|
||||
|
||||
pthread_barrier_wait(&barrier2);
|
||||
|
||||
wait(&wstatus);
|
||||
pthread_barrier_destroy(&barrier);
|
||||
pthread_barrier_destroy(&barrier2);
|
||||
|
||||
/* Pass only if the child hasn't return error */
|
||||
if (!WEXITSTATUS(wstatus))
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
static int child_fn_lock_with_error(void *arg)
|
||||
{
|
||||
struct lock_struct *lock = arg;
|
||||
struct robust_list_head head;
|
||||
int ret;
|
||||
|
||||
ret = set_list(&head);
|
||||
if (ret) {
|
||||
ksft_test_result_fail("set_robust_list error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = mutex_lock(lock, &head, true);
|
||||
if (ret) {
|
||||
ksft_test_result_fail("mutex_lock error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&barrier);
|
||||
|
||||
/* See comment at child_fn_lock() */
|
||||
usleep(SLEEP_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Same as robustness test, but inject an error where the mutex_lock() exits
|
||||
* earlier, just after setting list_op_pending and taking the lock, to test the
|
||||
* list_op_pending mechanism
|
||||
*/
|
||||
TEST(test_set_list_op_pending)
|
||||
{
|
||||
struct lock_struct lock = { .futex = 0 };
|
||||
_Atomic(unsigned int) *futex = &lock.futex;
|
||||
struct robust_list_head head;
|
||||
int ret, wstatus;
|
||||
|
||||
ret = set_list(&head);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = pthread_barrier_init(&barrier, NULL, 2);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ret = create_child(&child_fn_lock_with_error, &lock);
|
||||
ASSERT_NE(ret, -1);
|
||||
|
||||
pthread_barrier_wait(&barrier);
|
||||
ret = mutex_lock(&lock, &head, false);
|
||||
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
ASSERT_TRUE(*futex & FUTEX_OWNER_DIED);
|
||||
|
||||
wait(&wstatus);
|
||||
pthread_barrier_destroy(&barrier);
|
||||
|
||||
/* Pass only if the child hasn't return error */
|
||||
if (!WEXITSTATUS(wstatus))
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
else
|
||||
ksft_test_result_fail("%s\n", __func__);
|
||||
}
|
||||
|
||||
#define CHILD_NR 10
|
||||
|
||||
static int child_lock_holder(void *arg)
|
||||
{
|
||||
struct lock_struct *locks = arg;
|
||||
struct robust_list_head head;
|
||||
int i;
|
||||
|
||||
set_list(&head);
|
||||
|
||||
for (i = 0; i < CHILD_NR; i++) {
|
||||
locks[i].futex = 0;
|
||||
mutex_lock(&locks[i], &head, false);
|
||||
}
|
||||
|
||||
pthread_barrier_wait(&barrier);
|
||||
pthread_barrier_wait(&barrier2);
|
||||
|
||||
/* See comment at child_fn_lock() */
|
||||
usleep(SLEEP_US);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int child_wait_lock(void *arg)
|
||||
{
|
||||
struct lock_struct *lock = arg;
|
||||
struct robust_list_head head;
|
||||
int ret;
|
||||
|
||||
pthread_barrier_wait(&barrier2);
|
||||
ret = mutex_lock(lock, &head, false);
|
||||
|
||||
if (ret) {
|
||||
ksft_test_result_fail("mutex_lock error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!(lock->futex & FUTEX_OWNER_DIED)) {
|
||||
ksft_test_result_fail("futex not marked with FUTEX_OWNER_DIED\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Test a robust list of more than one element. All the waiters should wake when
|
||||
* the holder dies
|
||||
*/
|
||||
TEST(test_robust_list_multiple_elements)
|
||||
{
|
||||
struct lock_struct locks[CHILD_NR];
|
||||
pid_t pids[CHILD_NR + 1];
|
||||
int i, ret, wstatus;
|
||||
|
||||
ret = pthread_barrier_init(&barrier, NULL, 2);
|
||||
ASSERT_EQ(ret, 0);
|
||||
ret = pthread_barrier_init(&barrier2, NULL, CHILD_NR + 1);
|
||||
ASSERT_EQ(ret, 0);
|
||||
|
||||
pids[0] = create_child(&child_lock_holder, &locks);
|
||||
|
||||
/* Wait until the locker thread takes the look */
|
||||
pthread_barrier_wait(&barrier);
|
||||
|
||||
for (i = 0; i < CHILD_NR; i++)
|
||||
pids[i+1] = create_child(&child_wait_lock, &locks[i]);
|
||||
|
||||
/* Wait for all children to return */
|
||||
ret = 0;
|
||||
|
||||
for (i = 0; i < CHILD_NR; i++) {
|
||||
waitpid(pids[i], &wstatus, 0);
|
||||
if (WEXITSTATUS(wstatus))
|
||||
ret = -1;
|
||||
}
|
||||
|
||||
pthread_barrier_destroy(&barrier);
|
||||
pthread_barrier_destroy(&barrier2);
|
||||
|
||||
/* Pass only if the child hasn't return error */
|
||||
if (!ret)
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
static int child_circular_list(void *arg)
|
||||
{
|
||||
static struct robust_list_head head;
|
||||
struct lock_struct a, b, c;
|
||||
int ret;
|
||||
|
||||
ret = set_list(&head);
|
||||
if (ret) {
|
||||
ksft_test_result_fail("set_list error\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
head.list.next = &a.list;
|
||||
|
||||
/*
|
||||
* The last element should point to head list, but we short circuit it
|
||||
*/
|
||||
a.list.next = &b.list;
|
||||
b.list.next = &c.list;
|
||||
c.list.next = &a.list;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Create a circular robust list. The kernel should be able to destroy the list
|
||||
* while processing it so it won't be trapped in an infinite loop while handling
|
||||
* a process exit
|
||||
*/
|
||||
TEST(test_circular_list)
|
||||
{
|
||||
int wstatus;
|
||||
|
||||
create_child(child_circular_list, NULL);
|
||||
|
||||
wait(&wstatus);
|
||||
|
||||
/* Pass only if the child hasn't return error */
|
||||
if (!WEXITSTATUS(wstatus))
|
||||
ksft_test_result_pass("%s\n", __func__);
|
||||
}
|
||||
|
||||
TEST_HARNESS_MAIN
|
||||
Loading…
x
Reference in New Issue
Block a user